ci: never prune a manifest a tag still references - #57
Merged
Merged
Conversation
The inlined GHCR prune assumed "untagged" meant "build leftover". It doesn't: a multi-arch tag puts the tag on the INDEX, and the index's per-architecture child manifests are separate, untagged versions referenced only by digest. They are the image, not leftovers. With two children per build and KEEP=5, the window covered barely two builds, so the children of still-tagged indexes fell out of it and were deleted. On 2026-09-04 both children of the `1` index were gone: `ghcr.io/eetu/dice:1` still resolved to an index but was unpullable on every platform, and the invinite-tech host's podman-auto-update failed nightly. The container kept running only because the image was already in local storage — a server recreate would have left dice unable to start. continue-on-error meant none of this surfaced as a red build. The prune now collects every digest referenced by a tagged index (via buildx imagetools, which is already set up and logged in) and excludes those, then keeps the 5 newest of what genuinely is unreferenced. It also fails closed: if tagged multi-arch images exist but no referenced digests could be read, it refuses to delete on the strength of a blank answer. Verified against the live package with the window tightened to KEEP=0: the old logic selects 5 candidates, 2 of which are children of the live `main` tag and would break it; the new logic selects 3 and none are referenced.
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.
ghcr.io/eetu/dice:1is currently unpullable on every platform. The prune added in #56 deleted it.What happened
The step assumed
untaggedmeansbuild leftover:For a multi-arch image that isn't true. The tag goes on the index; the index's per-architecture child manifests are separate, untagged package versions referenced only by digest. They are the image. Delete them and the tag still resolves to an index, but every platform it points at is gone.
With 2 children per build and
KEEP: "5", the window covered barely two builds, so children of still-tagged indexes fell out and were deleted. Current state:continue-on-error: truemeant none of it ever showed up as a red build.Why it matters beyond a warning
The invinite-tech host has been failing
podman-auto-updatenightly since 00:10 today:The container is still up only because the image is already in local storage. A server recreate, an image prune, or any forced re-pull would have left dice unable to start — silently, since the app looks healthy right now.
The fix
Collect every digest referenced by a tagged index and exclude those, then keep the 5 newest of what genuinely is unreferenced. The registry read uses
docker buildx imagetools inspect, already set up and logged in earlier in the job, so no extra token handling.It also fails closed: if tagged multi-arch images exist but no referenced digests could be read, it refuses to prune rather than treating a blank answer as "nothing is referenced" — the same class of mistake that caused this.
Verified against the live package
Both logics delete nothing today (the damage is done, only 15 versions remain), so I re-ran with the window tightened to
KEEP=0to simulate it having slid:The keep-set resolved 20 referenced digests across 13 tags.
This does not yet fix
:1Merging this only rebuilds
:main— the semver tags (1,1.0,latest) aretype=semver, so they publish only on av*tag. Restoring:1needs a release. I'll follow up withjust release patch→ v1.0.9 once this is in, which republishes1.0.9/1.0/1/latestwith the fixed prune already live. The CHANGELOG entry here covers it.I'd also suggest a
workflow_dispatchtrigger as a separate change, so a future "republish this tag" doesn't need a version bump — happy to add it.