中控更新改为先同步 git pull 再重启,并修复前端已是最新时一直显示更新中。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+79
-70
@@ -1,8 +1,7 @@
|
||||
"""中控本机快速更新:git pull + pm2 restart."""
|
||||
"""中控本机快速更新:先同步 git pull,再异步 pm2 restart."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
@@ -23,14 +22,14 @@ def _repo_root() -> Path:
|
||||
return _ROOT
|
||||
|
||||
|
||||
def _run_git(args: list[str], *, cwd: Path) -> tuple[bool, str]:
|
||||
def _run_git(args: list[str], *, cwd: Path, timeout: float = 15) -> tuple[bool, str]:
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
["git", *args],
|
||||
cwd=str(cwd),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=15,
|
||||
timeout=timeout,
|
||||
)
|
||||
except FileNotFoundError:
|
||||
return False, "未找到 git"
|
||||
@@ -84,53 +83,42 @@ def read_git_version() -> dict[str, Any]:
|
||||
return out
|
||||
|
||||
|
||||
def _run_pull_and_restart(root: Path, script: Path) -> None:
|
||||
log_path = _UPDATE_LOG
|
||||
try:
|
||||
log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
env = os.environ.copy()
|
||||
env["REPO"] = str(root)
|
||||
if not (env.get("PM2_APP_NAME") or "").strip():
|
||||
env["PM2_APP_NAME"] = "crypto_okx_hub"
|
||||
try:
|
||||
with open(log_path, "a", encoding="utf-8") as logf:
|
||||
logf.write(
|
||||
time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ f" start pull_and_restart REPO={root}\n"
|
||||
)
|
||||
logf.flush()
|
||||
def _pm2_app_name() -> str:
|
||||
return (os.getenv("PM2_APP_NAME") or "").strip() or "crypto_okx_hub"
|
||||
|
||||
|
||||
def _schedule_pm2_restart(*, delay_seconds: float = 1.5) -> None:
|
||||
app_name = _pm2_app_name()
|
||||
delay = max(0.8, float(delay_seconds))
|
||||
|
||||
def _worker() -> None:
|
||||
time.sleep(delay)
|
||||
_append_log(f"pm2 restart {app_name} --update-env")
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
["bash", str(script)],
|
||||
cwd=str(root),
|
||||
env=env,
|
||||
stdout=logf,
|
||||
stderr=subprocess.STDOUT,
|
||||
["pm2", "restart", app_name, "--update-env"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=180,
|
||||
timeout=120,
|
||||
)
|
||||
logf.write(
|
||||
time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
+ f" finish returncode={proc.returncode}\n"
|
||||
_append_log(
|
||||
f"pm2 restart returncode={proc.returncode} "
|
||||
f"out={(proc.stdout or proc.stderr or '')[:300]}"
|
||||
)
|
||||
except Exception as e:
|
||||
_append_log("pull_and_restart error: " + (str(e) or "unknown"))
|
||||
except Exception as e:
|
||||
_append_log("pm2 restart error: " + (str(e) or "unknown"))
|
||||
|
||||
threading.Thread(target=_worker, name="hub-pm2-restart", daemon=True).start()
|
||||
|
||||
|
||||
def schedule_pull_and_restart(*, delay_seconds: float = 2.0) -> dict[str, Any]:
|
||||
def schedule_pull_and_restart(*, delay_seconds: float = 1.5) -> dict[str, Any]:
|
||||
"""同步 git pull,再异步 pm2 restart(便于前端立刻知道是否有新版本)."""
|
||||
if not sys.platform.startswith("linux"):
|
||||
msg = f"仅 Linux 服务器支持更新(当前 platform={sys.platform})"
|
||||
_append_log("reject: " + msg)
|
||||
return {"ok": False, "msg": msg, "deferred": False, "platform": sys.platform}
|
||||
|
||||
root = _repo_root()
|
||||
script = root / "deploy" / "pull_and_restart.sh"
|
||||
if not script.is_file():
|
||||
msg = f"缺少更新脚本: {script}"
|
||||
_append_log("reject: " + msg)
|
||||
return {"ok": False, "msg": msg, "deferred": False}
|
||||
|
||||
if not (root / "ecosystem.config.cjs").is_file():
|
||||
msg = f"缺少 ecosystem.config.cjs: {root}"
|
||||
_append_log("reject: " + msg)
|
||||
@@ -140,41 +128,62 @@ def schedule_pull_and_restart(*, delay_seconds: float = 2.0) -> dict[str, Any]:
|
||||
now = time.time()
|
||||
with _update_lock:
|
||||
if now < _update_pending_until:
|
||||
msg = "已有更新任务进行中,请稍后再试"
|
||||
return {"ok": False, "msg": msg, "deferred": False}
|
||||
_update_pending_until = now + 180.0
|
||||
return {"ok": False, "msg": "已有更新任务进行中,请稍后再试", "deferred": False}
|
||||
_update_pending_until = now + 120.0
|
||||
|
||||
version = read_git_version()
|
||||
delay = max(1.0, float(delay_seconds))
|
||||
before = read_git_version()
|
||||
before_sha = str(before.get("sha") or "")
|
||||
_append_log(f"git pull start before={before.get('short_sha') or ''} root={root}")
|
||||
ok_pull, pull_out = _run_git(["pull", "--ff-only"], cwd=root, timeout=90)
|
||||
if not ok_pull:
|
||||
# 回退再试普通 pull
|
||||
ok_pull, pull_out = _run_git(["pull"], cwd=root, timeout=90)
|
||||
after = read_git_version()
|
||||
after_sha = str(after.get("sha") or "")
|
||||
changed = bool(before_sha and after_sha and before_sha != after_sha)
|
||||
_append_log(
|
||||
f"git pull done ok={ok_pull} changed={changed} "
|
||||
f"after={after.get('short_sha') or ''} msg={pull_out[:200]}"
|
||||
)
|
||||
|
||||
def _worker() -> None:
|
||||
try:
|
||||
time.sleep(delay)
|
||||
_run_pull_and_restart(root, script)
|
||||
finally:
|
||||
with _update_lock:
|
||||
# 保留短暂冷却,避免连点
|
||||
pass
|
||||
|
||||
try:
|
||||
t = threading.Thread(target=_worker, name="hub-self-update", daemon=True)
|
||||
t.start()
|
||||
_append_log(
|
||||
f"accepted before={version.get('short_sha') or ''} delay={delay}s root={root}"
|
||||
)
|
||||
if not ok_pull and not after.get("ok"):
|
||||
with _update_lock:
|
||||
_update_pending_until = 0.0
|
||||
return {
|
||||
"ok": True,
|
||||
"msg": "更新已受理,即将 git pull 并重启",
|
||||
"deferred": True,
|
||||
"before_sha": version.get("sha") or "",
|
||||
"before_short_sha": version.get("short_sha") or "",
|
||||
"branch": version.get("branch") or "",
|
||||
"repo": str(root),
|
||||
"log": str(_UPDATE_LOG),
|
||||
"ok": False,
|
||||
"msg": pull_out or "git pull 失败",
|
||||
"deferred": False,
|
||||
"before_sha": before_sha,
|
||||
"before_short_sha": before.get("short_sha") or "",
|
||||
}
|
||||
|
||||
# pull 失败但 HEAD 仍可读:仍尝试重启(可能网络问题但本地可跑)
|
||||
try:
|
||||
_schedule_pm2_restart(delay_seconds=delay_seconds)
|
||||
except Exception as e:
|
||||
with _update_lock:
|
||||
_update_pending_until = 0.0
|
||||
msg = str(e) or "触发更新失败"
|
||||
_append_log("reject: " + msg)
|
||||
return {"ok": False, "msg": msg, "deferred": False}
|
||||
return {"ok": False, "msg": str(e) or "触发重启失败", "deferred": False}
|
||||
|
||||
if not ok_pull:
|
||||
msg = "git pull 未成功,已触发重启: " + (pull_out or "")
|
||||
elif changed:
|
||||
msg = f"已拉取新版本 {(before.get('short_sha') or '?')} → {(after.get('short_sha') or '?')},即将重启"
|
||||
else:
|
||||
msg = "代码已是最新,即将重启进程"
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"msg": msg,
|
||||
"deferred": True,
|
||||
"changed": changed,
|
||||
"pull_ok": ok_pull,
|
||||
"pull_msg": pull_out[:500],
|
||||
"before_sha": before_sha,
|
||||
"before_short_sha": before.get("short_sha") or "",
|
||||
"after_sha": after_sha,
|
||||
"after_short_sha": after.get("short_sha") or "",
|
||||
"branch": after.get("branch") or before.get("branch") or "",
|
||||
"repo": str(root),
|
||||
"log": str(_UPDATE_LOG),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user