From 4222d5a213038bf261c2dc3dfffadd3b919ad996 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 5 Jul 2026 23:00:33 +0200 Subject: [PATCH] ci(smoke): fix state tier hang from bare wait on a lingering daemon The state tier started actually running mpd in 74f0d33, which exposed a deadlock in teardown: state.sh launches `mpd --no-daemon &` and never kills it during the tier, and its cleanup() called smoke::stop *before* killing mpd. smoke::stop ended in a bare `wait`, which reaps *every* background job of the shell -- including the still-running mpd -- so it blocked until GitHub's 6h job timeout. continue-on-error doesn't help: it catches failures, not hangs. - lib.sh: smoke::stop now waits only on the PIDs it owns (waybar, compositor), so an unrelated daemon left running by a tier can't deadlock teardown. - state.sh: cleanup() tears down mpd/pulseaudio before smoke::stop. - smoke.yml: add timeout-minutes: 20 so a future hang fails fast instead of burning the default 6h runner budget. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/smoke.yml | 4 ++++ test/smoke/lib.sh | 9 ++++++++- test/smoke/state.sh | 4 +++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index 001b7ecf..e680aaea 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -29,6 +29,10 @@ env: jobs: smoke: runs-on: ubuntu-latest + # Safety net: the whole smoke job is minutes of work. Cap it so a hung tier + # (e.g. a daemon left running under a bare `wait`) fails fast instead of + # burning the default 6h runner budget. + timeout-minutes: 20 steps: - uses: actions/checkout@v6 diff --git a/test/smoke/lib.sh b/test/smoke/lib.sh index e3848442..82367206 100755 --- a/test/smoke/lib.sh +++ b/test/smoke/lib.sh @@ -194,5 +194,12 @@ smoke::assert_clean_exit() { smoke::stop() { kill "$WAYBAR_PID" 2>/dev/null || true swaymsg -q exit 2>/dev/null || kill "$COMP_PID" 2>/dev/null || true - wait 2>/dev/null || true + # Wait only on the processes we own. A bare `wait` reaps *every* background + # job of the caller's shell, so a tier that leaves an unrelated daemon + # running (state.sh's mpd, killed later in its own cleanup) would deadlock + # here until GitHub's 6h job timeout. + local p + for p in "$WAYBAR_PID" "$COMP_PID"; do + [ -n "$p" ] && wait "$p" 2>/dev/null || true + done } diff --git a/test/smoke/state.sh b/test/smoke/state.sh index 77fb31e2..697347f5 100755 --- a/test/smoke/state.sh +++ b/test/smoke/state.sh @@ -20,9 +20,11 @@ MPD_PID="" PA_STARTED="" cleanup() { - smoke::stop + # Tear our daemons down first: smoke::stop waits on the compositor/waybar, + # and a still-running mpd/pulseaudio must not be left for anything to block on. [ -n "$MPD_PID" ] && kill "$MPD_PID" 2>/dev/null || true [ -n "$PA_STARTED" ] && pulseaudio --kill 2>/dev/null || true + smoke::stop return 0 }