diff --git a/BrewBar/BrewBarApp.swift b/BrewBar/BrewBarApp.swift index 6a071df..909b414 100644 --- a/BrewBar/BrewBarApp.swift +++ b/BrewBar/BrewBarApp.swift @@ -82,6 +82,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { outdatedItem.submenu = outdatedSubmenu outdatedItem.isHidden = true // shown once a fetch finds outdated packages menu.addItem(outdatedItem) + menu.addItem(NSMenuItem(title: "🧹 Cleanup", action: #selector(cleanupAction), keyEquivalent: "")) + // isAlternate swaps this in for the item right above while ⌥ is held + let purgeItem = NSMenuItem(title: "🧹 Cleanup (purge cache)", action: #selector(cleanupPurgeAction), keyEquivalent: "") + purgeItem.keyEquivalentModifierMask = .option + purgeItem.isAlternate = true + menu.addItem(purgeItem) menu.addItem(NSMenuItem(title: "📜 Logs", action: #selector(showLogs), keyEquivalent: "l")) menu.addItem(NSMenuItem.separator()) @@ -271,6 +277,34 @@ class AppDelegate: NSObject, NSApplicationDelegate { refreshAll() } + @objc func cleanupAction() { + runCleanup(purgeCache: false) + } + + @objc func cleanupPurgeAction() { + runCleanup(purgeCache: true) + } + + /// --prune=all also deletes cached downloads brew would normally keep + /// (current versions); the only cost is re-downloading at next install + func runCleanup(purgeCache: Bool) { + statusMenuItem.title = "Cleaning up..." + runBrew(purgeCache ? "cleanup --prune=all" : "cleanup") { _, _ in + self.updateStatus(count: self.cachedOutdated.count) + } + } + + /// Interposed between the end of an upgrade and its closing refetch; + /// passes straight through unless the user opted in. + func cleanupAfterUpgradeIfEnabled(completion: @escaping () -> Void) { + guard Settings.cleanupAfterUpgrade else { + completion() + return + } + statusMenuItem.title = "Cleaning up..." + runBrew("cleanup") { _, _ in completion() } + } + @objc func upgradeAll() { if Settings.confirmBeforeUpgradeAll { guard confirmUpgrade() else { return } @@ -284,8 +318,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { let caskNames = self.cachedOutdated.casks.map(\.name) let upgradeCasksThenRefresh = { self.upgradeCasksSequentially(caskNames) { - // everything was just upgraded; don't chain an auto-upgrade - self.fetchOutdated(allowAutoUpgrade: false) + self.cleanupAfterUpgradeIfEnabled { + // everything was just upgraded; don't chain an auto-upgrade + self.fetchOutdated(allowAutoUpgrade: false) + } } } @@ -409,7 +445,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { let caskNames = casks ? cachedOutdated.casks.map(\.name) : [] let upgradeCasksThenRefresh = { self.upgradeCasksSequentially(caskNames) { - self.fetchOutdated(allowAutoUpgrade: false) + self.cleanupAfterUpgradeIfEnabled { + self.fetchOutdated(allowAutoUpgrade: false) + } } } @@ -454,7 +492,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { guard let formula = sender.representedObject as? String else { return } runBrew("upgrade \(formula)", askpassMessage: "upgrade \(formula)") { _, _ in - self.refreshAll() + self.cleanupAfterUpgradeIfEnabled { + self.refreshAll() + } } } diff --git a/BrewBar/Settings.swift b/BrewBar/Settings.swift index 60ab21b..24eac13 100644 --- a/BrewBar/Settings.swift +++ b/BrewBar/Settings.swift @@ -44,4 +44,9 @@ enum Settings { get { UserDefaults.standard.bool(forKey: "confirmBeforeUpgradeAll") } set { UserDefaults.standard.set(newValue, forKey: "confirmBeforeUpgradeAll") } } + + static var cleanupAfterUpgrade: Bool { + get { UserDefaults.standard.bool(forKey: "cleanupAfterUpgrade") } + set { UserDefaults.standard.set(newValue, forKey: "cleanupAfterUpgrade") } + } } diff --git a/BrewBar/SettingsWindowController.swift b/BrewBar/SettingsWindowController.swift index f5ad924..935abd5 100644 --- a/BrewBar/SettingsWindowController.swift +++ b/BrewBar/SettingsWindowController.swift @@ -22,6 +22,7 @@ class SettingsWindowController: NSWindowController { var notifyCheckbox: NSButton! var showCountCheckbox: NSButton! var confirmCheckbox: NSButton! + var cleanupCheckbox: NSButton! convenience init(appDelegate: AppDelegate) { let window = NSWindow( @@ -125,6 +126,12 @@ class SettingsWindowController: NSWindowController { action: #selector(toggleConfirm(_:)) ) + cleanupCheckbox = NSButton( + checkboxWithTitle: "Run brew cleanup after upgrades", + target: self, + action: #selector(toggleCleanup(_:)) + ) + let stack = NSStackView(views: [ frequencyRow, customRow, @@ -138,6 +145,7 @@ class SettingsWindowController: NSWindowController { notifyCheckbox, showCountCheckbox, confirmCheckbox, + cleanupCheckbox, launchAtLoginCheckbox, ]) stack.orientation = .vertical @@ -179,6 +187,7 @@ class SettingsWindowController: NSWindowController { notifyCheckbox.state = Settings.notifyOnNewUpdates ? .on : .off showCountCheckbox.state = Settings.showCountInMenuBar ? .on : .off confirmCheckbox.state = Settings.confirmBeforeUpgradeAll ? .on : .off + cleanupCheckbox.state = Settings.cleanupAfterUpgrade ? .on : .off } func separator() -> NSBox { @@ -311,6 +320,10 @@ class SettingsWindowController: NSWindowController { Settings.confirmBeforeUpgradeAll = sender.state == .on } + @objc func toggleCleanup(_ sender: NSButton) { + Settings.cleanupAfterUpgrade = sender.state == .on + } + @objc func toggleLaunchAtLogin(_ sender: NSButton) { do { if sender.state == .on {