Skip to content

fix(publisher-s3): upload RELEASES files after all other artifacts - #4419

Open
milanm wants to merge 2 commits into
electron:mainfrom
milanm:fix/publisher-s3-release-files-last
Open

milanm wants to merge 2 commits into
electron:mainfrom
milanm:fix/publisher-s3-release-files-last

Conversation

@milanm

@milanm milanm commented Sep 24, 2026 •

Copy link
Copy Markdown
  • I have read the contribution documentation for this project.
  • I agree to follow the code of conduct that this project follows, as appropriate.
  • The changes are appropriately documented (if applicable).
  • The changes have sufficient test coverage (if applicable).
  • The testsuite passes successfully on my local machine (if applicable).

Summarize your changes:

publisher-s3 uploads every artifact in a single Promise.all, so RELEASES and RELEASES.json can land before the .nupkg / .zip files they list. In practice they usually do, because maker-squirrel lists RELEASES first and it is by far the smallest file. A client that polls in that window sees a release whose packages are not there yet. If a package upload fails, the feed points at a file that never arrived until someone re-runs the publish.

For each platform and arch, this change uploads the other artifacts first, and the RELEASES files only after every one of them has succeeded. Different platforms and arches don't wait on each other, and the config is unchanged. Most of the diff in PublisherS3.ts is re-indentation from moving the upload body into a local uploadArtifact function. I also added one sentence about the ordering to the S3 publisher docs.

Three new specs:

  • RELEASES files are uploaded only after every other artifact of the same platform and arch has finished
  • a platform and arch's RELEASES files don't wait on another's uploads (darwin x64 + arm64)
  • no RELEASES file is uploaded when an artifact upload fails

Verified locally on Windows with Node 22: yarn test:fast (66 files, 660 passed), tsc -b packages, oxfmt --check and oxlint (no new warnings).

publisher-gcs has the same single Promise.all. Happy to make the same change there in this PR or in a follow-up, whichever you prefer.

Context: I noticed this while writing an Azure Blob Storage publisher on top of publisher-static, which uploads the release files last for the same reason.

publisher-s3 uploaded every artifact in one Promise.all, so RELEASES and
RELEASES.json could land before the .nupkg/.zip files they list. Clients that
polled in that window saw a release whose files were missing. If a package
upload failed, the feed was already pointing at a file that never arrived.

Upload the other artifacts first and the RELEASES files after all of them
have finished. Concurrency within each group is unchanged, and so is the
config.
@milanm
milanm requested a review from a team as a code owner September 24, 2026 19:18
@MarshallOfSound

Copy link
Copy Markdown
Member

@claude review

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Comment on lines 118 to 127
await Promise.all(
artifacts
.filter((artifact) => !artifact.isReleaseFile)
.map(uploadArtifact),
);
await Promise.all(
artifacts
.filter((artifact) => artifact.isReleaseFile)
.map(uploadArtifact),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 (optional) On a multi-platform release, a fast platform's manifest now waits on unrelated platforms' uploads to finish, delaying updates it didn't need to wait for. artifacts in publish() contains every platform/arch combined (release.ts calls publisher.publish() once per publisher with all makeResults), but the grouping at PublisherS3.ts:119-126 splits purely on isReleaseFile, not per platform/keyPrefix. So mac's RELEASES.json now blocks on a slow or failing Windows .nupkg upload even though they're unrelated. Before this change each upload resolved independently, so a fast platform's manifest could go live without waiting on a slower one. Fix: gate each release file's upload only on the non-release artifacts sharing its platform/arch (or keyPrefix), not on every artifact globally.

Why this was flagged

A release build produces artifacts for win32, darwin and linux in one make pass; release.ts:158-163 invokes publisher.publish() once with all of them in makeResults. PublisherS3.ts:119-122 uploads every non-release artifact across all platforms concurrently, then PublisherS3.ts:123-126 waits for that whole batch before uploading ANY RELEASES/RELEASES.json, including ones for platforms whose own artifacts already finished. On the base branch each artifact's upload resolved independently (single Promise.all, no cross-artifact wait), so a fast platform's manifest could publish without waiting on an unrelated slow/failing one. No existing check scopes the wait to matching platform/arch.

Verification: nit. The coupling is real and reachable. release.ts:158-163 calls publisher.publish({ makeResults }) exactly once with all platforms/arches combined, and PublisherS3.ts:46-59 folds every make result's artifacts into a single artifacts array. The new two-phase upload (HEAD PublisherS3.ts:118-121 uploads artifacts.filter(a => !a.isReleaseFile), then 123-126 uploads `artifacts.filter(a =>…

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scoped the wait per platform and arch in b1224ea. Artifacts are grouped by key prefix, platform and arch, each group uploads its RELEASES files after its own artifacts, and the groups run in parallel. Added a spec for a darwin x64 + arm64 publish where arm64's RELEASES.json goes out while the x64 zip is still uploading.

A RELEASES file lists the artifacts of its own platform and arch, so it only
needs to wait for those. Waiting for every artifact in the publish coupled
unrelated targets: in a darwin x64 + arm64 build, arm64's RELEASES.json waited
for the x64 zip, and a failed x64 upload held arm64 back as well.

Group the artifacts by key prefix, platform and arch. Each group uploads its
other artifacts first and its RELEASES files after them, and the groups run
in parallel.

This branch has not been deployed

No deployments
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.

2 participants