Files
System-Sync-Restore/lib/brew.sh
T
Oliver Surke 87aaf25235 fix: strip deprecated taps from Brewfile; restore Control Centre + menu bar
lib/brew.sh:
  Define _SSR_DEPRECATED_TAPS list (homebrew/core, /cask, /services,
  /test-bot, /bundle, /cask-fonts, /cask-drivers, /cask-versions).
  _ssr_brew_strip_deprecated_taps removes matching 'tap "…"' lines after
  dump so they never cause "Tapping X has failed!" errors on restore.

lib/macos-prefs.sh:
  Add com.apple.controlcenter, com.apple.systemuiserver and
  com.apple.notificationcenterui to SSR_MACOS_PREF_DOMAINS_DEFAULT so
  Control Centre module toggles, menu-bar extras order and Notification
  Centre widgets are included in the pref snapshot on every sync.
  Also add ControlCenter to the post-import service restart list.

lib/macos.sh:
  Add ControlCenter to ssr::macos::restart_ui so the menu bar and
  Control Centre panel pick up restored prefs immediately after mackup.
  Kill cfprefsd first in all restart sequences to flush the pref cache.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 09:55:29 +02:00

163 lines
5.9 KiB
Bash

#!/usr/bin/env bash
# lib/brew.sh — Homebrew install + Brewfile dump/restore.
ssr::brew::ensure_installed() {
if command -v brew >/dev/null 2>&1; then
return 0
fi
# Homebrew's install script requires sudo on macOS to write to /opt/homebrew
# (Apple Silicon) or /usr/local (Intel). Check before running it so we can
# give a clear error instead of a cryptic failure.
if ! sudo -n true 2>/dev/null; then
ssr::warn "Homebrew is not installed and sudo access is required to install it."
ssr::warn "Please run the following as an admin user and re-run ssr restore:"
ssr::warn " /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
ssr::die "Cannot install Homebrew without sudo access."
fi
ssr::log "Homebrew not found — installing"
# NONINTERACTIVE=1 skips the confirmation prompt; sudo is available (checked above).
NONINTERACTIVE=1 /bin/bash -c \
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Ensure brew is on PATH for both Apple Silicon and Intel layouts.
if [[ -x /opt/homebrew/bin/brew ]]; then
eval "$(/opt/homebrew/bin/brew shellenv)"
elif [[ -x /usr/local/bin/brew ]]; then
eval "$(/usr/local/bin/brew shellenv)"
fi
command -v brew >/dev/null 2>&1 || ssr::die "brew still not on PATH after install"
}
ssr::brew::update() {
ssr::log "brew update"
brew update --quiet
}
ssr::brew::upgrade() {
[[ "${SSR_BREW_UPGRADE:-true}" == "true" ]] || return 0
ssr::log "brew upgrade"
brew upgrade --quiet || ssr::warn "brew upgrade reported errors (continuing)"
}
ssr::brew::cleanup() {
[[ "${SSR_BREW_CLEANUP:-true}" == "true" ]] || return 0
ssr::log "brew cleanup"
brew cleanup --quiet || true
}
# Homebrew taps that have been deprecated and emptied upstream. Keeping them
# in the Brewfile causes "Tapping X has failed!" errors on every restore.
# brew bundle dump still emits them if they are locally present, so we strip
# them from the file after dumping.
_SSR_DEPRECATED_TAPS=(
homebrew/core
homebrew/cask
homebrew/services
homebrew/test-bot
homebrew/bundle
homebrew/cask-fonts
homebrew/cask-drivers
homebrew/cask-versions
)
_ssr_brew_strip_deprecated_taps() {
local file="$1"
local pattern
# Build an alternation pattern: homebrew/core|homebrew/cask|…
pattern="$(printf '%s|' "${_SSR_DEPRECATED_TAPS[@]}")"
pattern="${pattern%|}" # strip trailing |
local tmp; tmp="$(mktemp)"
# Match lines like: tap "homebrew/services"
grep -vE "^tap \"(${pattern})\"" "$file" > "$tmp" || true
mv -f "$tmp" "$file"
}
# Dumps Brewfile to the cloud backup dir with a timestamped rotation copy.
ssr::brew::dump() {
local backup_dir="$1" brewfile="$1/Brewfile"
ssr::ensure_dir "$backup_dir"
if [[ -f "$brewfile" ]]; then
cp -f "$brewfile" "$brewfile.bak"
fi
ssr::log "brew bundle dump → $brewfile"
(
cd "$backup_dir"
brew bundle dump --describe --quiet --force --file=Brewfile
)
# Remove deprecated taps that would fail on restore.
local removed
removed="$(grep -cE "^tap \"($(printf '%s|' "${_SSR_DEPRECATED_TAPS[@]}" | sed 's/|$//'))\"" \
"$brewfile" 2>/dev/null || echo 0)"
_ssr_brew_strip_deprecated_taps "$brewfile"
[[ "$removed" -gt 0 ]] && ssr::log "Stripped $removed deprecated tap(s) from Brewfile."
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 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)"
mdimport /Applications 2>/dev/null || true
[[ -d "$HOME/Applications" ]] && mdimport "$HOME/Applications" 2>/dev/null || true
if [[ $rc -ne 0 ]]; then
ssr::warn "brew bundle install finished with errors (rc=$rc) — some packages were not installed."
ssr::warn "Rerun manually: brew bundle install --file=\"$brewfile\""
else
ssr::ok "brew bundle install completed successfully."
fi
}