From d82d29fd7997d701678c81adbecd165cf7a2a68c Mon Sep 17 00:00:00 2001 From: cfdaily Date: Fri, 12 Jun 2026 15:05:06 +0800 Subject: [PATCH] fix(auto-deploy): prevent self-kill when pm2 restart runs inside webhook handler post_deploy commands that restart the current process (pm2 restart {pm2_name}) now use nohup+sleep to defer execution, allowing the webhook handler to return normally before the restart happens. Fix by jiangwei-infra, synced from install dir. --- src/api/toolchain_routes.py | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/api/toolchain_routes.py b/src/api/toolchain_routes.py index 1c266c8..9f62b57 100644 --- a/src/api/toolchain_routes.py +++ b/src/api/toolchain_routes.py @@ -557,21 +557,35 @@ async def _handle_pr_closed(payload: Dict[str, Any]) -> None: if needs_restart: post_deploy_cmds = target.get("post_deploy", []) + pm2_name = target.get("pm2_name", "") for cmd in post_deploy_cmds: logger.info("Auto-deploy: executing post_deploy: %s", cmd) - deploy_proc = await asyncio.create_subprocess_exec( - "sh", "-c", cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - _, deploy_err = await asyncio.wait_for(deploy_proc.communicate(), timeout=30) - if deploy_proc.returncode != 0: - logger.error("Auto-deploy: post_deploy failed: %s", deploy_err.decode()) - _send_deploy_failure_mail(repo, pr_number, pr_title, f"post_deploy 失败 ({cmd}): {deploy_err.decode()}") - break + # 如果命令会 restart 当前进程(自己杀自己),用延迟执行 + # 避免当前 webhook handler 被杀导致部署失败 + if pm2_name and f"pm2 restart {pm2_name}" in cmd: + actual_cmd = f"nohup sh -c 'sleep 2 && {cmd}' > /dev/null 2>&1 &" + deploy_proc = await asyncio.create_subprocess_exec( + "sh", "-c", actual_cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + # 不等待:nohup 后台执行,立即返回 + logger.info("Auto-deploy: self-restart deferred (nohup): %s", cmd) + else: + deploy_proc = await asyncio.create_subprocess_exec( + "sh", "-c", cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + _, deploy_err = await asyncio.wait_for(deploy_proc.communicate(), timeout=30) + + if deploy_proc.returncode != 0: + logger.error("Auto-deploy: post_deploy failed: %s", deploy_err.decode()) + _send_deploy_failure_mail(repo, pr_number, pr_title, f"post_deploy 失败 ({cmd}): {deploy_err.decode()}") + break else: - logger.info("Auto-deploy: all post_deploy commands succeeded (files: %s)", ", ".join(file_list[:5])) + logger.info("Auto-deploy: all post_deploy commands scheduled/succeeded (files: %s)", ", ".join(file_list[:5])) else: logger.info("Auto-deploy: docs-only change for %s, skip post_deploy", repo)