fix: restore Sparkle automatic update checks (#1597)

This commit is contained in:
Austin Wang 2026-03-17 03:07:38 -07:00 committed by GitHub
parent f585461868
commit e15825826f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 112 additions and 17 deletions

View file

@ -3,6 +3,47 @@ import Cocoa
import Combine
import SwiftUI
enum UpdateSettings {
static let automaticChecksKey = "SUEnableAutomaticChecks"
static let automaticallyUpdateKey = "SUAutomaticallyUpdate"
static let scheduledCheckIntervalKey = "SUScheduledCheckInterval"
static let sendProfileInfoKey = "SUSendProfileInfo"
static let migrationKey = "cmux.sparkle.automaticChecksMigration.v1"
static let scheduledCheckInterval: TimeInterval = 60 * 60 * 24
static func apply(to defaults: UserDefaults) {
defaults.register(defaults: [
automaticChecksKey: true,
automaticallyUpdateKey: false,
scheduledCheckIntervalKey: scheduledCheckInterval,
sendProfileInfoKey: false,
])
guard !defaults.bool(forKey: migrationKey) else { return }
// Repair older installs that may have ended up with automatic checks disabled
// before the updater defaults were embedded in Info.plist.
defaults.set(true, forKey: automaticChecksKey)
if let interval = defaults.object(forKey: scheduledCheckIntervalKey) as? NSNumber {
if interval.doubleValue <= 0 {
defaults.set(scheduledCheckInterval, forKey: scheduledCheckIntervalKey)
}
} else {
defaults.set(scheduledCheckInterval, forKey: scheduledCheckIntervalKey)
}
if defaults.object(forKey: automaticallyUpdateKey) == nil {
defaults.set(false, forKey: automaticallyUpdateKey)
}
if defaults.object(forKey: sendProfileInfoKey) == nil {
defaults.set(false, forKey: sendProfileInfoKey)
}
defaults.set(true, forKey: migrationKey)
}
}
/// Controller for managing Sparkle updates in cmux.
class UpdateController {
private(set) var updater: SPUUpdater
@ -27,13 +68,8 @@ class UpdateController {
}
init() {
// cmux checks for updates in the background, but keeps automatic download and
// profile submission disabled so all install intent stays user-driven.
let defaults = UserDefaults.standard
defaults.register(defaults: [
"SUSendProfileInfo": false,
"SUAutomaticallyUpdate": false,
])
UpdateSettings.apply(to: defaults)
let hostBundle = Bundle.main
self.userDriver = UpdateDriver(viewModel: .init(), hostBundle: hostBundle)
@ -63,19 +99,22 @@ class UpdateController {
// delegate now suppresses Sparkle's permission UI entirely.
if ProcessInfo.processInfo.environment["CMUX_UI_TEST_RESET_SPARKLE_PERMISSION"] == "1" {
let defaults = UserDefaults.standard
defaults.removeObject(forKey: "SUEnableAutomaticChecks")
defaults.removeObject(forKey: "SUSendProfileInfo")
defaults.removeObject(forKey: "SUAutomaticallyUpdate")
defaults.removeObject(forKey: UpdateSettings.automaticChecksKey)
defaults.removeObject(forKey: UpdateSettings.automaticallyUpdateKey)
defaults.removeObject(forKey: UpdateSettings.scheduledCheckIntervalKey)
defaults.removeObject(forKey: UpdateSettings.sendProfileInfoKey)
defaults.removeObject(forKey: UpdateSettings.migrationKey)
defaults.synchronize()
UpdateLogStore.shared.append("reset sparkle permission defaults (ui test)")
}
#endif
do {
updater.automaticallyChecksForUpdates = true
updater.automaticallyDownloadsUpdates = false
updater.sendsSystemProfile = false
try updater.start()
didStartUpdater = true
let interval = Int(updater.updateCheckInterval.rounded())
UpdateLogStore.shared.append(
"updater started (autoChecks=\(updater.automaticallyChecksForUpdates), interval=\(interval)s, autoDownloads=\(updater.automaticallyDownloadsUpdates))"
)
} catch {
userDriver.viewModel.state = .error(.init(
error: error,