Compare commits
No commits in common. "149f394ecf4ca136d768ecbefda1850c65669262" and "6a1c92a6fdd5f40952e90131396919edc00d0a24" have entirely different histories.
149f394ecf
...
6a1c92a6fd
|
|
@ -18,9 +18,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
var statusMenuItem: NSMenuItem!
|
var statusMenuItem: NSMenuItem!
|
||||||
var outdatedItem: NSMenuItem!
|
var outdatedItem: NSMenuItem!
|
||||||
var upgradeAllItem: NSMenuItem!
|
var upgradeAllItem: NSMenuItem!
|
||||||
var upgradeOnlyItem: NSMenuItem!
|
|
||||||
var upgradeFormulaeItem: NSMenuItem!
|
|
||||||
var upgradeCasksItem: NSMenuItem!
|
|
||||||
var outdatedSubmenu: NSMenu!
|
var outdatedSubmenu: NSMenu!
|
||||||
var versionItem: NSMenuItem!
|
var versionItem: NSMenuItem!
|
||||||
|
|
||||||
|
|
@ -77,18 +74,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
menu.addItem(NSMenuItem(title: "🔄 Refresh", action: #selector(refreshAction), keyEquivalent: "r"))
|
menu.addItem(NSMenuItem(title: "🔄 Refresh", action: #selector(refreshAction), keyEquivalent: "r"))
|
||||||
upgradeAllItem = NSMenuItem(title: "📦 Upgrade formulae & casks", action: #selector(upgradeAll), keyEquivalent: "u")
|
upgradeAllItem = NSMenuItem(title: "📦 Upgrade All", action: #selector(upgradeAll), keyEquivalent: "u")
|
||||||
upgradeAllItem.isHidden = true // shown once a fetch finds outdated packages
|
upgradeAllItem.isHidden = true // shown once a fetch finds outdated packages
|
||||||
menu.addItem(upgradeAllItem)
|
menu.addItem(upgradeAllItem)
|
||||||
upgradeOnlyItem = NSMenuItem(title: "🎯 Upgrade only…", action: nil, keyEquivalent: "")
|
|
||||||
let upgradeOnlySubmenu = NSMenu()
|
|
||||||
upgradeFormulaeItem = NSMenuItem(title: "Formulae", action: #selector(upgradeFormulaeOnly), keyEquivalent: "")
|
|
||||||
upgradeCasksItem = NSMenuItem(title: "Casks", action: #selector(upgradeCasksOnly), keyEquivalent: "")
|
|
||||||
upgradeOnlySubmenu.addItem(upgradeFormulaeItem)
|
|
||||||
upgradeOnlySubmenu.addItem(upgradeCasksItem)
|
|
||||||
upgradeOnlyItem.submenu = upgradeOnlySubmenu
|
|
||||||
upgradeOnlyItem.isHidden = true // shown only when both kinds are outdated
|
|
||||||
menu.addItem(upgradeOnlyItem)
|
|
||||||
outdatedItem = NSMenuItem(title: "📋 Outdated", action: nil, keyEquivalent: "")
|
outdatedItem = NSMenuItem(title: "📋 Outdated", action: nil, keyEquivalent: "")
|
||||||
outdatedSubmenu = NSMenu()
|
outdatedSubmenu = NSMenu()
|
||||||
outdatedItem.submenu = outdatedSubmenu
|
outdatedItem.submenu = outdatedSubmenu
|
||||||
|
|
@ -318,20 +306,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func upgradeAll() {
|
@objc func upgradeAll() {
|
||||||
upgrade(formulae: true, casks: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func upgradeFormulaeOnly() {
|
|
||||||
upgrade(formulae: true, casks: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func upgradeCasksOnly() {
|
|
||||||
upgrade(formulae: false, casks: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func upgrade(formulae: Bool, casks: Bool) {
|
|
||||||
if Settings.confirmBeforeUpgradeAll {
|
if Settings.confirmBeforeUpgradeAll {
|
||||||
guard confirmUpgrade(formulae: formulae, casks: casks) else { return }
|
guard confirmUpgrade() else { return }
|
||||||
}
|
}
|
||||||
|
|
||||||
statusMenuItem.title = "Upgrading..."
|
statusMenuItem.title = "Upgrading..."
|
||||||
|
|
@ -339,22 +315,22 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
runBrew("update") { _, _ in
|
runBrew("update") { _, _ in
|
||||||
// packages that turn outdated only after this brew update are not
|
// packages that turn outdated only after this brew update are not
|
||||||
// in cachedOutdated yet; the final refetch will surface them
|
// in cachedOutdated yet; the final refetch will surface them
|
||||||
let caskNames = casks ? self.cachedOutdated.casks.map(\.name) : []
|
let caskNames = self.cachedOutdated.casks.map(\.name)
|
||||||
let upgradeCasksThenRefresh = {
|
let upgradeCasksThenRefresh = {
|
||||||
self.upgradeCasksSequentially(caskNames) {
|
self.upgradeCasksSequentially(caskNames) {
|
||||||
self.cleanupAfterUpgradeIfEnabled {
|
self.cleanupAfterUpgradeIfEnabled {
|
||||||
// a manual upgrade just ran; 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 formulae, !self.cachedOutdated.formulae.isEmpty {
|
if self.cachedOutdated.formulae.isEmpty {
|
||||||
|
upgradeCasksThenRefresh()
|
||||||
|
} else {
|
||||||
self.runBrew("upgrade --formula") { _, _ in
|
self.runBrew("upgrade --formula") { _, _ in
|
||||||
upgradeCasksThenRefresh()
|
upgradeCasksThenRefresh()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
upgradeCasksThenRefresh()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -368,49 +344,27 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
statusMenuItem.title = "Upgrading \(next)..."
|
statusMenuItem.title = "Upgrading \(next)..."
|
||||||
upgradeCask(next) {
|
// an explicitly named cask upgrades even when self-updating, no --greedy needed
|
||||||
|
runBrew("upgrade --cask \(next)", askpassMessage: "upgrade \(next)") { _, _ in
|
||||||
self.upgradeCasksSequentially(Array(names.dropFirst()), completion: completion)
|
self.upgradeCasksSequentially(Array(names.dropFirst()), completion: completion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Self-updating apps (Zen, Chrome, …) can drift from what brew
|
func confirmUpgrade() -> Bool {
|
||||||
/// installed, and brew then refuses to upgrade the cask in place —
|
|
||||||
/// it only prints a warning naming the fix. Run that fix for it.
|
|
||||||
/// (An explicitly named cask upgrades even when self-updating, no
|
|
||||||
/// --greedy needed.)
|
|
||||||
func upgradeCask(_ name: String, completion: @escaping () -> Void) {
|
|
||||||
runBrew("upgrade --cask \(name)", askpassMessage: "upgrade \(name)") { output, _ in
|
|
||||||
guard output.contains("cannot be upgraded as-is") else {
|
|
||||||
completion()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.runBrew("reinstall --cask --force \(name)", askpassMessage: "reinstall \(name)") { _, _ in
|
|
||||||
completion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func confirmUpgrade(formulae: Bool, casks: Bool) -> Bool {
|
|
||||||
let packages = (formulae ? cachedOutdated.formulae : []) + (casks ? cachedOutdated.casks : [])
|
|
||||||
|
|
||||||
let alert = NSAlert()
|
let alert = NSAlert()
|
||||||
alert.alertStyle = .warning
|
alert.alertStyle = .warning
|
||||||
|
|
||||||
if packages.isEmpty {
|
if cachedOutdated.isEmpty {
|
||||||
alert.messageText = "Upgrade all outdated packages?"
|
alert.messageText = "Upgrade all outdated packages?"
|
||||||
} else {
|
} else {
|
||||||
alert.messageText = "Upgrade \(packages.count) package\(packages.count == 1 ? "" : "s")?"
|
alert.messageText = "Upgrade \(cachedOutdated.count) package\(cachedOutdated.count == 1 ? "" : "s")?"
|
||||||
}
|
}
|
||||||
|
|
||||||
// the running-app warning only applies when casks are being upgraded
|
var info = "Apps upgraded as casks (like a web browser) may be closed and replaced while running, without further warning. Save your work first."
|
||||||
var lines: [String] = []
|
if !cachedOutdated.isEmpty {
|
||||||
if casks {
|
info += "\n\n" + cachedOutdated.all.map(\.label).joined(separator: "\n")
|
||||||
lines.append("Apps upgraded as casks (like a web browser) may be closed and replaced while running, without further warning. Save your work first.")
|
|
||||||
}
|
}
|
||||||
if !packages.isEmpty {
|
alert.informativeText = info
|
||||||
lines.append(packages.map(\.label).joined(separator: "\n"))
|
|
||||||
}
|
|
||||||
alert.informativeText = lines.joined(separator: "\n\n")
|
|
||||||
|
|
||||||
alert.addButton(withTitle: "Upgrade")
|
alert.addButton(withTitle: "Upgrade")
|
||||||
alert.addButton(withTitle: "Cancel")
|
alert.addButton(withTitle: "Cancel")
|
||||||
|
|
@ -510,11 +464,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
outdatedSubmenu.removeAllItems()
|
outdatedSubmenu.removeAllItems()
|
||||||
outdatedItem.isHidden = outdated.isEmpty
|
outdatedItem.isHidden = outdated.isEmpty
|
||||||
upgradeAllItem.isHidden = outdated.isEmpty
|
upgradeAllItem.isHidden = outdated.isEmpty
|
||||||
// "only" is meaningful only when both kinds are outdated; with a
|
|
||||||
// single kind, the main item already upgrades exactly what there is
|
|
||||||
upgradeOnlyItem.isHidden = outdated.formulae.isEmpty || outdated.casks.isEmpty
|
|
||||||
upgradeFormulaeItem.title = "Formulae (\(outdated.formulae.count))"
|
|
||||||
upgradeCasksItem.title = "Casks (\(outdated.casks.count))"
|
|
||||||
|
|
||||||
for package in outdated.all {
|
for package in outdated.all {
|
||||||
let item = NSMenuItem(title: package.label, action: #selector(upgradeSingle(_:)), keyEquivalent: "")
|
let item = NSMenuItem(title: package.label, action: #selector(upgradeSingle(_:)), keyEquivalent: "")
|
||||||
|
|
@ -540,21 +489,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func upgradeSingle(_ sender: NSMenuItem) {
|
@objc func upgradeSingle(_ sender: NSMenuItem) {
|
||||||
guard let name = sender.representedObject as? String else { return }
|
guard let formula = sender.representedObject as? String else { return }
|
||||||
|
|
||||||
let finish = {
|
runBrew("upgrade \(formula)", askpassMessage: "upgrade \(formula)") { _, _ in
|
||||||
self.cleanupAfterUpgradeIfEnabled {
|
self.cleanupAfterUpgradeIfEnabled {
|
||||||
self.refreshAll()
|
self.refreshAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// casks go through upgradeCask for the reinstall fallback; a bare
|
|
||||||
// "brew upgrade <name>" would hit the same warning and do nothing
|
|
||||||
if cachedOutdated.casks.contains(where: { $0.name == name }) {
|
|
||||||
upgradeCask(name, completion: finish)
|
|
||||||
} else {
|
|
||||||
runBrew("upgrade \(name)", askpassMessage: "upgrade \(name)") { _, _ in finish() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refreshing offline would silently succeed on stale data: brew update
|
/// Refreshing offline would silently succeed on stale data: brew update
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ class SettingsWindowController: NSWindowController {
|
||||||
)
|
)
|
||||||
|
|
||||||
confirmCheckbox = NSButton(
|
confirmCheckbox = NSButton(
|
||||||
checkboxWithTitle: "Ask for confirmation before bulk upgrades",
|
checkboxWithTitle: "Ask for confirmation before Upgrade All",
|
||||||
target: self,
|
target: self,
|
||||||
action: #selector(toggleConfirm(_:))
|
action: #selector(toggleConfirm(_:))
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue