ci(docker): also publish -shadow image variants#508
Conversation
Add a `variant` matrix dimension (default + shadow) to the docker publish workflow so every tag gets a Shadow-simulator-compatible twin (e.g. `latest`+`latest-shadow`, `unstable`+`unstable-shadow`). The shadow build args mirror the `shadow-docker-build` Makefile target (SHADOW=1, FEATURES=shadow-integration, --no-default-features, unlocked), so consumers no longer have to build the Shadow image by hand. Both arches are built for each variant and combined into per-tag and sha-<sha> multi-arch manifests, keeping the shadow set fully symmetric with the regular set.
🤖 Kimi Code ReviewThe PR correctly implements a matrix strategy to build Shadow-simulator-compatible images alongside standard ones. No critical bugs detected, but several points warrant attention: Verification Required:
Positive Observations:
Minor Suggestion:
Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt |
🤖 Codex Code ReviewNo findings. The change is limited to CI packaging, not consensus/state-transition code. In .github/workflows/docker_publish.yaml, the new Residual risk: I couldn’t execute the GitHub Actions workflow here, so the only unverified part is runtime behavior of the Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
Greptile SummaryThis PR extends the Docker publish workflow to build and publish Shadow-simulator-compatible image variants alongside the regular ones. Every tag now gets a
Confidence Score: 4/5Safe to merge; the workflow correctly builds and publishes both regular and shadow image variants with properly isolated caches and manifests. The matrix restructuring and cache-scope keying are correct. The only notable gap is that the manifest job hardcodes the suffix loop independently of the build matrix variant list, meaning a future third variant would get per-arch images built but no multi-arch manifest assembled without a manual edit to two separate places. .github/workflows/docker_publish.yaml — specifically the suffix loop in the publish-manifest step, which is decoupled from the build matrix variant list.
|
| Filename | Overview |
|---|---|
| .github/workflows/docker_publish.yaml | Adds a cross-product build matrix (arch × variant) to publish shadow-simulator-compatible image twins alongside regular images; manifest loop correctly combines per-arch layers but hardcodes the suffix list independently of the matrix. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
trigger([Push to main / workflow_dispatch]) --> matrix
subgraph matrix["build-image matrix (4 jobs)"]
A1["amd64 × default\ntag-amd64"]
A2["amd64 × shadow\ntag-shadow-amd64"]
B1["arm64 × default\ntag-arm64"]
B2["arm64 × shadow\ntag-shadow-arm64"]
end
matrix --> manifest["publish-manifest"]
subgraph manifest["publish-manifest"]
M1["suffix='':\ndocker buildx imagetools create\ntag, sha-SHORT\nfrom tag-amd64 + tag-arm64"]
M2["suffix='-shadow':\ndocker buildx imagetools create\ntag-shadow, sha-SHORT-shadow\nfrom tag-shadow-amd64 + tag-shadow-arm64"]
end
M1 --> R1[("ghcr.io/…:tag\nghcr.io/…:sha-SHORT")]
M2 --> R2[("ghcr.io/…:tag-shadow\nghcr.io/…:sha-SHORT-shadow")]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
trigger([Push to main / workflow_dispatch]) --> matrix
subgraph matrix["build-image matrix (4 jobs)"]
A1["amd64 × default\ntag-amd64"]
A2["amd64 × shadow\ntag-shadow-amd64"]
B1["arm64 × default\ntag-arm64"]
B2["arm64 × shadow\ntag-shadow-arm64"]
end
matrix --> manifest["publish-manifest"]
subgraph manifest["publish-manifest"]
M1["suffix='':\ndocker buildx imagetools create\ntag, sha-SHORT\nfrom tag-amd64 + tag-arm64"]
M2["suffix='-shadow':\ndocker buildx imagetools create\ntag-shadow, sha-SHORT-shadow\nfrom tag-shadow-amd64 + tag-shadow-arm64"]
end
M1 --> R1[("ghcr.io/…:tag\nghcr.io/…:sha-SHORT")]
M2 --> R2[("ghcr.io/…:tag-shadow\nghcr.io/…:sha-SHORT-shadow")]
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
.github/workflows/docker_publish.yaml:119-133
**Hardcoded suffix loop decoupled from build matrix**
The manifest job iterates `for suffix in "" "-shadow"` rather than deriving suffixes from the build matrix. If a third variant is added to the `variant:` list in `build-image`, the build will push per-arch images for it but the manifest step will silently skip creating the multi-arch manifest for that variant. The two lists need to be kept in sync manually.
Reviews (1): Last reviewed commit: "ci(docker): also publish -shadow image v..." | Re-trigger Greptile
| for suffix in "" "-shadow"; do | ||
| # First tag also gets an immutable sha-<sha> tag. | ||
| docker buildx imagetools create \ | ||
| -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t} \ | ||
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}-amd64 \ | ||
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}-arm64 | ||
| -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${FIRST_TAG}${suffix} \ | ||
| -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${SHORT}${suffix} \ | ||
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${FIRST_TAG}${suffix}-amd64 \ | ||
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${FIRST_TAG}${suffix}-arm64 | ||
|
|
||
| # Remaining tags. | ||
| for t in "${TAG_ARRAY[@]:1}"; do | ||
| docker buildx imagetools create \ | ||
| -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}${suffix} \ | ||
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}${suffix}-amd64 \ | ||
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}${suffix}-arm64 | ||
| done |
There was a problem hiding this comment.
Hardcoded suffix loop decoupled from build matrix
The manifest job iterates for suffix in "" "-shadow" rather than deriving suffixes from the build matrix. If a third variant is added to the variant: list in build-image, the build will push per-arch images for it but the manifest step will silently skip creating the multi-arch manifest for that variant. The two lists need to be kept in sync manually.
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/docker_publish.yaml
Line: 119-133
Comment:
**Hardcoded suffix loop decoupled from build matrix**
The manifest job iterates `for suffix in "" "-shadow"` rather than deriving suffixes from the build matrix. If a third variant is added to the `variant:` list in `build-image`, the build will push per-arch images for it but the manifest step will silently skip creating the multi-arch manifest for that variant. The two lists need to be kept in sync manually.
How can I resolve this? If you propose a fix, please make it concise.
🤖 Claude Code ReviewReview:
|
Address PR review feedback: note that the -shadow variant builds unlocked (so those images are non-reproducible, per Claude/Kimi), and flag that the manifest suffix loop must stay in sync with the variant matrix (per Greptile).
What
Publish a second, Shadow-simulator-compatible set of Docker images alongside the regular ones. Every tag now gets a
-shadowtwin:latestlatest-shadowunstableunstable-shadowsha-<sha>sha-<sha>-shadow<tag>-shadowHow
variantdimension (default+shadow) to the build matrix, cross-producted witharch(amd64/arm64) → 4 build jobs.shadowvariant passes the same build args as theshadow-docker-buildMakefile target:SHADOW=1,FEATURES=shadow-integration,--no-default-features, andLOCKED=(unlocked, since the Shadow build injects an uncommittedquinn-udppatch).arch-variantso the two variants don't clobber each other's cache.sha-<sha>multi-arch manifests for each set.Notes / assumptions
linux/amd64, we can drop the arm64 shadow build by trimming one matrix line to roughly halve the added CI cost.Draft for review of the arch-scope decision before merging.