Host the site and release downloads on byre.run via Cloudflare
This commit is contained in:
+15
-4
@@ -127,22 +127,33 @@ A new build reaches the service by replacing `/Applications/Byre.app`: `mac/depl
|
||||
|
||||
## Releases
|
||||
|
||||
A release is one notarized disk image, `byre-<version>.dmg`, holding `Byre.app`: the macOS app, with the server (`Contents/Helpers/byre`), `byre-hands` (`Contents/Helpers/byre-hands`) and `byre-update` (`Contents/Helpers/byre-update`) inside it. Every Mac installs the same app; the server's Mac also runs `byre install` from it (see "Running as a service"). Releases are on the repo's releases page on g.fc.run, `https://g.fc.run/ai/byre/releases`.
|
||||
A release is one notarized disk image, `byre-<version>.dmg`, holding `Byre.app`: the macOS app, with the server (`Contents/Helpers/byre`), `byre-hands` (`Contents/Helpers/byre-hands`) and `byre-update` (`Contents/Helpers/byre-update`) inside it. Every Mac installs the same app; the server's Mac also runs `byre install` from it (see "Running as a service"). Release images are served from `https://dl.byre.run/` (see "Website"), and each release's notes are on the repo's releases page on g.fc.run, `https://g.fc.run/ai/byre/releases`.
|
||||
|
||||
`mac/package.sh` builds one for whatever the checkout holds, into `mac/.build/release/<version>/`, `<version>` being the bundle's `CFBundleShortVersionString`, and prints the image's path. It builds with `mac/bundle.sh` (signed with Developer ID, see `docs/macos.md`), zips the app and notarizes it with `xcrun notarytool submit --keychain-profile byre-notary --wait`, staples the ticket, builds the disk image with `hdiutil` (the app and an `/Applications` link), signs, notarizes and staples that too, and writes its SHA-256 beside it. It then signs the image for Sparkle with `sign_update --account byre` and writes `appcast.xml`, a feed with that one release.
|
||||
|
||||
A deploy is that image installed rather than published: `mac/deploy.sh` runs `package.sh`, installs the image's app with `mac/install.sh <dmg>` (mounts the image, copies `Byre.app` to `/Applications/Byre.app.new`, removes the old bundle and moves the new one into place, so nothing running from the old bundle has its files written over; it prints the installed `ByreCommit`); the service sees its executable replaced and restarts itself (see "Running as a service"). Client Macs get the same image through the client tools and `install.sh` (`AGENTS.md`, "Deploying"). A deployed build's `CFBundleVersion` is at least the last release's, so the updater never offers to go back to it.
|
||||
|
||||
`mac/release.sh <notes>` publishes one, run on byre's host from a checkout whose `HEAD` carries a `v<version>` tag already pushed to `origin`, with a clean tree; it fetches the tag from `origin` and stops unless it names `HEAD`, then runs `package.sh`. Last, it creates the release as a draft through the Gitea API (`POST /api/v1/repos/ai/byre/releases`, its notes the Markdown file `<notes>`, which the releasing agent writes from the diffs since the previous tag, as `AGENTS.md`, "Releasing", describes), uploads the image, its `.sha256` and `appcast.xml` as assets, and only then publishes it (`PATCH` with `draft: false`), so the `latest` release always has its appcast. A run that fails part way leaves the draft on the releases page; delete it there before running again. Everything lands in `mac/.build/release/<version>/`, and the script prints the release page.
|
||||
`mac/release.sh <notes>` publishes one, run on byre's host from a checkout whose `HEAD` carries a `v<version>` tag already pushed to `origin`, with a clean tree; it fetches the tag from `origin` and stops unless it names `HEAD`, then runs `package.sh`. It uploads to the R2 bucket `byre` with `wrangler r2 object put` (run through `npx`): `byre-<version>.dmg` and its `.sha256`, cached for a year, then the same image as `byre.dmg`, the site's download link, and last `appcast.xml`, both cached for 5 minutes, so the appcast never names an image that isn't there yet, and then purges those two from Cloudflare's cache so the new release shows at once. Last, it creates the release on g.fc.run as a draft through the Gitea API (`POST /api/v1/repos/ai/byre/releases`), its notes the Markdown file `<notes>`, which the releasing agent writes from the diffs since the previous tag, as `AGENTS.md`, "Releasing", describes, attaches copies of the image and its `.sha256` (not the appcast), and only then publishes it (`PATCH` with `draft: false`). A run that fails after the uploads leaves the draft on the releases page; delete it there before running again. Everything lands in `mac/.build/release/<version>/`, and the script prints the release page.
|
||||
|
||||
It needs, on that Mac:
|
||||
|
||||
- a Developer ID Application certificate in the keychain (`security find-identity -v -p codesigning` lists it), with its key's access answered **Always Allow** for `codesign` (the first signing asks for the login keychain password on the Mac's screen, and a prompt left waiting fails that run with "timestamps differ"), and `mac/byre.provisionprofile` being the Developer ID profile for `run.fc.byre.mac` with iCloud (`security cms -D -i mac/byre.provisionprofile` prints it);
|
||||
- a `notarytool` keychain profile named `byre-notary`, made with `xcrun notarytool store-credentials byre-notary --key <AuthKey.p8> --key-id <id> --issuer <issuer>` from an App Store Connect API key;
|
||||
- the Sparkle signing key in the login keychain under account `byre` (`~/Library/Caches/byre/sparkle-<version>/bin/generate_keys --account byre -p` prints its public key, which must match `SUPublicEDKey` in `mac/Info.plist`; `-x <file>` exports it for safekeeping). The script downloads Sparkle's tools to that cache directory when they are missing;
|
||||
- a g.fc.run token that can write the repo's releases, as the generic password with service `byre-release` (`security add-generic-password -s byre-release -a g.fc.run -w <token>`).
|
||||
- a g.fc.run token that can write the repo's releases, as the generic password with service `byre-release` (`security add-generic-password -s byre-release -a g.fc.run -w <token>`);
|
||||
- the Cloudflare token (see "Website").
|
||||
|
||||
The app checks `https://g.fc.run/ai/byre/releases/download/latest/appcast.xml` for updates (see "Updates" in `docs/macos.md`); Gitea serves the `latest` name from the newest release, so each release's appcast replaces the last.
|
||||
The app checks `https://dl.byre.run/appcast.xml` for updates (see "Updates" in `docs/macos.md`); each release's appcast replaces the last.
|
||||
|
||||
## Website
|
||||
|
||||
byre.run and byre.team are registered at Porkbun, with Cloudflare as their DNS, in the Cloudflare account `cloudflare@flamingcow.io`.
|
||||
|
||||
- `https://byre.run/` (and `www.byre.run`) is the Cloudflare Pages project `byre`: `site/index.html`, with `mac/icon.svg` and the fonts from `mac/fonts/`. `site/deploy.sh` stages those files and publishes them with `wrangler pages deploy`. Releases don't redeploy it; its download link is `https://dl.byre.run/byre.dmg`, which each release replaces.
|
||||
- `https://dl.byre.run/` is the R2 bucket `byre`'s custom domain, holding what `mac/release.sh` uploads (see "Releases").
|
||||
- byre.team, and every path on it, redirects to `https://byre.run/` through a redirect rule on its zone (Rules → Redirect Rules in the dashboard), over proxied `AAAA 100::` records for `byre.team` and `www.byre.team`.
|
||||
|
||||
`site/deploy.sh` and `mac/release.sh` read a Cloudflare API token from the generic password with service `byre-cloudflare` (`security add-generic-password -s byre-cloudflare -a cloudflare -w`, which prompts for it); it needs Account → Cloudflare Pages: Edit and Workers R2 Storage: Edit, and Zone → DNS: Edit, Single Redirect: Edit, Zone Settings: Edit and Cache Purge: Purge on both zones. byre.run's Browser Cache TTL is "Respect Existing Headers", so browsers keep the 5-minute cache on `byre.dmg` and `appcast.xml`. The account ID is in both scripts.
|
||||
|
||||
Clients reach `tailscale serve` over HTTP/2; it proxies to byre over HTTP/1.1 and passes SSE through unbuffered.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user