Skip to content

refactor(cli): the put commands build through one build_image() - #8

Merged
earakely-scale merged 3 commits into
mainfrom
edgararakelyan/build-image-lift
Sep 30, 2026
Merged

earakely-scale merged 3 commits into
mainfrom
edgararakelyan/build-image-lift

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

The six put commands ran nine copies of the same docker build. They now call one function, agent_env.utils.docker_build.build_image(), and library code can call it too. The bundle writers that build an env's or an agent's image from its folder come next, and they can't import the CLI.

command builds
a2a-agent put 1
env gateway put 1
env mcp-server put 1
env service-db put 3 (service-db, db-web, db-mcp)
env website put 2 (backend, frontend)
env website-browser put 1, the only one with a --build-arg

What changes

def build_image(dockerfile: Path, context: Path, tag: str, *, platform: str | None,
                build_args: Mapping[str, str] | None = None) -> None:
  • Build only. Each caller still puts the image with DockerImageArtifact.put, as before. The callers pass that different arguments, and a later change will rework its build-context half.
  • platform has no default, so a caller must choose one. None or "" builds for this host. The put commands pass their --platform value, whose default is still linux/amd64.
  • Stdlib only. Importing it pulls in neither agent_env.cli nor the providers; a test pins that.
  • DEFAULT_BUILD_PLATFORM moves to the new module. cli.utils.docker_build_platform_args is gone; nothing outside the CLI imported it.

What a user sees when a build fails. The error is now DockerBuildError, which the CLI renders like its other user-facing errors: one error and exit 1, with the traceback only under --verbose. That replaces five different per-command prefixes, and service-db's Aborted!.

The message now carries docker's whole output, not just its stderr, so the legacy builder's RUN output is no longer dropped. Output that isn't UTF-8 is replaced rather than crashing the decode. A missing docker binary is the same one line instead of a traceback.

$ agent-env env gateway put --id gw
Building gateway Docker image...
Error: docker build of env-gateway failed (exit 1):
...
Step 4/8 : COPY gateway/requirements.txt .
failed to get destination image "sha256:6b7a…": … does not provide the specified platform (linux/amd64)

The successful path and its progress lines are unchanged.

Not in this PR

  • agent-env up still bootstraps its infra envs by shelling out to the put commands. Moving those bodies into library code belongs with the bootstrap work.
  • The three builds that run on a remote sandbox (put_from_github, the env snapshot image, run_docker_container) are shell strings built on the VM, so they're left as they are.
  • Plugin surface: build_image isn't added to it, since its signature may still change.
  • Existing problems a straight lift keeps:
    • tags built from raw ids aren't valid for @local or uppercase ids; the bundle writer must tag through image_repository();
    • a failed later build leaves the earlier artifacts behind (website's backend; service-db's main image, and db-web after a db-mcp failure);
    • the fixed infra tags race under concurrent puts.

Testing

  • Unit tier (make unit-test): 5,628 passed, 13 skipped.
  • tst/unit/utils/docker_build_test.py: the argv for linux/amd64, linux/arm64, "" and None with a build arg; a failed build's message; a fake docker on PATH whose stdout, stderr and a non-UTF-8 byte all reach the error; a missing docker; import purity.
  • tst/unit/cli/build_platform_test.py: each of the six puts calls build_image with its Dockerfile, context, tag, --platform and build args, and a failed build ends in one Error: line with exit 1. With the builds succeeding, service-db's three builds and website's two are each checked, so all nine are covered. None of the six had a test of its build arguments before.
  • Nine patch sites (three in shared helpers) that patched cli.env.<module>.subprocess.run now patch build_image. The one whose build was meant to fail raises DockerBuildError, so it can't reach the real DockerImageArtifact.put.
  • Real Docker in CI: the slow tier builds the gateway, service-db and mcp-server images through these commands. The a2a-agent, website and website-browser puts have no real-Docker run in CI, so the unit tests above are their coverage.
  • By hand, in an isolated HOME:
    • with no docker on PATH, env gateway put and env service-db put each print one error line, exit 1, and write no store;
    • with Docker running, a build failure shows docker's output. On this arm64 Mac, main fails the same gateway build the same way, with the legacy builder building amd64;
    • a host-native build_image of a trivial Dockerfile builds an arm64 image, and a missing context raises DockerBuildError.

🤖 Generated with Claude Code

RetriggerConfidence Score: 5/5

The PR appears safe to merge; this review found no outstanding finding.

What we checked:

  • Package versions drift apart: Both editable package entries in uv.lock match their manifests. The publish job reads the protocol version from its manifest before pinning the framework build.

Summary

Six local put commands now use one shared Docker image builder, which library code can call without importing the CLI. Build failures use one error type that keeps Docker’s full output.

  • The shared builder handles platform and build-argument settings for all nine images.
  • The CLI prints build failures as one user-facing error, with a traceback only in verbose mode.
  • The existing artifact and environment registration steps remain in the commands.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Six put commands] --> B[build_image]
  B --> C[Local Docker build]
  C --> D[DockerImageArtifact.put]
  B -- Build fails --> E[DockerBuildError]
  E --> F[CLI error and exit 1]
Loading

Reviews (2) · Last reviewed commit: "Merge remote-tracking branch 'origin/mai..."

earakely-scale and others added 2 commits September 30, 2026 06:42
The six put commands ran nine copies of the same `docker build` (a2a-agent, gateway, mcp-server,
service-db x3, website x2, website-browser). They now call agent_env.utils.docker_build.build_image(),
which library code can import too: stdlib only, no click, no providers.

- build_image(dockerfile, context, tag, *, platform, build_args=None) builds only; each caller still
  puts the image. platform has no default, so a caller chooses; None or "" builds host-native.
- A failed build raises DockerBuildError with docker's whole output (stderr merged into stdout, bytes
  that don't decode replaced). The CLI renders it as one user-facing error and exits 1, replacing the
  five per-site prefixes and service-db's "Aborted!". A missing docker binary is the same one line
  instead of a traceback.
- DEFAULT_BUILD_PLATFORM moves to the new module; docker_build_platform_args goes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…age captures output

- The put test stopped each command at its first build, so service-db's db-web and db-mcp builds and
  website's frontend build were never checked. A test with the builds succeeding now asserts all of them;
  website's backend and frontend get separate folders so a swapped context shows.
- A fake docker on PATH checks that both streams, and a byte that isn't UTF-8, reach DockerBuildError.
- Drop two mock names the lift left unused.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@earakely-scale
earakely-scale requested review from a team and polakamtejas as code owners September 30, 2026 16:43
@earakely-scale
earakely-scale merged commit 3277d9c into main Sep 30, 2026
13 checks passed
@earakely-scale
earakely-scale deleted the edgararakelyan/build-image-lift branch September 30, 2026 17:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant