Harden local debug stale listener cleanup

This commit is contained in:
npc0-hue
2026-07-14 18:27:25 +08:00
parent 4ba5bd6971
commit f64eb0831f
4 changed files with 86 additions and 11 deletions
@@ -16,6 +16,8 @@ git clone git@git.npc0.com:admin343/run.git run
If `RUN_REPO_DIR` is missing or does not contain a run `go.mod`, local debug scripts fail with a clear setup message. If `RUN_REPO_DIR` is missing or does not contain a run `go.mod`, local debug scripts fail with a clear setup message.
Local debug start/stop scripts treat pid files as hints, not the only source of truth. If a previous local debug process still owns the configured platform or web port after pid files are removed, the scripts identify it by cwd/log ownership under this repository and stop it before starting a new stack.
## Docker Compose ## Docker Compose
The compose file keeps the run service for all-in-one local deployment, but its build context points at `${RUN_REPO_DIR:-./run}` and uses the external repository's `Dockerfile`. The compose file keeps the run service for all-in-one local deployment, but its build context points at `${RUN_REPO_DIR:-./run}` and uses the external repository's `Dockerfile`.
@@ -3,6 +3,7 @@
- [x] Add OpenSpec proposal/design/spec for the repository split. - [x] Add OpenSpec proposal/design/spec for the repository split.
- [x] Remove `run/` from browser-owned project roots and structure checks. - [x] Remove `run/` from browser-owned project roots and structure checks.
- [x] Update README, local debug scripts, and Docker Compose to use `RUN_REPO_DIR`. - [x] Update README, local debug scripts, and Docker Compose to use `RUN_REPO_DIR`.
- [x] Harden local debug start/stop against stale owned listeners when pid files are missing.
- [x] Remove tracked `run/` source files from `browser.git`. - [x] Remove tracked `run/` source files from `browser.git`.
- [x] Run `scripts/check-structure.sh`. - [x] Run `scripts/check-structure.sh`.
- [x] Run `openspec validate split-run-into-independent-repository --strict`. - [x] Run `openspec validate split-run-into-independent-repository --strict`.
+29
View File
@@ -23,6 +23,24 @@ port_listener_pid() {
lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | head -n 1 || true lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | head -n 1 || true
} }
listener_owned_by_local_debug() {
local pid="$1"
local details
details="$(lsof -nP -p "$pid" 2>/dev/null || true)"
[[ "$details" == *"$ROOT_DIR"* || "$details" == *"$LOCAL_DEBUG_ROOT"* ]]
}
wait_for_pid_exit() {
local pid="$1"
for _ in $(seq 1 20); do
if ! kill -0 "$pid" 2>/dev/null; then
return 0
fi
sleep 0.25
done
return 1
}
ensure_port_available() { ensure_port_available() {
local name="$1" local name="$1"
local port="$2" local port="$2"
@@ -35,6 +53,17 @@ ensure_port_available() {
if managed_pid_running "$pid_file" && [[ "$listener_pid" == "$(cat "$pid_file")" ]]; then if managed_pid_running "$pid_file" && [[ "$listener_pid" == "$(cat "$pid_file")" ]]; then
return 0 return 0
fi fi
if listener_owned_by_local_debug "$listener_pid"; then
printf 'stopping stale %s listener pid %s on port %s\n' "$name" "$listener_pid" "$port"
kill "$listener_pid" 2>/dev/null || true
if ! wait_for_pid_exit "$listener_pid"; then
printf 'stale %s listener pid %s did not stop after SIGTERM; sending SIGKILL\n' "$name" "$listener_pid"
kill -9 "$listener_pid" 2>/dev/null || true
wait_for_pid_exit "$listener_pid" || true
fi
rm -f "$pid_file"
return 0
fi
printf '%s port %s is already used by pid %s; choose another LOCAL_DEBUG_*_PORT or stop the existing listener\n' "$name" "$port" "$listener_pid" >&2 printf '%s port %s is already used by pid %s; choose another LOCAL_DEBUG_*_PORT or stop the existing listener\n' "$name" "$port" "$listener_pid" >&2
return 1 return 1
} }
+46 -3
View File
@@ -7,9 +7,7 @@ source "$ROOT_DIR/scripts/local-debug-env.sh"
if [[ ! -d "$LOCAL_DEBUG_PID_DIR" ]]; then if [[ ! -d "$LOCAL_DEBUG_PID_DIR" ]]; then
printf 'no local debug pid directory at %s\n' "$LOCAL_DEBUG_PID_DIR" printf 'no local debug pid directory at %s\n' "$LOCAL_DEBUG_PID_DIR"
exit 0 else
fi
for name in platform_web run platform; do for name in platform_web run platform; do
pid_file="$LOCAL_DEBUG_PID_DIR/$name.pid" pid_file="$LOCAL_DEBUG_PID_DIR/$name.pid"
if [[ ! -f "$pid_file" ]]; then if [[ ! -f "$pid_file" ]]; then
@@ -24,3 +22,48 @@ for name in platform_web run platform; do
fi fi
rm -f "$pid_file" rm -f "$pid_file"
done done
fi
wait_for_pid_exit() {
local pid="$1"
for _ in $(seq 1 20); do
if ! kill -0 "$pid" 2>/dev/null; then
return 0
fi
sleep 0.25
done
return 1
}
listener_owned_by_local_debug() {
local pid="$1"
local details
details="$(lsof -nP -p "$pid" 2>/dev/null || true)"
[[ "$details" == *"$ROOT_DIR"* || "$details" == *"$LOCAL_DEBUG_ROOT"* ]]
}
stop_listener_on_port() {
local name="$1"
local port="$2"
local pid
pid="$(lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | head -n 1 || true)"
if [[ -z "$pid" ]]; then
return 0
fi
if listener_owned_by_local_debug "$pid"; then
printf 'stopping %s listener pid %s on port %s\n' "$name" "$pid" "$port"
kill "$pid" 2>/dev/null || true
if ! wait_for_pid_exit "$pid"; then
printf '%s listener pid %s did not stop after SIGTERM; sending SIGKILL\n' "$name" "$pid"
kill -9 "$pid" 2>/dev/null || true
wait_for_pid_exit "$pid" || true
fi
return 0
else
printf '%s port %s is still used by pid %s outside local debug ownership\n' "$name" "$port" "$pid" >&2
fi
}
stop_listener_on_port platform "$LOCAL_DEBUG_PLATFORM_PORT"
stop_listener_on_port platform_web "$LOCAL_DEBUG_WEB_PORT"