diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index 603523e80..01031c9a5 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -75,7 +75,10 @@ class AppDelegate: NSObject, override init() { terminalManager = TerminalManager(ghostty) updaterController = SPUStandardUpdaterController( - startingUpdater: true, + // Important: we must not start the updater here because we need to read our configuration + // first to determine whether we're automatically checking, downloading, etc. The updater + // is started later in applicationDidFinishLaunching + startingUpdater: false, updaterDelegate: updaterDelegate, userDriverDelegate: nil ) @@ -109,6 +112,9 @@ class AppDelegate: NSObject, // Initial config loading configDidReload(ghostty) + // Start our update checker. + updaterController.startUpdater() + // Register our service provider. This must happen after everything is initialized. NSApp.servicesProvider = ServiceProvider() @@ -376,6 +382,12 @@ class AppDelegate: NSObject, default: UserDefaults.standard.removeObject(forKey: "NSQuitAlwaysKeepsWindows") } + // Sync our auto-update settings + updaterController.updater.automaticallyChecksForUpdates = + ghostty.config.autoUpdate == .check || ghostty.config.autoUpdate == .download + updaterController.updater.automaticallyDownloadsUpdates = + ghostty.config.autoUpdate == .download + // Config could change keybindings, so update everything that depends on that syncMenuShortcuts() terminalManager.relabelAllTabs() diff --git a/macos/Sources/Ghostty/Ghostty.Config.swift b/macos/Sources/Ghostty/Ghostty.Config.swift index e917bf2c0..8ea9371fe 100644 --- a/macos/Sources/Ghostty/Ghostty.Config.swift +++ b/macos/Sources/Ghostty/Ghostty.Config.swift @@ -360,12 +360,29 @@ extension Ghostty { _ = ghostty_config_get(config, &v, key, UInt(key.count)) return v; } + + var autoUpdate: AutoUpdate { + let defaultValue = AutoUpdate.check + guard let config = self.config else { return defaultValue } + var v: UnsafePointer? = nil + let key = "auto-update" + guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return defaultValue } + guard let ptr = v else { return defaultValue } + let str = String(cString: ptr) + return AutoUpdate(rawValue: str) ?? defaultValue + } } } // MARK: Configuration Enums extension Ghostty.Config { + enum AutoUpdate : String { + case off + case check + case download + } + enum ResizeOverlay : String { case always case never diff --git a/src/config/Config.zig b/src/config/Config.zig index d853f0481..c6c5e8c94 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -1462,6 +1462,30 @@ term: []const u8 = "xterm-ghostty", /// running. Defaults to an empty string if not set. @"enquiry-response": []const u8 = "", +/// Control the auto-update functionality of Ghostty. This is only supported +/// on macOS currently, since Linux builds are distributed via package +/// managers that are not centrally controlled by Ghostty. +/// +/// Checking or downloading an update does not send any information to +/// the project beyond standard network information mandated by the +/// underlying protocols. To put it another way: Ghostty doesn't explicitly +/// add any tracking to the update process. The update process works by +/// downloading information about the latest version and comparing it +/// client-side to the current version. +/// +/// Valid values are: +/// +/// * `off` - Disable auto-updates. +/// * `check` - Check for updates and notify the user if an update is +/// available, but do not automatically download or install the update. +/// * `download` - Check for updates, automatically download the update, +/// notify the user, but do not automatically install the update. +/// +/// The default value is `check`. +/// +/// Changing this value at runtime works after a small delay. +@"auto-update": AutoUpdate = .check, + /// This is set by the CLI parser for deinit. _arena: ?ArenaAllocator = null, @@ -4096,6 +4120,13 @@ pub const LinuxCgroup = enum { @"single-instance", }; +/// See auto-updates +pub const AutoUpdate = enum { + off, + check, + download, +}; + pub const Duration = struct { /// Duration in nanoseconds duration: u64 = 0,