Skip to content

feat(updater): add Sparkle Stage 1 - minimal UpdateService, menu, and guards (opt-in)#99

Merged
momenbasel merged 1 commit into
momenbasel:mainfrom
3manu31:feature/add-sparkle-updater
May 23, 2026
Merged

feat(updater): add Sparkle Stage 1 - minimal UpdateService, menu, and guards (opt-in)#99
momenbasel merged 1 commit into
momenbasel:mainfrom
3manu31:feature/add-sparkle-updater

Conversation

@3manu31

@3manu31 3manu31 commented May 17, 2026

Copy link
Copy Markdown
Contributor

feat(updater): add Sparkle Stage 1 - minimal UpdateService (opt-in)

What does this PR do?

  • Adds a minimal, opt-in in-app updater integration using Sparkle via SPM.
  • Wires an UpdateService that initializes Sparkle's updater controller (without starting it automatically) and exposes checkForUpdates().
  • Adds a Check for Updates command in the app menu to trigger the updater.
  • Keeps a safe fallback: when no SUFeedURL is present the button opens the GitHub Releases page.

This is a conservative Stage 1 change intended only to add the UI hook and updater wiring so maintainers can review without needing CI secrets, appcast signing, or release-process changes.

Type of change

  • Bug fix
  • New feature (opt-in, non-invasive)
  • Performance improvement
  • UI/UX enhancement
  • Documentation
  • Other (describe)

Files changed

  • project.yml — add Sparkle SPM package and target product dependency
  • PureMac/Services/UpdateService.swift — new minimal updater service
  • PureMac/PureMacApp.swift — add Updates command menu and Check for Updates button
  • PureMac/ViewModels/AppState.swift — small concurrency fix (MainActor wrap for Logger call)
  • PureMac.xcodeproj/project.pbxproj — Xcode project updated to include Sparkle/package entries

Why this change

  • Stage 1: Introduce a safe, reviewable integration that does not require signing keys or CI changes.
  • Keeps the codebase ready for a separate Stage 2 PR that would add appcast generation and signed updates.

Related issues

Testing done

  • Built locally: xcodegen generateopen PureMac.xcodeproj → Build succeeded.
  • Runtime: Tested on macOS Tahoe (26). Check for Updates opens Releases when SUFeedURL is not set; when SUFeedURL is present Sparkle's updater is started and checks for updates.

How to test locally

  1. Ensure xcodegen is installed: brew install xcodegen
  2. Generate project: xcodegen generate
  3. Open in Xcode: open PureMac.xcodeproj
  4. Build & run. From the app menu: Updates → Check for Updates.
    • With no SUFeedURL in the app's Info.plist the app opens the GitHub Releases page (safe fallback).
    • When maintainers add a feed URL + signed appcast, Sparkle will handle update checks.

Notes for reviewers

  • This PR is intentionally minimal and conservative. It adds no CI changes, no signing keys, and no production appcast generation.
  • If maintainers want Stage 2 (signed appcast, CI workflow updates), I can prepare a separate PR describing the required secrets and workflow changes.

AI-assistance disclosure

Drafted using Claude Code. Built the project locally to confirm my version compiles. I understand what the code does.

@momenbasel

Copy link
Copy Markdown
Owner

Thanks for the Stage 1 design — the opt-in shape (no automatic checking, GitHub Releases fallback when SUFeedURL is unset) is exactly the right way to land Sparkle without committing to an appcast yet. Two things before this can merge:

1. Rebase against current main. #103 just landed and rewrites the trashDirectly callback signature in AppState.swift, so this PR conflicts. While you're rebasing, please drop the AppState.swift change entirely — wrapping Logger.shared.log in Task { @MainActor in ... } was a workaround for a concurrency warning that #103 fixed by a different route (the whole closure is now hopped onto MainActor via Task { @MainActor in } from the outside). The Sparkle PR shouldn't need to touch AppState at all.

2. Stage 2 follow-up. Please open a tracking issue for Stage 2 covering: appcast generation in the release workflow, EdDSA signing key management (where the private key lives, how releases sign the appcast), Info.plist additions (SUFeedURL, SUPublicEDKey), and any helper-bundle code-sign/notarize implications for Sparkle's Autoupdate.app and Updater.app. I'm happy to land Stage 1 as-is once the conflict is resolved, but I don't want the Sparkle dep sitting in main without a plan for the signed-update path.

Tag #94 so it tracks back to the feature request. Thanks again.

@momenbasel momenbasel force-pushed the feature/add-sparkle-updater branch from a20b521 to 18e3bb3 Compare May 23, 2026 03:48
@momenbasel momenbasel merged commit 5e5d257 into momenbasel:main May 23, 2026
momenbasel added a commit that referenced this pull request May 23, 2026
Bundles three user-facing fixes and one new feature for the next release:

- #103: Protected-app uninstall now routes admin-required paths through
  the vetted CleaningEngine admin cleaner (fixes #93/#95/#97/#102).
- #96: Orphan-file context menu uses activateFileViewerSelecting +
  exposes Copy Path / Move to Trash per row.
- #92: AppInfoFetcher.appSize drops the broken directory-inode fast
  path; sums per-file allocated size so installed-app sizes match
  Finder's Size on disk.
- #101: pt-BR localization, locale parity test guard, Settings language
  picker.
- #99: Sparkle Stage 1 - opt-in UpdateService with Releases-page
  fallback. Signed-appcast work tracked in #104.
momenbasel pushed a commit that referenced this pull request May 24, 2026
Stage 1 of in-app updates. Adds Sparkle as an opt-in dependency.

- New `UpdateService` (singleton) wraps `SPUStandardUpdaterController` started with `startingUpdater: false` so no automatic update checks happen at launch.
- `Updates → Check for Updates` command added to the main menu.
- Safe fallback: when no `SUFeedURL` is configured in Info.plist, the menu item opens https://github.com/momenbasel/PureMac/releases/latest in NSWorkspace instead of starting Sparkle.
- Sparkle SPM package pinned to `from: 2.0.0` in project.yml.

Stage 2 (signed appcast, EdDSA key management, release-workflow integration that makes auto-update actually fire on new releases) is tracked separately.

Rebased onto post-#103 main; the original PR's unrelated AppState concurrency-warning workaround was dropped during rebase. Verified: `xcodebuild build` succeeds on macOS 13 / Xcode 16.

Refs #94.
momenbasel added a commit that referenced this pull request May 24, 2026
Bundles three user-facing fixes and one new feature for the next release:

- #103: Protected-app uninstall now routes admin-required paths through
  the vetted CleaningEngine admin cleaner (fixes #93/#95/#97/#102).
- #96: Orphan-file context menu uses activateFileViewerSelecting +
  exposes Copy Path / Move to Trash per row.
- #92: AppInfoFetcher.appSize drops the broken directory-inode fast
  path; sums per-file allocated size so installed-app sizes match
  Finder's Size on disk.
- #101: pt-BR localization, locale parity test guard, Settings language
  picker.
- #99: Sparkle Stage 1 - opt-in UpdateService with Releases-page
  fallback. Signed-appcast work tracked in #104.
@3manu31 3manu31 deleted the feature/add-sparkle-updater branch May 25, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants