a58d9fd385
pipefail+SIGPIPE 三处(本机复现rc=141确定性,run724根因): - L82端口释放检测被反转(最危险): netstat大输出|grep -q命中即提前退出→上游tr吃SIGPIPE(141)→pipefail判整管道失败→走||分支谎报「已释放」→下一步/run撞10048竞态 - L91 API等待循环同雷=run724死因: 检测恒假→120轮耗尽exit1→收尾三步全跳过 - L98 /docs探活同族(输出仅3字节几乎不触发,顺手修) 修法: grep -q 'x' → grep 'x' >/dev/null(读完整流不提前退出,无SIGPIPE) workflow内裸ssh全量补 -o ConnectTimeout=20(对齐vps_restart_resident.sh写法): 单次ssh挂死原会让整步无限冻结; vps-deploy.yml 9处+ci-cd.yml 2处(NAS) 收尾三步抽幂等脚本 scripts/ci/vps_finalize.sh(工作包#2): - run681/715/724三实录: 部署本体成功但收尾被前序步骤失败整段跳过,只能人工拼三段补 - 现在workflow尾步自动调; 被跳过时Mac本机手跑即补: GTOKEN=<token> bash scripts/ci/vps_finalize.sh <部署的sha> - 常驻重启失败不阻断tag/issue记账(代码已就位,末尾非零退出提醒); tag -f+push -f天然幂等; issue按真实gap开/关 关issue按真实gap(工作包#3,2026-08-21事故): - 旧代码硬编码VPS_LOG=""无脑关issue——用户dispatch填旧sha(f28f9cf≠be315a0)漏推P0后,gap非空也关→用户误以为推完 - 修=finalize算vps-deployed..origin/master真实gap,非空则issue保持open并列剩余清单 - gap改按subject算(--format='%h %s'|grep,与enforce-label同定义): --grep匹配整个message,commit body里引用的命令文本会误命中(曾误开issue#7); ci-cd.yml check-vps-needed同步改 - vps_pending_check.py: GITHUB_SHA缺失时回退VPS_DEPLOY_SHA(支持Mac手跑); 有上轮部署sha时body加「仍未上VPS」警示
78 lines
3.0 KiB
Python
78 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""CI 用: 维护 [待推VPS] issue 信号(Gitea 无 approval gate, 用 issue open/close 当黄/绿)。
|
|
|
|
被 ci-cd.yml 的 nas-verify 与 vps-deploy.yml 复用:
|
|
- nas-verify 后: VPS_LOG=vps-deployed..HEAD 的 [vps] commit → 有则开/更新待推 issue
|
|
- vps-deploy 成功后(已打 vps-deployed=HEAD): VPS_LOG="" → 关闭所有待推 issue
|
|
|
|
env:
|
|
GTOKEN Gitea token(操作 issue)
|
|
GITHUB_REPOSITORY sanguo/sanguo_vnpy_v2
|
|
GITHUB_SHA 本次 commit
|
|
VPS_LOG 待推 commit 的 "git log --format='%h %s'" 输出(空=无待推)
|
|
"""
|
|
import os
|
|
import json
|
|
import urllib.request
|
|
|
|
API_BASE = "http://192.168.2.154:3000/api/v1/repos/"
|
|
|
|
|
|
def main():
|
|
token = os.environ["GTOKEN"]
|
|
repo = os.environ["GITHUB_REPOSITORY"]
|
|
sha = os.environ.get("GITHUB_SHA") or os.environ.get("VPS_DEPLOY_SHA") or "HEAD"
|
|
vps_log = os.environ.get("VPS_LOG", "").strip()
|
|
api = API_BASE + repo
|
|
|
|
def req(method, path, data=None):
|
|
r = urllib.request.Request(api + path, method=method)
|
|
r.add_header("Authorization", "token " + token)
|
|
r.add_header("Content-Type", "application/json")
|
|
if data is not None:
|
|
r.data = json.dumps(data).encode()
|
|
try:
|
|
with urllib.request.urlopen(r, timeout=20) as resp:
|
|
body = resp.read()
|
|
return json.loads(body) if body else {}
|
|
except Exception as e:
|
|
print(f" API {method} {path} 失败: {e}")
|
|
return {}
|
|
|
|
issues = req("GET", "/issues?state=open&type=issues") or []
|
|
pending = [i for i in issues if str(i.get("title", "")).startswith("[待推VPS]")]
|
|
|
|
if not vps_log:
|
|
# 无待推 → 关闭所有待推 issue(gap 清空 = 绿)
|
|
print("✅ 无待推 VPS commit (vps-deployed..HEAD 无 [vps])")
|
|
for i in pending:
|
|
req("PATCH", f"/issues/{i['number']}", {"state": "closed"})
|
|
print(f" 已关闭待推 issue #{i['number']} (gap 清空)")
|
|
return
|
|
|
|
n = len([l for l in vps_log.splitlines() if l.strip()])
|
|
print(f"⚠️ 待推 VPS: {n} 个 [vps] commit")
|
|
print(vps_log)
|
|
prev = os.environ.get("VPS_DEPLOY_SHA", "")
|
|
body = (
|
|
f"nas-verify 绿 @{sha[:7]}, 以下 [vps] commit 待推 VPS:\n"
|
|
f"```\n{vps_log}\n```\n\n"
|
|
+ (
|
|
f"⚠️ 上一轮 vps-deploy 只部署到 @{prev[:7]}, 上述 commit 仍未上 VPS —— issue 保持 open(不再无脑关)。\n\n"
|
|
if prev else ""
|
|
)
|
|
+ f"推法: Gitea → Actions → vps-deploy.yml → Run workflow, sha={sha}\n"
|
|
f"agent: dispatch vps-deploy.yml inputs={{sha:{sha}}}"
|
|
)
|
|
if pending:
|
|
for i in pending:
|
|
req("POST", f"/issues/{i['number']}/comments", {"body": body})
|
|
print(f" 已更新待推 issue #{i['number']}")
|
|
else:
|
|
res = req("POST", "/issues", {"title": f"[待推VPS] {n} 个 commit 待推生产", "body": body})
|
|
print(f" 已开待推 issue #{res.get('number', '?')} (open=黄/待推)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|