add brew cleanup: menu action, ⌥ purge variant, optional post-upgrade run
A Cleanup menu item runs brew cleanup; holding ⌥ swaps it for a purge variant (cleanup --prune=all, which also drops cached downloads of current versions). A new opt-out-by-default setting interposes a plain cleanup between the end of any upgrade path and its closing refetch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
3f130af56a
commit
e2413e52d2
|
|
@ -76,6 +76,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
outdatedItem.submenu = outdatedSubmenu
|
outdatedItem.submenu = outdatedSubmenu
|
||||||
outdatedItem.isHidden = true // shown once a fetch finds outdated packages
|
outdatedItem.isHidden = true // shown once a fetch finds outdated packages
|
||||||
menu.addItem(outdatedItem)
|
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(title: "📜 Logs", action: #selector(showLogs), keyEquivalent: "l"))
|
||||||
|
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
@ -252,6 +258,34 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
refreshAll()
|
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() {
|
@objc func upgradeAll() {
|
||||||
if Settings.confirmBeforeUpgradeAll {
|
if Settings.confirmBeforeUpgradeAll {
|
||||||
guard confirmUpgrade() else { return }
|
guard confirmUpgrade() else { return }
|
||||||
|
|
@ -265,10 +299,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
let caskNames = self.cachedOutdated.casks.map(\.name)
|
let caskNames = self.cachedOutdated.casks.map(\.name)
|
||||||
let upgradeCasksThenRefresh = {
|
let upgradeCasksThenRefresh = {
|
||||||
self.upgradeCasksSequentially(caskNames) {
|
self.upgradeCasksSequentially(caskNames) {
|
||||||
|
self.cleanupAfterUpgradeIfEnabled {
|
||||||
// everything was just upgraded; don't chain an auto-upgrade
|
// everything was just upgraded; don't chain an auto-upgrade
|
||||||
self.fetchOutdated(allowAutoUpgrade: false)
|
self.fetchOutdated(allowAutoUpgrade: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.cachedOutdated.formulae.isEmpty {
|
if self.cachedOutdated.formulae.isEmpty {
|
||||||
upgradeCasksThenRefresh()
|
upgradeCasksThenRefresh()
|
||||||
|
|
@ -382,9 +418,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
let caskNames = casks ? cachedOutdated.casks.map(\.name) : []
|
let caskNames = casks ? cachedOutdated.casks.map(\.name) : []
|
||||||
let upgradeCasksThenRefresh = {
|
let upgradeCasksThenRefresh = {
|
||||||
self.upgradeCasksSequentially(caskNames) {
|
self.upgradeCasksSequentially(caskNames) {
|
||||||
|
self.cleanupAfterUpgradeIfEnabled {
|
||||||
self.fetchOutdated(allowAutoUpgrade: false)
|
self.fetchOutdated(allowAutoUpgrade: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if formulae {
|
if formulae {
|
||||||
runBrew("upgrade --formula") { _ in
|
runBrew("upgrade --formula") { _ in
|
||||||
|
|
@ -427,9 +465,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
guard let formula = sender.representedObject as? String else { return }
|
guard let formula = sender.representedObject as? String else { return }
|
||||||
|
|
||||||
runBrew("upgrade \(formula)", askpassMessage: "upgrade \(formula)") { _ in
|
runBrew("upgrade \(formula)", askpassMessage: "upgrade \(formula)") { _ in
|
||||||
|
self.cleanupAfterUpgradeIfEnabled {
|
||||||
self.refreshAll()
|
self.refreshAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func refreshAll() {
|
func refreshAll() {
|
||||||
statusMenuItem.title = "Updating..."
|
statusMenuItem.title = "Updating..."
|
||||||
|
|
|
||||||
|
|
@ -44,4 +44,9 @@ enum Settings {
|
||||||
get { UserDefaults.standard.bool(forKey: "confirmBeforeUpgradeAll") }
|
get { UserDefaults.standard.bool(forKey: "confirmBeforeUpgradeAll") }
|
||||||
set { UserDefaults.standard.set(newValue, 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") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ class SettingsWindowController: NSWindowController {
|
||||||
var notifyCheckbox: NSButton!
|
var notifyCheckbox: NSButton!
|
||||||
var showCountCheckbox: NSButton!
|
var showCountCheckbox: NSButton!
|
||||||
var confirmCheckbox: NSButton!
|
var confirmCheckbox: NSButton!
|
||||||
|
var cleanupCheckbox: NSButton!
|
||||||
|
|
||||||
convenience init(appDelegate: AppDelegate) {
|
convenience init(appDelegate: AppDelegate) {
|
||||||
let window = NSWindow(
|
let window = NSWindow(
|
||||||
|
|
@ -125,6 +126,12 @@ class SettingsWindowController: NSWindowController {
|
||||||
action: #selector(toggleConfirm(_:))
|
action: #selector(toggleConfirm(_:))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cleanupCheckbox = NSButton(
|
||||||
|
checkboxWithTitle: "Run brew cleanup after upgrades",
|
||||||
|
target: self,
|
||||||
|
action: #selector(toggleCleanup(_:))
|
||||||
|
)
|
||||||
|
|
||||||
let stack = NSStackView(views: [
|
let stack = NSStackView(views: [
|
||||||
frequencyRow,
|
frequencyRow,
|
||||||
customRow,
|
customRow,
|
||||||
|
|
@ -138,6 +145,7 @@ class SettingsWindowController: NSWindowController {
|
||||||
notifyCheckbox,
|
notifyCheckbox,
|
||||||
showCountCheckbox,
|
showCountCheckbox,
|
||||||
confirmCheckbox,
|
confirmCheckbox,
|
||||||
|
cleanupCheckbox,
|
||||||
launchAtLoginCheckbox,
|
launchAtLoginCheckbox,
|
||||||
])
|
])
|
||||||
stack.orientation = .vertical
|
stack.orientation = .vertical
|
||||||
|
|
@ -179,6 +187,7 @@ class SettingsWindowController: NSWindowController {
|
||||||
notifyCheckbox.state = Settings.notifyOnNewUpdates ? .on : .off
|
notifyCheckbox.state = Settings.notifyOnNewUpdates ? .on : .off
|
||||||
showCountCheckbox.state = Settings.showCountInMenuBar ? .on : .off
|
showCountCheckbox.state = Settings.showCountInMenuBar ? .on : .off
|
||||||
confirmCheckbox.state = Settings.confirmBeforeUpgradeAll ? .on : .off
|
confirmCheckbox.state = Settings.confirmBeforeUpgradeAll ? .on : .off
|
||||||
|
cleanupCheckbox.state = Settings.cleanupAfterUpgrade ? .on : .off
|
||||||
}
|
}
|
||||||
|
|
||||||
func separator() -> NSBox {
|
func separator() -> NSBox {
|
||||||
|
|
@ -311,6 +320,10 @@ class SettingsWindowController: NSWindowController {
|
||||||
Settings.confirmBeforeUpgradeAll = sender.state == .on
|
Settings.confirmBeforeUpgradeAll = sender.state == .on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func toggleCleanup(_ sender: NSButton) {
|
||||||
|
Settings.cleanupAfterUpgrade = sender.state == .on
|
||||||
|
}
|
||||||
|
|
||||||
@objc func toggleLaunchAtLogin(_ sender: NSButton) {
|
@objc func toggleLaunchAtLogin(_ sender: NSButton) {
|
||||||
do {
|
do {
|
||||||
if sender.state == .on {
|
if sender.state == .on {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue