fix(ci): derive binary publish channels from trigger to kill 409 race #19

Merged
David merged 2 commits from fix/GOV-14-trigger-derived-publish-channels into main 2026-06-22 19:03:04 +02:00
Owner

Background

Each of the three binary build workflows (build-binary.yml Linux glibc, build-binary-static.yml static musl, build-binary-windows.yml) triggers on push to both branches: [main] and tags: [v*]. A release commit is simultaneously a push to main and the v* tag push, so both events fire for the same SHA. Each run resolved its publish tag set from git describe via oci-build/get-tags.nu --joined, which yields the overlapping latest and <sha> channels for both events. The two runs then race to DELETE-then-PUT the same immutable generic-package files at those channels, producing intermittent 409 package file already exists and red release CI. Tracked as GOV-14; the same failure mode was seen in CLAUDE-122 / GOV-13.

Fix

Resolve the publish channels from the workflow trigger (github.ref_type / github.ref_name) instead of git describe, so the two events publish disjoint channels:

  • tag push (github.ref_type == 'tag') publishes only the <version> channel.
  • push to main publishes latest plus the per-commit <sha> channel.

Neither event writes a channel the other writes, so there is no concurrent writer on any immutable generic-package file and the 409 cannot occur. latest still tracks the release because the release tag sits on the same SHA already pushed to main. This mirrors the trigger-derived approach proven in CI on the monkey repo (PR #51).

Deviation from monkey #51

monkey's git describe set was [<version>, latest] with no per-commit <sha> channel, so its main-push branch resolved to latest only. This repo's get-tags.nu additionally emits a <sha> channel, so the main-push branch here keeps latest plus <sha> (not latest only) to preserve <sha> availability for normal non-release main pushes. The change stays minimal and surgical: the three "Get artifact tags" steps now resolve tags inline from the trigger and no longer call oci-build/get-tags.nu; that script is left in place untouched (no Rust/Python touched, YAML-only).

Files changed

  • .forgejo/workflows/build-binary.yml
  • .forgejo/workflows/build-binary-static.yml
  • .forgejo/workflows/build-binary-windows.yml

Refs GOV-14, GOV-13, CLAUDE-122.

## Background Each of the three binary build workflows (`build-binary.yml` Linux glibc, `build-binary-static.yml` static musl, `build-binary-windows.yml`) triggers on `push` to both `branches: [main]` and `tags: [v*]`. A release commit is simultaneously a push to main and the `v*` tag push, so both events fire for the same SHA. Each run resolved its publish tag set from `git describe` via `oci-build/get-tags.nu --joined`, which yields the overlapping `latest` and `<sha>` channels for both events. The two runs then race to DELETE-then-PUT the same immutable generic-package files at those channels, producing intermittent `409 package file already exists` and red release CI. Tracked as GOV-14; the same failure mode was seen in CLAUDE-122 / GOV-13. ## Fix Resolve the publish channels from the workflow trigger (`github.ref_type` / `github.ref_name`) instead of `git describe`, so the two events publish disjoint channels: - tag push (`github.ref_type == 'tag'`) publishes only the `<version>` channel. - push to main publishes `latest` plus the per-commit `<sha>` channel. Neither event writes a channel the other writes, so there is no concurrent writer on any immutable generic-package file and the 409 cannot occur. `latest` still tracks the release because the release tag sits on the same SHA already pushed to main. This mirrors the trigger-derived approach proven in CI on the monkey repo (PR #51). ## Deviation from monkey #51 monkey's `git describe` set was `[<version>, latest]` with no per-commit `<sha>` channel, so its main-push branch resolved to `latest` only. This repo's `get-tags.nu` additionally emits a `<sha>` channel, so the main-push branch here keeps `latest` plus `<sha>` (not `latest` only) to preserve `<sha>` availability for normal non-release main pushes. The change stays minimal and surgical: the three "Get artifact tags" steps now resolve tags inline from the trigger and no longer call `oci-build/get-tags.nu`; that script is left in place untouched (no Rust/Python touched, YAML-only). ## Files changed - `.forgejo/workflows/build-binary.yml` - `.forgejo/workflows/build-binary-static.yml` - `.forgejo/workflows/build-binary-windows.yml` Refs GOV-14, GOV-13, CLAUDE-122.
fix(ci): derive binary publish channels from trigger to kill 409 race
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 29s
639cc5fa16
A release commit fires both the push-to-main and the v* tag-push events for each of the three binary build workflows (Linux glibc, static musl, Windows). Both events previously resolved their publish tag set from git describe (via oci-build/get-tags.nu), so both runs computed the overlapping `latest` and `<sha>` channels and raced to DELETE-then-PUT the same immutable generic-package files. That intermittently produced `409 package file already exists` and red release CI (CLAUDE-122 / GOV-13).

Resolve the publish channels from the workflow trigger instead, so the two events publish disjoint channels: a tag push (github.ref_type == 'tag') publishes only the `<version>` channel; a push to main publishes `latest` plus the per-commit `<sha>` channel. Neither event writes a channel the other writes, so there is no concurrent writer on any immutable file. `latest` still tracks the release because the release tag sits on the same SHA already pushed to main.

Deviation from the monkey PR #51 reference: this repo's get-tags.nu also emits a per-commit `<sha>` channel that monkey's git-describe set did not, so the main-push branch keeps `latest` plus `<sha>` (rather than `latest` only) to preserve `<sha>` availability for non-release main pushes. The three files now resolve tags inline and no longer call oci-build/get-tags.nu; the script is left in place untouched.

#GOV-14

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merge branch 'main' into fix/GOV-14-trigger-derived-publish-channels
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 3m56s
Create release / Create release from merged PR (pull_request) Has been skipped
8a1c47a336
David merged commit 0270e23f9d into main 2026-06-22 19:03:04 +02:00
David deleted branch fix/GOV-14-trigger-derived-publish-channels 2026-06-22 19:03:04 +02:00
Commenting is not possible because the repository is archived.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
pandoras-box/youtrack-mcp!19
No description provided.