From 488a9a214075908bed49b19db47024cdd9a55b44 Mon Sep 17 00:00:00 2001 From: "Oliver Surke (Gitea)" Date: Tue, 12 May 2026 09:49:34 +0200 Subject: [PATCH] feat: pre-auth sudo once before brew bundle install with keepalive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Homebrew cask installers call sudo internally for each package that needs admin privileges. Without pre-authentication, macOS prompts for a password on every such package across a long brew bundle run. _ssr_sudo_keepalive_start: Calls 'sudo -v' once (prompts user if not already cached), then spawns a background loop that calls 'sudo -n true' every 60s to prevent the default 5-minute credential cache from expiring mid-run. _ssr_sudo_keepalive_stop: Kills the keepalive loop after brew bundle completes. If sudo is unavailable (non-admin user), a warning is shown and the run continues — individual cask installers that need sudo will prompt as before. Co-authored-by: Cursor --- lib/brew.sh | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/lib/brew.sh b/lib/brew.sh index e30a2a4..093de7d 100644 --- a/lib/brew.sh +++ b/lib/brew.sh @@ -62,19 +62,61 @@ ssr::brew::dump() { ssr::ok "Brewfile written ($(wc -l < "$brewfile" | tr -d ' ') lines)" } +# Pre-authenticate sudo once and keep credentials alive in the background. +# macOS caches sudo credentials for 5 minutes (300s); the keepalive loop +# refreshes every 60s so Homebrew cask installers never hit a timeout mid-run. +# +# Usage: +# _ssr_sudo_keepalive_start # prompts once, starts background loop +# … long-running commands … +# _ssr_sudo_keepalive_stop # kills the loop + +_SSR_SUDO_KEEPALIVE_PID="" + +_ssr_sudo_keepalive_start() { + # Check whether sudo is available at all (may already be cached). + if ! sudo -v 2>/dev/null; then + ssr::warn "sudo authentication failed — cask installs may prompt per-package." + return 0 + fi + ssr::ok "sudo credentials cached — Homebrew casks will not prompt during install." + + # Background loop: re-validate every 60s to prevent 5-minute timeout. + ( + while true; do + sleep 60 + sudo -n true 2>/dev/null || break + done + ) & + _SSR_SUDO_KEEPALIVE_PID=$! +} + +_ssr_sudo_keepalive_stop() { + if [[ -n "$_SSR_SUDO_KEEPALIVE_PID" ]]; then + kill "$_SSR_SUDO_KEEPALIVE_PID" 2>/dev/null || true + wait "$_SSR_SUDO_KEEPALIVE_PID" 2>/dev/null || true + _SSR_SUDO_KEEPALIVE_PID="" + fi +} + ssr::brew::restore() { local brewfile="$1" [[ -f "$brewfile" ]] || ssr::die "Brewfile not found: $brewfile" ssr::log "brew bundle install ← $brewfile" + # Authenticate sudo once up front so Homebrew cask installers don't + # prompt individually for each package that needs admin privileges. + _ssr_sudo_keepalive_start + # Suppress per-app Spotlight indexing: mas would otherwise run mdimport - # after every single App Store installation, which adds minutes of latency - # spread across the run. We do one batch mdimport after bundle completes. + # after every App Store installation. One batch pass runs below instead. local rc=0 MAS_NO_AUTO_INDEX=1 brew bundle install --file="$brewfile" --verbose || rc=$? + _ssr_sudo_keepalive_stop + # Single batch Spotlight index of /Applications after all installs. - ssr::log "Indexing /Applications in Spotlight (one-time batch after all installs)" + ssr::log "Indexing /Applications in Spotlight (one-time batch)" mdimport /Applications 2>/dev/null || true [[ -d "$HOME/Applications" ]] && mdimport "$HOME/Applications" 2>/dev/null || true