Skip to content

Ship one update zip per installed bundle folder name - #549

Merged
thesiti92 merged 2 commits into
mainfrom
feat/update-bundle-payloads
Sep 24, 2026
Merged

thesiti92 merged 2 commits into
mainfrom
feat/update-bundle-payloads

Conversation

@thesiti92

Copy link
Copy Markdown
Contributor

Why

Squirrel.Mac renames an install to the folder name inside the update zip whenever the installed app's executable and folder names match (SQRLUpdater useUpdateBundleName). After #543 the zip carries Whiteboard.app, so every Review.app becomes Whiteboard.app on 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.mjs lists the bundle folder names each channel still has in the field. Stable: Review (default) and Whiteboard. Preview: Whiteboard Preview (default) and Review Preview.
  • notarize-macos.sh copies the stapled app under each folder name and zips it: Review-darwin-arm64-<v>.zip and Whiteboard-darwin-arm64-<v>.zip. The DMG stays Whiteboard.app.
  • validate-release-artifacts.mjs checks each zip's top-level folder matches its bundle name and writes latest.json with a bundles map keyed by folder name, plus the default url/sha256hash as before.
  • Updater client: the darwin service adds ?bundle=<its own .app folder name> to the feed URL.
  • Workflows upload every zip and verify the feed answers with the right zip both with and without the parameter.
  • README: the feed section and the sentence about renamed installs.

Result: Review.app installs stay Review.app, fresh installs are Whiteboard.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 bundles and ?bundle=), which is backward compatible and should deploy before the next release.

Validation

  • node --test on the packaging scripts: 25 pass, including the manifest schema change.
  • Dry run of the zip loop and assertZipFolder on a fake bundle: both zips carry the right top-level folder, mismatches throw.
  • Fork typecheck: no errors under platform/update (the remaining fresh-worktree errors are the ungenerated protocol module).
  • oxlint, oxfmt, shellcheck, actionlint on the touched files.
  • Not yet exercised end to end: a preview release after merge will show both zips uploaded and the feed picking by ?bundle=.

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
Agent-Session: 78ffd160-974b-41b4-b7dd-d536de7c2833
Agent-Session: f1eb7ebd-a71b-44e2-8563-daff06cc220e
Agent-Session: aad32659-bbbe-45b2-b163-317afc1a6a83
@thesiti92
thesiti92 merged commit b5670b0 into main Sep 24, 2026
1 check passed
@thesiti92
thesiti92 deleted the feat/update-bundle-payloads branch September 24, 2026 04:31
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.
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