When services..build.target is set in docker-compose.yaml, mocker compose up --build builds the entire Dockerfile (all stages) instead of stopping at the named target stage. The underlying container
build --target works correctly when invoked directly, so the bug is in mocker's compose → build translation: the target field is parsed from the compose file but not forwarded to container build.
Environment
- macOS 26.4.1 (build 25E253), Apple Silicon (arm64)
- container CLI 0.12.3 (commit f989901)
- mocker 0.2.0
- Builder image: ghcr.io/apple/container-builder-shim/builder:0.12.0
Reproduction:
Minimal Dockerfile:
FROM debian:bookworm AS base
RUN echo "base stage built"
FROM base AS optional
ARG REQUIRED_TOKEN
RUN test -n "$REQUIRED_TOKEN" # fails if arg is empty
RUN echo "optional stage built"
Minimal docker-compose.yaml:
services:
app:
build:
context: .
target: base
args:
- REQUIRED_TOKEN=${REQUIRED_TOKEN-}
Steps:
mocker compose up --build
Expected
Only the base stage is built, matching docker compose / BuildKit semantics. optional is never executed, so the missing REQUIRED_TOKEN is irrelevant.
Actual
The builder executes the optional stage and fails on RUN test -n "$REQUIRED_TOKEN":
=> ERROR [linux/arm64 optional 1/2] RUN test -n "$REQUIRED_TOKEN"
Error: unknown: "failed to solve: process \"/bin/sh -c test -n \\\"$REQUIRED_TOKEN\\\"\" did not complete successfully: exit code: 1"
Error: failed to solve: Build failed with exit code 1
using Apple container directly to build image works fine
container build --target base -f Dockerfile -t repro-app .
[+] Building 0.4s (6/6) FINISHED
=> [resolver] fetching image...docker.io/library/debian:bookworm 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 790B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> oci-layout://docker.io/library/debian:bookworm@sha256:ed4fcc40bb1162b6d2d32e7bec15044d13963779abbe63f67f1cd62b06220519 0.0s
=> => resolve docker.io/library/debian:bookworm@sha256:ed4fcc40bb1162b6d2d32e7bec15044d13963779abbe63f67f1cd62b06220519 0.0s
=> CACHED [linux/arm64 base 1/2] RUN echo "base stage built" 0.0s
=> exporting to oci image format 0.3s
=> => exporting layers 0.0s
=> => exporting manifest sha256:47fd4adb2c79acd1aa89a242c773e4fff086d2e9696923a11c7afc4034692c0b 0.0s
=> => exporting config sha256:885cb6ab5422d0366102c7254e2587ca546dfad7d0d134f113e5b1922fc67c94 0.0s
=> => exporting manifest list sha256:5a9d0db863deabf53198722c6a2f1728e5f29c997c189b44118959869e9f1315 0.0s
=> => sending tarball 0.3s
Successfully built repro-app:latest
When services..build.target is set in docker-compose.yaml, mocker compose up --build builds the entire Dockerfile (all stages) instead of stopping at the named target stage. The underlying container
build --target works correctly when invoked directly, so the bug is in mocker's compose → build translation: the target field is parsed from the compose file but not forwarded to container build.
Environment
Reproduction:
Minimal Dockerfile:
Minimal docker-compose.yaml:
Steps:
mocker compose up --buildExpected
Only the base stage is built, matching docker compose / BuildKit semantics. optional is never executed, so the missing REQUIRED_TOKEN is irrelevant.
Actual
The builder executes the optional stage and fails on RUN test -n "$REQUIRED_TOKEN":
using Apple
containerdirectly to build image works fine