surface brew failures instead of reporting a false all-up-to-date
runBrew now reports the process exit status to its completion handler. refreshAll and fetchOutdated stop on failure and show "Check failed — see Logs": NWPathMonitor cannot see a network that is up but broken (captive portal, dead DNS, git host down), while the exit code of the brew command itself catches every failure mode. Upgrade commands keep ignoring the flag on purpose — a failed upgrade leaves the package in the outdated list at the next fetch, so the final state stays correct. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c6372ece05
commit
64b9bae564
|
|
@ -220,7 +220,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
|
||||
/// askpassMessage customizes the sudo dialog ("...password to <message>.");
|
||||
/// defaults to naming the brew command.
|
||||
func runBrew(_ command: String, askpassMessage: String? = nil, completion: @escaping (String) -> Void = { _ in }) {
|
||||
func runBrew(_ command: String, askpassMessage: String? = nil, completion: @escaping (_ output: String, _ success: Bool) -> Void = { _, _ in }) {
|
||||
Self.brewQueue.async {
|
||||
let brew = self.resolveBrewPath()
|
||||
let cleaned = command.replacingOccurrences(of: "brew ", with: "")
|
||||
|
|
@ -246,7 +246,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
try process.run()
|
||||
} catch {
|
||||
self.log("ERROR: \(error)\n")
|
||||
DispatchQueue.main.async { completion("") }
|
||||
DispatchQueue.main.async { completion("", false) }
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -256,8 +256,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
let output = String(data: data, encoding: .utf8) ?? ""
|
||||
|
||||
self.log(output)
|
||||
let success = process.terminationStatus == 0
|
||||
if !success {
|
||||
self.log("✗ exited with status \(process.terminationStatus)\n")
|
||||
}
|
||||
// completion always on main: callers update AppKit UI, which is main-thread only
|
||||
DispatchQueue.main.async { completion(output) }
|
||||
DispatchQueue.main.async { completion(output, success) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -274,7 +278,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
|
||||
statusMenuItem.title = "Upgrading..."
|
||||
|
||||
runBrew("update") { _ in
|
||||
runBrew("update") { _, _ in
|
||||
// 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)
|
||||
|
|
@ -288,7 +292,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
if self.cachedOutdated.formulae.isEmpty {
|
||||
upgradeCasksThenRefresh()
|
||||
} else {
|
||||
self.runBrew("upgrade --formula") { _ in
|
||||
self.runBrew("upgrade --formula") { _, _ in
|
||||
upgradeCasksThenRefresh()
|
||||
}
|
||||
}
|
||||
|
|
@ -305,7 +309,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
|
||||
statusMenuItem.title = "Upgrading \(next)..."
|
||||
// an explicitly named cask upgrades even when self-updating, no --greedy needed
|
||||
runBrew("upgrade --cask \(next)", askpassMessage: "upgrade \(next)") { _ in
|
||||
runBrew("upgrade --cask \(next)", askpassMessage: "upgrade \(next)") { _, _ in
|
||||
self.upgradeCasksSequentially(Array(names.dropFirst()), completion: completion)
|
||||
}
|
||||
}
|
||||
|
|
@ -343,7 +347,15 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
// --greedy also lists casks that self-update (brew skips them by default)
|
||||
let command = Settings.includeGreedyCasks ? "outdated --greedy --json" : "outdated --json"
|
||||
|
||||
runBrew(command) { output in
|
||||
runBrew(command) { output, success in
|
||||
// parsing a failure's output would yield .none and masquerade as
|
||||
// "all up to date"; keep the cached state and its icon instead
|
||||
guard success else {
|
||||
self.statusMenuItem.title = "⚠️ Check failed — see Logs"
|
||||
self.setMenuBarIcon(self.cachedOutdated.isEmpty ? "brewbar-uptodate" : "brewbar-outdated")
|
||||
return
|
||||
}
|
||||
|
||||
let outdated = BrewParser.parseOutdatedJSON(output)
|
||||
|
||||
self.cachedOutdated = outdated
|
||||
|
|
@ -402,7 +414,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
}
|
||||
|
||||
if formulae {
|
||||
runBrew("upgrade --formula") { _ in
|
||||
runBrew("upgrade --formula") { _, _ in
|
||||
upgradeCasksThenRefresh()
|
||||
}
|
||||
} else {
|
||||
|
|
@ -441,7 +453,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
@objc func upgradeSingle(_ sender: NSMenuItem) {
|
||||
guard let formula = sender.representedObject as? String else { return }
|
||||
|
||||
runBrew("upgrade \(formula)", askpassMessage: "upgrade \(formula)") { _ in
|
||||
runBrew("upgrade \(formula)", askpassMessage: "upgrade \(formula)") { _, _ in
|
||||
self.refreshAll()
|
||||
}
|
||||
}
|
||||
|
|
@ -472,14 +484,21 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
|
||||
statusMenuItem.title = "Updating..."
|
||||
|
||||
runBrew("update") { _ in
|
||||
// NWPathMonitor can't see a network that is up but broken (captive
|
||||
// portal, dead DNS, git host down); brew update's exit code can.
|
||||
// Proceeding anyway would read stale local data and report a false OK.
|
||||
runBrew("update") { _, success in
|
||||
guard success else {
|
||||
self.statusMenuItem.title = "⚠️ Check failed — see Logs"
|
||||
return
|
||||
}
|
||||
self.fetchOutdated() // ✅ ONLY place calling outdated
|
||||
}
|
||||
}
|
||||
|
||||
/// ✅ Brew version (correct)
|
||||
func refreshBrewVersion() {
|
||||
runBrew("--version") { output in
|
||||
runBrew("--version") { output, _ in
|
||||
let firstLine = BrewParser.parseVersion(output)
|
||||
self.versionItem.title = "🏷 \(firstLine)"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue