Uh oh!
There was an error while loading. Please reload this page.
ci: make release creation idempotent across the mac and windows jobs - #37
Merged
Conversation
Both workflows trigger on the same v* tag and both need the Release to exist before uploading. Both used check-then-create, which is a TOCTOU race between two jobs that start at the same moment: both view, both miss, both create, and one gets a 422. The two then failed differently, which is what made it confusing to diagnose. mac.yml runs under `set -euo pipefail` with the failing create on the right of a `||`, so the job died before uploading. windows.yml uses pwsh, which does not stop on a native command's non-zero exit, so the failed create was swallowed and the job went green. Same race, red mac, green windows. Both now attempt creation and treat "it exists now" as success, whichever platform won; a create that fails with no release behind it still fails the job loudly. This was previously self-healing via `gh release upload --clobber`, but --clobber is deliberately gone (#5), so a re-run now needs manual asset deletion — which is what made this worth fixing rather than retrying past. windows.yml also gets $ErrorActionPreference = 'Stop' and $PSNativeCommandUseErrorActionPreference = $true, so a failing gh command fails the step instead of being ignored. That makes the trailing $LASTEXITCODE check on `gh release upload` unreachable, so it's removed rather than left as config that reads as load-bearing and isn't. Note the issue's preferred option — a separate release-creation job that both builds `needs:` — isn't reachable here: mac.yml and windows.yml are separate workflows and `needs:` doesn't cross workflow boundaries. Idempotent creation is the option that works with the current structure. Closes#19
z33b0t
commented
Jul 29, 2026
ContributorAuthor
@z33b0t ready to merge. All 8 checks green. Two things to weigh before merging, both in the PR body:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Both workflows fire on the same
v*tag and both used check-then-create, so both could miss, both create, and one gets a 422.The issue's preferred option isn't available
mac.ymlandwindows.ymlare separate workflows, andneeds:doesn't cross workflow boundaries — there's no job in one that the other can depend on. Making creation genuinely idempotent (the issue's first option) is what works with the current structure. A separate release workflow the builds poll for would be a bigger change and re-introduces waiting; happy to go there if you'd prefer it, but it seemed like the wrong trade for this.Verified against a stubbed
ghRan the exact logic with a stub modelling the race — first
viewmisses, the other job creates in the gap,createthen returns 422:And all three outcomes of the new logic independently:
::error::, exit 1, no uploadWindows now fails loudly
Added
$ErrorActionPreference = 'Stop'and$PSNativeCommandUseErrorActionPreference = $true, per the issue's last paragraph. Knock-on: the trailingif ($LASTEXITCODE -ne 0)aftergh release uploadis now unreachable, so I removed it rather than leave config that reads as load-bearing and isn't (the exact failure mode #9 is about).Verification gap
The real proof is a tag push publishing a release, which I'm obviously not doing. YAML parses, the bash block passes
bash -n, and the logic is tested as above — but the pwsh side is not executed anywhere: no pwsh on this machine, and the Windows job only runs its publish step on av*tag. The PowerShell is reviewed, not run. Worth your eye on that block specifically before the next release.Closes#19