5.9 KiB
Releasing the Sprout Desktop App
This guide covers the end-to-end process for releasing the Sprout desktop app, including secrets setup, cutting releases, and troubleshooting.
Prerequisites / Secrets Setup
The following GitHub repository secrets must be configured before the first release:
| Secret | Description |
|---|---|
SPROUT_RELAY_URL |
WebSocket relay URL baked into the release binary (for example wss://...) |
SPROUT_UPDATER_PUBLIC_KEY |
Tauri updater public key (generate with pnpm tauri signer generate) |
TAURI_SIGNING_PRIVATE_KEY |
Tauri updater private key |
TAURI_SIGNING_PRIVATE_KEY_PASSWORD |
Password for the private key |
OSX_CODESIGN_ROLE |
IAM role ARN for Block's Apple codesigning service |
CODESIGN_S3_BUCKET |
S3 bucket for codesigning artifacts |
Generating Tauri Updater Keys
cd desktop
pnpm tauri signer generate -w ~/.tauri/sprout.key
This generates a keypair:
- The public key goes in the
SPROUT_UPDATER_PUBLIC_KEYsecret. - The private key goes in the
TAURI_SIGNING_PRIVATE_KEYsecret.
Store the password you chose in TAURI_SIGNING_PRIVATE_KEY_PASSWORD.
Cutting a Release
From any branch (typically main):
just desktop-release 0.3.0
Or equivalently:
git tag desktop/v0.3.0
git push origin desktop/v0.3.0
That's it. CI extracts the version from the tag and writes it into
package.json, tauri.conf.json, and Cargo.toml at build time. The
versions checked into the repo are not used for releases — the tag is the
source of truth.
Verify
Check GitHub Releases for:
- The versioned release (e.g.
desktop/v0.3.0) - The
sprout-desktop-latestrolling release (updated with every release)
What CI Does
The sprout-desktop-release.yml workflow:
- Extracts the version from the git tag once into
RELEASE_VERSION. - Sets the version into
package.json,tauri.conf.json, andCargo.tomlusingset-version-from-tag.mjs. - Regenerates
Cargo.lockto match the patchedCargo.toml. - Validates all required secrets are present.
- Builds the release config with the updater public key and endpoint.
- Builds the Tauri app (unsigned).
- Signs and notarizes the macOS bundle via
block/apple-codesign-action. - Re-packages the signed app into a DMG and updater archive.
- Signs the updater archive with the Tauri updater key.
- Publishes the updater manifest (
latest.json) to the rollingsprout-desktop-latestrelease. - Publishes the DMG to both the versioned and rolling releases.
Local Release Build (Testing)
Local builds will not be codesigned or notarized — that only happens in CI
via block/apple-codesign-action. Local builds are useful for testing the
updater runtime config and DMG packaging.
# Set updater env vars
export SPROUT_UPDATER_PUBLIC_KEY="your-public-key"
export SPROUT_UPDATER_ENDPOINT="https://github.com/block/sprout/releases/download/sprout-desktop-latest/latest.json"
# Generate release config
cd desktop
pnpm run tauri:release:config
# Build (unsigned) — pass a version to set it before building
just desktop-release-build version=0.3.0
You can also set the version separately without building:
just desktop-set-version 0.3.0
Auto-Updates
The app uses tauri-plugin-updater to check for updates. The updater endpoint
is:
https://github.com/block/sprout/releases/download/sprout-desktop-latest/latest.json
This latest.json is updated on every release and contains the download URL
and signature for the latest version.
Relay URL Configuration
The app connects to the relay via the SPROUT_RELAY_URL environment variable.
- Production releases: The GitHub release workflow builds the app with
SPROUT_RELAY_URLsourced from theSPROUT_RELAY_URLrepository secret, which is baked into the release binary as its default relay URL. - Local release builds: Export
SPROUT_RELAY_URLbefore runningjust desktop-release-buildif you want a non-localhost relay URL compiled into the app. - Development: If not set, it defaults to
ws://localhost:3000.
How Versioning Works
The git tag is the single source of truth for the release version. The version
fields in package.json, tauri.conf.json, and Cargo.toml on main are
not used for releases — CI overwrites them at build time from the tag.
This means the tagged commit will show a different version in its source files than what the release actually contains. This is an accepted tradeoff — the tag is the canonical version, the commit is just the code state at release time. This is standard practice in ecosystems like Docker, Go, and Rust where the tag drives the version.
Troubleshooting
-
"Missing required desktop release secrets": Ensure all secrets listed in Prerequisites are configured in GitHub repo settings.
-
Codesigning failures: Verify
OSX_CODESIGN_ROLEandCODESIGN_S3_BUCKETare configured correctly. Check theblock/apple-codesign-actionstep logs for details. -
Build failures: If versions are wrong, check that the tag follows the format
desktop/v<semver>(e.g.desktop/v0.3.0). CI extracts the version from the tag automatically. -
"A public key has been found, but no private key": The Tauri build should not require
TAURI_SIGNING_PRIVATE_KEY. If you see this, the build is trying to generate updater artifacts before the signed app bundle exists. The updater archive is supposed to be created and signed later from the notarized app.