From 0020316dab553ebdedc154708cdc0410cb7bcac0 Mon Sep 17 00:00:00 2001 From: maxsoch Date: Tue, 7 Jul 2026 05:45:35 +0200 Subject: [PATCH] upgrade casks one at a time so sudo prompts name the app Batched cask upgrades give no per-package hook, so the password dialog could only echo the whole command. upgradeCasksSequentially chains one "upgrade --cask " per outdated cask (from the typed cache), each with an askpass message naming that cask; the status line follows along ("Upgrading tailscale-app..."). Used by both Upgrade All and auto-upgrade; formulae stay batched since they don't need sudo. Explicitly named casks upgrade even when self-updating, so the per-cask commands need no --greedy. Casks that turn outdated only after the preceding brew update surface in the final refetch. Co-Authored-By: Claude Fable 5 --- BrewBar/BrewBarApp.swift | 48 ++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/BrewBar/BrewBarApp.swift b/BrewBar/BrewBarApp.swift index ce7fb7b..0499a28 100644 --- a/BrewBar/BrewBarApp.swift +++ b/BrewBar/BrewBarApp.swift @@ -225,15 +225,39 @@ class AppDelegate: NSObject, NSApplicationDelegate { statusMenuItem.title = "Upgrading..." - // must match the outdated listing: greedy-listed casks are skipped by - // plain "upgrade" and would stay outdated forever - let command = Settings.includeGreedyCasks ? "upgrade --greedy" : "upgrade" - runBrew("update") { _ in - self.runBrew(command) { _ in - // everything was just upgraded; don't chain an auto-upgrade - self.fetchOutdated(allowAutoUpgrade: false) + // packages that turn outdated only after this brew update are not + // in cachedOutdated yet; the final refetch will surface them + 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) + } } + + if self.cachedOutdated.formulae.isEmpty { + upgradeCasksThenRefresh() + } else { + self.runBrew("upgrade --formula") { _ in + upgradeCasksThenRefresh() + } + } + } + } + + /// Upgrades casks one at a time so each sudo prompt can name the app it + /// is for — a batched "upgrade --cask" gives no per-package hook. + func upgradeCasksSequentially(_ names: [String], completion: @escaping () -> Void) { + guard let next = names.first else { + completion() + return + } + + statusMenuItem.title = "Upgrading \(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) } } @@ -321,13 +345,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { func runAutoUpgrade(formulae: Bool, casks: Bool) { statusMenuItem.title = "Auto-upgrading..." + let caskNames = casks ? cachedOutdated.casks.map(\.name) : [] let upgradeCasksThenRefresh = { - if casks { - let command = Settings.includeGreedyCasks ? "upgrade --cask --greedy" : "upgrade --cask" - self.runBrew(command) { _ in - self.fetchOutdated(allowAutoUpgrade: false) - } - } else { + self.upgradeCasksSequentially(caskNames) { self.fetchOutdated(allowAutoUpgrade: false) } } @@ -372,7 +392,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { @objc func upgradeSingle(_ sender: NSMenuItem) { guard let formula = sender.representedObject as? String else { return } - runBrew("upgrade \(formula)") { _ in + runBrew("upgrade \(formula)", askpassMessage: "upgrade \(formula)") { _ in self.refreshAll() } }