fix compose include:, project name, volumes, dry-run, build tags and image references - #77
Merged
Merged
Conversation
- include: resolved recursively — short and long form, per-entry project_directory and env_file, cycle guard and depth cap, parent definitions winning; an included file's relative bind mounts and build context anchor to its own directory and its name: stays local (#69) - unknown service names error with "no such service" and exit 1 instead of reporting success on an empty selection (#70) - project name resolves as -p, COMPOSE_PROJECT_NAME (environment, then .env), top-level name:, then the directory, and config prints what it resolved (#71) - down --volumes removes the project's named volumes and leaves external ones alone; up and down now agree on the volume set they own (#72) - compose build tags <project>-<service>:latest, the same tag the runtime starts the service from, instead of shadowing the base image (#73) - --dry-run is declared once and honored by every mutating subcommand, returning before any manager, engine or directory is touched (#74) - images reads real SIZE and CREATED from each image's manifest and config, rendering N/A rather than a confident zero when unavailable (#75) - image references resolve literally before normalizing, so rmi removes the image that was named and can remove locally built ones (#76) Found while reviewing the above: service selection matched by substring, so stop web also stopped webhook, and project scoping by name prefix let one project reach another's containers; both are now label-based. Volume names that would escape the volumes directory are rejected, and compose config renders build:, environment and quoted scalars as valid YAML. BREAKING CHANGE: projects that set COMPOSE_PROJECT_NAME or a top-level name: resolve to that name now, and names are normalized to [a-z0-9_-], so existing containers, volumes and networks created under the previous name are no longer matched. compose build tags <project>-<service>:latest instead of the bare service name. MockerKit: ImageInfo.size and ImageInfo.created are optional, and ComposeEvent has a new volumeRemoved case.
This was referenced Aug 9, 2026
Closed
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the eight compose and image defects reported in #69-#76. Every report was
verified against the source first, and every fix was then reproduced end to end with a
binary built from this branch against the real
containerbackend.What changed
#69 —
include:was silently dropped.ComposeFile.parseonly readservices/networks/volumes, so a project whose services all arrive throughinclude:parsed to an empty model andupexited 0 having created nothing.Includes now resolve recursively: short and long form,
pathas a string or a list,per-entry
project_directoryandenv_file, a cycle guard plus a depth cap, andparent definitions overriding included ones. An included file's relative bind-mount
sources and build context anchor to that entry's project directory, and its
top-level
name:no longer leaks into the including project. A malformedinclude:now errors instead of parsing to nothing.
#70 — unknown service names exited 0.
up/create/build/pull/push/stop/start/rm/kill/restart/pause/unpause/exec/attach/logs/ps/top/portnow report
no such service: <name>and exit 1.downis unchanged — it takes noservice arguments, in mocker or upstream.
#71 —
COMPOSE_PROJECT_NAMEignored,configprintedname: default. The projectname now resolves as
-p→COMPOSE_PROJECT_NAMEin the environment → in.env→top-level
name:→ directory basename, normalized to Compose's character set, andconfigprints the resolved value.#72 —
down --volumesnever removed anything. The flag was parsed and never read.down -vnow removes the project's named volumes, reports them in the progress output,and leaves
external: truevolumes alone. An explicitname:on a volume is honoredfor both creation and removal.
#73 —
compose buildtagged images with the bare service name. It computed its own<service>:latestinstead ofComposeService.buildTag, so a Dockerfile withFROM caddy:latestresolved to mocker's own previous build. Builds now tag<project>-<service>:latest, matching the tag the runtime looks up.#74 —
--dry-runwas declared 35 times and read zero times. It is now declared onceon the shared compose options and honored by every mutating subcommand, which reports
what it would do and returns before touching any manager, engine or directory. It is
also accepted in Docker's position (
compose --dry-run up).#75 —
imagesshowedZero KB/in 0 secfor everything. SIZE and CREATED are nowread from each image's manifest and config. A genuinely unknown value renders as
N/Arather than a confident zero.
#76 —
rmideleted the wrong image. References were normalized before the storelookup, so
rmi caddy:latestdeleted the pulleddocker.io/library/caddy:latestwhileleaving the local build, and mocker-built images could not be removed at all. Reference
resolution now tries the literal reference first, then
<name>:latest, then thenormalized form, and deletes exactly the reference that matched.
Fixes found while reviewing this change
stop/start/rm/kill/pause/unpause/logs/restartselected containers bysubstring, so
stop webalso stoppedwebhook; and project scoping by name prefix letproject
appreachapp-prod's containers —restartstops and removes what itselects. Selection is now by the project/service labels compose has always written.
VolumeManagernow rejects names that would escape the volumes directory: a composefile can supply a volume name verbatim.
up's progress counter counted declared resources rather than work performed, so are-run printed
1/2with one line. It now matchesdown.compose configprinted Swift's own struct description forbuild:and a Swift tuplefor each environment entry, and did not quote values containing
#or:. Its outputnow parses back as YAML.
Upgrade notes
COMPOSE_PROJECT_NAMEor a top-levelname:— which mocker previously ignored — your project name changes to the value youactually set, and containers/volumes/networks created under the old name are no longer
matched. Bring the project down before upgrading, or remove the old resources by hand.
compose buildtags change from<service>:latestto<project>-<service>:latest.Old bare-tagged images stay on disk until removed;
uprebuilds under the new tag.ImageInfo.sizeandImageInfo.createdare now optional. The DockerEngine API surface is unchanged — unknown values map to
0at that boundary only.carried
com.mocker.compose.project/.servicesince the first release, so this is ano-op for them, but a container merely named like one (created by hand through the
containerCLI) is no longer picked up by compose subcommands. That is deliberate:by name alone, project
app+ serviceprod-webis indistinguishable from projectapp-prod+ serviceweb, anddown/restartremove what they select.Verification
swift buildclean,swift test406 tests / 32 suites green (345 before). Every issuewas also reproduced end to end against the real backend on this branch: include-only
project parsed, unknown service exits 1,
.envproject name applied,down -vremovinga live container's volume while leaving an external one,
compose buildproducingbuild-alpine:latestand the container starting from that same tag,build --dry-runleaving no image,
imagesshowing4.1 MB/3mo ago, andrmiremoving abare-tagged mocker build.
Not covered
README.zh-CN.md,docs/zh-CN/) still describe the old project-namebehavior and lack the
include:section.rmistill does not accept image IDs or digest prefixes (mocker images -q | xargs mocker rmi), which is a separate gap from the reference resolution fixed here.compose build --dry-runperforms a real build and leaves a tagged image #74:compose rm -vand
compose down --rmi.${COMPOSE_PROJECT_NAME}interpolationwhen it comes from
-por the file'sname:, since interpolation runs before the fileis parsed.
ComposeNetworkstill has noexternal:field, so networks do not get the ownershiphandling volumes just gained.
up/downonly ever touch<project>-<network>, so anexternal network is never removed — it simply cannot be joined yet.
ref #69, ref #70, ref #71, ref #72, ref #73, ref #74, ref #75, ref #76