devAlice
← Mac

Mac-Entwicklungsumgebung wöchentlich warten — brew · Toolchains · SDKs mit einem Befehl aktualisieren

Ein einzelnes wöchentliches Shell-Skript, das Homebrew, Mac App Store, npm globals, rustup, Cargo-Binärdateien, pipx, Flutter, CocoaPods und oh-my-zsh aktualisiert.

Man steckt Zeit in die Einrichtung eines neuen Geräts — und vergisst dann, es aktuell zu halten. Zwei Monate später schleppt ein einzelnes brew install 30 transitive Abhängigkeiten mit, native Builds schlagen wegen SDK-Nichtübereinstimmungen fehl, und die Sicherheitszertifikate (ca-certificates) driften still dem Ablauf entgegen.

Ich denke, was eine wöchentliche Update-Routine so wertvoll macht, ist nicht das Updaten selbst, sondern das Wissen, wann ein Update gebrochen ist — weil ein integriertes Skript, das jeden Schritt meldet, sofortiges Feedback gibt, statt dass man Wochen später mysteriöse Build-Fehler debuggt. Früher habe ich Updates ad hoc durchgeführt; heute ist das wöchentliche Skript mein Standard, da ich verstanden habe, dass koordiniertes Updaten weniger Reibung erzeugt als zufällige Einzelupdates.

Dieser Artikel liefert das 9-Schritte-Integrationsscript, das genau das verhindert. Einmal pro Woche ausführen — fertig.

TL;DR

  • Führt brew + mas + npm -g + rustup + cargo install-update + pipx + flutter + pod + omz der Reihe nach aus
  • Jeder Schritt überspringt automatisch, wenn das Tool nicht installiert ist, und ein Fehler in einem stoppt den Rest nicht
  • Endet mit einem nur zur Information macOS-System-Update-Check (kein Auto-Install — Neustarts sind zu riskant)
  • ~100 Zeilen einfaches bash

Warum der Aufwand

1. Zertifikat-Ablauf

ca-certificates liefert jedes Quartal ein neues Bundle. Zwei Quartale überspringen und einige HTTPS-Aufrufe schlagen fehl. Du denkst vielleicht, es betrifft dich nicht, aber npm-Registry, Homebrew selbst und die GitHub-API sind alle betroffen.

2. Mobile Build SDK-Drift

Besonders riskant auf Flutter/iOS-Geräten. Flutter stable wird 3–4 Mal pro Monat ausgeliefert; das CocoaPods-Spec-Repo wirft bei pod install „spec not found", wenn es eine Woche nicht synchronisiert wurde. Ein wöchentliches pod repo update allein beseitigt 80 % dieses Schmerzes.

3. Sprach-Toolchain-Anhäufung

Globals, die über rustup, pipx oder cargo installiert sind, benötigen jeweils ihre eigenen Updates. Vergiss es, und eines Tages wirst du entdecken, dass cargo install das Neueste nicht holen kann wegen rust-version-Nichtübereinstimmung.

Voraussetzungen

  • macOS 12+ + Homebrew (Mac-Ersteinrichtung)
  • Für die vollen 9 Schritte: mas, pipx, cargo-update, flutter, cocoapods, oh-my-zsh vorinstalliert
  • Einige Tools fehlen ist in Ordnung — diese Schritte werden SKIP elegant

Einmalige Vorbereitung

brew install mas pipx cocoapods
brew install --cask flutter
cargo install cargo-update
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
pipx ensurepath

cargo install cargo-update kompiliert den ersten Lauf — das dauert 2–3 Minuten. Flutter ist beim ersten Download ~1 GB groß.

9 Update-Schritte

#SchrittBefehlHinweise
1Homebrewbrew update && brew upgrade && brew cleanupFormeln + Casks
2Mac App Storemas upgradeApp-Store-Apps (Xcode usw.)
3npm globalsnpm update -gGlobale npm-Pakete
4Rust-Toolchainrustup updateStable-Kanal
5Cargo-Binärdateiencargo install-update -aErfordert cargo-update-Erweiterung
6pipxpipx upgrade-allGlobale Python-Tools
7Flutter SDKflutter upgradeAktuellste des aktuellen Kanals
8CocoaPods-Repopod repo updateiOS-Build-Spec-Synchronisierung
9oh-my-zsh~/.oh-my-zsh/tools/upgrade.shzsh-Framework

Das integrierte Skript

Als z. B. ~/bin/update-system.sh speichern. chmod +x und verwenden.

