The five pages moved from docs/ into website/docs/developer-guide were never added to sidebars.ts, so the site did not link them, and they kept repo-relative links (../tests/..., ../scripts/..., ../website/docs/...) that only resolved from the old docs/ location. Register them under a Packaging & Releases category and point the repo-file links at GitHub; package-management.md now links the moved completion note in place.
89 lines
4.8 KiB
Markdown
89 lines
4.8 KiB
Markdown
# macOS bundle updates
|
|
|
|
Tagged macOS bundles use `electron-updater`. The bundled and Light stamps
|
|
name that owner. Stable and canary are separate application identities; each
|
|
updates within its baked channel. Development and bootstrap installs keep
|
|
checkout updates.
|
|
|
|
One-off builds carry `source: commit-build` and have no updater. Their package
|
|
identity includes the short commit SHA, so different commit builds can coexist.
|
|
Checks and apply requests explain that the developer must provide a new build.
|
|
This applies to the desktop and its bundled CLI, not to an unrelated remote
|
|
backend the desktop connects to.
|
|
|
|
## Feed contract
|
|
|
|
Channel registration lives in R2 at `releases/channels/NAME.json`, not in a
|
|
checked-in channel map. Channel bundles resolve that record and its digest-bound
|
|
build manifest before selecting an immutable native feed. The admitted request
|
|
keeps the application identity stable across builds and records the source commit
|
|
separately from the increasing native package version.
|
|
|
|
Existing tagged clients retain their native feed URLs. The generic
|
|
`apps/desktop/update-feed.cjs` adapter computes these legacy paths; it does not
|
|
register channels. The builder writes the legacy URL into `app-update.yml`;
|
|
`updates.desktop_feed_base_url` can supply 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/` between `darwin/` and the channel.
|
|
- Artifacts: `releases/tag/TAG/FILENAME`, shared by download links and feeds.
|
|
- Channel-build artifacts and native metadata: `releases/channel-builds/BUILD_ID/`.
|
|
|
|
Retirement is a different operation from a native update: the old channel points
|
|
to a qualified official destination, not to a differently named package in its
|
|
own native feed. The explicit migration action must preserve user state and
|
|
verify destination readiness before removing the preview application. Keeping
|
|
the qualified receiver manifest immutable lets an offline preview migrate after
|
|
the destination channel has advanced; the destination subsequently owns updates.
|
|
|
|
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.
|
|
|
|
`python -m scripts.releases.r2 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_LINK` and `CSC_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
|
|
|
|
Helper tests exercise the strategy, native-event ordering, feed validation,
|
|
conditional publication, and retention. They are not proof of a signed install
|
|
or actual app replacement.
|
|
|
|
Native macOS packaged-update drivers are part of the existing
|
|
[install/update family](https://github.com/NousResearch/hermes-agent/blob/main/tests/install/BUNDLED_UPDATES.md). The stable gate
|
|
requires signed-package transitions on both architectures. Each acceptance
|
|
claim needs a successful native run for the exact old/new package pair.
|
|
Workflow definitions and historical helper results do not establish acceptance
|
|
of the current head. See [PM audit status](pm-audit-status.md) for scoped receipts.
|