Skip to content

ci: run acceptance in the merge queue, serialise trunk - #43

Merged
klabast merged 1 commit into
mainfrom
ci/trunk-based-delivery
Aug 28, 2026
Merged

ci: run acceptance in the merge queue, serialise trunk#43
klabast merged 1 commit into
mainfrom
ci/trunk-based-delivery

Conversation

@klabast

@klabast klabast commented Aug 28, 2026

Copy link
Copy Markdown
Owner

Makes "merge all the PRs at once" safe by construction instead of by hand-sequencing them. ADR: docs/adr/0001-trunk-based-delivery-pipeline.md.

what was wrong

Acceptance only ran after merge. image / acceptance / promote were gated on push to main, so a PR never built a candidate and never ran E2E — you can see it on any recent PR: Build candidate image — skipping, Acceptance / E2E — skipping. The rate-limit change in #41 was green on its PR and would have broken trunk: the suite runs three device passes against one server from one address and accumulates more credential failures than the new per-IP limit allowed. I caught it by reading the feature files, not because CI told me.

Nothing tested the combination. Every PR was verified against main as it was when the branch was cut, never as it would be. #40 and #41 touched the same tree and were independent only by luck — I trial-merged them locally before merging, which is the merge queue's job.

Promote was an unguarded race. No concurrency group, so two merges minutes apart produce two overlapping trunk pipelines, both ending in docker buildx imagetools create --tag :latest. Last writer wins, nothing enforces order, so :latest can land on the older commit with every job green — silent, no failure signal. Two runs did overlap while merging #41 and #42; the order happened to hold.

what changes

event stages
pull_request commit stage only — the PR head isn't what lands
merge_group full pipeline — trunk + the queued change. the gate
push (main) full pipeline + promote

Plus a concurrency group that serialises trunk, cancels only superseded PR runs, and per-job timeout-minutes so a hung job can't sit on a runner.

Acceptance now runs twice per change, once in the queue and once on trunk. That's deliberate — squash-merging makes a new sha, and promoting an artifact built from a sha that was never tested would give up the single-artifact property the pipeline exists to protect.

merge order matters

This PR must land before the merge queue is turned on. Enabling the queue first would deadlock it: the queue would run merge_group against a workflow that has no merge_group trigger, so no check would ever report. I'll enable the ruleset right after this merges, with the admin role as a bypass actor so a stuck queue can never lock you out of your own repo.

test-mpd-image is deliberately not a required check — it's path-filtered, and a required check that doesn't run on every merge group hangs the queue.

🤖 Generated with Claude Code

Three gaps, all of which showed up merging #40/#41/#42.

Acceptance only ran after merge — image/acceptance/promote were gated on
push to main, so a PR never built a candidate and never ran E2E. The
rate-limit change in #41 was green on its PR and would have broken trunk:
the suite runs three device passes from one address and trips the new
per-IP credential limit. Caught by reading the feature files, not by CI.

Nothing tested the combination. Each PR was verified against main as it
was when the branch was cut, never against main as it would be.

Promote was an unguarded race. No concurrency group, so two merges
minutes apart give two overlapping trunk pipelines both retagging
:latest, last writer wins. :latest can end up on the older commit with
every job green. Two runs did overlap; the order held by luck.

Now: pull_request runs the commit stage only (the PR head isn't what
lands). merge_group runs the whole pipeline against trunk + the queued
change — that's the gate. push to main runs it again and promotes,
because squash-merging makes a new sha and the released artifact must be
one that was actually tested.

Trunk and queue runs never cancel; superseded PR runs do.

Merge queue itself is enabled after this lands — turning it on first
would deadlock, since the queue would run merge_group against a workflow
that has no merge_group trigger.

ADR in docs/adr/0001.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@klabast
klabast merged commit 670e707 into main Aug 28, 2026
5 checks passed
@klabast
klabast deleted the ci/trunk-based-delivery branch August 28, 2026 08:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant