docs/operations/releases.md
Releases
rac is released from GitHub Actions with the Build macOS binaries of
Helium workflow (.github/workflows/build.yml). It builds Apple Silicon
by default (Intel or both are options), packages and signs each disk image,
writes the update feeds, and publishes a GitHub release. Installed copies
of rac find the new version through that release. How updates work, and why,
is in internals/overview.md.
One-time setup
Make an update signing key:
sh1 python3 -m venv /tmp/sparkle-venv 2 /tmp/sparkle-venv/bin/pip install cryptography 3 /tmp/sparkle-venv/bin/python devutils/rac/sparkle.py keygen Keep a copy of both lines somewhere safe, such as a password manager. If the private key is lost, every installed copy of rac stops accepting updates, and people have to download rac again by hand.
Add these repository secrets in GitHub (Settings > Secrets and variables > Actions):
Secret Value PROD_MACOS_SPARKLE_ED_PUB_KEYThe key after public:fromkeygen, without the labelRAC_SPARKLE_ED_PRIVATE_KEYThe key after private:fromkeygen, without the labelFor signed and notarized builds, add the Apple Developer secrets too:
PROD_MACOS_CERTIFICATE(the Developer ID certificate as base64.p12),PROD_MACOS_CERTIFICATE_PWD,PROD_MACOS_CERTIFICATE_NAME,PROD_MACOS_CI_KEYCHAIN_PWD(any new password),PROD_MACOS_NOTARIZATION_APPLE_ID,PROD_MACOS_NOTARIZATION_TEAM_ID, andPROD_MACOS_NOTARIZATION_PWD(an app-specific password). Without them, CI signs ad hoc, and macOS blocks the first launch of the downloaded app until it's allowed in System Settings > Privacy & Security. Updates still work, because Sparkle checks rac's own signature.PROD_MACOS_SPECIAL_ENTITLEMENTS_PROFILE_B64(a base64 provisioning profile) is optional; without it the build has no branded entitlements.
Never change the signing key once rac is released. Builds only accept updates signed with the key they were built with.
The website
Do this once, when the first release is out. Until then the site's download page is hidden and shows sample data.
- Set
RELEASE_READYtotrueinsite/src/config.ts. The download buttons and the nav then go to/download, search engines may index it, and it shows the latest release instead of sample data. - Add a
GITHUB_TOKENenvironment variable to the Vercel project: a fine-grained token with no permissions beyond public repositories. The site reads the latest release from GitHub's API at build time, and without a token, builds share a low rate limit and can fail. - Rebuild the site after each release, so the download page shows it. In
Vercel, create a deploy hook for
main(Settings > Git > Deploy Hooks), save its URL as the repository secretVERCEL_DEPLOY_HOOK, and add a last step to the release job in.github/workflows/build.yml:yaml1 - name: Rebuild the website 2 run: curl -fsS -X POST "${{ secrets.VERCEL_DEPLOY_HOOK }}"
Making a release
- Raise the version in
rac_version.txtand commit it. Use a patch bump (0.1.1) for fixes, a minor bump (0.2.0) for features or a Helium sync. The workflow stops if the version isn't higher than every earlier release. - In GitHub, open Actions > Build macOS binaries of Helium > Run
workflow, choose
main, and check Create a release after build is done. Architectures defaults toarm64; choosex86_64orbothto build Intel too. A full build takes several hours per architecture. One architecture failing doesn't cancel the other. If a build phase can't download the previous phase's checkpoint, it retries twice before failing. - When it finishes, check the new release. It should have a disk image and
appcast-<arch>.xmlfor each architecture you built, and a.deltafile from each of the last few releases (none on the first). A release only has the feeds of the architectures it built, so installed copies of an architecture that was left out stop seeing updates until a release includes it again. It's tagged with the version, such as0.1.1, on the commit the workflow was started from. - Open https://racbrowser.com/download and check that it shows the new version.
Installed copies check the feed at launch and then hourly, download the update in
the background, and install it when rac quits. The feed's address
(notkainoa/rac) is built into rac, in rac/updates/update-feed.patch.
Don't mark a release as a prerelease or a draft. GitHub's latest link
skips them, so nobody would get the update.
Testing an update locally
Dev builds don't include the updater. To try a change to it:
- Make a throwaway key with
/tmp/sparkle-venv/bin/python devutils/rac/sparkle.py keygen(the venv from the one-time setup). - Add to
build/src/out/Default/args.gn:Then build. Remove the lines and rebuild when you're done.1 enable_sparkle=true 2 sparkle_ed_key="<public key>" - Make a newer copy of
rac.app: raiseCFBundleVersionandCFBundleShortVersionStringin itsInfo.plist, runcodesign --force --deep --sign -on it, and put it in a disk image withhdiutil create. - Write a feed for it, with the private key in
RAC_SPARKLE_ED_PRIVATE_KEY:Serve it withsh1 /tmp/sparkle-venv/bin/python devutils/rac/sparkle.py appcast --arch arm64 --dmg <dmg> \ 2 --base-url http://127.0.0.1:8765/ --version <new version> \ 3 --out <folder>/appcast-arm64.xml python3 -m http.server 8765 --bind 127.0.0.1 --directory <folder>. - Copy the current
rac.appto another name insidebuild/src/out/Default(the copy only works there, because the dev build loads its libraries from that folder). Launch it with a throwaway profile and the local feed:sh1 build/src/out/Default/<copy>.app/Contents/MacOS/rac --user-data-dir=/tmp/<profile> \ 2 --custom-update-server-url=http://127.0.0.1:8765/ - Quit the copy once About rac says to relaunch. Don't click Relaunch:
it starts rac without
--user-data-dir, which opens the real profile.
Sparkle stores its state in the me.kainoa.rac defaults and in
~/Library/Caches/me.kainoa.rac. Afterward, remove the SU* keys
(defaults delete me.kainoa.rac SULastCheckTime, and so on) so a later
test starts fresh.