fix(release): prevent electron-builder duplicate-draft race#105
Conversation
electron-builder's GitHub publisher creates a draft release per artifact when none exists yet (getOrCreateRelease lists releases and only calls createRelease when no draft matches the tag). When several artifacts (zip, dmg, their blockmaps, latest-mac.yml) finish near-simultaneously, multiple publishers clear that check before any draft exists and each creates its own draft. The assets then split across two incomplete drafts; a release published from a split draft 404s the macOS updater because latest-mac.yml points at a .zip attached to the other draft. Pre-create the draft before the build so every publisher attaches to it instead of racing to create one, and add a post-build verification step that fails the job unless exactly one release carries all five expected artifacts. Arch test guards both steps (presence + ordering) against silent removal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 23 minutes and 4 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe release workflow gains two new shell steps: one that pre-creates a GitHub Release draft before the macOS electron-builder publish job (eliminating the race where parallel publishers each attempt to create the draft), and one post-build step that asserts exactly one release exists for the tag with all five expected macOS assets. Two architecture tests and a documentation line are added to match. ChangesRelease Workflow Draft Pre-creation and Artifact Verification
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ScreenshotsUpdated for |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5231be2e1e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Line 104: Replace the direct interpolation of steps.version.outputs.value at
lines 104 and 134 in the release.yml workflow file with the shell-safe syntax
${GITHUB_REF_NAME#v}. This prevents shell metacharacter injection
vulnerabilities that could occur if a pushed tag contains special characters
like dollar signs or backticks. Update the --title flag and any other locations
using steps.version.outputs.value to use the GITHUB_REF_NAME variable stripped
of the 'v' prefix instead, which is evaluated safely within the shell
environment.
- Around line 136-161: Fix two issues in the release verification step. First,
add --slurp to the gh api command that counts releases (before the --paginate
flag) to safely handle pagination by collecting all pages before applying the jq
filter, preventing multi-line count values that break the arithmetic comparison.
Second, after collecting the assets list with gh release view, add a check that
verifies the exact count of assets equals 5 (the expected count: latest-mac.yml,
zip, zip.blockmap, dmg, dmg.blockmap). If the asset count is not exactly 5, log
an error showing both the asset count and the assets present, then exit with
code 1. This ensures no stale or unexpected assets slip through to production.
In `@tests/Arch/ReleaseWorkflowTest.php`:
- Around line 34-41: The current test in ReleaseWorkflowTest.php only verifies
that expected asset names are contained in the workflow output using multiple
toContain() assertions, but it does not test the failure case where unexpected
or stale assets are present. Add assertions that verify the workflow properly
rejects workflows with unexpected assets to ensure the exact-count verification
is properly guarded. This means adding test cases that confirm the release
workflow verification fails when there are assets beyond the expected five,
alongside the existing positive assertions that verify the expected asset names
appear.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5987a004-5d9b-419a-8ff8-253934d8bdcc
📒 Files selected for processing (3)
.github/CLAUDE.md.github/workflows/release.ymltests/Arch/ReleaseWorkflowTest.php
Address PR review findings on the release-workflow guard:
- Pagination: gh api --paginate applied --jq per page, so once releases
span >1 page the count became multi-line and the -ne test errored out,
silently bypassing the guard. Fetch with --paginate --slurp and run jq
once over the combined document.
- Template injection: stop interpolating ${{ steps.version.outputs.value }}
into run blocks (a tag like v1.2.3$(...) would execute before the shell
parses it). Derive version="${tag#v}" in-shell instead. The env: usage in
the build step is safe and stays.
- Exact-set check: verify the release carries exactly the five expected
assets, failing on unexpected stale extras as well as missing ones.
Arch tests extended to guard the slurp, exact-set, and in-shell-version
behaviors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Addressed all review findings in
Validated locally: actionlint clean, Core suite 1514 passing (incl. 3 arch tests), PHPStan 0 errors, and the verify logic simulated across happy-path / multi-page / missing / unexpected / split cases. |



Why
The v0.17.4 release build succeeded but produced a broken release: electron-builder created two draft releases for the tag and split the assets across them. One draft held
rfa-0.17.4-arm64.zip; the other heldlatest-mac.yml, the.dmg, and the blockmaps. Publishing either alone would have 404'd every existing user's macOS auto-updater, becauselatest-mac.ymlpoints at a.zipthat lived on the other draft.Root cause confirmed against the installed source (
electron-publish/out/gitHubPublisher.js,getOrCreateReleaseat lines 55–103): the publisher lists releases and only callscreateRelease()when no draft matches the tag. When multiple artifacts finish near-simultaneously, several publishers clear that check before any draft exists and each creates its own draft — a classic check-then-create (TOCTOU) race.What
if (release.draft) return release) instead of racing to create one. Idempotent — skips creation if a release for the tag already exists, so re-runs are safe.latest-mac.yml,…-arm64.zip,…-arm64.zip.blockmap,…-arm64.dmg,…-arm64.dmg.blockmap). A split or dropped upload can no longer reach a human as a publishable draft.tests/Arch/ReleaseWorkflowTest.php) guards both steps — presence and ordering (pre-create before build, verify after) — against silent removal..github/CLAUDE.md.Testing
composer test:lint— passcomposer test:types— 0 errorsactionlint+ YAML parse — cleanNote: this workflow change can only be exercised on the next real tag push (PR CI runs lint/types/tests, not the macOS release build). The v0.17.4 release itself was already manually repaired and published correctly.
🤖 Generated with Claude Code
Summary by CodeRabbit
Chores
Tests