@devrouter/cli 0.0.52 → 0.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/devrouter-process +87 -0
- package/dist/devrouter.js +1380 -960
- package/package.json +1 -1
- package/upgrade-prompts/0.0.53.md +42 -0
package/bin/devrouter-process
CHANGED
|
@@ -10,6 +10,13 @@ Usage:
|
|
|
10
10
|
|
|
11
11
|
Options:
|
|
12
12
|
--fingerprint <value> Runtime identity. Defaults to command, workspace, adapter, and safe allowlisted environment identity.
|
|
13
|
+
An explicit fingerprint is caller-owned, including preparation identity.
|
|
14
|
+
--prepare-command <shell command>
|
|
15
|
+
Ensure only: run a nonempty command with bash before a changed/new runtime.
|
|
16
|
+
Included byte-for-byte in the default fingerprint; skipped on runtime reuse.
|
|
17
|
+
Must run synchronously in the foreground: no daemonizing, detached sessions
|
|
18
|
+
or process groups, or other supervision escapes. Cancellation terminates
|
|
19
|
+
its original group and reaps the direct child; container init reaps orphans.
|
|
13
20
|
--log <path> Log file. Defaults to /tmp/devrouter-process-<name>.log.
|
|
14
21
|
|
|
15
22
|
Environment:
|
|
@@ -42,6 +49,7 @@ name=""
|
|
|
42
49
|
process_match=""
|
|
43
50
|
fingerprint=""
|
|
44
51
|
log_file=""
|
|
52
|
+
prepare_command=""
|
|
45
53
|
|
|
46
54
|
while [ "$#" -gt 0 ]; do
|
|
47
55
|
case "$1" in
|
|
@@ -68,6 +76,13 @@ while [ "$#" -gt 0 ]; do
|
|
|
68
76
|
log_file="$2"
|
|
69
77
|
shift 2
|
|
70
78
|
;;
|
|
79
|
+
--prepare-command)
|
|
80
|
+
[ "$action" = "ensure" ] || die "--prepare-command is only valid for ensure."
|
|
81
|
+
require_value "$@"
|
|
82
|
+
[[ "$2" =~ [^[:space:]] ]] || die "--prepare-command requires a nonempty command."
|
|
83
|
+
prepare_command="$2"
|
|
84
|
+
shift 2
|
|
85
|
+
;;
|
|
71
86
|
--help|-h)
|
|
72
87
|
usage
|
|
73
88
|
exit 0
|
|
@@ -151,6 +166,9 @@ if [ -z "$fingerprint" ]; then
|
|
|
151
166
|
for argument in "$@"; do
|
|
152
167
|
printf 'argv\0%s\0' "$argument"
|
|
153
168
|
done
|
|
169
|
+
if [ -n "$prepare_command" ]; then
|
|
170
|
+
printf 'prepare-command\0%s\0' "$prepare_command"
|
|
171
|
+
fi
|
|
154
172
|
for env_name in "${fingerprint_env_names[@]}"; do
|
|
155
173
|
printf 'environment-name\0%s\0' "$env_name"
|
|
156
174
|
emit_environment_field environment-value "$env_name"
|
|
@@ -385,18 +403,87 @@ fi
|
|
|
385
403
|
unknown_process_exists &&
|
|
386
404
|
die "Found an unowned '$name' process; refusing to start a duplicate or kill it."
|
|
387
405
|
|
|
406
|
+
preparation_pid=""
|
|
407
|
+
pending_runtime_pid=""
|
|
408
|
+
interrupted=0
|
|
409
|
+
|
|
410
|
+
# The direct child can still be between fork and setsid when cancellation arrives.
|
|
411
|
+
# Signal both that child and its eventual group, and never wait on a live child.
|
|
412
|
+
stop_preparation_child() {
|
|
413
|
+
local child="$1"
|
|
414
|
+
local signal attempt limit
|
|
415
|
+
|
|
416
|
+
for signal in TERM KILL; do
|
|
417
|
+
limit="$term_timeout"
|
|
418
|
+
[ "$signal" != KILL ] || limit="$kill_timeout"
|
|
419
|
+
kill -"$signal" -- "-$child" "$child" 2>/dev/null || true
|
|
420
|
+
for ((attempt = 0; attempt <= limit; attempt += 1)); do
|
|
421
|
+
if ! process_group_alive "$child" && ! process_alive "$child"; then
|
|
422
|
+
wait "$child" 2>/dev/null || true
|
|
423
|
+
return 0
|
|
424
|
+
fi
|
|
425
|
+
[ "$attempt" -eq "$limit" ] || sleep 1
|
|
426
|
+
done
|
|
427
|
+
done
|
|
428
|
+
echo "[devrouter-process] Could not terminate preparation/startup group $child." >&2
|
|
429
|
+
return 1
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
cleanup_preparation() {
|
|
433
|
+
local result="$?"
|
|
434
|
+
# Repeated cancellation must not interrupt group cleanup or release the lock early.
|
|
435
|
+
trap '' HUP INT TERM
|
|
436
|
+
if [ -n "$preparation_pid" ]; then
|
|
437
|
+
stop_preparation_child "$preparation_pid" || result=1
|
|
438
|
+
fi
|
|
439
|
+
if [ -n "$pending_runtime_pid" ]; then
|
|
440
|
+
stop_preparation_child "$pending_runtime_pid" || result=1
|
|
441
|
+
rm -f "$state_file" "$state_file.tmp.$BASHPID"
|
|
442
|
+
fi
|
|
443
|
+
trap - EXIT
|
|
444
|
+
exit "$result"
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if [ -n "$prepare_command" ]; then
|
|
448
|
+
trap 'interrupted=129' HUP
|
|
449
|
+
trap 'interrupted=130' INT
|
|
450
|
+
trap 'interrupted=143' TERM
|
|
451
|
+
trap cleanup_preparation EXIT
|
|
452
|
+
echo "[devrouter-process] Preparing '$name'..."
|
|
453
|
+
env DEVROUTER_PROCESS_NAME="$name" \
|
|
454
|
+
DEVROUTER_PROCESS_FINGERPRINT="$fingerprint" \
|
|
455
|
+
setsid bash -c "$prepare_command" 9>&- </dev/null &
|
|
456
|
+
preparation_pid=$!
|
|
457
|
+
# A trapped signal interrupts Bash's wait. Record it even if it arrives before
|
|
458
|
+
# assignment of $!, then enter cleanup with the exact child still recorded.
|
|
459
|
+
[ "$interrupted" -eq 0 ] || exit "$interrupted"
|
|
460
|
+
preparation_status=0
|
|
461
|
+
wait "$preparation_pid" || preparation_status=$?
|
|
462
|
+
[ "$interrupted" -eq 0 ] || exit "$interrupted"
|
|
463
|
+
[ "$preparation_status" -eq 0 ] || die "Preparation for '$name' failed (exit $preparation_status)."
|
|
464
|
+
if process_group_alive "$preparation_pid"; then
|
|
465
|
+
die "Preparation for '$name' left running children; a synchronous foreground command is required."
|
|
466
|
+
fi
|
|
467
|
+
preparation_pid=""
|
|
468
|
+
fi
|
|
469
|
+
|
|
470
|
+
[ "$interrupted" -eq 0 ] || exit "$interrupted"
|
|
388
471
|
echo "[devrouter-process] Starting '$name' (logs: $log_file)..."
|
|
389
472
|
env DEVROUTER_PROCESS_NAME="$name" \
|
|
390
473
|
DEVROUTER_PROCESS_FINGERPRINT="$fingerprint" \
|
|
391
474
|
setsid "$@" 9>&- >"$log_file" 2>&1 </dev/null &
|
|
392
475
|
pid=$!
|
|
393
476
|
pgid="$pid"
|
|
477
|
+
[ -z "$prepare_command" ] || pending_runtime_pid="$pid"
|
|
394
478
|
|
|
395
479
|
for attempt in {1..10}; do
|
|
480
|
+
[ "$interrupted" -eq 0 ] || exit "$interrupted"
|
|
396
481
|
if process_owned "$pid" "$pgid" "$fingerprint"; then
|
|
397
482
|
printf '%s %s %s\n' "$pid" "$pgid" "$fingerprint" >"$state_file.tmp.$BASHPID"
|
|
398
483
|
mv "$state_file.tmp.$BASHPID" "$state_file"
|
|
484
|
+
[ "$interrupted" -eq 0 ] || exit "$interrupted"
|
|
399
485
|
echo "[devrouter-process] '$name' started (PID $pid)."
|
|
486
|
+
pending_runtime_pid=""
|
|
400
487
|
exit 0
|
|
401
488
|
fi
|
|
402
489
|
if ! process_alive "$pid"; then
|