Skip to content

Tamp Docker

Scott Singleton edited this page May 11, 2026 · 4 revisions

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;

Verbs

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 pull

Each returns a CommandPlan. Common knobs (SetWorkingDirectory, EnvironmentVariables[k]=v) come from DockerSettingsBase.

Docker.Login

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 type

This 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

Docker.Logout(s => s.SetServer("registry.example.com"));
// or:  Docker.Logout()           // → docker logout (default registry)

Settings: Server.

Docker.Build (BuildKit by default, 0.3.0+)

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.

Docker.LegacyBuild (pre-BuildKit fallback)

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 flags

Settings (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

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

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

Docker.Pull(s => s
    .SetImage("alpine:3.20")
    .SetPlatform("linux/arm64"));

Settings: Image (required), AllTags, Quiet, DisableContentTrust, Platform.

Recipes

Build + tag + push (single-arch)

[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)),
    });

Multi-arch build (buildx setup is your responsibility)

Target BuildMultiArch => _ => _.Executes(() =>
    Docker.Build(s => s
        .SetContext(".")
        .AddTag(ImageTag)
        .SetPlatform("linux/amd64,linux/arm64")
        .SetOutputType("type=registry")));   // requires `docker buildx` and a builder

Cleanup with AssuredAfterFailure

Target 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.

Notes on V27

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:

  • --platform on pull requires Docker 24+
  • --all-tags on push requires Docker 23+
  • OutputType=type=registry (the buildx feature) requires buildx to 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.

See also

Clone this wiki locally