feat: komodo-pin — move a deploy pin from the release that produced it - #57
Merged
Conversation
komodo-deploy.yml redeploys a stack, which is right for a self-builder on a floating :latest and does nothing for a stack whose compose interpolates a pinned version: redeploying without moving the pin re-runs the same image. Observed 2026-08-18 on cshuttle/nmon — the merge fired the deploy hook, the stack redeployed, and the app on screen did not change, because the deploy repo still named the previous release. For those stacks the deploy event is the PIN MOVING, so this writes the version it was handed into the deploy repo's pin file and stops. Whatever that repo already does on a push deploys it; no second deploy path to keep in step with the first. It is not a dependency bot and does not replace one. Renovate still owns "is there a newer version and may it land unattended" and will find the pin already current. What this removes is the wait: Renovate's schedule plus the ghcr tag list, which lags the packages API by minutes (measured at 3 and 8 on two consecutive releases the same night). Called from a release, the version is already known — nothing to discover, nothing to poll. A major opens a PR rather than committing, because a major means the deploy needs a human step — the same reason release-image makes the version a human decision. The script also refuses what it cannot verify: a non-semver version or current pin, a key matching zero or several lines, and any version older than the one pinned, since the release dispatch button can re-run an old version and must not roll production back. python3 + stdlib only (the ARC image has no gh, jq or PyYAML). The ten decisions above are unit-tested in selftest by extracting the embedded script and driving it against a stubbed API — it edits what production runs, in a repo the caller cannot see, and nothing downstream re-checks the result.
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.
komodo-deploy.ymlredeploys a stack. That is the right event for a self-builder running a floating:latest, and it does nothing for a stack whose compose interpolates a pinned version — redeploying without moving the pin re-runs the same image.Observed on cshuttle/nmon, 2026-08-18: the merge fired the deploy hook, the stack redeployed, and the app on screen did not change, because the deploy repo still named the previous release. Getting that release live took a hand-driven chain — wait for the ghcr tag list, run Renovate, run it again for the automerge — repeated for five releases in one evening.
For a pinned stack the deploy event is the pin moving. So this writes the version it was handed into the deploy repo's pin file and stops; whatever that repo already does on a push (a ResourceSync, a path-change route) is what deploys. No second deploy path to keep in step with the first.
Not a dependency bot
Renovate still owns "is there a newer version, and may it land unattended", and after this runs it finds the pin already current. What this removes is the wait: Renovate's schedule, plus the ghcr tag list, which lags the packages API by minutes — measured at 3 and 8 on two consecutive releases the same night. Called from a release, the version is already known; there is nothing to discover and nothing to poll.
What it refuses
It edits the line that decides which image production runs, in a repo the caller cannot see from its own CI, and nothing downstream re-checks the result. So it fails rather than guesses:
vMAJOR.MINOR.PATCHrelease-image.ymlmakes the version a human decisionTesting
python3+ stdlib only — the ARC runner image has nogh,jqor PyYAML. All ten decisions are unit-tested in selftest by extracting the embedded script from the YAML and driving it against a stubbed API, asserting on the requests it makes: the minor path commits to the branch with only that one line changed (rest of the file byte-for-byte), the major path writes to apin/*branch and opens a PR while committing nothing to main, and each refusal writes nothing at all.Local:
python3 tests/test_komodo_pin.py→ 10 passed.actionlintclean.Rollout
Needs a token with Contents R/W (plus PRs write) on the deploy repo only, as an Actions secret on the caller repo. Consumers land after this is released and tagged.
🤖 Generated with Claude Code