#!/usr/bin/env bash
#
# Bulk-System-Tool-Update — macOS
# Verwendung: ./update-system.sh
#
# Umfang:
#   1. Homebrew (Formeln + Casks)
#   2. Mac App Store (mas, wenn installiert)
#   3. npm globals
#   4. Rust-Toolchain (rustup)
#   5. Cargo-Binärdateien (cargo install-update -a, benötigt cargo-update)
#   6. pipx (Globale Python-Tools)
#   7. Flutter SDK (flutter upgrade)
#   8. CocoaPods-Repo (pod repo update — iOS-Build-Dep)
#   9. oh-my-zsh (omz update)
#
# Endet mit `softwareupdate -l` Hinweis (kein Auto-Install — Neustart-Risiko)
#
 
set -uo pipefail
 
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
GRAY='\033[0;90m'
NC='\033[0m'
 
TOTAL=9
STEP=0
 
step() {
    STEP=$((STEP + 1))
    printf "\n${CYAN}[%d/%d] %s${NC}\n" "$STEP" "$TOTAL" "$1"
}
 
ok()   { printf "  ${GREEN}OK${NC} %s\n" "$1"; }
skip() { printf "  ${GRAY}SKIP${NC} %s\n" "$1"; }
warn() { printf "  ${YELLOW}WARN${NC} %s\n" "$1"; }
fail() { printf "  ${RED}FAIL${NC} %s\n" "$1"; }
 
printf "${CYAN}━━━ System-Update (macOS) ━━━${NC}\n"
 
# fnm initialisieren, damit npm-Schritt Node finden kann
if command -v fnm >/dev/null 2>&1; then
    eval "$(fnm env --use-on-cd --shell bash 2>/dev/null)" 2>/dev/null || true
fi
 
# 1. Homebrew
step "Homebrew (Formeln + Casks)"
if command -v brew >/dev/null 2>&1; then
    if brew update && brew upgrade && brew cleanup; then
        ok "brew update / upgrade / cleanup"
    else
        fail "brew-Schritt teilweise fehlgeschlagen — Log oben prüfen"
    fi
else
    skip "brew nicht installiert"
fi
 
# 2. Mac App Store
step "Mac App Store (mas)"
if command -v mas >/dev/null 2>&1; then
    mas upgrade && ok "mas upgrade" || fail "mas upgrade"
else
    skip "mas nicht installiert (brew install mas)"
fi
 
# 3. npm globals
step "npm globals"
if command -v npm >/dev/null 2>&1; then
    npm update -g && ok "npm update -g" || fail "npm update -g"
else
    skip "npm nicht installiert"
fi
 
# 4. Rust-Toolchain
step "Rust-Toolchain (rustup)"
if command -v rustup >/dev/null 2>&1; then
    rustup update && ok "rustup update" || fail "rustup update"
else
    skip "rustup nicht installiert"
fi
 
# 5. Cargo-Binärdateien
step "Cargo-Binärdateien (cargo install-update -a)"
if command -v cargo >/dev/null 2>&1; then
    if cargo install-update --version >/dev/null 2>&1; then
        cargo install-update -a && ok "cargo install-update -a" || fail "cargo install-update -a"
    else
        warn "cargo-update fehlt (installieren: cargo install cargo-update)"
    fi
else
    skip "cargo nicht installiert"
fi
 
# 6. pipx
step "pipx (Globale Python-Tools)"
if command -v pipx >/dev/null 2>&1; then
    pipx upgrade-all && ok "pipx upgrade-all" || fail "pipx upgrade-all"
else
    skip "pipx nicht installiert (brew install pipx)"
fi
 
# 7. Flutter SDK
step "Flutter SDK (flutter upgrade)"
if command -v flutter >/dev/null 2>&1; then
    flutter upgrade && ok "flutter upgrade" || fail "flutter upgrade"
else
    skip "flutter nicht installiert"
fi
 
# 8. CocoaPods-Repo
step "CocoaPods-Repo (pod repo update)"
if command -v pod >/dev/null 2>&1; then
    pod repo update && ok "pod repo update" || fail "pod repo update"
else
    skip "pod nicht installiert (brew install cocoapods)"
fi
 
# 9. oh-my-zsh
step "oh-my-zsh (omz update)"
if [ -d "$HOME/.oh-my-zsh" ]; then
    OMZ_UPDATER="$HOME/.oh-my-zsh/tools/upgrade.sh"
    if [ -x "$OMZ_UPDATER" ]; then
        zsh "$OMZ_UPDATER" && ok "oh-my-zsh aktualisiert" || fail "oh-my-zsh"
    else
        skip "oh-my-zsh upgrade.sh fehlt"
    fi
else
    skip "oh-my-zsh nicht installiert"
fi
 
