Repository navigation
fix(release): let two prunes of the same build tags both succeed - #594
Conversation
Two post-green runs can name the same build tags for deletion. When the rival's deletion lands after this run's delete push has read origin's refs, the server refuses the deletion of a ref that is already gone, git push exits non-zero, and the job failed with its build tag minted and latest not moved. A refused delete push is now judged by the prune's postcondition: origin's build tags are read back, and the prune is done when none of the tags it named is left. A refusal that leaves a named tag on origin is thrown as before, in git's words. A push git accepts is trusted without a read-back. The fixture gains origin-side hooks, which play a rival landing inside a push, after it read origin's refs and before origin writes any: the gap a push plan cannot reach. One test has the rival take every named tag there and expects the prune to succeed; another has it take one while origin declines the other and expects the refusal.
File size check0 over a hard cap (fails), 6 warning(s).
Split the file, wrap the line, shorten or exempt the comment, or list the path in 5 managed file(s) skipped; repo-platform owns them. |
Member shape check3 finding(s).
Add the blank lines, make the comment one line directly above the item, comment every field or none, or list the path in 4 generated file(s) skipped. |
There was a problem hiding this comment.
🟢 Approval recommended
There are no active review issues blocking the concurrent tag-pruning change.
No open issues 🎉
What changed in this PR
This PR lets concurrent release runs successfully prune the same build tags, allowing the release pipeline to continue updating latest when a rival has already deleted every targeted tag.
Changes:
- Re-read origin after a refused deletion push; succeed only when all targeted tags are gone, otherwise preserve the push error.
- Add isolated origin-hook fixtures and regression tests for complete and partial rival deletions.
- Clarify comments describing push outcomes and tags deleted by rival runs.
| File | Description |
|---|---|
test/scripts/release-pipeline-fixture.ts |
Adds isolated origin hooks to simulate changes during a push. |
test/scripts/release-pipeline-build.test.ts |
Tests successful concurrent pruning and failure when a targeted tag remains. |
.github/scripts/release-pipeline/tags.ts |
Checks remote refs after refused prune pushes and clarifies result comments. |
.github/scripts/release-pipeline/git.ts |
Updates the push helper's documentation to describe completed deletions. |
Review effort: Balanced
…onment Every git the suites run, the fixture's own calls and the pipeline under test, inherits the process environment. With GIT_DIR or GIT_CONFIG in it (git exports GIT_DIR to a hook), a fixture command such as the origin's config write landed in the file or repository those variables name: the repository the tests were launched from. The fixture now takes every GIT_* variable out of the environment where it already puts its shim on PATH, points global and system config at /dev/null, and puts the caller's variables back when the file's tests end. Identity needs nothing new: each clone carries the fixture's in its own config, which an exported author or committer would outrank. One test runs the fixture with a decoy GIT_DIR and GIT_CONFIG in the environment. Both decoys stay byte-identical, origin holds its own config, the pipeline's tag lands on origin, and the caller's variables are back once the fixture ended.
Before
After
A rival run took the same two tags after this run's delete push read origin's refs.
Not covered: runs whose lists differ. If the server rejects the whole push, a tag only one run named stays and that job fails.
How
latest.Proof
Technical details
pre-receivehook deletes the tags, which is after the push read origin's refs and before origin locks them.core.hooksPath.GIT_*variable out of the environment and points global and system config at/dev/null, then puts the caller's variables back.GIT_DIRandGIT_CONFIGin the environment before the fixture seeds, both decoys stay byte-identical and origin holds its own config.updatehook declines the other, so one rejection names a tag that is gone and one a tag that stays.release-pipeline.tswords and this change leaves alone.pruneddoc comment now says a rival may have done the deleting.REPO=<tree> CAPTURE_OUT=<file> bun test ./capture.test.ts, the script below, once on a copy oforigin/mainand once on this branch./tmp/fixture, and trailing spaces are trimmed..github/scripts/release-pipeline/{tags,git}.ts,test/scripts/release-pipeline-{build.test,fixture,fixture.test}.ts.