diff --git a/.local/bin/docker-nuke b/.local/bin/docker-nuke index d3f780f..2dcab19 100755 --- a/.local/bin/docker-nuke +++ b/.local/bin/docker-nuke @@ -55,6 +55,38 @@ OPT_DEEP=0 bin_exists() { command -v "$1" >/dev/null 2>&1; } +# Run a command with a hard time limit. Returns 124 if it timed out. +# Call inside a conditional so `set -e` does not abort on a non-zero return. +run_timeout() { + local secs="$1" + shift + if bin_exists timeout; then + timeout "$secs" "$@" + return $? + fi + if bin_exists gtimeout; then + gtimeout "$secs" "$@" + return $? + fi + + # Pure-bash fallback: run in background and poll. + "$@" & + local pid=$! + local waited=0 + while kill -0 "$pid" 2>/dev/null; do + if [ "$waited" -ge "$secs" ]; then + kill -9 "$pid" 2>/dev/null || true + wait "$pid" 2>/dev/null || true + return 124 + fi + sleep 1 + waited=$((waited + 1)) + done + local rc=0 + wait "$pid" || rc=$? + return "$rc" +} + log() { printf '%s\n' "$*"; } warn() { printf '%s\n' "$*" >&2; } diff --git a/.local/bin/test-docker-nuke.sh b/.local/bin/test-docker-nuke.sh index 053ae43..08d97e0 100755 --- a/.local/bin/test-docker-nuke.sh +++ b/.local/bin/test-docker-nuke.sh @@ -93,6 +93,44 @@ test_parse_args() { } test_parse_args +printf '\n=== run_timeout ===\n' + +test_run_timeout() { + local out rc + + out="$(run_timeout 5 printf 'hello')" + assert_eq "returns command stdout" "hello" "$out" + + run_timeout 5 true + assert_eq "propagates success" "0" "$?" + + run_timeout 5 false + assert_eq "propagates failure" "1" "$?" + + rc=0 + run_timeout 1 sleep 5 || rc=$? + assert_eq "returns 124 on timeout" "124" "$rc" + + # Force the pure-bash fallback by making bin_exists report nothing available. + # The override is scoped to a subshell so later tests still see the real one. + rc=0 + ( + # shellcheck disable=SC2329 # invoked indirectly by run_timeout's name lookup + bin_exists() { return 1; } + run_timeout 1 sleep 5 + ) || rc=$? + assert_eq "fallback path also returns 124" "124" "$rc" + + rc=0 + out="$( + # shellcheck disable=SC2329 # invoked indirectly by run_timeout's name lookup + bin_exists() { return 1; } + run_timeout 5 printf 'fallback-ok' + )" || rc=$? + assert_eq "fallback path returns stdout" "fallback-ok" "$out" +} +test_run_timeout + printf '\n=== summary ===\n' teardown_tmp printf 'ran %d, failed %d\n' "$TESTS_RUN" "$TESTS_FAILED"