Update client Macs before the server and keep a copy of each deploy

This commit is contained in:
Ian Gulliver
2026-10-22 08:17:33 -07:00
parent 519199eba4
commit 6b09ffb362
3 changed files with 15 additions and 14 deletions
+10 -11
View File
@@ -12,27 +12,26 @@ These are requirements, not preferences. Changing one needs the owner's agreemen
A deploy is a release that isn't uploaded: the same notarized disk image, installed on the owner's Macs instead of published. When asked to deploy, do exactly this, from the worktree root:
1. Run `mac/deploy.sh`, always as the last action of the turn. It runs `mac/package.sh` (the build, both notarizations and the disk image, as for a release, into `mac/.build/release/<version>/`), builds the iPhone app with `mac/phone.sh` and uploads it to TestFlight (`docs/iphone.md`), and installs the image's app as `/Applications/Byre.app` with `mac/install.sh`. The service runs `/Applications/Byre.app/Contents/Helpers/byre`; within 5 seconds it sees that file replaced and exits, and launchd starts the new server and `byre-hands`. Everything is signed with Developer ID, so the macOS privacy grants (Screen Recording, Accessibility, Automation) survive.
2. When you resume, confirm it came up: read the tail of `~/Library/Logs/byre.log`, and `GET /api/tailnet` at the loopback address (`byre check` prints it as `loopback`), whose `state` must be `Running`. Report the new build number first (`plutil -extract CFBundleVersion raw /Applications/Byre.app/Contents/Info.plist` prints it; the iPhone build uploaded to TestFlight has the same one), then what you deployed and what you checked.
3. Once the service is confirmed healthy, update each client Mac whose app is running, through the client tools and without asking first: upload the disk image (`mac/.build/release/<version>/byre-<version>.dmg`) and `mac/install.sh`, quit the app, run `install.sh <dmg>`, and `open /Applications/Byre.app`. The app reads its server address from its iCloud-synced SwiftData store, so it reconnects to prod with no setup. Report each Mac's build number (`install.sh` prints it, then the commit).
4. End the report with release notes for everything on main since the last release tag (`git describe --tags --abbrev=0` names it), written for a byre user in the "Releasing" shape (`## New` and `## Fixed`, each a bullet with a bold short name and what it does or what now works; no `## Install`). Read `git diff <tag>..HEAD`, not just the commit subjects, and leave out changes no user sees.
1. Run `mac/deploy.sh`. It runs `mac/package.sh` (the build, both notarizations and the disk image, as for a release, into `mac/.build/release/<version>/`), builds the iPhone app with `mac/phone.sh` and uploads it to TestFlight (`docs/iphone.md`), and copies the disk image and `mac/install.sh` to `~/byre-deploy/byre.dmg` and `~/byre-deploy/install.sh`. That copy stays there for installing by hand on Macs that aren't connected. It installs nothing.
2. Sort the client Macs `list_clients` shows (leaving out the iPhone) by whether they run a byre server of their own: one does when `launchctl print gui/$(id -u)/run.fc.byre` exits 0 there (`client_bash`). Update the app on each one that runs no server, one Mac at a time, through the client tools and without asking first: upload `~/byre-deploy/byre.dmg` and `~/byre-deploy/install.sh` to the same paths there (`client_upload`), then run `~/byre-deploy/install.sh ~/byre-deploy/byre.dmg` there through `client_bash` and wait for it to finish. It prints the build number, then the commit; report them. Don't quit or relaunch the app: it keeps running the old build, still connected, and the new one starts the next time the owner restarts it. Every one of these must be done before any server restarts, since a protocol change can leave an old client unable to reach a new server to be updated.
3. Only then update each client Mac that runs a server, the same way. Its server sees its executable replaced and restarts on the new build.
4. Install on the Mac you run on with `mac/install.sh ~/byre-deploy/byre.dmg`, always as the last action of the turn. The service runs `/Applications/Byre.app/Contents/Helpers/byre`; within 5 seconds it sees that file replaced and exits, and launchd starts the new server and `byre-hands`. Everything is signed with Developer ID, so the macOS privacy grants (Screen Recording, Accessibility, Automation) survive. Don't relaunch its app.
5. When you resume, confirm it came up: read the tail of `~/Library/Logs/byre.log`, and `GET /api/tailnet` at the loopback address (`byre check` prints it as `loopback`), whose `state` must be `Running`. Report the new build number first (`plutil -extract CFBundleVersion raw /Applications/Byre.app/Contents/Info.plist` prints it; the iPhone build uploaded to TestFlight has the same one), then what you deployed and what you checked.
6. End the report with release notes for everything on main since the last release tag (`git describe --tags --abbrev=0` names it), written for a byre user in the "Releasing" shape (`## New` and `## Fixed`, each a bullet with a bold short name and what it does or what now works; no `## Install`). Read `git diff <tag>..HEAD`, not just the commit subjects, and leave out changes no user sees.
Deploying means running `mac/deploy.sh` on iso. Never stand in for it by running `mac/package.sh` and `mac/install.sh` yourself, not even to build once for several Macs. Every other Mac gets the disk image `mac/deploy.sh` built, after iso is confirmed healthy (step 3).
Deploying means building with `mac/deploy.sh` on the Mac you run on. Never stand in for it by running `mac/package.sh` yourself, not even to build once for several Macs. Every Mac, this one included, gets the disk image `mac/deploy.sh` left in `~/byre-deploy/`.
Besides iso, beast also runs its own byre server:
- Upload the disk image to beast as `~/byre-deploy/byre.dmg` and `mac/install.sh` as `~/byre-deploy/install.sh` (`client_upload`), then run `~/byre-deploy/run.sh` there detached, through `client_bash`: `perl -MPOSIX -e 'fork and exit; POSIX::setsid(); open STDIN,"</dev/null"; open STDOUT,">>$ENV{HOME}/byre-deploy/run.log"; open STDERR,">&STDOUT"; exec "/bin/zsh","$ENV{HOME}/byre-deploy/run.sh"'`. It quits the app, installs, and reopens it; the beast client drops for about a minute and `list_clients` shows it back on the new commit. Do the same for every other running client Mac, and relaunch iso's app only if it is running.
- Check each server with the bearer token in `~/Library/Application Support/byre/loopback-token`: `GET https://byre-<host>.tailcecab.ts.net/api/tailnet` (`state` is `Running`) and `/api/admin/status` (report zombies, protocol errors, rate limits). The loopback address serves `/api/tailnet` but not `/api/admin/status`.
Check each server (this Mac's and each client Mac's from step 3) with the bearer token in `~/Library/Application Support/byre/loopback-token`: `GET https://byre-<host>.tailcecab.ts.net/api/tailnet` (`state` is `Running`) and `/api/admin/status` (report zombies, protocol errors, rate limits). The loopback address serves `/api/tailnet` but not `/api/admin/status`.
What to expect:
- The notarizations take a minute or two each; `notarytool` prints "In Progress" until "Accepted". A rejection stops the script before anything is installed; `xcrun notarytool log <id> --keychain-profile byre-notary` says why.
- You run inside byre, so the restart kills your own turn just after the install. That is expected, not a reason to skip it. You resume with a "byre restarted while you were mid-turn" message; do the checks then, and confirm the service started after your install (the log shows `executable replaced, exiting` just before it).
- The restart can cut off the record of the last tool call, so you may resume with no memory of having deployed. Before concluding that another agent restarted byre, compare the service's start time (the last `listening` line in the log) with when you ran `mac/deploy.sh`: a restart a few minutes after it, once the notarizations are done, is most likely your install. If byre restarts without your install, another agent deployed its own branch, and the running build is theirs.
- The restart can cut off the record of the last tool call, so you may resume with no memory of having deployed. Before concluding that another agent restarted byre, compare the service's start time (the last `listening` line in the log) with when you ran `mac/install.sh`: a restart within seconds of it is most likely your install. If byre restarts without your install, another agent deployed its own branch, and the running build is theirs.
## Releasing
A release is a deploy that is uploaded, and goes to every Mac through the app's updater. Make one only when the owner asks. It needs a `v<version>` tag on a commit of main, pushed by itself (`git push origin v<version>`, never moving or recreating a tag), and a clean checkout at that tag on iso, where `mac/release.sh <notes>` checks the tag, runs `mac/package.sh`, uploads the image, its `.sha256` and `appcast.xml` to `dl.byre.run`, and creates the release on g.fc.run with copies of the image and its `.sha256`, and the Markdown file `<notes>` as its notes. `docs/operations.md`, "Releases", has the details.
A release is a deploy that is uploaded, and goes to every Mac through the app's updater. Make one only when the owner asks. It needs a `v<version>` tag on a commit of main, pushed by itself (`git push origin v<version>`, never moving or recreating a tag), and a clean checkout at that tag on the Mac byre runs on, where `mac/release.sh <notes>` checks the tag, runs `mac/package.sh`, uploads the image, its `.sha256` and `appcast.xml` to `dl.byre.run`, and creates the release on g.fc.run with copies of the image and its `.sha256`, and the Markdown file `<notes>` as its notes. `docs/operations.md`, "Releases", has the details.
Before running it, write the notes yourself from the diffs since the previous tag (`git describe --tags --abbrev=0 v<version>^` names it; read `git diff <previous>..v<version>`, not just the commit subjects). Write the file outside the checkout, so the tree stays clean. It is a changelog for people who use byre, in this shape:
+1 -1
View File
@@ -36,7 +36,7 @@ Each preview gets its own port on byre's node rather than a path under byre's or
## Releases
A release is one notarized disk image, `byre-<version>.dmg`, holding `Byre.app` with the server, `byre-hands` and `byre-update` in `Contents/Helpers`. Images are served from `https://dl.byre.run/`; each release's notes are on `https://g.fc.run/ai/byre/releases`. `mac/package.sh` builds one, `mac/deploy.sh` installs it locally (see `AGENTS.md`, "Deploying"), and `mac/release.sh <notes>` publishes it from a clean checkout at a pushed `v<version>` tag. The build number (`CFBundleVersion`) of both the Mac and iPhone apps is the Unix time of the commit they were built from (`git log -1 --format=%ct`), so one deploy's apps share it; the apps send it in their `Byre-Build` header and the Status tab's client list shows it. A deployed build's `CFBundleVersion` is at least the last release's, so the updater never offers to go back to it.
A release is one notarized disk image, `byre-<version>.dmg`, holding `Byre.app` with the server, `byre-hands` and `byre-update` in `Contents/Helpers`. Images are served from `https://dl.byre.run/`; each release's notes are on `https://g.fc.run/ai/byre/releases`. `mac/package.sh` builds one, `mac/deploy.sh` builds one for installing on the owner's Macs and keeps a copy in `~/byre-deploy/` (see `AGENTS.md`, "Deploying"), and `mac/release.sh <notes>` publishes it from a clean checkout at a pushed `v<version>` tag. The build number (`CFBundleVersion`) of both the Mac and iPhone apps is the Unix time of the commit they were built from (`git log -1 --format=%ct`), so one deploy's apps share it; the apps send it in their `Byre-Build` header and the Status tab's client list shows it. A deployed build's `CFBundleVersion` is at least the last release's, so the updater never offers to go back to it.
The appcast must never name an image that isn't available yet, and a published g.fc.run release must already have its attachments. If a failed release run leaves a draft on the releases page, delete it there before running again.
+4 -2
View File
@@ -7,5 +7,7 @@ mac=$(cd "$(dirname "$0")" && pwd)
rm -rf "$mac/.build/phone/iphoneos/export"
xcodebuild -exportArchive -archivePath "$mac/.build/phone/iphoneos/Byre.xcarchive" -exportOptionsPlist "$mac/phone/export.plist" -exportPath "$mac/.build/phone/iphoneos/export" -allowProvisioningUpdates
version=$(plutil -extract CFBundleShortVersionString raw "$mac/.build/Byre.app/Contents/Info.plist")
dmg=$mac/.build/release/$version/byre-$version.dmg
"$mac/install.sh" "$dmg"
mkdir -p "$HOME/byre-deploy"
cp "$mac/.build/release/$version/byre-$version.dmg" "$HOME/byre-deploy/byre.dmg"
cp "$mac/install.sh" "$HOME/byre-deploy/install.sh"
echo "$HOME/byre-deploy/byre.dmg"