Skip to content

ci: publish the desktop release atomically and stop the create race - #102

Merged
elkaix merged 1 commit into
mainfrom
fix/desktop-release-atomic-publish
Aug 17, 2026
Merged

ci: publish the desktop release atomically and stop the create race#102
elkaix merged 1 commit into
mainfrom
fix/desktop-release-atomic-publish

Conversation

@elkaix

@elkaix elkaix commented Aug 17, 2026

Copy link
Copy Markdown
Member

Related Issue

No issue. The first real desktop release (desktop-v0.1.3) exposed the problems described below; both were diagnosed from the run logs and from electron-builder's own source.

Problem

The two platform jobs race to create the release. electron-builder's getOrCreateRelease (packages/electron-publish/src/gitHubPublisher.ts) lists the releases, looks for one whose tag matches, and calls createRelease() when none does. That is a check-then-act with no lock. The macOS and Windows jobs start together, both see no release, and both POST /releases. One wins; the other gets 422 Validation Failed: Published releases must have a valid tag. electron-builder's doesErrorMeanAlreadyExists guard covers only asset upload conflicts, so the create path has no retry at all.

That is exactly what happened on desktop-v0.1.3: Windows created the release at 04:02:42Z, macOS failed one second later.

Publishing per platform leaves half a release. Because the winner publishes immediately, the failed macOS job left a live, published v0.1.3 containing the Windows installer, a stray .dmg.blockmap, and no .dmg at all. Anyone landing on it during that window got a release with no macOS build.

A re-run can silently ship nothing. For a non-draft release published more than two hours ago, getOrCreateRelease returns null, and doUpload then logs skipped publishing and returns normally — the job goes green having uploaded no assets. The macOS re-run only worked because it landed inside the two-hour window.

What changed

  • prepare creates the release once, before either platform job. It is created as a draft deliberately: a draft needs no valid git tag, so this step cannot hit the same 422, and getOrCreateRelease returns an existing draft before it consults either the release type or the two-hour rule. It is idempotent — an existing release, draft or published, is left untouched.
  • mac and windows now needs: prepare, so they find the draft and reuse it instead of creating anything. Both set EP_GH_IGNORE_TIME so a re-run cannot silently upload nothing.
  • publish runs only after both platforms succeed. It names each expected artifact — .dmg, -mac.zip, latest-mac.yml, -Setup.exe, latest.yml — and flips the draft only when all five are present. A count check would not do: the half-release that shipped had four assets.

A platform failure now leaves a draft that no one can download and that is fixed by re-running that one job, instead of a live release missing an operating system.

Verification

  • The workflow parses, and every run: block passes bash -n.
  • The asset gate was run against two fixtures: the real, complete v0.1.3 asset list (publishes) and the exact broken list that shipped (stays a draft, naming the three missing macOS artifacts). .dmg$ correctly rejects .dmg.blockmap.
  • EP_GH_IGNORE_TIME was read from electron-builder's source rather than from docs, and the spelling matches isEnvTrue(process.env.EP_GH_IGNORE_TIME).

Not verified end to end. Exercising this needs a real release, and cutting one only to test a workflow change is not worth it. The first genuine exercise is the next desktop tag.

Note for reviewers

The App token is now minted in four jobs per release rather than two.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works. — a workflow cannot be unit-tested here; the asset gate was instead exercised against real and broken fixtures, as described above.
  • Ran gen-changesets skill, or this PR needs no changeset. — CI-only change under .github/, nothing enters a package artifact.
  • Ran gen-docs skill, or this PR needs no doc update.

The two platform jobs each called electron-builder's getOrCreateRelease, which
lists the releases and creates one when no tag matches. That is a check-then-act
with no lock, so both decided to create: one won and the other failed with
'Published releases must have a valid tag'. A prepare job now creates the
release once, as a draft, which every platform job then finds and reuses.

Publishing moves to a final job that runs only after both platforms uploaded and
that names each expected artifact before flipping the draft. A failed platform
now leaves a draft nobody can download rather than a live release missing an
operating system.

Both platform jobs set EP_GH_IGNORE_TIME, since electron-builder otherwise
refuses to upload to a release published more than two hours ago by logging
'skipped publishing' and exiting zero.
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@elkaix, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 4 minutes

Limit details: You’ve used all 3 included reviews currently available under your plan.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

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, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5e9b36e9-f322-47a6-a44c-5fea88847413

📥 Commits

Reviewing files that changed from the base of the PR and between cba1341 and d8b5f27.

📒 Files selected for processing (1)
  • .github/workflows/desktop-release.yml

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Aug 17, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@pymodel/pythinker-code@d8b5f27
npx https://pkg.pr.new/@pymodel/pythinker-code@d8b5f27

commit: d8b5f27

@elkaix

elkaix commented Aug 17, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@elkaix
elkaix merged commit 13a0621 into main Aug 17, 2026
12 checks passed
@elkaix
elkaix deleted the fix/desktop-release-atomic-publish branch August 17, 2026 04:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant