| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- #!/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 <<EOF
- Usage: $0 [options]
- --json, --redact, --quiet, --verbose
- Reports:
- 1. macOS Software Update auto-policy
- 2. Pending updates (softwareupdate -l)
- 3. macOS version + build
- 4. Update install history (last 10)
- 5. App Store auto-update settings
- 6. Pending app updates from Mac App Store
- Skip the spinner: 'softwareupdate -l' contacts Apple's servers and can take
- 30-60 seconds. The script prints expected-delay markers.
- EOF
- exit 0 ;;
- *) shift ;;
- esac
- done
- source "$(dirname "$0")/_lib/common.sh"
- parse_common_flags "$@"
- maybe_filter_self "$@"
- # ----------------------------------------------------------------------------
- section "1. AUTO-UPDATE POLICY"
- # ----------------------------------------------------------------------------
- # Read from com.apple.SoftwareUpdate
- auto_check=$(defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticCheckEnabled 2>/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 <name> # specific update"
- fi
|