From 14b6bbecb41e332b174a2c88a60a4794be230411 Mon Sep 17 00:00:00 2001 From: ericwyuan Date: Tue, 1 Sep 2026 14:34:27 +0800 Subject: [PATCH] =?UTF-8?q?fix(deploy):=20=E7=BE=A4=E6=99=96=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=20pgrep=EF=BC=8C=E9=82=A3=E4=B8=89=E5=A4=84=E3=80=8C?= =?UTF-8?q?=E5=AE=88=E5=8D=AB=E3=80=8D=E4=B8=80=E7=9B=B4=E6=98=AF=E7=A9=BA?= =?UTF-8?q?=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一版给 push.sh 加的 pid 比对刚跑就露馅了:`sh: pgrep: command not found`, 于是 BEFORE 和 AFTER 都是空字符串,`[ -n "$BEFORE" ]` 直接跳过检查——我刚加 的安全网自己就是个摆设。 顺手查了一圈,stop.sh 和 start.sh 里同样的 pgrep 也一直在空转: - stop.sh 的 `pgrep ... || exit 0` 每次都以 127 失败,永远匹配不到那个提前 返回,所以每次停服都白等满 15 秒,也从没真正确认过残留 worker 已经没了。 - start.sh 的等待循环同理,白等 20 秒。 DSM 有 pkill 没有 pgrep,而且非特权的 ps 看不见 root 起的进程(服务是开机 以 root 启动的),两者任一都会让检查静默返回「没有」——这正是最危险的答案, 因为它长得和「已经停干净了」一模一样。 - push.sh 改用 `sudo ps -eo pid,args | grep`;并且 AFTER 为空时直接报错退出, 不再把「看不见」当成「通过」 - stop.sh / start.sh 改用 `ps -eo args | grep -q` Co-Authored-By: Claude Opus 5 --- deploy/push.sh | 19 ++++++++++++++++--- deploy/start.sh | 7 ++++++- deploy/stop.sh | 9 ++++++++- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/deploy/push.sh b/deploy/push.sh index a4d8bc7..b3ad0e5 100755 --- a/deploy/push.sh +++ b/deploy/push.sh @@ -79,7 +79,15 @@ $TAR -C "$REPO/client/build" . | sh_ "tar xzf - -C '$STATIC'" # Hence sudo, matching how the service actually runs. SUDO="echo '$PASS' | sudo -S" echo "==> restart (sudo: the service runs as root, started at boot)" -BEFORE=$(sh_ "pgrep -f '$APP/backend/.venv/bin/gunicorn' | tr '\n' ',' " || true) +# DSM has no pgrep, and an unprivileged `ps` cannot see a root-owned process +# — either one silently returns nothing, which would make the pid comparison +# below always "pass" and put the check right back where it started. +pids_() { + sh_ "echo '$PASS' | sudo -S ps -eo pid,args 2>/dev/null \ + | grep '$APP/backend/.venv/bin/gunicorn' | grep -v grep \ + | awk '{print \$1}' | sort -n | tr '\n' ','" +} +BEFORE=$(pids_ || true) sh_ "cd '$APP' && $SUDO sh deploy/stop.sh >/dev/null 2>&1; sleep 3; $SUDO sh deploy/start.sh" \ || { echo "restart failed" >&2; exit 1; } @@ -89,8 +97,13 @@ sh_ "curl -sf -m 5 -o /dev/null -w 'local api: %{http_code}\n' \ # A 200 alone proves nothing: it is exactly what a surviving old master # returns. The master pid must have changed for the new code to be loaded. -AFTER=$(sh_ "pgrep -f '$APP/backend/.venv/bin/gunicorn' | tr '\n' ',' " || true) -if [ -n "$BEFORE" ] && [ "$BEFORE" = "$AFTER" ]; then +AFTER=$(pids_ || true) +if [ -z "$AFTER" ]; then + echo "cannot see any gunicorn process - the pid check could not run." >&2 + echo "Verify by hand before trusting this deploy." >&2 + exit 1 +fi +if [ "$BEFORE" = "$AFTER" ]; then echo "the gunicorn pids did not change ($AFTER) - the old process is still" >&2 echo "serving and your changes are NOT live. Check $APP/logs/error.log." >&2 exit 1 diff --git a/deploy/start.sh b/deploy/start.sh index 536d538..ae2f553 100755 --- a/deploy/start.sh +++ b/deploy/start.sh @@ -11,9 +11,14 @@ GUNICORN="$APP/backend/.venv/bin/gunicorn" # the new master then started, reported success, and served nothing — the # site was down while every log line looked normal. "$APP/deploy/stop.sh" >/dev/null 2>&1 + +# `ps | grep`, not pgrep: DSM has no pgrep, so this loop used to fail with 127 +# every second and wait the full 20s whether or not anything was still running. +alive() { ps -eo args 2>/dev/null | grep -q "^$GUNICORN"; } + i=0 while [ $i -lt 20 ]; do - pgrep -f "$GUNICORN" >/dev/null 2>&1 || break + alive || break sleep 1 i=$((i + 1)) done diff --git a/deploy/stop.sh b/deploy/stop.sh index 485c49a..f8da08d 100755 --- a/deploy/stop.sh +++ b/deploy/stop.sh @@ -9,9 +9,16 @@ fi # The master's workers do not always go with it, and a surviving worker keeps # :8124 bound — which looks exactly like a healthy start that serves nothing. +# +# `ps | grep` rather than pgrep: DSM does not ship pgrep, so the original +# check failed with 127 on every iteration — never matching the `|| exit 0` +# early return, always burning the full 15 seconds, and never actually +# confirming anything. +alive() { ps -eo args 2>/dev/null | grep -q "^$GUNICORN"; } + i=0 while [ $i -lt 15 ]; do - pgrep -f "$GUNICORN" >/dev/null 2>&1 || exit 0 + alive || exit 0 sleep 1 i=$((i + 1)) done