Ship one update zip per installed bundle folder name - #549
Merged
Merged
Conversation
Squirrel.Mac renames an install to the folder name inside the update zip whenever the installed app's executable and folder names match. With the zip now carrying Whiteboard.app, every Review.app turned into Whiteboard.app on its next update, the Dock tile became a question mark, and the post-install relaunch failed with -67068 because ShipIt re-verified the old path. Package the same signed, stapled app under each folder name still in the field (Review.app and Whiteboard.app for stable, Whiteboard Preview and Review Preview for preview) and zip each copy, so the rename is a no-op for every install. latest.json lists the payloads under bundles, keyed by folder name, with the channel's default first; the client sends its own folder name as ?bundle= and the update Worker answers with the matching zip. Stable defaults to Review for clients that predate the parameter; preview defaults to Whiteboard Preview, where most installs have already crossed the rename. release-channel.mjs owns the list, notarize-macos.sh produces the zips, validate-release-artifacts.mjs checks each zip's top-level folder and emits the manifest, and the workflows upload every zip and verify both answers from the feed. Agent-Session: 78ffd160-974b-41b4-b7dd-d536de7c2833 Agent-Session: f1eb7ebd-a71b-44e2-8563-daff06cc220e Agent-Session: aad32659-bbbe-45b2-b163-317afc1a6a83
sidkmenon
approved these changes
Sep 24, 2026
Agent-Session: 78ffd160-974b-41b4-b7dd-d536de7c2833 Agent-Session: f1eb7ebd-a71b-44e2-8563-daff06cc220e Agent-Session: aad32659-bbbe-45b2-b163-317afc1a6a83
thesiti92
added a commit
that referenced
this pull request
Sep 24, 2026
…#551) `assertZipFolder` from #549 piped the whole `unzip -Z1` listing through `execFileSync`, whose default buffer is 1 MB; the packaged app's listing is larger, so preview run 35956033252 failed its Validate release artifacts step with `spawnSync unzip ENOBUFS`. Reduce the listing to its distinct top-level names in the shell (`cut | sort -u`) before it reaches Node. Validation: dry run on a fake bundle with 40k files (5.8 MB listing) passes for the right folder and rejects the wrong one; script tests pass; `pnpm lint` and `oxfmt` clean.
thesiti92
added a commit
that referenced
this pull request
Sep 24, 2026
…he bundle name (#569) Squirrel can't run in a Linux container, so the end-to-end check for #549/#553 needs a Mac. This adds a dispatchable workflow that does it on a GitHub macOS runner instead of someone's machine. `update-smoke.sh <quality> <from-version> <Review|Whiteboard>` downloads that update zip, runs the app from a throwaway folder with isolated state, waits for Squirrel to stage the channel's current release, quits, and asserts the bundle kept its folder name, carries the new version, and ShipIt reported success. The workflow runs one job per zip, so a pre-rename (`Review…app`) and a renamed (`Whiteboard…app`) install are both covered, each on its own runner since Squirrel state is keyed by bundle id. App and installer logs are uploaded either way. Not runnable until it lands on main (`workflow_dispatch` needs the default branch). First run planned: `quality=preview`, `from_version=0.0.34-preview.20260924.59` against the current .66.
thesiti92
added a commit
that referenced
this pull request
Sep 24, 2026
## Why Squirrel renames an install to the update's `CFBundleExecutable`, not to the zip's folder name (`SQRLInstaller renamedTargetIfNeededWithTargetURL:` compares the installed folder name with the update's executable name). The Review zip from #549 carries `Review.app` whose executable is still `Whiteboard`, so a 0.0.33 `Review.app` updating to 0.0.34 was renamed to `Whiteboard.app`, the Dock tile broke, and ShipIt's relaunch failed at the old path. Seen on a real 0.0.33 → 0.0.34 update today. The runner smoke run passed because its starting copy (the .59 Review zip) already had mismatched executable and folder names, which turns the rename off before that comparison runs. It did not model a genuine pre-rename install. ## What changes - `notarize-macos.sh`: for each zip whose bundle name differs from the build's, rename `Contents/MacOS/<exe>`, set `CFBundleExecutable`, re-sign the outer bundle with the same identity and `app.plist` entitlements (nested helpers and frameworks keep their signatures), verify, then notarize and staple that copy on its own before zipping. One build, one extra notarization per extra zip. - `validate-release-artifacts.mjs`: `assertZipBundle` now also reads each zip's `Info.plist` and fails when the executable name differs from the bundle name. Against the published 0.0.34 Review zip it fails with the exact message; against a fixed copy it passes. - Comments and README say "executable name" instead of "folder name". ## Validation - Script tests: 25 pass. `pnpm lint`, `oxfmt`, `shellcheck`, `bash -n`. - Dry run on the real 0.0.34 Review zip: after the executable rename and an outer-bundle-only re-sign, `codesign --verify --deep --strict` passes and nested code retains its team identity. - Needs a preview release to prove the signed, notarized copy end to end from an install whose executable and folder names match (e.g. preview .56 or stable 0.0.33). Fixes the rename for every remaining `Review.app` install once 0.0.35 ships. Installs already renamed to `Whiteboard.app` by 0.0.34 are unaffected either way.
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 free
to 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.
Why
Squirrel.Mac renames an install to the folder name inside the update zip whenever the installed app's executable and folder names match (
SQRLUpdateruseUpdateBundleName). After #543 the zip carriesWhiteboard.app, so everyReview.appbecomesWhiteboard.appon its next update: the Dock tile turns into a question mark and the post-install relaunch fails (-67068, ShipIt re-verifying the old path). Seen on two preview installs today.What changes
One build, one signing and notarization pass, two zips:
release-channel.mjslists the bundle folder names each channel still has in the field. Stable:Review(default) andWhiteboard. Preview:Whiteboard Preview(default) andReview Preview.notarize-macos.shcopies the stapled app under each folder name and zips it:Review-darwin-arm64-<v>.zipandWhiteboard-darwin-arm64-<v>.zip. The DMG staysWhiteboard.app.validate-release-artifacts.mjschecks each zip's top-level folder matches its bundle name and writeslatest.jsonwith abundlesmap keyed by folder name, plus the defaulturl/sha256hashas before.?bundle=<its own .app folder name>to the feed URL.Result:
Review.appinstalls stayReview.app, fresh installs areWhiteboard.app, nobody's Dock breaks, and the relaunch after update works. Old clients without the parameter get the channel default.Depends on Fix-Fast/dev#1085 (Worker reads
bundlesand?bundle=), which is backward compatible and should deploy before the next release.Validation
node --teston the packaging scripts: 25 pass, including the manifest schema change.assertZipFolderon a fake bundle: both zips carry the right top-level folder, mismatches throw.platform/update(the remaining fresh-worktree errors are the ungenerated protocol module).oxlint,oxfmt,shellcheck,actionlinton the touched files.?bundle=.