Local Sparkle Update Testing
April 19, 2026 · View on GitHub
Test the full Sparkle in-app update flow locally before pushing releases. This validates that code signing configurations work correctly with Sparkle's XPC installer in sandboxed mode.
Important
The release workflow signs with codesign which does not substitute Xcode build variables like $(PRODUCT_BUNDLE_IDENTIFIER). All signing scripts pre-process the entitlements file with sed to substitute the actual bundle ID. Without this, Sparkle's XPC mach-lookup connections fail with error 4005.
Prerequisites
-
Self-signed certificate in your login keychain:
# Generate if missing ./scripts/create-signing-cert.sh -
Sparkle EdDSA private key file (same key used in
SPARKLE_PRIVATE_KEYGitHub secret):export SPARKLE_PRIVATE_KEY_FILE=~/path/to/sparkle_private_key.pem -
Built Sparkle artifacts (the
sign_updatebinary):- Build the project once in Xcode (
Cmd+B) to populate SPM artifacts - The script auto-discovers
sign_updatefrombuild/*/SourcePackages/artifacts/
- Build the project once in Xcode (
How It Works
The script creates a simulated update scenario:
┌────────────────────┐ appcast.xml ┌───────────────────────┐
│ Installed v99.0.0 │ ──── checks ───────► │ Local HTTP :8089 │
│ /Applications/ │ │ ├── appcast.xml │
│ Snapzy.app │ ◄── downloads ────── │ └── Snapzy-test.dmg │
└────────────────────┘ v99.0.1 DMG └───────────────────────┘
- Builds an Xcode archive (reused across runs)
- Creates v1 (99.0.0) — patches
Info.plist, signs, installs to/Applications - Creates v2 (99.0.1) — patches
Info.plist, signs, creates DMG - Signs DMG with Sparkle EdDSA key
- Generates
appcast.xmlpointing tohttp://localhost:8089/Snapzy-test.dmg - Starts a local HTTP server on port 8089
Signing Modes
| Mode | Sparkle helpers | Main app | Purpose |
|---|---|---|---|
test-current | Self-signed cert | Self-signed cert | Reproduce error 4005 |
test-hybrid | Ad-hoc (-) | Self-signed cert | Validate hybrid fix |
"Sparkle helpers" = Installer.xpc, Downloader.xpc, Autoupdate, Updater.app, Sparkle.framework
Usage
Test current signing (reproduce error 4005)
export SPARKLE_PRIVATE_KEY_FILE=~/path/to/sparkle_private_key.pem
./scripts/test-update-local.sh test-current
- Wait for build + server start
- Open Snapzy from
/Applications - Menu bar → Preferences → About → Check for Updates
- Expected: Error 4005 — "remote port connection was invalidated"
Ctrl+Cto stop server
Test hybrid signing (validate fix)
./scripts/test-update-local.sh test-hybrid
- Wait for build + server start
- Open Snapzy from
/Applications - Menu bar → Preferences → About → Check for Updates
- Expected: Update downloads and installs — app relaunches as v99.0.1
Ctrl+Cto stop server
Clean up
./scripts/test-update-local.sh clean
Removes /tmp/test-sparkle-update/. Does not remove /Applications/Snapzy.app — re-install from a release DMG or use test-tcc-local.sh to restore.
Notes
- Test versions (
99.0.0,99.0.1) avoid conflicts with real releases - First run builds the archive (~2-5 min); subsequent runs reuse it
- The feed URL in v1's
Info.plistis patched tohttp://localhost:8089/appcast.xml - Server runs on port 8089 to avoid collisions with common dev servers
- Archive is stored at
/tmp/test-sparkle-update/archive/— delete to force rebuild
Related
- Self-signed certificate setup
- Release workflow
scripts/test-tcc-local.sh— TCC permission persistence testing (separate concern)