feat(imageauto): pull-request writeback for protected branches - #89
Merged
Conversation
…e open threads Cleans up drift in open-threads.md: the 2026-06-14 and 2026-07-17 [OPEN]/[WAITING] markers were superseded by later entries but still read as live (roady #34, the .pem deletion, tokenFile stripping, PAT decommission). Adds an authoritative current-state section at the top. Records the nox investigation: excluding go.sum was CORRECT (the findings were ~99% versions not in the build), the real bugs were in nox itself, and all three are now fixed on nox main via #248. Retracts the claim that rollops had a live GO-2026-5932 — it is scoped to x/crypto/openpgp, which rollops does not import. Claude-Session: https://claude.ai/code/session_01QaHY7x5xKyZWP3RVQ4AgpH
Session log, status.md, decisions.md and open-threads.md. Headline: the task 'fix the 28 repos excluding go.sum' was based on a wrong note in this memory and would have injected ~5,263 false positives. The exclusions were correct; the bug was in nox. Pulling it surfaced that every dependency finding ever produced was medium with an empty summary, so a critical dependency CVE could never trip the high/critical gate. Fixed in Nox-HQ/nox#248, released as v1.10.0, shared workflow pinned. Canary before the pin bump: agent-go 56 gate-failing (42 critical). Also retracts the earlier claim that rollops has a live GO-2026-5932. Claude-Session: https://claude.ai/code/session_01QaHY7x5xKyZWP3RVQ4AgpH
Image automation deployed by committing the digest bump directly to the tracked branch. On a protected branch — one requiring pull requests or status checks — that push is rejected (GH006), the config goes =error in the reconcile summary, and the target silently stops updating. There was no way to run rollops against a protected main at all. Add `imagePolicy.writeback: pull-request` (default stays `push`, so existing configs are unchanged). In PR mode rollopsd never writes the tracked branch: it commits the bump on a deterministic branch (rollops/image/<config-name>), force-pushes it, and opens — or refreshes — a PR into the tracked branch, enabling GitHub auto-merge so the change lands the moment required checks pass. The load-bearing semantic: PR mode returns ref="" from Process, i.e. it does NOT deploy this cycle. The bump lives only on the PR, so the cluster never leads Git; the deploy happens through the ordinary reconcile once the PR merges and the tracked branch advances to carry it. That is what keeps a protected branch and the running target consistent. Details: - Idempotent: a duplicate-head 422 is resolved to the existing PR and reused, so re-running each poll cycle neither errors nor spawns PRs. The head branch is force-created from the tracked branch each time, so it tracks the current base. - Auto-merge is best-effort: a repo with auto-merge disabled (or a token lacking permission) still gets the PR opened and waits for a human — writeback is unblocked either way, never left in an error. - The git layer refuses to run the PR-commit path on the tracked branch itself. - GitHub only, for now; ownerRepoFromURL handles https/ssh/git remotes. Tested: PR API against a stub server (create, reuse-existing, auto-merge enabled/disabled, hard failure surfaces); an end-to-end imageauto PR-mode run on a real local git repo + stub API asserting no deploy, tracked branch untouched, and the bump pushed to the PR branch; push mode proven unchanged. Motivated by a real incident: rollopsd could not deploy klarlabs.de (and mnemos, senat-os) because their mains are protected — every image bump had been silently failing to push. Claude-Session: https://claude.ai/code/session_012qFD1UhQT9tiKkMsPAskK7
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.
Problem
Image automation deploys by committing the digest bump directly to the tracked branch. On a protected branch (PRs / status checks required) that push is rejected:
The failure is silent: the config shows
=errorin the reconcile summary and the target just stops updating. There was no way to run rollops against a protectedmainat all.This is not hypothetical — rollopsd could not deploy
klarlabs.de(normnemos,senat-os) for exactly this reason; every bump had been failing to push.Fix
Add
imagePolicy.writeback: pull-request(default stayspush— existing configs unchanged):In PR mode rollopsd never writes the tracked branch. It commits the bump on
rollops/image/<config-name>, force-pushes it, and opens (or refreshes) a PR into the tracked branch, enabling GitHub auto-merge so it lands when required checks pass.The load-bearing semantic
PR mode returns
ref=""fromProcess— it does not deploy this cycle. The bump lives only on the PR, so the cluster never leads Git. The deploy happens through the ordinary reconcile once the PR merges and the branch advances. That keeps a protected branch and the running target consistent.Details
422resolves to the existing PR and reuses it, so polling neither errors nor spawns PRs.ownerRepoFromURLhandles https/ssh/git remotes.Tests
PR API against a stub server (create, reuse-existing, auto-merge enabled/disabled, hard-failure surfaces); an end-to-end imageauto PR-mode run on a real local git repo + stub API asserting no deploy, tracked branch untouched, bump pushed to the PR branch; and push mode proven unchanged. Full module:
build+test ./...+golangci-lintall green.Docs
docs/image-automation.mdgains a "Writeback to a protected branch" section;docs/keel-migration.mdgets a callout (keel's direct push has no protected-branch equivalent — this is it).https://claude.ai/code/session_012qFD1UhQT9tiKkMsPAskK7