From 62c783d5b3722aafcf13ddf811d8366f3a952c0b Mon Sep 17 00:00:00 2001 From: amanthanvi Date: Sun, 28 Dec 2025 10:49:00 -0500 Subject: [PATCH 1/3] Fix macOS update detection timeout --- lib/check/all.sh | 13 +++++++------ tests/system_maintenance.bats | 23 ++++++++++++++++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/lib/check/all.sh b/lib/check/all.sh index 0c12274..ac05e27 100644 --- a/lib/check/all.sh +++ b/lib/check/all.sh @@ -236,7 +236,7 @@ check_macos_update() { if [[ $(get_software_updates) == "Updates Available" ]]; then updates_available="true" - # Verify with softwareupdate -l (short timeout) to reduce false positives + # Verify with softwareupdate using --no-scan (fast) to reduce false positives local sw_output="" local sw_status=0 local spinner_started=false @@ -245,7 +245,9 @@ check_macos_update() { spinner_started=true fi - if ! sw_output=$(run_with_timeout 5 softwareupdate -l 2> /dev/null); then + if sw_output=$(run_with_timeout 10 softwareupdate -l --no-scan 2> /dev/null); then + sw_status=0 + else sw_status=$? fi @@ -253,10 +255,9 @@ check_macos_update() { stop_inline_spinner fi - # If command failed, timed out, or returned empty, treat as no updates to avoid false positives - if [[ $sw_status -ne 0 || -z "$sw_output" ]]; then - updates_available="false" - elif echo "$sw_output" | grep -q "No new software available"; then + # Prefer avoiding false negatives: if the system indicates updates are pending, + # only clear the flag when softwareupdate explicitly reports no updates. + if [[ $sw_status -eq 0 && -n "$sw_output" ]] && echo "$sw_output" | grep -q "No new software available"; then updates_available="false" fi fi diff --git a/tests/system_maintenance.bats b/tests/system_maintenance.bats index 0daf044..8c5e8a3 100644 --- a/tests/system_maintenance.bats +++ b/tests/system_maintenance.bats @@ -186,25 +186,38 @@ EOF [[ "$output" == *"COUNT=0"* ]] } -@test "check_macos_update warns when update available" { +@test "check_macos_update avoids slow softwareupdate scans" { run bash --noprofile --norc <<'EOF' set -euo pipefail source "$PROJECT_ROOT/lib/core/common.sh" source "$PROJECT_ROOT/lib/check/all.sh" -softwareupdate() { - echo "* Label: macOS 99" - return 0 +defaults() { echo "1"; } + +run_with_timeout() { + shift + if [[ "${1:-}" == "softwareupdate" && "${2:-}" == "-l" && "${3:-}" == "--no-scan" ]]; then + cat <<'OUT' +Software Update Tool + +Software Update found the following new or updated software: +* Label: macOS 99 +OUT + return 0 + fi + return 124 } start_inline_spinner(){ :; } stop_inline_spinner(){ :; } check_macos_update +echo "MACOS_UPDATE_AVAILABLE=$MACOS_UPDATE_AVAILABLE" EOF [ "$status" -eq 0 ] - [[ "$output" == *"macOS"* ]] + [[ "$output" == *"Update available"* ]] + [[ "$output" == *"MACOS_UPDATE_AVAILABLE=true"* ]] } @test "run_with_timeout succeeds without GNU timeout" { From 014e15f7216304bef474a4b28e45b23c732daf60 Mon Sep 17 00:00:00 2001 From: amanthanvi Date: Sun, 28 Dec 2025 10:59:40 -0500 Subject: [PATCH 2/3] Add macOS update check edge case tests --- tests/system_maintenance.bats | 85 +++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/tests/system_maintenance.bats b/tests/system_maintenance.bats index 8c5e8a3..dc93696 100644 --- a/tests/system_maintenance.bats +++ b/tests/system_maintenance.bats @@ -220,6 +220,91 @@ EOF [[ "$output" == *"MACOS_UPDATE_AVAILABLE=true"* ]] } +@test "check_macos_update clears update flag when softwareupdate reports no updates" { + run bash --noprofile --norc <<'EOF' +set -euo pipefail +source "$PROJECT_ROOT/lib/core/common.sh" +source "$PROJECT_ROOT/lib/check/all.sh" + +defaults() { echo "1"; } + +run_with_timeout() { + shift + if [[ "${1:-}" == "softwareupdate" && "${2:-}" == "-l" && "${3:-}" == "--no-scan" ]]; then + cat <<'OUT' +Software Update Tool + +Finding available software +No new software available. +OUT + return 0 + fi + return 124 +} + +start_inline_spinner(){ :; } +stop_inline_spinner(){ :; } + +check_macos_update +echo "MACOS_UPDATE_AVAILABLE=$MACOS_UPDATE_AVAILABLE" +EOF + + [ "$status" -eq 0 ] + [[ "$output" == *"System up to date"* ]] + [[ "$output" == *"MACOS_UPDATE_AVAILABLE=false"* ]] +} + +@test "check_macos_update keeps update flag when softwareupdate times out" { + run bash --noprofile --norc <<'EOF' +set -euo pipefail +source "$PROJECT_ROOT/lib/core/common.sh" +source "$PROJECT_ROOT/lib/check/all.sh" + +defaults() { echo "1"; } + +run_with_timeout() { + shift + if [[ "${1:-}" == "softwareupdate" && "${2:-}" == "-l" && "${3:-}" == "--no-scan" ]]; then + return 124 + fi + return 124 +} + +start_inline_spinner(){ :; } +stop_inline_spinner(){ :; } + +check_macos_update +echo "MACOS_UPDATE_AVAILABLE=$MACOS_UPDATE_AVAILABLE" +EOF + + [ "$status" -eq 0 ] + [[ "$output" == *"Update available"* ]] + [[ "$output" == *"MACOS_UPDATE_AVAILABLE=true"* ]] +} + +@test "check_macos_update skips softwareupdate when defaults shows no updates" { + run bash --noprofile --norc <<'EOF' +set -euo pipefail +source "$PROJECT_ROOT/lib/core/common.sh" +source "$PROJECT_ROOT/lib/check/all.sh" + +defaults() { echo "0"; } + +run_with_timeout() { + echo "SHOULD_NOT_CALL_SOFTWAREUPDATE" + return 0 +} + +check_macos_update +echo "MACOS_UPDATE_AVAILABLE=$MACOS_UPDATE_AVAILABLE" +EOF + + [ "$status" -eq 0 ] + [[ "$output" == *"System up to date"* ]] + [[ "$output" == *"MACOS_UPDATE_AVAILABLE=false"* ]] + [[ "$output" != *"SHOULD_NOT_CALL_SOFTWAREUPDATE"* ]] +} + @test "run_with_timeout succeeds without GNU timeout" { run bash --noprofile --norc -c ' set -euo pipefail From 9239571be193834d20f0d30614b166da539c25c8 Mon Sep 17 00:00:00 2001 From: amanthanvi Date: Sun, 28 Dec 2025 11:18:12 -0500 Subject: [PATCH 3/3] Tighten macOS update check and add empty-output test --- lib/check/all.sh | 4 +-- tests/system_maintenance.bats | 52 +++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/lib/check/all.sh b/lib/check/all.sh index ac05e27..5062db6 100644 --- a/lib/check/all.sh +++ b/lib/check/all.sh @@ -246,7 +246,7 @@ check_macos_update() { fi if sw_output=$(run_with_timeout 10 softwareupdate -l --no-scan 2> /dev/null); then - sw_status=0 + : else sw_status=$? fi @@ -257,7 +257,7 @@ check_macos_update() { # Prefer avoiding false negatives: if the system indicates updates are pending, # only clear the flag when softwareupdate explicitly reports no updates. - if [[ $sw_status -eq 0 && -n "$sw_output" ]] && echo "$sw_output" | grep -q "No new software available"; then + if [[ $sw_status -eq 0 && -n "$sw_output" ]] && echo "$sw_output" | grep -qE '^[[:space:]]*No new software available'; then updates_available="false" fi fi diff --git a/tests/system_maintenance.bats b/tests/system_maintenance.bats index dc93696..4d19b03 100644 --- a/tests/system_maintenance.bats +++ b/tests/system_maintenance.bats @@ -195,7 +195,12 @@ source "$PROJECT_ROOT/lib/check/all.sh" defaults() { echo "1"; } run_with_timeout() { + local timeout="${1:-}" shift + if [[ "$timeout" != "10" ]]; then + echo "BAD_TIMEOUT:$timeout" + return 124 + fi if [[ "${1:-}" == "softwareupdate" && "${2:-}" == "-l" && "${3:-}" == "--no-scan" ]]; then cat <<'OUT' Software Update Tool @@ -218,6 +223,7 @@ EOF [ "$status" -eq 0 ] [[ "$output" == *"Update available"* ]] [[ "$output" == *"MACOS_UPDATE_AVAILABLE=true"* ]] + [[ "$output" != *"BAD_TIMEOUT:"* ]] } @test "check_macos_update clears update flag when softwareupdate reports no updates" { @@ -229,7 +235,12 @@ source "$PROJECT_ROOT/lib/check/all.sh" defaults() { echo "1"; } run_with_timeout() { + local timeout="${1:-}" shift + if [[ "$timeout" != "10" ]]; then + echo "BAD_TIMEOUT:$timeout" + return 124 + fi if [[ "${1:-}" == "softwareupdate" && "${2:-}" == "-l" && "${3:-}" == "--no-scan" ]]; then cat <<'OUT' Software Update Tool @@ -252,6 +263,7 @@ EOF [ "$status" -eq 0 ] [[ "$output" == *"System up to date"* ]] [[ "$output" == *"MACOS_UPDATE_AVAILABLE=false"* ]] + [[ "$output" != *"BAD_TIMEOUT:"* ]] } @test "check_macos_update keeps update flag when softwareupdate times out" { @@ -263,7 +275,12 @@ source "$PROJECT_ROOT/lib/check/all.sh" defaults() { echo "1"; } run_with_timeout() { + local timeout="${1:-}" shift + if [[ "$timeout" != "10" ]]; then + echo "BAD_TIMEOUT:$timeout" + return 124 + fi if [[ "${1:-}" == "softwareupdate" && "${2:-}" == "-l" && "${3:-}" == "--no-scan" ]]; then return 124 fi @@ -280,6 +297,41 @@ EOF [ "$status" -eq 0 ] [[ "$output" == *"Update available"* ]] [[ "$output" == *"MACOS_UPDATE_AVAILABLE=true"* ]] + [[ "$output" != *"BAD_TIMEOUT:"* ]] +} + +@test "check_macos_update keeps update flag when softwareupdate returns empty output" { + run bash --noprofile --norc <<'EOF' +set -euo pipefail +source "$PROJECT_ROOT/lib/core/common.sh" +source "$PROJECT_ROOT/lib/check/all.sh" + +defaults() { echo "1"; } + +run_with_timeout() { + local timeout="${1:-}" + shift + if [[ "$timeout" != "10" ]]; then + echo "BAD_TIMEOUT:$timeout" + return 124 + fi + if [[ "${1:-}" == "softwareupdate" && "${2:-}" == "-l" && "${3:-}" == "--no-scan" ]]; then + return 0 + fi + return 124 +} + +start_inline_spinner(){ :; } +stop_inline_spinner(){ :; } + +check_macos_update +echo "MACOS_UPDATE_AVAILABLE=$MACOS_UPDATE_AVAILABLE" +EOF + + [ "$status" -eq 0 ] + [[ "$output" == *"Update available"* ]] + [[ "$output" == *"MACOS_UPDATE_AVAILABLE=true"* ]] + [[ "$output" != *"BAD_TIMEOUT:"* ]] } @test "check_macos_update skips softwareupdate when defaults shows no updates" {