docs/RELEASING.md
Releasing Restless
Releases are built on a Mac, not in CI, because they need the Developer ID
certificate, Apple's notary service and the Sparkle signing key, all of which
live in the login keychain. CI (.github/workflows/ci.yml) only checks that an
unsigned build compiles.
One-time setup
- Developer ID certificate. "Developer ID Application" for team
VW9NDM428Hmust be in the login keychain. Check with:sh1 security find-identity -v -p codesigning .env. Copy it from.env.example. It sets the team, the signing identity andNOTARY_PROFILE=Restless.- Notary credentials. Create an app-specific password for the team's
Apple ID at appleid.apple.com → Sign-In & Security → App-Specific Passwords.
Then store it as the
Restlesskeychain profile:It prompts for the password, so it never lands in your shell history.sh1 xcrun notarytool store-credentials "Restless" \ 2 --apple-id "APPLE_ID_EMAIL" --team-id VW9NDM428H - Sparkle signing key. The private EdDSA key is in the login keychain
under the account
restless; its public half isSUPublicEDKeyinproject.yml. Back it up somewhere safe, such as a password manager. Run./build.shonce first, so Sparkle's tools are downloaded, then:Move that file into your password manager and delete it. Never commit it: anyone with this key can sign updates that every installed copy will accept. Without it, no future release can update existing installs.sh1 .build/SourcePackages/artifacts/sparkle/Sparkle/bin/generate_keys --account restless -x "$HOME/restless-sparkle-key.txt"
Every release
From an up-to-date, clean main:
| 1 | ./release.sh 1.1.0 |
It first checks .env, the certificate, the notary profile, gh and the
Sparkle key, so a missing credential fails in seconds. The first time, macOS
may ask to let generate_keys use the Sparkle key: choose Always Allow.
Then it runs these steps:
- Builds and signs
Restless.appwith version1.1.0. The build number is the commit count onmain, which only grows, and that's what Sparkle compares. - Notarizes and staples the app.
- Zips the app. Sparkle's
generate_appcastthen signs the zip and writesappcast.xml. - Publishes the GitHub release
v1.1.0with both files attached.
The app's feed is
https://github.com/notkainoa/restless/releases/latest/download/appcast.xml,
so every installed copy sees the new release as soon as it's published.
If a step fails, the script says which one. Anything it built is kept in
dist/release-<version>/, and nothing is published until the last step.