diff --git a/.local/bin/docker-nuke b/.local/bin/docker-nuke index b7296e2..4acebd1 100755 --- a/.local/bin/docker-nuke +++ b/.local/bin/docker-nuke @@ -785,15 +785,32 @@ main() { local verdict="daemon did not return within ${RESTART_TIMEOUT}s${dump_note}${kill_note}" elapsed=$(( $(date +%s) - started )) - local still_running - still_running="docker-nuke: still running: $(docker_pids | tr '\n' ' ')" - local deep_hint + + # The advice depends on WHY the daemon is absent, and the tell is whether any + # Docker process survived the relaunch. No survivors means Docker Desktop was + # started but did not stay up -- an app crashing on startup, which no + # programmatic relaunch can work around. Survivors mean it is up but the + # daemon is not answering yet: still starting, or wedged. + local survivors dump_hint + survivors="$(docker_pids | tr '\n' ' ')" if [ -n "${DUMP_DIR:-}" ]; then - deep_hint="docker-nuke: try 'docker-nuke --deep', or check disk space in $DUMP_DIR/disk.txt" + dump_hint="docker-nuke: see $DUMP_DIR/summary.txt for triage, and disk.txt for free space" else - deep_hint="docker-nuke: try 'docker-nuke --deep'; no dump was captured this run to check disk space in" + dump_hint="docker-nuke: no dump was captured this run" + fi + + if [ -z "$survivors" ]; then + finish "$verdict" 1 "$elapsed" \ + "docker-nuke: no Docker processes are running -- Docker Desktop was relaunched but did not stay up" \ + "docker-nuke: launch Docker Desktop from the Dock or Spotlight; a programmatic relaunch cannot recover an app that crashes on startup" \ + "docker-nuke: if it keeps crashing, check Console.app for com.docker.backend, then update or reinstall Docker Desktop" \ + "$dump_hint" + else + finish "$verdict" 1 "$elapsed" \ + "docker-nuke: still running: $survivors" \ + "docker-nuke: Docker is up but the daemon is not answering -- it may still be starting; retry, or use 'docker-nuke --deep' to also stop root helpers" \ + "$dump_hint" fi - finish "$verdict" 1 "$elapsed" "$still_running" "$deep_hint" return 1 } diff --git a/.local/bin/test-docker-nuke.sh b/.local/bin/test-docker-nuke.sh index 4e8a1fe..7889b05 100755 --- a/.local/bin/test-docker-nuke.sh +++ b/.local/bin/test-docker-nuke.sh @@ -1246,9 +1246,44 @@ test_main_daemon_never_returns() { rc=$? # not `|| rc=$?` -- see the note on the first main() test above assert_eq "daemon never returns: rc 1" "1" "$rc" assert_contains "daemon never returns: warns it did not return" "$out" "did not return" + + # No surviving Docker processes means the app was relaunched but did not stay + # up, so the advice must point at a manual Dock launch -- NOT at --deep or at + # disk space, which is what a real run got wrong in the field. + assert_contains "no survivors: says Docker did not stay up" "$out" "did not stay up" + assert_contains "no survivors: advises launching from the Dock" "$out" "Dock or Spotlight" + assert_not_contains "no survivors: does NOT suggest --deep" "$out" "--deep" } test_main_daemon_never_returns +test_main_daemon_never_returns_with_survivors() { + local rc out dump_dir="$TMPROOT/main-survivors" + + out="$( + set -euo pipefail + # shellcheck disable=SC2329 # invoked indirectly by main (sourced from docker-nuke) via docker_healthy name lookup + docker_healthy() { return 1; } + # shellcheck disable=SC2329 # invoked indirectly by main (sourced from docker-nuke) via run_dump name lookup + run_dump() { DUMP_DIR="$dump_dir"; mkdir -p "$DUMP_DIR"; return 0; } + # shellcheck disable=SC2329 # invoked indirectly by main (sourced from docker-nuke) via nuke_docker name lookup + nuke_docker() { return 0; } + # shellcheck disable=SC2329 # invoked indirectly by main (sourced from docker-nuke) via start_docker name lookup + start_docker() { return 0; } + # shellcheck disable=SC2329 # invoked indirectly by main (sourced from docker-nuke) via wait_for_daemon name lookup + wait_for_daemon() { return 1; } + # Docker processes ARE alive: it is up but not answering, a different case. + # shellcheck disable=SC2329 # invoked indirectly by main (sourced from docker-nuke) via docker_pids name lookup + docker_pids() { printf '4242\n'; } + main &1 + )" + rc=$? # not `|| rc=$?` -- see the note on the first main() test above + assert_eq "survivors: rc 1" "1" "$rc" + assert_contains "survivors: reports the surviving pid" "$out" "4242" + assert_contains "survivors: suggests --deep for this case" "$out" "--deep" + assert_not_contains "survivors: does NOT advise a Dock launch" "$out" "Dock or Spotlight" +} +test_main_daemon_never_returns_with_survivors + printf '\n=== summary ===\n' teardown_tmp printf 'ran %d, failed %d\n' "$TESTS_RUN" "$TESTS_FAILED"