From 8f184c9f2e3b3e5646c099c22225698000fad9d9 Mon Sep 17 00:00:00 2001 From: claude_dev Date: Fri, 14 Aug 2026 10:06:54 +0800 Subject: [PATCH] =?UTF-8?q?ci(vps-deploy):=20=E7=BA=B3=E5=85=A5=E5=89=8D?= =?UTF-8?q?=E7=AB=AFdist=E5=90=8C=E6=AD=A5+API=E9=87=8D=E5=90=AF(=E6=B2=BB?= =?UTF-8?q?=E5=85=AC=E7=BD=91=E5=89=8D=E7=AB=AF=E6=97=A0=E7=AE=A1=E7=BA=BF?= =?UTF-8?q?=E6=89=8B=E5=8A=A8=E6=AD=A5=E9=AA=A4):=20Mac=E6=9E=84=E5=BB=BA+?= =?UTF-8?q?tar+scp=E2=86=92VPS=E6=8D=A2dist(=E8=84=9A=E6=9C=AC=E5=85=A5?= =?UTF-8?q?=E4=BB=93scripts/nas=5Fsync,vps=5Fupdate=5Fdist.py);=20?= =?UTF-8?q?=E9=87=8D=E5=90=AF=E5=8A=A0=E7=AB=AF=E5=8F=A3=E9=87=8A=E6=94=BE?= =?UTF-8?q?=E7=AD=89=E5=BE=85(=E9=98=B2end=E2=86=92run=E7=AB=9E=E6=80=8110?= =?UTF-8?q?048=E9=9D=99=E9=BB=98=E5=B4=A9,=E4=BB=8A=E6=97=A5=E5=AE=9E?= =?UTF-8?q?=E8=B8=A9)+=E8=B5=B7=E6=9C=8D=E6=8E=A2=E6=B4=BB(=E4=B8=8A?= =?UTF-8?q?=E9=99=9010min)=20[nas]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/vps-deploy.yml | 34 +++++++++++++++++++++++++++++ scripts/nas_sync/vps_update_dist.py | 34 +++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 scripts/nas_sync/vps_update_dist.py diff --git a/.gitea/workflows/vps-deploy.yml b/.gitea/workflows/vps-deploy.yml index 0fdd5a0..3fa8ba3 100644 --- a/.gitea/workflows/vps-deploy.yml +++ b/.gitea/workflows/vps-deploy.yml @@ -40,6 +40,40 @@ jobs: run: bash scripts/nas_sync/promote.sh --target vps - name: VPS 冒烟 (LocalUnifiedProvider 真数据) run: ssh 49.232.102.198 'cd C:\sanguo_vnpy_v2 && C:\Python310\python.exe -X utf8 scripts/data_platform/verify_unified_e2e.py' + - name: 前端构建 + 推 dist 到 VPS (公网前端无管线, 此处补齐) + run: | + set -e + cd frontend + npm ci + npm run build + cd .. + tar czf /tmp/dist_latest.tgz -C frontend/dist . + scp -o ConnectTimeout=20 /tmp/dist_latest.tgz 49.232.102.198:C:/sanguo_vnpy_v2/dist_latest.tgz + - name: VPS 替换 dist + 重启 sanguo-api (等端口释放防 10048 竞态, 再等起服) + run: | + set -e + ssh 49.232.102.198 'C:\Python310\python.exe -X utf8 C:\sanguo_vnpy_v2\scripts\nas_sync\vps_update_dist.py' | tee /tmp/dist_swap.log + grep -q DIST_UPDATE_DONE /tmp/dist_swap.log + # 停 API → 等端口彻底释放(旧实例被杀后端口有滞留, 立即重启会 10048 静默崩) + ssh 49.232.102.198 'schtasks /end /tn sanguo-api' || true + for i in $(seq 1 30); do + ssh 49.232.102.198 'netstat -ano -p tcp' | tr -d '\r' | grep -q ':8000.*LISTEN' || { echo "port freed after $((i*2))s"; break; } + sleep 2 + done + ssh 49.232.102.198 'schtasks /run /tn sanguo-api' + # 等新实例监听 + /docs 可用(VPS 启动需数分钟, 上限 10 分钟) + for i in $(seq 1 60); do + if ssh 49.232.102.198 'netstat -ano -p tcp' | tr -d '\r' | grep -q ':8000.*LISTEN'; then + echo "API listening after ~$((i*10))s"; break + fi + sleep 10 + if [ "$i" = "60" ]; then echo "API 10 分钟未监听 8000"; exit 1; fi + done + for i in $(seq 1 12); do + ssh 49.232.102.198 'powershell -NoProfile -Command "(Invoke-WebRequest -UseBasicParsing http://127.0.0.1:8000/docs).StatusCode"' 2>/dev/null | grep -q 200 && { echo "API /docs 200"; exit 0; } + sleep 10 + done + echo "API 监听但 /docs 2 分钟未返回 200"; exit 1 - name: mark vps-deployed tag (VPS 版本真相, session 用 git log vps-deployed..HEAD 查未部署) env: TOKEN: ${{ github.token }} diff --git a/scripts/nas_sync/vps_update_dist.py b/scripts/nas_sync/vps_update_dist.py new file mode 100644 index 0000000..8d972fa --- /dev/null +++ b/scripts/nas_sync/vps_update_dist.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +"""VPS: 替换 frontend/dist 为最新构建(先备份旧版)。 + +用法(VPS 上): C:\\Python310\\python.exe -X utf8 C:\\sanguo_vnpy_v2\\scripts\\nas_sync\\vps_update_dist.py +输入: C:\\sanguo_vnpy_v2\\dist_latest.tgz (Mac 构建后 scp 上来的 tar 包, 内容为 dist 根) +输出: 尾行 DIST_UPDATE_DONE(成功标记, CI 用) +""" +import os +import re +import shutil +import tarfile + +ROOT = r"C:\sanguo_vnpy_v2" +TGZ = os.path.join(ROOT, "dist_latest.tgz") +DIST = os.path.join(ROOT, "frontend", "dist") +BAK = os.path.join(ROOT, "frontend", "dist_bak") + +if not os.path.exists(TGZ): + raise SystemExit(f"tgz not found: {TGZ}") + +if os.path.exists(BAK): + shutil.rmtree(BAK) +if os.path.exists(DIST): + shutil.move(DIST, BAK) + print("old dist ->", BAK, flush=True) +os.makedirs(DIST, exist_ok=True) +with tarfile.open(TGZ) as t: + t.extractall(DIST) +idx = os.path.join(DIST, "index.html") +with open(idx, encoding="utf-8") as f: + head = f.read(400) +m = re.search(r"assets/index-[^\"']*", head) +print("new index:", m.group(0) if m else "?", flush=True) +print("DIST_UPDATE_DONE", flush=True)