# macOS-System-Update — nur zur Information
printf "\n${CYAN}━━━ macOS System-Update-Check (nur zur Information) ━━━${NC}\n"
if command -v softwareupdate >/dev/null 2>&1; then
    SU_OUT=$(softwareupdate -l 2>&1)
    if echo "$SU_OUT" | grep -qi "no new software\|No updates"; then
        printf "${GRAY}  Aktuell — keine System-Updates${NC}\n"
    else
        printf "${YELLOW}  ⚠ System-Updates verfügbar:${NC}\n"
        echo "$SU_OUT" | sed 's/^/    /'
        printf "${GRAY}  Installieren mit: sudo softwareupdate -ia --restart  (startet neu)${NC}\n"
    fi
fi
 
printf "\n${CYAN}━━━ Fertig ━━━${NC}\n"
printf "${GRAY}Checks: brew doctor / brew outdated / npm outdated -g / flutter doctor${NC}\n"

Beispielausgabe

Der erste Lauf ist lang (5 Minuten, wenn brew 13 ausstehende Upgrades hat). Folgeläufe dauern 1–2 Minuten.

━━━ System-Update (macOS) ━━━

[1/9] Homebrew (Formeln + Casks)
==> Upgrading 13 outdated packages:
ca-certificates 2026-03-19 -> 2026-05-14
ruby 4.0.3 -> 4.0.4
sqlite 3.53.0 -> 3.53.1
python@3.14 3.14.4_1 -> 3.14.5
...
==> This operation has freed approximately 18.8MB of disk space.
  OK brew update / upgrade / cleanup

[2/9] Mac App Store (mas)
  OK mas upgrade

[3/9] npm globals
  OK npm update -g

[4/9] Rust-Toolchain (rustup)
  stable-aarch64-apple-darwin unchanged - rustc 1.95.0
  OK rustup update

...

[9/9] oh-my-zsh (omz update)
Hooray! Oh My Zsh has been updated!
  OK oh-my-zsh aktualisiert

━━━ macOS System-Update-Check (nur zur Information) ━━━
  Aktuell — keine System-Updates

━━━ Fertig ━━━

Automatisierung — Wie weit?

Vollständige Automatisierung wird nicht empfohlen. Große brew-Versions-Sprünge (z. B. vercel-cli 53→54) bringen gelegentlich Breaking Changes mit, die man direkt nach dem Update bemerken sollte.

Ein vernünftiger Mittelweg ist eine Erinnerung, kein Auto-Run:

~/Library/LaunchAgents/local.update-reminder.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>local.update-reminder</string>
  <key>ProgramArguments</key>
  <array>
    <string>osascript</string>
    <string>-e</string>
    <string>display notification "Wöchentlicher update-system Check" with title "Dev Maintenance"</string>
  </array>
  <key>StartCalendarInterval</key>
  <dict>
    <key>Weekday</key><integer>5</integer>
    <key>Hour</key><integer>10</integer>
    <key>Minute</key><integer>0</integer>
  </dict>
</dict>
</plist>

Aktivieren:

launchctl load -w ~/Library/LaunchAgents/local.update-reminder.plist

Jeden Freitag um 10:00 Uhr eine einzelne Desktop-Benachrichtigung. Du entscheidest, wann du ausführst.

Stolperfallen

  • Kein Auto-Install für macOS-System-Updates: softwareupdate -ia --restart erzwingt einen Neustart. IDE, Terminals, Docker-Container — alles weg. Das Skript listet nur verfügbare Updates auf; die Installation erfolgt manuell zu einem selbst gewählten Zeitpunkt.
  • pod repo update ist langsam (5+ Minuten möglich): Das Trunk-Repo ist groß — umso länger, je mehr Wochen übersprungen wurden.
  • Oh My Zsh hat eine eigene Auto-Update-Option: .zshrc-Einstellung zstyle ':omz:update' mode auto. Harmlose Überschneidung mit diesem Skript.
  • Cargo kompiliert: cargo install-update -a kompiliert frische Versionen installierter Binärdateien — der erste Lauf kann mehrere Minuten dauern.

Windows-Nutzer?

Dieselbe Idee in PowerShell, abdeckend MSYS2 + winget + Sprach-Toolchains → Windows-Entwicklungsumgebung wöchentliche Wartung.

Zusammenfassung

  • Einzeln aktualisieren → geht in Vergessenheit
  • Einzelnes integriertes Skript → einfach ausführen, fertig
  • Automatisierung nur als Erinnerung, niemals als Auto-Run
  • Einmal pro Woche verhindert brew-Abhängigkeits-Stürme, SDK-Drift und Zertifikat-Ablauf