Repository navigation
fix(gitops): deliver from codefly's ghcr.io image and restart a squash-merged promotion branch - #1142
Merged
antoinetoussaint-byte merged 1 commit intoOct 24, 2026
Conversation
…h-merged promotion branch Two defects in `codefly deploy gitops publish`: - Every delivery Job (and every cell file's delivery carrier) pinned docker.io/curlimages/curl. It now runs codefly's own base companion, ghcr.io/codefly-dev/codefly pinned by the digest of its 0.0.4 multi-arch index, which carries every command the delivery script calls. Nothing in a delivery is pulled from Docker Hub. - A reviewed publish continued an existing promotion branch even when the base had moved past it (its pull request squash-merged, the branch kept), so the new commit forked from a stale base and conflicted add/add on every file. It now continues a branch only when the branch contains the base; otherwise it starts from the base and replaces the branch with a push leased on the stale tip, recorded in the plan as replacesBranchRevision (part of the plan ID) and printed by `plan`. A stale branch carrying a change outside the module path is still refused, never overwritten. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
What broke
pkg/gitops/delivery_jobs.gopinneddocker.io/curlimages/curl@sha256:d94d07…as the delivery carrier, so every module's delivery Job — and every cell file's declared delivery carrier — pulled from Docker Hub (anonymous pulls rate-limited, and a cell's nodes are not promised a route to it).clonePromotionRepositorycontinued an existingorigin/codefly/promote-<module>-<env>even when the base had moved past it. After a promotion PR is squash-merged and its branch kept, the next publish forked from the stale base, so every file conflicted add/add againstmain— seen on obin-ai/platform-obin feat: serve typed code-unit discovery #251/Explicit runtime formula must outrank generic source fallback #252.What changed
deliveryImageis nowghcr.io/codefly-dev/codefly@sha256:8fcaaea34622eb1743a5b755c7c6540a0be245cb37c8b2188732f45c8207a833, the multi-arch index of codefly's base companion tag0.0.4. That is the newest published tag; the package has no other tags apart from its cosign.sig. The comment says it is codefly-owned, on ghcr.io, and never Docker Hub. Because the cell file declares the same image (cellDeliverysplits it), the cell's carrier repository is nowghcr.io/codefly-dev/codefly.gitIsAncestor(base, branch)). Otherwise the publish starts from the base and replaces the branch withgit push --force-with-lease=refs/heads/<branch>:<old tip>. The replaced tip is recorded asPublishPlan.ReplacesBranchRevision(replacesBranchRevision,omitempty). It is part ofpublishPlanID, so a branch that moves after review refuses the publish.printPublishPlanwarns with the tip being replaced. Before anything is replaced,refuseUnrelatedPromotionChangesstill runs against the stale tip, so a branch carrying a change outside the module path is refused rather than overwritten. A branch that already contains the base keeps the existing retry/continue behaviour (same signed commit, PR and receipt).refuseUnrelatedPromotionChangesnow takes the revisions to hold as a variadic list and skips empty ones. This keepspreparePublishunder the gocyclo limit and avoids a second loop.Behaviour change to note: a PR-path publish whose branch is already merged (contained in the base) and whose render equals the base now reports
promotion has no changes. Before, it re-offered the already-merged commit, which GitHub would refuse as a PR with no commits.TestRollbackRefusesUnresolvedConsumedContractUnlessAllowedrelied on that: it rolled back to the treemainalready delivered. Its fixture now merges a second promotion before rolling back to the first, so the rollback has a tree to re-promote. Its assertions are unchanged, and the adjusted test also passes against the old code.Classification
Fix, at the owner. The delivery image and the promotion-branch logic both live in the CLI's GitOps publish path, so nothing here works around another repo.
Reuse
ghcr.io/codefly-dev/codefly, already published and signed), and the existing machinery for git leases and ancestry (gitIsAncestor), unrelated-change refusal, plan-ID binding and snapshot lineage.Tests
delivery_jobs_test.go: the Job's image must start withghcr.io/codefly-dev/and carry@sha256:.delivery_carrier_contract_test.goTestTheCellDeclaresCodeflysOwnDeliveryImage: the cell's carrier image repository isghcr.io/codefly-dev/codefly, its digest is a sha256, and repository@digest equals the Job's image.publish_test.goTestPublishRestartsAPromotionBranchItsSquashMergeLeftBehind: squash-merge with the branch kept. The plan'sReplacesBranchRevisionequals the old tip. After publish,mainis an ancestor of the branch, the branch's diff againstmaintouches only the module path,git merge-tree --write-tree main <branch>succeeds, and snapshot lineage holds. With the fix disabled it fails on both the plan assertion and themerge-base --is-ancestor maincheck.publish_test.goTestPublishRefusesToReplaceAStaleBranchCarryingUnrelatedChanges:mainmoved and the stale branch carriesunrelated.txt. The plan is refused withunrelated changeand the remote branch stays at its tip.TestPublishRetriesPRAndReceiptForExistingSignedBranchCommit,TestOpenOrUpdatePullRequest*,TestPublishPreservesSnapshotLineageAfterSquashAndBranchDeletion,TestPublishRejectsUnrelatedExistingPromotionChanges.go test ./pkg/gitops/... ./cmd/deploy/... -count=1(all ok) andgolangci-lint run ./...v2.14.0 (0 issues).Image proof:
docker buildx imagetools inspect ghcr.io/codefly-dev/codefly:0.0.4gives indexsha256:8fcaaea3…a833(linux/amd64 and linux/arm64). I ran the exactdeliveryScriptin that image, pinned by digest, as--user 65532:65532 --read-only --cap-drop ALL --security-opt no-new-privilegeswith read-only documents and token mounts:sh,curl,date,basename,cat,tail,sed,printf(a shell builtin) andsleepare all present.Docs
docs/module-manifests.md: the delivery Job section names the ghcr.io image and the cell re-render consequence. The local-promotion section gains the hosted counterpart of the rule: continue only a branch that contains the base, otherwise replace it under a lease, and refuse rather than overwrite unrelated changes.docs/commands.md: the publish description states the same rule.Rollout consequence
Changing the carrier image changes every cell file, because the cell declares the delivery carrier's image for admission. Each environment's cell must be re-rendered, and that cell file must be published with or before the first delivery Job that runs the new image. Otherwise admission refuses the new Job's pod.
Not verified
file://remote, not against GitHub.🤖 Generated with Claude Code