Skip to content

Commit e139ec0

Browse files
committed
Add a hard timeout to the CI smoke harness
1 parent 972d7e2 commit e139ec0

2 files changed

Lines changed: 56 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ All notable changes to `devloop` will be recorded in this file.
88
- Made the CI smoke test wait for file watching to start before editing
99
the watched fixture file, and retry the watched write until the state
1010
change is observed, avoiding startup races on macOS runners.
11+
- Added a hard wall-clock timeout and bounded shutdown to the CI smoke
12+
harness so failed runs die loudly instead of hanging in CI.
1113

1214
### Changed
1315
- Split Linux and macOS CI into separate badgeable workflows backed by

scripts/ci-smoke.sh

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,67 @@ set -euo pipefail
44
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
55
fixture_src="${repo_root}/fixtures/ci-smoke"
66
tmp_dir="$(mktemp -d)"
7+
parent_pid="$$"
8+
smoke_timeout_seconds="${CI_SMOKE_TIMEOUT_SECONDS:-120}"
9+
shutdown_grace_seconds="${CI_SMOKE_SHUTDOWN_GRACE_SECONDS:-10}"
10+
log_path="${tmp_dir}/devloop.log"
11+
12+
dump_log() {
13+
if [[ -n "${log_path}" && -f "${log_path}" ]]; then
14+
echo "----- devloop ci-smoke log -----" >&2
15+
cat "${log_path}" >&2
16+
echo "----- end devloop ci-smoke log -----" >&2
17+
fi
18+
}
19+
20+
stop_devloop() {
21+
if [[ -z "${devloop_pid:-}" ]] || ! kill -0 "${devloop_pid}" 2>/dev/null; then
22+
return
23+
fi
24+
25+
kill -INT "${devloop_pid}" 2>/dev/null || true
26+
local deadline=$((SECONDS + shutdown_grace_seconds))
27+
while kill -0 "${devloop_pid}" 2>/dev/null; do
28+
if (( SECONDS >= deadline )); then
29+
echo "timed out waiting for devloop to exit; forcing kill" >&2
30+
kill -KILL "${devloop_pid}" 2>/dev/null || true
31+
break
32+
fi
33+
sleep 1
34+
done
35+
wait "${devloop_pid}" || true
36+
}
37+
38+
start_watchdog() {
39+
(
40+
sleep "${smoke_timeout_seconds}"
41+
echo "ci-smoke.sh exceeded ${smoke_timeout_seconds}s timeout" >&2
42+
dump_log
43+
if [[ -n "${devloop_pid:-}" ]] && kill -0 "${devloop_pid}" 2>/dev/null; then
44+
kill -TERM "${devloop_pid}" 2>/dev/null || true
45+
sleep 2
46+
kill -KILL "${devloop_pid}" 2>/dev/null || true
47+
fi
48+
kill -TERM "${parent_pid}" 2>/dev/null || true
49+
sleep 2
50+
kill -KILL "${parent_pid}" 2>/dev/null || true
51+
) &
52+
watchdog_pid=$!
53+
}
54+
755
cleanup() {
8-
if [[ -n "${devloop_pid:-}" ]] && kill -0 "${devloop_pid}" 2>/dev/null; then
9-
kill -INT "${devloop_pid}" 2>/dev/null || true
10-
wait "${devloop_pid}" || true
56+
if [[ -n "${watchdog_pid:-}" ]] && kill -0 "${watchdog_pid}" 2>/dev/null; then
57+
kill "${watchdog_pid}" 2>/dev/null || true
1158
fi
59+
stop_devloop
1260
rm -rf "${tmp_dir}"
1361
}
1462
trap cleanup EXIT
63+
trap dump_log ERR
1564

1665
cp -R "${fixture_src}/." "${tmp_dir}/"
1766
chmod +x "${tmp_dir}/scripts/read-watched.sh"
1867

19-
log_path="${tmp_dir}/devloop.log"
2068
state_path="${tmp_dir}/.devloop/state.json"
2169
devloop_bin="${repo_root}/target/debug/devloop"
2270

@@ -26,6 +74,7 @@ fi
2674

2775
"${devloop_bin}" run --config "${tmp_dir}/devloop.toml" >"${log_path}" 2>&1 &
2876
devloop_pid=$!
77+
start_watchdog
2978

3079
python3 - "$state_path" <<'PY'
3180
import json
@@ -87,5 +136,4 @@ print(log_path.read_text(), file=sys.stderr)
87136
raise SystemExit("timed out waiting for changed state")
88137
PY
89138

90-
kill -INT "${devloop_pid}"
91-
wait "${devloop_pid}"
139+
stop_devloop

0 commit comments

Comments
 (0)