Repository navigation
fix: keep the build green and single when the registry refuses the cache - #3
Merged
Merged
Conversation
The stub suite gains a cache export the registry refuses with 403 (the build must stay green and run once) and pins the retry warning's bytes with a non-1 exit code. A new suite runs the step with the real buildx against a registry that answers every request with 403, and CI runs it. Both fail on the current action.yml: a refused export fails the build, and the step then builds a second time without the cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015S8GLJdafAKhEFyWk5HdjB
With insecure set beside http, BuildKit tried TLS first and never reached the 403 handler, so the cases read a TLS error instead of a refusal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015S8GLJdafAKhEFyWk5HdjB
The layer cache export now carries ignore-error=true, so a registry that refuses it (a job whose credentials may not write the layer cache, or a cache repository not created yet) neither fails the build nor makes the step build a second time. BuildKit already reads a refused import as a cache miss, so the import is unchanged. extra-cache-to entries are passed as given. The retry after a build that fails with the cache on now warns "Build failed (exit N) with the layer cache on; building again without it." and names no cause it cannot know. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015S8GLJdafAKhEFyWk5HdjB
latchkey-ops-bot
marked this pull request as ready for review
November 8, 2026 22:19
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.
Part of latchkey-dev/latchkey#4403. Needed before latchkey-dev/latchkey#4410 reaches prod.
Held. Not to be merged or tagged until Daniel releases it. Customers run this action as
@v1, so nothing here reaches them until thev1tag moves, and merging publishes this README on GitHub and on the Marketplace listing at once. The tag move has to land no later than latchkey-dev/latchkey#4410 reaches prod. No tag or release is created or moved by this pull request.Problem
Once latchkey-dev/latchkey#4410 lands, the cache credentials vendor gives a Latchkey on GH job whose run is not proved to be the repository's own (a fork's job, or any
pull_request_targetorworkflow_runjob) the image mirrors without the layer cache: no pull and no push on the organization's layer cache repository.Today the action passes no
ignore-errorto its--cache-to. A registry that refuses the cache export fails the build, and the action then builds again without the cache. Such a job's build runs twice and bills its minutes twice, and the retry warning names a cause (a cache repository that may not exist yet) that is not the cause.Change
The layer cache export becomes
--cache-to type=registry,ref=<repo>:<tag>,mode=<cache-mode>,ignore-error=true. A refused or failed export no longer fails the build, so the step builds once.--cache-fromis unchanged: BuildKit already reads a cache import it is refused as a cache miss (below).extra-cache-toandextra-cache-fromentries are passed as given. They are the workflow's own, so the action does not change how their errors are handled.The retry stays for a build that fails with the cache on. Its warning is now, with N the build's real exit code:
::warning::Build failed (exit N) with the layer cache on; building again without it.That line is 84 bytes, sha256 prefix
77b407a73785c19d, as signed for latchkey-dev/latchkey#4403.README: the
--cache-toflag in "How it works" step 3 gains,ignore-error=true. No other wording changes.Why this mechanism
ignore-erroris BuildKit's own registry cache exporter attribute ("specify if error is ignored in case cache export fails", defaultfalse). The export still runs and still writes the cache wherever the credentials allow it. Only its failure stops failing the build.solver/llbsolver/bridge.go). The real-buildx suite below proves it against a registry that answers 403, so no import mechanism is added.What a refused export looks like after this change: BuildKit prints its own error on the "exporting cache to registry" step, the step is green, and the build ran once.
cache-configuredstaystrue. It reports that the cache flags were configured, not that the cache was written.Tests
test/cache-tag.test.sh(stub docker, the step read out ofaction.yml) now has 20 cases. The stub models BuildKit by failing a refused export unless its--cache-tocarriesignore-error=true, andrun_stepnow keeps the step's exit status. New and changed cases:--cache-tostill passed, no retry warning).exit 7, and it is the only warning.On the old
action.yml, 2 of the 20 fail, the defect itself: the refused export builds twice, and the old warning. Eight mutations of the fix each turn the suite red: droppingignore-error,ignore-error=false, moving it to--cache-from, dropping the export, removing the retry, the old warning text, a hard-coded exit code, and the warning without its closing period.test/registry-refusal.test.sh(new, CI job "Cache refused by the registry") runs realdocker buildxagainst a local registry that answers every request with 403, through adocker-containerbuilder:ignore-error.ignore-error=true.ignore-errorstripped builds twice, so case 4 catches its removal with the real tool.The tests landed first on this branch. On the tests-only head
cca3de1both jobs were red: the stub suite with 18 passed and 2 failed, the real-buildx suite with 4 passed and 1 failed (the step built twice, BuildKit reportingerror writing layer blob: denied). Run 42529396676. The run before it, onbf6fc4c, read a TLS error instead of a refusal, because the test builder's registry config setinsecurebesidehttp.cca3de1fixes that.CI
On the fix head
927c5df, both jobs are green: "Cache tag derivation" 20 passed, 0 failed, and "Cache refused by the registry" 5 passed, 0 failed. Run 42529562640.🤖 Generated with Claude Code
https://claude.ai/code/session_015S8GLJdafAKhEFyWk5HdjB