| @@ -0,0 +1,44 @@ | |||
| 1 | + | # Auto-update options for D-Patch (macOS, Swift, distributed as bare DMGs) | |
| 2 | + | ||
| 3 | + | Short answer: **Sparkle 2** — the de-facto standard, and basically the only serious native option for a non-sandboxed, non-App-Store app. | |
| 4 | + | ||
| 5 | + | ## The options | |
| 6 | + | ||
| 7 | + | | Option | What it is | Used by | | |
| 8 | + | |---|---|---| | |
| 9 | + | | **Sparkle 2** | Framework: checks an `appcast.xml` feed, downloads, EdDSA-verifies, installs & relaunches. Delta updates, "install to /Applications", Check-for-Updates menu UX | The default for indie/OSS Mac apps for ~20 years | | |
| 10 | + | | **Hand-rolled GitHub Releases check** | `GET /repos/<owner>/<repo>/releases/latest`, compare against `CFBundleVersion`, alert + link to DMG | Small OSS apps that don't want the machinery | | |
| 11 | + | | **Homebrew cask** | Zero code — users get `brew upgrade` | Complements either, for the CLI crowd | | |
| 12 | + | | Squirrel.Mac | Electron's updater guts | Electron apps; effectively dead as a native Swift choice | | |
| 13 | + | | App Store / Software Update | Apple's own path | Not applicable — a patcher app will never pass review/sandboxing | | |
| 14 | + | ||
| 15 | + | ## Sparkle 2 specifics for D-Patch | |
| 16 | + | ||
| 17 | + | - SwiftPM dependency, `Sparkle` module, ~10 lines of app code (`SPUStandardUpdaterController` + a Check-for-Updates menu item). | |
| 18 | + | - **Keys**: one-time `generate_keys` → EdDSA keypair; public key in Info.plist, private key in CI secret. | |
| 19 | + | - **Appcast**: `appcast.xml` with per-release entries; at release time `sign_update <dmg>` produces the signature + length for the entry. Can be committed to a branch and served via raw.githubusercontent/Pages — no server needed. | |
| 20 | + | - **build.sh gets ~5 lines**: Sparkle ships two XPC services (`Autoupdate`, `Updater`) that must be copied into `Contents/XPCServices` before signing. Framework + XPC, no `Bundle.module` accessor — doesn't touch the resource-bundle accessor minefield escaped in the 0.4.4 fix. | |
| 21 | + | - **release.yml gets one step**: sign the DMG, append the appcast entry. | |
| 22 | + | ||
| 23 | + | ### The ad-hoc signing question (verify up front) | |
| 24 | + | ||
| 25 | + | D-Patch is ad-hoc signed. Sparkle's integrity story with ad-hoc hosts rides on the **EdDSA key** rather than matching code signatures — OSS projects (IINA et al.) ship Sparkle this way. Still worth a 30-minute spike: test appcast, 0.4.4 → fake-0.4.5 update on a throwaway copy under `/var/folders`, confirm it installs cleanly. | |
| 26 | + | ||
| 27 | + | ### Why it's disproportionately nice here | |
| 28 | + | ||
| 29 | + | Sparkle's installer replaces the bundle itself — the downloaded update never goes through first-launch Gatekeeper assessment, so the "app is damaged / remove quarantine attr" dance users currently do disappears for every future update. That alone justifies it for an app distributed as bare DMGs. | |
| 30 | + | ||
| 31 | + | Parallel unlock: a real Developer ID certificate ($99/yr Apple Developer Program) fixes the *first-install* Gatekeeper experience too, and makes Sparkle's validation story trivial. Nice-to-have, not a prerequisite. | |
| 32 | + | ||
| 33 | + | ## Recommendation | |
| 34 | + | ||
| 35 | + | - **Minimum viable**: hand-rolled checker — an afternoon, zero infra, users still manually install. | |
| 36 | + | - **The proper answer**: Sparkle 2 — about half a day including the build.sh/release.yml changes and a dry-run update test. This is the one "most people" use. | |
| 37 | + | ||
| 38 | + | ## Concrete changes if taking the Sparkle route | |
| 39 | + | ||
| 40 | + | 1. `Package.swift`: add `sparkle-project/Sparkle` (2.x). | |
| 41 | + | 2. `build.sh`: copy the two XPC services into `Contents/XPCServices` before codesign. | |
| 42 | + | 3. `DPatchApp`: wire `SPUStandardUpdaterController`, add Check-for-Updates menu item; Info.plist keys `SUFeedURL` + `SUPublicEDKey`. | |
| 43 | + | 4. CI: store the EdDSA private key as a repo secret; release.yml runs `sign_update` on the DMG and appends the entry to `appcast.xml`. | |
| 44 | + | 5. Spike: end-to-end test update on a throwaway install before wiring it into a real release. | |
robin / auto-update-options.md
Last active 2 hours ago
robin revised this gist 2 hours ago · 2290812
1 file changed, 44 insertions