Desktop app — release, signing & auto-update
Covers the developer-credential work (TODO §D4). The pipeline is
.github/workflows/desktop-release.yml;
trigger it by pushing a desktop-v* tag.
1. Updater signing keys (one-time)
Section titled “1. Updater signing keys (one-time)”cargo tauri signer generate -w ~/.tauri/duduclaw.key- Put the public key in
src-tauri/tauri.conf.json > plugins.updater.pubkey. - Put the private key + password in repo secrets
TAURI_SIGNING_PRIVATE_KEY/TAURI_SIGNING_PRIVATE_KEY_PASSWORD.
The updater endpoint is the GitHub latest.json (already configured). On each
release tauri-action emits a signed latest.json; the client verifies the
signature before installing, and rejects a mismatch (§D4.4).
2. macOS — Developer ID + notarization
Section titled “2. macOS — Developer ID + notarization”Secrets:
| Secret | What |
|---|---|
APPLE_CERTIFICATE |
base64 of the Developer ID Application .p12 |
APPLE_CERTIFICATE_PASSWORD |
its password |
APPLE_SIGNING_IDENTITY |
Developer ID Application: <Name> (<TEAMID>) |
APPLE_ID / APPLE_PASSWORD |
Apple account + app-specific password |
APPLE_TEAM_ID |
10-char team id |
tauri-action signs (hardened runtime, using src-tauri/entitlements.plist) and
notarizes when these are present. For a manual artifact use
scripts/desktop/sign-notarize-macos.sh.
Acceptance (§D4.1): download the .dmg on a machine that never saw the cert
→ it opens with no “unidentified developer” Gatekeeper prompt.
3. Windows — Authenticode
Section titled “3. Windows — Authenticode”Secrets WINDOWS_CERT_PFX_BASE64 / WINDOWS_CERT_PASSWORD. The workflow signs
the .msi via scripts/desktop/sign-windows.ps1.
Acceptance (§D4.2): SmartScreen does not block (EV = immediate trust).
4. Linux
Section titled “4. Linux”.AppImage + .deb are built unsigned (WebKitGTK runtime dep declared). Not a
release blocker (§D4.3).
5. Cut a release
Section titled “5. Cut a release”The normal path is the core release ritual — scripts/release.sh <bump> bumps
src-tauri/tauri.conf.json with every other manifest and tags BOTH v<X.Y.Z>
and desktop-v<X.Y.Z> on the bump commit; git push --tags then triggers this
pipeline alongside the core one. (Desktop sat frozen at 1.33.0 for 11 releases
while these were separate manual steps — don’t go back to hand-tagging.)
For a desktop-only rebuild of an existing version:
git tag desktop-v1.44.0 <bump-commit>git push origin desktop-v1.44.0The workflow builds the 4-target matrix, signs/notarizes, and publishes the
release directly (no draft) with installers + latest.json. A final job
copies latest.json to the fixed-tag desktop-updater release — that fixed
URL is the updater endpoint, because releases/latest/... would point at the
much more frequent core releases and 404.
5a. Updates when attached to an external gateway (unreleased)
Section titled “5a. Updates when attached to an external gateway (unreleased)”The shell still downloads new versions in the background. When it runs its own gateway sidecar it behaves as before: notification, tray「重啟並更新」, install on quit. When it is attached to a gateway it does not own, the app and the gateway are separate installs on separate channels, so the shell does not install on its own: the notification and the tray item point at the dashboard 系統更新 page, and quitting does not install the staged update. That page updates the gateway first and the app only up to the gateway’s version. The page reads the relation through the desktop_update_info command (bundled or external).
6. Certificate hygiene
Section titled “6. Certificate hygiene”- Never commit certs/keys; only GitHub secrets.
- Rotate the Apple app-specific password and the updater key on personnel change.
- Keep
tauri.conf.jsonversion == coreCargo.tomlworkspace version so the updater never ships a shell/core mismatch (§D4.4 / §D4.5).