feat(updater): add Sparkle Stage 2 integration#108
Open
3manu31 wants to merge 1 commit into
Open
Conversation
Owner
|
Thanks for this — it's a clean, conservative Stage 2 and the direction is right (opt-in UI, appcast generation in CI, safe Releases-page fallback when Before this can land safely on a production auto-updater, Stage 2 needs the full signed-update chain verified end-to-end, not just the plumbing:
I'd like to take 1–4 in a dedicated commit on top of this, generate the key, and do the tamper test before flipping the feed on. Keeping #104/#94 open to track that. The Updates settings UI here is good groundwork — let's keep it gated behind the unset-feed fallback for now. |
This was referenced Jun 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(updater): add Sparkle Stage 2 - signed appcast + release-workflow integration
What does this PR do?
• Completes the Stage 2 signed-update plumbing from [#104] without changing the Stage 1 runtime fallback (follow up from my PR #99).
• Adds Sparkle feed configuration to the app plist so the app can consume a signed appcast when one is available.
• Adds a new Updates section in Settings so users can opt in or out of automatic update checks and choose how often the app checks.
• Extends the release workflow to generate
appcast.xmlafter the final DMG/ZIP artifacts are produced and to publish the appcast with the release assets.• Keeps the safe fallback: when no
SUFeedURLis configured, the updater still opens the GitHub Releases page.This is intentionally a conservative Stage 2 change. It finishes the release plumbing and leaves unrelated app/runtime behavior alone.
Type of change
Files changed
•
PureMac/Info.plist— add Sparkle feed + public key configuration and keep automatic checks enabled.•
PureMac/Views/Settings/SettingsView.swift— add the Updates settings UI for automatic checks and check interval selection.•
.github/workflows/release.yml— generateappcast.xml, upload release assets, and keep the formula bump steps aligned.•
scripts/SECRETS.md— document the new Sparkle signing secret and the required release credentials.•
scripts/release-local.sh— keep the local release path aligned with CI for dry runs and inspection.•
PureMac/Services/UpdateService.swift— keep the Stage 1 fallback path intact.•
PureMac/PureMacApp.swift— keep the Updates menu wiring intact.•
PureMac.xcodeproj/project.pbxprojandproject.yml— project wiring updates that were already needed for the Sparkle configuration.Why this change
• Stage 2 is the signed-appcast half of the updater story. Stage 1 already landed the opt-in menu/fallback behavior.
• The new Settings UI makes the updater user-facing, so people can control automatic checks instead of being locked into a fixed schedule.
• This closes the release-side work needed to make Sparkle use a signed appcast instead of the Releases-page fallback.
• It keeps the app safe for local development builds and does not force the updater into a production feed when one is not configured.
Related issues
• Closes the Stage 2 ask in #94 once shipped.
• Implements the checklist in #104.
Testing done
• Built the app locally and confirmed the Stage 1 updater behavior still works.
• Confirmed the Settings screen exposes automatic update toggling and interval selection.
• Ran a local signed-feed dry test with a hosted appcast and confirmed Sparkle offers the update.
• Confirmed the signed install path relaunches correctly.
• Confirmed a tampered DMG is rejected by Sparkle verification.
• Reviewed the release workflow shape to confirm it includes appcast generation, artifact upload, and formula update steps.
How to test locally
SUFeedURLthat points at a signed appcast.What could not be done here, and why
• A full GitHub Actions release run could not be completed because the maintainer’s paid Apple Developer / App Store Connect credentials are not available in this environment.
• Real Developer ID signing, notarization, and stapling in CI therefore remain blocked on external secrets that only the maintainer can provide.
• The workflow and documentation for that path are in place, but the final production proof has to wait for those credentials.
Notes for reviewers
• This PR is intentionally minimal and conservative.
• It does not add Stage 2 UX extras like delta updates, channels, or in-app changelog rendering.
• The Stage 1 fallback stays intact so local development builds without a feed keep opening GitHub Releases.
• The tamper-rejection test is included because it proves the signed-appcast path is actually active.
AI-assistance disclosure
Drafted using Claude Code. Built and tested locally to confirm the non-apple-related Stage 2 pieces work as expected.