CI concurrency group cancels other branches' runs #231
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
.forgejo/workflows/ci.yml:9-11:On a
pull_requesteventforgejo.refdoes not resolve to a per-PR value, so every open PR shares one concurrency group and each new run cancels whatever is in flight - including runs on unrelated branches.Measured on PR #224 (all times UTC, from
/api/v1/repos/robbertbos/waggle/actions/tasks):3ca9a4c(PR #224) startse2e,backend-test,backend-test-postgres, bothbuildjobs andpip-auditare cancelled4f75c3f4and0ba2f3e5(other branches) startThe cancellation window sits exactly inside the window where the other branches' runs are queued. Same-PR cancellation works correctly and is wanted: the run for
f052be07was cancelled at 20:48:10 when3ca9a4cwas pushed to the same PR.Effects:
failurewithHas been cancelledon up to 8 of 14 checks while nothing is actually broken, which blocks merging on a red record that says nothing about the code.e2eandbackend-test-postgresrun 3-5 minutes, so they are almost always the ones still in flight when someone else pushes. The fast checks pass and the expensive ones never finish - the reverse of what you want.Fix is to make the group unique per PR, falling back to the ref for branch and tag pushes:
Worth checking
.forgejo/workflows/*.yamlfor the same pattern in the other workflows while you are in there -test-buildandsecurity-scanwere cancelled in the same sweep, so they likely share the defect.Not fixed in #224: that PR is an accessibility change and a CI-config fix wants its own verification (push to two branches at once and confirm both runs survive).
Checked this against the Forgejo source and against the full task history. It is not a defect - closing.
forgejo.refis already per-PROn a
pull_requestevent Forgejo setsrun.Ref = pr.GetGitRefName(), i.e.refs/pull/<n>/head(services/actions/notifier_helper.go:115-116, v15.0.3 - the version this instance runs).services/actions/context.go:59hands that exact field to the context used to evaluateconcurrency.group. So the group already resolves toci-refs/pull/224/head, unique per PR.The measurement on #224 was misread
Pulling all 8026 entries from
/actions/tasksand reconstructing the window:0ba2f3e5is another branchhead_branch= #224 - the same PR4f75c3f4started inside the cancellation windowThe actual sequence: run 2030 (#224,
3ca9a4cb) is cancelled because a new push lands on the same PR around 20:48:56, whose jobs appear at 20:49:13 as run 2036 (0ba2f3e5). The cancellation timestamp precedes the new run's jobs because Forgejo callsCancelPreviousWithConcurrencyGroupbeforeInsertRun(services/actions/workflows.go:167-171). That is the same mechanism that cancelledf052be07at 20:48:10 - which the issue itself calls correct and wanted.Counter-test
If the groups collided,
cancel-in-progresswould kill every run that is in flight when another branch starts. Over all ci.yml runs whose durations are not polluted by the nightly cleanup sweep:The proposed fix is a no-op
ci-${{ forgejo.event.pull_request.number || forgejo.ref }}changes the group fromci-refs/pull/224/headtoci-224. Equally unique, same behaviour. Andtest-build.yaml/security-scan.yamlcarry noconcurrencyblock at all - they fall back to Forgejo's default<ref>_<workflow>_<event>__auto, which is also per-ref.One loose end
One cluster I could not fully explain: on 2026-07-25 between 11:50 and 12:30 (the Renovate merge train #183 to #186) runs on main and on several PRs are cancelled within seconds of each other's start, in a chain. That does look like what this issue describes. But it is the last such occurrence in the whole dataset - everything after it (07-26, 08-05) is cleanly same-branch, and the proposed fix would not address it either, since a push-to-main run is among the cancelled ones and that has no PR number. Not reproducible on the current version. Worth a new issue if it comes back.