Skip to content

fix(ci): stop docker-merge from writing a broken GHCR manifest index - #943

Merged
hanzei merged 1 commit into
masterfrom
fix/docker-manifest-annotate-corrupts-index
Aug 28, 2026
Merged

fix(ci): stop docker-merge from writing a broken GHCR manifest index#943
hanzei merged 1 commit into
masterfrom
fix/docker-manifest-annotate-corrupts-index

Conversation

@hanzei

@hanzei hanzei commented Aug 28, 2026

Copy link
Copy Markdown
Owner

Summary

  • docker-merge in .github/workflows/docker.yml ran docker buildx imagetools create twice for GHCR PR images: once to build the multi-arch index from the per-platform digests, then again to attach an index:org.opencontainers.image.description annotation — but the second call sourced its sub-manifests from the tag the first call had just written, instead of from the original digests.
  • That self-referential copy left GHCR with an index whose child manifests (the linux/amd64/linux/arm64 images and their build attestations) never durably landed. docker buildx imagetools inspect still reported success (it only reads the index), but any real pull failed with manifest unknown — confirmed on ghcr.io/hanzei/jot:pr-942, where all four child manifest digests 404 by themselves.
  • Fix: fold the annotation into the single original imagetools create call instead of re-copying through the tag. release.yml (Docker Hub) doesn't have this second annotate step and is unaffected.

No app code changed — CI workflow only, no visual artifact.

Test plan

  • Validated the edited YAML parses correctly
  • Confirm the next docker.yml run on a PR produces a pr-<N> GHCR tag whose per-platform manifests are pullable (not just imagetools inspect-visible)

…t index

The "Annotate manifest with description" step re-ran imagetools create,
copying sub-manifests from the tag the previous step had just written
instead of from their original per-platform digests, purely to attach
an index-level description annotation. That self-referential copy left
GHCR with an index whose child manifests (platform images and build
attestations) never durably landed, so any pull of the tag failed with
"manifest unknown" even though imagetools inspect reported success.

Fold the annotation into the single, original imagetools create call
instead of re-copying through the tag.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f5ba42f2-0ae9-4d0d-bc9e-fa7c8c6e7674

📥 Commits

Reviewing files that changed from the base of the PR and between 7d01943 and bac1f39.

📒 Files selected for processing (1)
  • .github/workflows/docker.yml

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

The Docker workflow now passes DESCRIPTION to the manifest creation command. The command applies the OCI description annotation while it creates the manifest from digest references. The separate follow-up annotation command was removed.

Poem

I twitch my nose at manifests bright
The description joins the build tonight
Digest by digest, the images align
One creation command makes them shine
No second annotation hops in view
A tidy workflow, fresh as dew

Merge Risk: ⚪ Minimal · up to bac1f

This localized CI-only change folds the OCI annotation into the original manifest creation step to avoid producing broken GHCR indexes; no actionable merge-blocking risk remains after normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly explains the GHCR manifest-index failure and the CI workflow fix.
Title check ✅ Passed The title clearly identifies the CI fix for the broken GHCR manifest index.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0 files. (1 skipped: 1 unsupported.)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@hanzei
hanzei merged commit 038df62 into master Aug 28, 2026
21 of 22 checks passed
@hanzei
hanzei deleted the fix/docker-manifest-annotate-corrupts-index branch August 28, 2026 10:37
hanzei added a commit that referenced this pull request Aug 28, 2026
…944)

* fix(ci): stop docker-cleanup from deleting other PRs' live manifests

The cleanup job's "delete every untagged version" step assumed untagged
versions were always orphans of the closing PR's own image. In fact a
multi-arch manifest list never tags its per-platform children or
attestation manifests -- only the top-level index carries the tag -- so
every other open PR's pr-N tag depends on untagged versions the same
way. Closing any PR wiped the still-referenced children of every other
PR's image, which is why a freshly rebuilt pr-942 kept coming back
broken even after fixing the docker-merge job in #943.

Only delete an untagged version once no remaining tag's manifest index
references its digest, and only once it's old enough (10 minutes) that
a concurrent docker-merge run for another PR isn't mistaken for an
orphan before it tags its index.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(ci): fail closed on registry errors in docker-cleanup

The reference-collection loop and the GHCR token fetch swallowed curl
failures silently: an empty/failed response fed into jq produced no
output but exited 0, so a single transient registry error degraded
"is this manifest referenced" into a false negative -- causing the
deletion loop to delete a still-live tag's children, the exact failure
mode this fix exists to prevent.

Check the token is non-empty and abort on any manifest fetch failure.
The fetch loop moves from a pipe to a process substitution so `exit 1`
inside it terminates the step instead of just the subshell a pipe would
create.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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