-
Notifications
You must be signed in to change notification settings - Fork 0
Tamp Docker
Wrapper for the Docker 27.x CLI. The image-publish workflow is the focus today: login, build, tag, push, pull, logout. Compose / volume / network / swarm aren't in scope for the first cut — they're plausible additions when there's demand.
using Tamp.Docker.V27;Docker.Login(s => …) // docker login
Docker.Logout(s => …) // docker logout
Docker.Build(s => …) // docker build
Docker.Tag(s => …) // docker tag
Docker.Push(s => …) // docker push
Docker.Pull(s => …) // docker pullEach returns a CommandPlan. Common knobs (SetWorkingDirectory, EnvironmentVariables[k]=v) come from DockerSettingsBase.
The headline feature: when a Secret password is supplied, the wrapper uses --password-stdin so the password value never appears on the command line or in the OS process table.
[Secret("Container registry password", EnvironmentVariable = "REGISTRY_PASSWORD")]
Secret RegistryPassword;
Target RegistryLogin => _ => _.Executes(() =>
Docker.Login(s => s
.SetServer("registry.example.com")
.SetUsername("ci")
.SetPassword(RegistryPassword))); // Secret typeThis emits:
docker login --username ci --password-stdin registry.example.com
stdin: <password value>
The password value is automatically registered with the executor's redaction table — any subsequent log line that happens to echo it gets scrubbed.
Settings: Server, Username, Password (Secret).
Docker.Logout(s => s.SetServer("registry.example.com"));
// or: Docker.Logout() // → docker logout (default registry)Settings: Server.
Docker.Build routes to docker buildx build — BuildKit-backed builds with RUN --mount=type=cache, # syntax=docker/dockerfile:1.x frontends, named build contexts, and the rest of modern Dockerfile syntax. In 2026 reality the legacy builder is the exotic case; the wrapper default reflects that.
Docker.Build(s => s
.SetContext(".") // build context (positional last arg)
.SetDockerfile("Dockerfile.prod")
.AddTag("registry.example.com/myapp:latest")
.AddTag($"registry.example.com/myapp:{Git.Commit[..7]}")
.SetBuildArg("VERSION", "1.2.3")
.SetLabel("org.opencontainers.image.source", "https://github.com/tamp-build/tamp")
.AddPlatform("linux/amd64")
.AddPlatform("linux/arm64")
.AddCacheFrom("type=registry,ref=registry.example.com/myapp:cache")
.SetPush(true)
.SetPull(true));Settings (DockerBuildxBuildSettings): Context (defaults to .), Dockerfile, Tags, BuildArgs, Labels, Target, Platforms (collection), CacheFrom / CacheTo (collections), Outputs (collection), Push, Load, NoCache, Pull, Network, Progress, SecretsArgs, Metadata, Sbom, Provenance.
If your Dockerfile predates BuildKit-only syntax and you specifically need the legacy docker build (no buildx subcommand), use LegacyBuild:
Docker.LegacyBuild(s => s
.SetContext(".")
.AddTag("myapp:legacy")
.SetQuiet(true)
.SetOutputType("type=tar")); // single-value, legacy-only flagsSettings (DockerLegacyBuildSettings): same shape as the old 0.2.0 Docker.Build. Includes legacy-only flags (SetQuiet, SetPlatform (single value), SetOutputType (single value)) that BuildKit replaced with collection-shaped equivalents.
Migration from 0.2.0: compile errors point you at the right path. Most calls keep working as Docker.Build; SetQuiet / single-value SetPlatform / single-value SetOutputType need either LegacyBuild or the buildx-collection equivalents (AddPlatform, AddOutput).
Docker.Tag(s => s
.SetSource("myapp:latest")
.SetTarget("registry.example.com/myapp:1.2.3"));Both SetSource and SetTarget are required; missing either throws at plan-construction time.
Docker.Push(s => s
.SetImage("registry.example.com/myapp:1.2.3"));
// or push every tag of an image:
Docker.Push(s => s
.SetImage("registry.example.com/myapp")
.SetAllTags(true));Settings: Image (required), AllTags, Quiet, DisableContentTrust, Platform.
Docker.Pull(s => s
.SetImage("alpine:3.20")
.SetPlatform("linux/arm64"));Settings: Image (required), AllTags, Quiet, DisableContentTrust, Platform.
[GitRepository] readonly GitRepository Git;
string ImageBase => "registry.example.com/myapp";
string ImageTag => $"{ImageBase}:{Git.Commit[..7]}";
string ImageLatest => $"{ImageBase}:latest";
Target BuildImage => _ => _.Executes(() =>
Docker.Build(s => s
.SetContext(".")
.AddTag(ImageTag)
.AddTag(ImageLatest)
.SetBuildArg("Version", "1.2.3")
.SetPull(true)));
Target PushImage => _ => _
.DependsOn(nameof(BuildImage), nameof(RegistryLogin))
.Executes(() => new[]
{
Docker.Push(s => s.SetImage(ImageTag)),
Docker.Push(s => s.SetImage(ImageLatest)),
});Target BuildMultiArch => _ => _.Executes(() =>
Docker.Build(s => s
.SetContext(".")
.AddTag(ImageTag)
.SetPlatform("linux/amd64,linux/arm64")
.SetOutputType("type=registry"))); // requires `docker buildx` and a builderTarget Cleanup => _ => _
.AssuredAfterFailure()
.DependsOn(nameof(PushImage))
.Executes(() => Docker.Logout(s => s.SetServer("registry.example.com")));AssuredAfterFailure() ensures Logout runs even when an earlier target in the pipeline failed — important so credentials don't linger in the runner's Docker config.
The package version pin is for the CLI surface that ships with Docker Desktop / Docker Engine 27.x. If your CI runner has an older Docker (24.x, 25.x, 26.x), most flags in this wrapper still work but a small subset are 27-only. Worth knowing:
-
--platformonpullrequires Docker 24+ -
--all-tagsonpushrequires Docker 23+ -
OutputType=type=registry(the buildx feature) requiresbuildxto be installed and a builder configured
For older runtimes that need a different CLI surface, a hypothetical Tamp.Docker.V24 could be published as a sibling package (per ADR 0002) — open an issue if you need this.
- Module Catalog
-
Parameter & Secret Injection —
[Secret]and stdin-based password passing -
Failure Handling —
AssuredAfterFailurefor cleanup pattern
Start here
Modules
- Module Catalog (canonical list, 50+ satellites)
- .NET toolchain
- Containers
- JS toolchain
- Supply-chain security
Analyzers
Tooling
Execution
CI integration
Editor integration
Migration
Reference
Project