quickrun.sh 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. #!/usr/bin/env bash
  2. # mac-ops :: quickrun.sh
  3. # One-shot "what's wrong with my Mac?" runner. Sequences the 5 highest-yield
  4. # audits and emits a single consolidated report.
  5. #
  6. # This is the script to run if you have 60 seconds and want to know whether
  7. # something needs attention. For deep dives, drill into individual scripts.
  8. set -u
  9. SHORT_DAYS=7
  10. while [[ $# -gt 0 ]]; do
  11. case "$1" in
  12. --days) SHORT_DAYS="$2"; shift 2 ;;
  13. --help|-h)
  14. cat <<EOF
  15. Usage: $0 [options]
  16. --days N Lookback for log-based audits (default: 7)
  17. --json Emit consolidated NDJSON
  18. --redact Mask private addrs / hostnames / serials
  19. Runs (in order):
  20. 1. health-audit — orchestrator across 8 rungs
  21. 2. startup-audit — login items + launchd inventory
  22. 3. storage-pressure — APFS snapshots + caches breakdown
  23. 4. wake-reasons — pmset log analysis (last week)
  24. 5. tcc-audit (denied) — what privacy permissions were denied
  25. Output: one consolidated SUMMARY at end with all PASS/FAIL counts and the
  26. top 3 issues to address.
  27. Time: 60-90 seconds typical (log show queries dominate).
  28. EOF
  29. exit 0 ;;
  30. *) shift ;;
  31. esac
  32. done
  33. source "$(dirname "$0")/_lib/common.sh"
  34. parse_common_flags "$@"
  35. maybe_filter_self "$@"
  36. SCRIPTS_DIR="$(dirname "$0")"
  37. # Aggregate counters across all sub-scripts
  38. TOTAL_PASS=0
  39. TOTAL_FAIL=0
  40. TOTAL_WARN=0
  41. TOTAL_INFO=0
  42. ALL_FAILS=()
  43. ALL_WARNS=()
  44. run_subscript() {
  45. local label="$1"
  46. local script="$2"
  47. shift 2
  48. note ""
  49. note "════════════════════════════════════════════════════════════════"
  50. note " $label"
  51. note "════════════════════════════════════════════════════════════════"
  52. # Capture sub-script output (suppress its own SUMMARY since we'll do our own)
  53. local out
  54. out=$(bash "$SCRIPTS_DIR/$script" --quiet "$@" 2>&1 || true)
  55. # Echo the sub-script's findings (everything before its SUMMARY)
  56. echo "$out" | sed -n '/^=== /,/^=== SUMMARY ===/p' | sed '/^=== SUMMARY ===/q' | sed '$d'
  57. # Extract sub-script's pass/fail/warn counts from its SUMMARY line
  58. local summary_line
  59. summary_line=$(echo "$out" | grep -E "^ PASS: [0-9]+" | head -1)
  60. if [[ -n "$summary_line" ]]; then
  61. local pass fail warn info
  62. pass=$(echo "$summary_line" | grep -oE "PASS: [0-9]+" | awk '{print $2}')
  63. fail=$(echo "$summary_line" | grep -oE "FAIL: [0-9]+" | awk '{print $2}')
  64. warn=$(echo "$summary_line" | grep -oE "WARN: [0-9]+" | awk '{print $2}')
  65. info=$(echo "$summary_line" | grep -oE "INFO: [0-9]+" | awk '{print $2}')
  66. TOTAL_PASS=$((TOTAL_PASS + ${pass:-0}))
  67. TOTAL_FAIL=$((TOTAL_FAIL + ${fail:-0}))
  68. TOTAL_WARN=$((TOTAL_WARN + ${warn:-0}))
  69. TOTAL_INFO=$((TOTAL_INFO + ${info:-0}))
  70. fi
  71. # Collect FAIL and WARN lines for the consolidated summary
  72. while IFS= read -r line; do
  73. [[ -n "$line" ]] && ALL_FAILS+=("[$label] $line")
  74. done < <(echo "$out" | grep -E "^\[FAIL\]" | sed 's/^\[FAIL\] //')
  75. while IFS= read -r line; do
  76. [[ -n "$line" ]] && ALL_WARNS+=("[$label] $line")
  77. done < <(echo "$out" | grep -E "^\[WARN\]" | sed 's/^\[WARN\] //')
  78. }
  79. note " Starting mac-ops quickrun (this takes ~60-90s due to log show queries)..."
  80. run_subscript "1. HEALTH AUDIT" "health-audit.sh" --days "$SHORT_DAYS"
  81. run_subscript "2. STARTUP INVENTORY" "startup-audit.sh"
  82. run_subscript "3. STORAGE PRESSURE" "storage-pressure.sh"
  83. run_subscript "4. WAKE REASONS (last ${SHORT_DAYS}d)" "wake-reasons.sh" --since "${SHORT_DAYS}d"
  84. run_subscript "5. TCC DENIALS" "tcc-audit.sh" --denied
  85. # ----------------------------------------------------------------------------
  86. note ""
  87. note "════════════════════════════════════════════════════════════════"
  88. note " CONSOLIDATED VERDICT"
  89. note "════════════════════════════════════════════════════════════════"
  90. if [[ "$JSON_MODE" -eq 1 ]]; then
  91. printf '{"type":"quickrun_summary","pass":%d,"fail":%d,"warn":%d,"info":%d,"fail_count":%d,"warn_count":%d}\n' \
  92. "$TOTAL_PASS" "$TOTAL_FAIL" "$TOTAL_WARN" "$TOTAL_INFO" \
  93. "${#ALL_FAILS[@]}" "${#ALL_WARNS[@]}"
  94. else
  95. echo
  96. echo " Aggregate: PASS $TOTAL_PASS FAIL $TOTAL_FAIL WARN $TOTAL_WARN INFO $TOTAL_INFO"
  97. echo
  98. if [[ "${#ALL_FAILS[@]}" -gt 0 ]]; then
  99. echo " ⚠ FAILURES (${#ALL_FAILS[@]}):"
  100. for f in ${ALL_FAILS[@]+"${ALL_FAILS[@]}"}; do
  101. echo " • $f"
  102. done | head -10
  103. fi
  104. if [[ "${#ALL_WARNS[@]}" -gt 0 ]]; then
  105. echo
  106. echo " ⚠ WARNINGS (${#ALL_WARNS[@]}):"
  107. for w in ${ALL_WARNS[@]+"${ALL_WARNS[@]}"}; do
  108. echo " • $w"
  109. done | head -10
  110. fi
  111. if [[ "${#ALL_FAILS[@]}" -eq 0 ]] && [[ "${#ALL_WARNS[@]}" -eq 0 ]]; then
  112. echo " ✓ System looks clean. No FAILs or WARNs across 5 audits."
  113. fi
  114. echo
  115. echo " Next steps:"
  116. echo " - Drill into any FAIL: see 'Next:' lines emitted by each sub-script"
  117. echo " - Run individual scripts with --verbose for more detail"
  118. echo " - See references/worked-examples.md for diagnostic patterns"
  119. fi