Cancel a build orphaned by force-push when a runner claims it !272

merged merged by cmc on 2026-09-05 21:17 UTC · krz/gitbay:build-orphan-sha into main

Discussion

cmc

A build is queued against a commit sha. A force-push orphans the old head, and every build still queued for it fails at clone with "unable to read tree" — failures indistinguishable from real ones in build list. That is routine here: signed commits mean fast-forward-only merges, so any branch whose target advances gets rebased and force-pushed. This merge request's own branch did it twice.

A runner claiming a build now checks the sha is still reachable, cancels it if not, and claims the next one instead, so a runner gets a real build or nothing — never an impossible one. Claim time rather than push time catches deleted branches and pruned objects too, and yields cancelled rather than failure, which is the actual complaint.

The check fails open on anything ambiguous. Only two outcomes cancel: cat-file -e exiting exactly 1, which is the documented "object absent" contract, and the object existing while no ref of any kind contains it. A missing directory, a directory that is not a repository, a missing git binary, or any unexpected exit returns an error and the build runs. A build that fails at clone is a nuisance; a CI system that quietly refuses to run builds is not.

Two review findings shaped that. Peeling to ^{commit} made a missing object and a non-repository both exit 128, so a corrupted or mid-restore repository would have had its whole queue cancelled. And restricting for-each-ref --contains to refs/heads and refs/tags cancelled every fork merge request's build, since a fork head lives at refs/merge-requests/<n>/head — caught by the full suite, not by any selector either of us would have chosen.

runBuildCancel's status-resolution block is now shared rather than copied, so the rule that a cancelled build points at an earlier passing build of the same commit lives in one place.

Closes #152