Skip to content

Sparkle Stage 2 — signed appcast + release-workflow integration #104

@momenbasel

Description

@momenbasel

Tracking issue for Stage 2 of the in-app updater (Stage 1 landed in #99).

Stage 1 wired Sparkle as an opt-in SPM dependency with a Check for Updates menu item. When no SUFeedURL is set the menu opens the GitHub Releases page; when one is set Sparkle starts. Stage 2 is making the signed-appcast path actually work end-to-end.

Required pieces

1. EdDSA key pair (ed25519)

  • Generate locally with ./bin/generate_keys from Sparkle's distribution.
  • Public key (SUPublicEDKey) goes into Info.plist (committed).
  • Private key (ed25519_priv.pem or keychain item) goes to a GitHub Actions secret. Document the secret name (SPARKLE_ED25519_PRIVATE_KEY proposed) in scripts/SECRETS.md.

2. Info.plist additions

  • SUFeedURLhttps://momenbasel.github.io/PureMac/appcast.xml (proposed; GitHub Pages publishes alongside the repo) or a release-asset URL like https://github.com/momenbasel/PureMac/releases/latest/download/appcast.xml.
  • SUPublicEDKey — base64-encoded public key.
  • SUEnableInstallerLauncherService, SUEnableDownloaderService — defaults OK; document if we deviate.
  • SUScheduledCheckInterval — default 86400 (daily). Make sure this matches user expectation.

3. Appcast generation in release.yml

  • After notarized DMG lands, sign it with ./bin/sign_update <dmg> (Sparkle helper) — emits a sha256 + signature.
  • Generate or update appcast.xml with the new <item> (version, sparkle:edSignature, sparkle:shortVersionString, release notes URL, minimumSystemVersion=13.0).
  • Commit the updated appcast.xml back to the repo / Pages branch, or upload as a release asset that SUFeedURL points at.

4. Sparkle helper-bundle code-signing

  • Sparkle ships Sparkle.framework/Versions/B/Resources/Updater.app, Autoupdate.app, and XPC helper bundles. The current release workflow only signs the main app — codesign --deep --force -s … on the outer .app typically picks them up, but verify with codesign --verify --deep --strict --verbose=4 and add explicit signing steps if it doesn't.
  • Notarization needs all bundled helpers signed by the same Developer ID. Confirm notarytool submit accepts the bundle on a test run.

5. Hardened Runtime / entitlements

  • Sparkle requires com.apple.security.cs.allow-jit or similar in some configurations. Re-check PureMac.entitlements against Sparkle 2.x sandboxing guide. If we need any extra entitlements they go in here.

6. Test plan before release

  • Build a dummy appcast.xml advertising a higher version, point SUFeedURL at it, build v2.3.0 with the current Info.plist values, verify the in-app update dialog appears and the download verifies against the EdDSA signature.
  • Verify a tampered DMG is rejected (delete a byte from the signed DMG, rehost, watch Sparkle refuse).

7. UX

  • The Releases-page fallback in UpdateService stays as the no-config branch (useful for local dev builds without an appcast).
  • Consider adding a Settings → Updates section exposing 'Check automatically' (SUEnableAutomaticChecks) and 'Check interval' so users can opt out.

Out of scope (separate follow-ups)

  • Delta updates (sparkle:enclosure patch entries).
  • Beta/staging channel (sparkle:channel).
  • In-app changelog rendering vs opening the release notes URL externally.

Closes the Stage 2 ask in #94 once shipped.

Refs #99, #94.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions