Skip to content

Commit aa43cdd

Browse files
committed
fix(ci): also stop the detached telemetry flush an HTTP end-to-end app leaves behind
`next dev` spawns its telemetry flush detached, in a session of its own, even with telemetry disabled. That process outlives the app's session and writes .next/dev/trace after stop-session.sh returned, so the next app's `rm -rf .next/dev` could fail with "Directory not empty". Each app now starts under an E2E_APP tag that every process it spawns inherits, and stop-session.sh stops and waits for tagged processes as well as the session's members.
1 parent 43bab1f commit aa43cdd

2 files changed

Lines changed: 43 additions & 27 deletions

File tree

‎.github/scripts/stop-session.sh‎

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,52 @@
11
#!/usr/bin/env bash
2-
# Stops every process in a session started with `setsid`, and returns only once none is left.
2+
# Stops every process an app started, and returns only once none is left.
33
#
4-
# Usage: stop-session.sh <session-leader-pid>
4+
# Usage: stop-session.sh <session-leader-pid> <app-tag>
55
#
6-
# `next dev` runs its server and workers as child processes. Waiting on `next dev` alone returns
7-
# while those can still be running, and the next app in the job reuses the same .next directory.
8-
# Every member of the session is signalled, including one that moved to its own process group,
9-
# and one that started after the first signal.
6+
# Start the app as `E2E_APP=<app-tag> setsid <command> &`. Its processes are the members of the
7+
# leader's session, plus any process that inherited `E2E_APP=<app-tag>` but left the session.
8+
# `next dev` needs both: its server and workers stay in the session, while its telemetry flush is
9+
# spawned detached, in a session of its own, and writes .next/dev/trace after `next dev` exits.
10+
# The next app in the job wipes that directory, so it must not start while any of them runs.
11+
#
12+
# Every member is signalled, including one that moved to its own process group, and one that
13+
# started after the first signal.
1014
#
1115
# Zombies don't count as running: they have exited and only await reaping. The leader is one
1216
# until the shell that started it waits on it, which that shell does after this returns.
1317
set -u
1418

1519
leader=$1
20+
tag=$2
1621
grace_seconds=10
1722

18-
session_pids() {
19-
ps -s "$leader" -o pid=,stat= 2>/dev/null | awk '$2 !~ /^Z/ { print $1 }'
23+
app_pids() {
24+
{
25+
ps -s "$leader" -o pid=,stat= 2>/dev/null | awk '$2 !~ /^Z/ { print $1 }'
26+
grep -lzx "E2E_APP=$tag" /proc/[0-9]*/environ 2>/dev/null | cut -d/ -f3
27+
} | sort -u
2028
}
2129

22-
signal_session() {
30+
signal_app() {
2331
local pids
24-
pids=$(session_pids)
32+
pids=$(app_pids)
2533
[ -z "$pids" ] || kill "-$1" $pids 2>/dev/null || true
2634
}
2735

2836
running() {
29-
[ -n "$(session_pids)" ]
37+
[ -n "$(app_pids)" ]
3038
}
3139

3240
leader_running() {
3341
ps -p "$leader" -o stat= 2>/dev/null | grep -qv '^Z'
3442
}
3543

44+
list_app() {
45+
local pids
46+
pids=$(app_pids | paste -sd, -)
47+
[ -z "$pids" ] || ps -p "$pids" -o pid,sid,stat,etimes,args 2>/dev/null | cut -c1-200 || true
48+
}
49+
3650
# Centiseconds since boot: monotonic, and independent of the locale's decimal separator.
3751
now_cs() {
3852
local uptime
@@ -47,7 +61,7 @@ signal_while() {
4761
shift
4862
while ((10#$(now_cs) < deadline)); do
4963
"$@" || return 0
50-
signal_session "$signal"
64+
signal_app "$signal"
5165
sleep 0.1
5266
done
5367
! "$@"
@@ -56,15 +70,15 @@ signal_while() {
5670
deadline=$((10#$(now_cs) + grace_seconds * 100))
5771
signal_while TERM leader_running
5872
if ! leader_running && running; then
59-
echo "Processes from session $leader outlived its leader:"
60-
ps -s "$leader" -o pid,stat,etimes,args 2>/dev/null | cut -c1-200 || true
73+
echo "Processes from app $tag outlived its leader:"
74+
list_app
6175
fi
6276
signal_while TERM running && exit 0
6377

64-
echo "::warning::Processes from session $leader were still running ${grace_seconds}s after SIGTERM:"
65-
ps -s "$leader" -o pid,stat,etimes,args 2>/dev/null | cut -c1-200 || true
78+
echo "::warning::Processes from app $tag were still running ${grace_seconds}s after SIGTERM:"
79+
list_app
6680
deadline=$((10#$(now_cs) + grace_seconds * 100))
6781
signal_while KILL running && exit 0
6882

69-
echo "::error::Processes from session $leader survived SIGKILL for ${grace_seconds}s."
83+
echo "::error::Processes from app $tag survived SIGKILL for ${grace_seconds}s."
7084
exit 1

‎.github/workflows/test-build.yml‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,10 @@ jobs:
211211
# same lifecycle. It starts from an empty Turbopack dev cache: a cache written under
212212
# other NEXT_PUBLIC_* values, by a server that `next dev` SIGKILLs 100ms after
213213
# SIGTERM, can panic Turbopack or wedge a route compile on restore. It runs in its
214-
# own session, and stop-session.sh returns only once all of it has exited, so no app
215-
# starts beside one still writing .next/dev.
214+
# own session under an E2E_APP tag, and stop-session.sh returns only once every
215+
# process in that session or carrying that tag has exited (Next's telemetry flush
216+
# runs detached and still writes .next/dev), so no app starts beside one still
217+
# writing .next/dev.
216218
- name: Verify SCIM, administration and workflow comparisons over real HTTP
217219
working-directory: apps/sim
218220
env:
@@ -240,11 +242,11 @@ jobs:
240242
server_log="$report_dir/scim-next.log"
241243
mkdir -p "$report_dir"
242244
rm -rf .next/dev
243-
setsid node ../../node_modules/next/dist/bin/next dev --hostname 127.0.0.1 --port 3017 > "$server_log" 2>&1 &
245+
E2E_APP=scim setsid node ../../node_modules/next/dist/bin/next dev --hostname 127.0.0.1 --port 3017 > "$server_log" 2>&1 &
244246
server_pid=$!
245247
finish() {
246248
status=$?
247-
bash ../../.github/scripts/stop-session.sh "$server_pid" || status=1
249+
bash ../../.github/scripts/stop-session.sh "$server_pid" scim || status=1
248250
wait "$server_pid" 2>/dev/null || true
249251
awk '/^ (GET|POST|PUT|PATCH|DELETE|HEAD) \/api\// { print }' "$server_log" > "$report_dir/scim-http-status.log"
250252
if [ "$status" -ne 0 ]; then
@@ -293,11 +295,11 @@ jobs:
293295
server_log="$report_dir/cli-next.log"
294296
mkdir -p "$report_dir"
295297
rm -rf .next/dev
296-
setsid node ../../node_modules/next/dist/bin/next dev --hostname 127.0.0.1 --port 3018 > "$server_log" 2>&1 &
298+
E2E_APP=cli setsid node ../../node_modules/next/dist/bin/next dev --hostname 127.0.0.1 --port 3018 > "$server_log" 2>&1 &
297299
server_pid=$!
298300
finish() {
299301
status=$?
300-
bash ../../.github/scripts/stop-session.sh "$server_pid" || status=1
302+
bash ../../.github/scripts/stop-session.sh "$server_pid" cli || status=1
301303
wait "$server_pid" 2>/dev/null || true
302304
if [ "$status" -ne 0 ]; then
303305
tail -n 200 "$server_log"
@@ -344,11 +346,11 @@ jobs:
344346
server_log="$report_dir/stop-after-next.log"
345347
mkdir -p "$report_dir"
346348
rm -rf .next/dev
347-
setsid node ../../node_modules/next/dist/bin/next dev --hostname 127.0.0.1 --port 3018 > "$server_log" 2>&1 &
349+
E2E_APP=stop-after setsid node ../../node_modules/next/dist/bin/next dev --hostname 127.0.0.1 --port 3018 > "$server_log" 2>&1 &
348350
server_pid=$!
349351
finish() {
350352
status=$?
351-
bash ../../.github/scripts/stop-session.sh "$server_pid" || status=1
353+
bash ../../.github/scripts/stop-session.sh "$server_pid" stop-after || status=1
352354
wait "$server_pid" 2>/dev/null || true
353355
awk '/^ (GET|POST|PUT|PATCH|DELETE|HEAD) \/api\// { print }' "$server_log" > "$report_dir/stop-after-http-status.log"
354356
if [ "$status" -ne 0 ]; then
@@ -395,11 +397,11 @@ jobs:
395397
server_log="$report_dir/desktop-inbox-next.log"
396398
mkdir -p "$report_dir"
397399
rm -rf .next/dev
398-
setsid node ../../node_modules/next/dist/bin/next dev --hostname 127.0.0.1 --port 3019 > "$server_log" 2>&1 &
400+
E2E_APP=desktop-inbox setsid node ../../node_modules/next/dist/bin/next dev --hostname 127.0.0.1 --port 3019 > "$server_log" 2>&1 &
399401
server_pid=$!
400402
finish() {
401403
status=$?
402-
bash ../../.github/scripts/stop-session.sh "$server_pid" || status=1
404+
bash ../../.github/scripts/stop-session.sh "$server_pid" desktop-inbox || status=1
403405
wait "$server_pid" 2>/dev/null || true
404406
awk '/^ (GET|POST|PUT|PATCH|DELETE|HEAD) \/api\// { print }' "$server_log" > "$report_dir/desktop-inbox-http-status.log"
405407
if [ "$status" -ne 0 ]; then

0 commit comments

Comments
 (0)