Route packaged macOS bundles and Light through the updater strategy. Use electron-updater 6.8.9 and wait for native signature acceptance before backend teardown. Keep checkout and Store ownership separate. Share Darwin feed paths between packaging, runtime and publication. Validate both native feeds, verify streamed artifact hashes, prevent same-tag artifact replacement, and conditionally update the channel pointer. Protect live feed references during canary retention. Use one notarization owner. Require publishing credentials and validate the stapled app. Keep Windows, Linux and Termux jobs unchanged. Verified with updater/feed unit and transport tests, release-helper tests, desktop typechecks, the desktop JS build, and workflow lint. No E2E, native macOS install, release dispatch or public publication was run.
3.1 KiB
macOS bundle updates
The packaged macOS app uses electron-updater. The bundled and Light stamps
name that owner. Development and bootstrap installs keep checkout updates.
Windows App Installer and Store ownership are unchanged.
Feed contract
apps/desktop/update-feed.cjs defines each channel directory and filename.
The builder writes that URL into app-update.yml. The client uses this file
unless updates.desktop_feed_base_url supplies an explicit bucket-base URL.
- Stable:
releases/darwin/stable/stable-mac.yml - Canary:
releases/darwin/canary/canary-mac.yml - Light: the same paths with
light/betweendarwin/and the channel. - Artifacts:
releases/tag/TAG/FILENAME, shared by download links and feeds.
The current workflow builds the bundled variant, on ARM64 and Intel runners. Light has separate client/feed routing but no release matrix leg in this change.
r2-release.mjs finalize requires one metadata file for each architecture,
named arm64-CHANNEL-mac.yml and x64-CHANNEL-mac.yml. It rejects wrong
versions, variants, architectures, hashes and inconsistent legacy path fields.
Each referenced ZIP/DMG is streamed back and checked against its SHA-512 and
size. Publication checks the live version, conditionally replaces its ETag,
and reads back the resulting feed. Same-tag macOS artifacts cannot be overwritten
with different bytes. Mutable feeds use Cache-Control: no-store.
Canary retention protects the artifacts and blockmaps referenced by live feeds.
An unreadable feed prevents pruning.
Client lifecycle
Checks never download automatically. Apply rechecks the release, downloads it, and waits for Squirrel.Mac to accept the signed app. Only then does Hermes stop its app-owned backends and request installation/relaunch. Unrelated quits do not trigger installation. Downloads and native-verification failures leave backends running. Concurrent checks cannot replace an apply operation's target. The existing checkout updater never mutates the sealed app bundle.
Release environment
The existing release-signing environment supplies:
CSC_LINKandCSC_KEY_PASSWORD: Developer ID Application signing identity.APPLE_API_KEY_P8,APPLE_API_KEY_ID,APPLE_API_ISSUER: notarization.CLOUDFLARE_R2_ACCOUNT_ID,CLOUDFLARE_R2_ACCESS_KEY_ID,CLOUDFLARE_R2_SECRET_ACCESS_KEY: bucket access secrets.CLOUDFLARE_R2_BUCKET,CLOUDFLARE_R2_PUBLIC_URL: repository/environment vars.
Publishing requires the Apple credentials. The existing after-sign hook owns notarization, so electron-builder's second notarization path is disabled. The publish gate verifies the signature, stapled ticket and Gatekeeper assessment. The Darwin publish job waits for both native builds and serializes channel writes.
Verification limits
Local tests exercise the strategy, native-event ordering, feed validation, conditional publication and retention with injected OS/network boundaries. The desktop TypeScript and JavaScript build run on the development host. These checks are not proof of a signed macOS install or an actual app replacement. No E2E work, release dispatch or public feed publication is included here.