From 5b1820b7e1734c0dd7357794134923b011e38026 Mon Sep 17 00:00:00 2001 From: maxsoch Date: Tue, 7 Jul 2026 05:59:48 +0200 Subject: [PATCH] fix: run brew commands on a serial queue, one at a time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit runBrew dispatched onto the global concurrent queue, so overlapping operations (refresh timer firing mid-upgrade, quick manual actions) ran multiple brew processes at once — each able to summon its own sudo password dialog, hence duplicate password windows. A private serial queue makes every brew command wait for the previous one, so a second dialog cannot appear while one is open. Log output no longer interleaves either. Co-Authored-By: Claude Fable 5 --- BrewBar/BrewBarApp.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/BrewBar/BrewBarApp.swift b/BrewBar/BrewBarApp.swift index 0499a28..22c96b7 100644 --- a/BrewBar/BrewBarApp.swift +++ b/BrewBar/BrewBarApp.swift @@ -169,10 +169,15 @@ class AppDelegate: NSObject, NSApplicationDelegate { return url } + /// Serial: brew commands run strictly one after another. Concurrent brew + /// processes could each summon their own sudo password dialog (and their + /// log output interleaved); queueing them prevents both. + static let brewQueue = DispatchQueue(label: "fr.socheleau.BrewBar.brew") + /// askpassMessage customizes the sudo dialog ("...password to ."); /// defaults to naming the brew command. func runBrew(_ command: String, askpassMessage: String? = nil, completion: @escaping (String) -> Void = { _ in }) { - DispatchQueue.global().async { + Self.brewQueue.async { let brew = self.resolveBrewPath() let cleaned = command.replacingOccurrences(of: "brew ", with: "") let arguments = [brew] + cleaned.split(separator: " ").map(String.init)