123 lines
4.1 KiB
Bash
123 lines
4.1 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
set -uo pipefail
|
||
|
|
|
||
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
|
|
RUN_DIR="$ROOT_DIR/.run"
|
||
|
|
PID_FILE="$RUN_DIR/dev-all.pids"
|
||
|
|
|
||
|
|
# Processes actually spawned by dev-all.sh look like this on the command
|
||
|
|
# line (dev-all.sh always `cd`s into the target dir first, so the binary is
|
||
|
|
# invoked with a *relative* path — never anchor these on an absolute path).
|
||
|
|
# Env-var assignments (e.g. HAIXUN_NODE_WORKER_TYPE=...) must never be used
|
||
|
|
# as a match pattern: once `env FOO=bar cmd` execs `cmd`, the assignment is
|
||
|
|
# gone from the process's argv/cmdline and can never match again.
|
||
|
|
PATTERNS=(
|
||
|
|
"go workers|bin/worker -f etc/gateway.worker.yaml"
|
||
|
|
"gateway|bin/gateway -f etc/gateway.yaml"
|
||
|
|
"node style-8d worker|style-8d-worker.ts"
|
||
|
|
"keyword-search tool|keyword-search-server.ts"
|
||
|
|
"resolve-media tool|resolve-media-server.ts"
|
||
|
|
"web dev server|vite.*--strictPort"
|
||
|
|
)
|
||
|
|
|
||
|
|
port_in_use() {
|
||
|
|
local port="$1"
|
||
|
|
ss -tlnp 2>/dev/null | grep -qE ":${port} "
|
||
|
|
}
|
||
|
|
|
||
|
|
free_port() {
|
||
|
|
local port="$1"
|
||
|
|
if ! port_in_use "$port"; then
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
echo "[dev-all-stop] freeing port :$port"
|
||
|
|
if command -v fuser >/dev/null 2>&1; then
|
||
|
|
fuser -k "${port}/tcp" 2>/dev/null || true
|
||
|
|
else
|
||
|
|
local line pid
|
||
|
|
while IFS= read -r line; do
|
||
|
|
for pid in $(grep -oE 'pid=[0-9]+' <<<"$line" | cut -d= -f2); do
|
||
|
|
kill "$pid" 2>/dev/null || true
|
||
|
|
done
|
||
|
|
done < <(ss -tlnp 2>/dev/null | grep -E ":${port} " || true)
|
||
|
|
fi
|
||
|
|
sleep 0.3
|
||
|
|
}
|
||
|
|
|
||
|
|
# Stop everything dev-all.sh recorded in its pidfile. Each recorded pid is a
|
||
|
|
# process-group leader (dev-all.sh launches jobs via `setsid`), so signalling
|
||
|
|
# the negative pid takes down the whole subtree (npm -> tsx -> node, etc.),
|
||
|
|
# not just the immediate child. This is the reliable path and also covers
|
||
|
|
# runs where dev-all.sh itself was killed abruptly (e.g. `kill -9`, closed
|
||
|
|
# terminal) and never got to run its own cleanup trap.
|
||
|
|
stop_from_pidfile() {
|
||
|
|
if [[ ! -f "$PID_FILE" ]]; then
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
echo "[dev-all-stop] stopping processes recorded in $PID_FILE"
|
||
|
|
local pid label
|
||
|
|
while read -r pid label; do
|
||
|
|
[[ -n "${pid:-}" ]] || continue
|
||
|
|
if kill -0 "$pid" 2>/dev/null; then
|
||
|
|
echo "[dev-all-stop] -> ${label:-pgid $pid} (pgid $pid): TERM"
|
||
|
|
kill -TERM -- "-$pid" 2>/dev/null || kill -TERM "$pid" 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
done < "$PID_FILE"
|
||
|
|
sleep 1
|
||
|
|
while read -r pid label; do
|
||
|
|
[[ -n "${pid:-}" ]] || continue
|
||
|
|
if kill -0 "$pid" 2>/dev/null; then
|
||
|
|
echo "[dev-all-stop] -> ${label:-pgid $pid} (pgid $pid) 仍在執行,改用 KILL"
|
||
|
|
kill -KILL -- "-$pid" 2>/dev/null || kill -KILL "$pid" 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
done < "$PID_FILE"
|
||
|
|
rm -f "$PID_FILE"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Fallback for anything the pidfile doesn't know about: processes started
|
||
|
|
# before this pidfile mechanism existed, started outside dev-all.sh, or left
|
||
|
|
# over from a crashed run on a previous checkout. Matches the *actual*
|
||
|
|
# observed command line, not env vars or absolute paths (see PATTERNS above).
|
||
|
|
stop_pattern() {
|
||
|
|
local label="$1"
|
||
|
|
local pattern="$2"
|
||
|
|
if pgrep -f "$pattern" >/dev/null 2>&1; then
|
||
|
|
echo "[dev-all-stop] stopping leftover: $label"
|
||
|
|
pkill -f "$pattern" 2>/dev/null || true
|
||
|
|
sleep 0.3
|
||
|
|
pkill -9 -f "$pattern" 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
stop_from_pidfile
|
||
|
|
|
||
|
|
for port in 8890 9101 9102; do
|
||
|
|
free_port "$port"
|
||
|
|
done
|
||
|
|
|
||
|
|
for port in $(seq 5173 5199); do
|
||
|
|
free_port "$port"
|
||
|
|
done
|
||
|
|
|
||
|
|
for entry in "${PATTERNS[@]}"; do
|
||
|
|
stop_pattern "${entry%%|*}" "${entry#*|}"
|
||
|
|
done
|
||
|
|
|
||
|
|
sleep 0.5
|
||
|
|
|
||
|
|
# Verify instead of just hoping: print anything that survived so it's
|
||
|
|
# obvious when a leftover truly needs manual attention.
|
||
|
|
all_patterns="$(printf '%s|' "${PATTERNS[@]#*|}")"
|
||
|
|
all_patterns="${all_patterns%|}"
|
||
|
|
remaining="$(pgrep -f "$all_patterns" 2>/dev/null || true)"
|
||
|
|
if [[ -n "$remaining" ]]; then
|
||
|
|
echo "[dev-all-stop] 警告:仍有殘留程序未關閉:" >&2
|
||
|
|
# shellcheck disable=SC2086
|
||
|
|
ps -o pid,ppid,cmd -p $(echo "$remaining" | paste -sd, -) 2>/dev/null >&2 || true
|
||
|
|
echo "[dev-all-stop] 可手動執行: kill -9 $(echo "$remaining" | paste -sd' ' -)" >&2
|
||
|
|
else
|
||
|
|
echo "[dev-all-stop] 已確認沒有殘留的 dev-all 相關程序"
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "[dev-all-stop] done"
|