#!/usr/bin/env bash # mac-ops :: update-state.sh # macOS Software Update audit: auto-update settings, pending updates, # update history, App Store update settings. set -u while [[ $# -gt 0 ]]; do case "$1" in --help|-h) cat </dev/null) auto_download=$(defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticDownload 2>/dev/null) auto_install_macos=$(defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates 2>/dev/null) auto_install_app=$(defaults read /Library/Preferences/com.apple.commerce AutoUpdate 2>/dev/null) auto_install_security=$(defaults read /Library/Preferences/com.apple.SoftwareUpdate ConfigDataInstall 2>/dev/null) show_bool() { case "$1" in 1) echo "ON" ;; 0) echo "OFF" ;; *) echo "(unset)" ;; esac } note " Software Update preferences:" note " Check for updates automatically: $(show_bool "$auto_check")" note " Download updates when available: $(show_bool "$auto_download")" note " Install macOS updates: $(show_bool "$auto_install_macos")" note " Install app updates from App Store: $(show_bool "$auto_install_app")" note " Install system data + security: $(show_bool "$auto_install_security")" if [[ "$auto_check" == "1" ]] && [[ "$auto_install_security" == "1" ]]; then log_pass "Security updates" "auto-install ON" else log_warn "Security updates" "auto-install OFF — patches won't apply without manual action" fi # ---------------------------------------------------------------------------- section "2. MACOS VERSION" # ---------------------------------------------------------------------------- prod=$(sw_vers -productName 2>/dev/null) ver=$(sw_vers -productVersion 2>/dev/null) build=$(sw_vers -buildVersion 2>/dev/null) note " $prod $ver ($build)" log_info "macOS version" "$ver build $build" # ---------------------------------------------------------------------------- section "3. PENDING UPDATES" # ---------------------------------------------------------------------------- note " Checking with Apple's servers (this can take 30-60s)..." pending=$(softwareupdate -l 2>&1 | tail -20) if echo "$pending" | grep -q "No new software available"; then log_pass "Pending updates" "none — system is current" elif echo "$pending" | grep -q "Software Update found"; then note " Pending list:" echo "$pending" | grep -E "(\*|^Software|Title:|Action:|Recommended:)" | head -20 | sed 's/^/ /' update_count=$(echo "$pending" | grep -c "Title:" || echo 0) log_warn "Pending updates" "$update_count items pending" else log_info "softwareupdate output" "$(echo "$pending" | head -3 | tr '\n' ' ')" fi # ---------------------------------------------------------------------------- section "4. RECENT UPDATE HISTORY" # ---------------------------------------------------------------------------- history_plist="/Library/Updates/index.plist" note " Last 10 install events from /Library/Updates (best-effort):" if [[ -r "$history_plist" ]]; then # Can't easily parse the plist without knowing the schema; show # mtime of the directory entries as a proxy ls -lt /Library/Updates 2>/dev/null | head -10 | sed 's/^/ /' fi # Also check installer logs recent_installs=$(log show --last 30d --style compact \ --predicate 'process == "softwareupdated" AND eventMessage CONTAINS[c] "installed"' \ 2>/dev/null | tail -5) if [[ -n "$recent_installs" ]]; then note "" note " Recent softwareupdated install entries (log, last 30d):" echo "$recent_installs" | sed 's/^/ /' fi # ---------------------------------------------------------------------------- section "5. MAS / APP STORE" # ---------------------------------------------------------------------------- mas_check=$(defaults read /Library/Preferences/com.apple.commerce AutoUpdate 2>/dev/null) note " App Store auto-update: $(show_bool "$mas_check")" # Is mas installed (third-party MAS CLI)? if command -v mas >/dev/null 2>&1; then note "" note " mas (Mac App Store CLI) is installed." mas_outdated=$(mas outdated 2>/dev/null) if [[ -n "$mas_outdated" ]]; then n=$(echo "$mas_outdated" | wc -l | tr -d ' ') log_info "Pending MAS updates" "$n (via mas outdated)" echo "$mas_outdated" | head -10 | sed 's/^/ /' else log_pass "MAS apps" "all up to date" fi fi # ---------------------------------------------------------------------------- section "6. RECOMMENDED UPDATES" # ---------------------------------------------------------------------------- # Recommended security/system updates that Apple wants you to install # (subset of softwareupdate -l output) recommended=$(softwareupdate -l 2>&1 | awk '/Recommended: YES/{print prev} {prev=$0}' | head -5) if [[ -n "$recommended" ]]; then log_warn "Recommended updates pending" "see list" echo "$recommended" | sed 's/^/ /' fi # ---------------------------------------------------------------------------- emit_summary if [[ "$JSON_MODE" -eq 0 ]]; then echo note " Install playbook:" note " softwareupdate -l # list pending" note " sudo softwareupdate -i -a -R # install all (recommended), reboot if needed" note " softwareupdate --install # specific update" fi