# -*- coding: utf-8 -*- r'''把一个版本的代码和成品部署到 Lightsail。 用法: python _build/deploy_lightsail_release.py 2.1.3 python _build/deploy_lightsail_release.py 2.1.3 --code-only python _build/deploy_lightsail_release.py 2.1.3 --files-only == 为什么写成文件 == 这一轮我用 `python -c @"..."@` 传带 `$(...)` 的远端命令, PowerShell 把 `$(systemctl ...)` 当成本机的子表达式执行了 (报 "无法将 systemctl 项识别为 cmdlet"),还吃掉引号导致 Python 语法错。凡是超过两行的远端命令,一律写成文件跑。 ''' from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "_build")) from lssh import run, upload # noqa: E402 RELAY_DIR = "/opt/ykxmz-relay" PORT = "13245" PUBLIC = "http://13.54.30.196:%s" % PORT CODE = ["relay.py", "admin.py", "landing.py", "showcase.py"] ARTIFACTS = ["ykxmz-setup-{v}.exe", "ykxmz-watch-{v}.apk", "ykxmz-watch-{v}-说明.txt", "ykxmz-server-{v}.zip"] def main() -> int: args = [a for a in sys.argv[1:] if a] if not args: print(__doc__) return 1 version = args[0] code_only = "--code-only" in args files_only = "--files-only" in args print("=" * 62) print(" 部署 %s 到 Lightsail" % version) print("=" * 62) if not code_only: print() print(" ── 1. 传成品到 downloads/ ──") run("mkdir -p %s/downloads" % RELAY_DIR, timeout=60, quiet=True) for tpl in ARTIFACTS: name = tpl.format(v=version) local = ROOT / "dist" / name if not local.is_file(): print(" [跳过] %s 不存在" % name) continue upload(str(local), "%s/downloads/%s" % (RELAY_DIR, name)) if not files_only: print() print(" ── 2. 传服务端代码 ──") for name in CODE: local = ROOT / "relay" / name if not local.is_file(): print(" [跳过] %s 不存在" % name) continue upload(str(local), "%s/%s" % (RELAY_DIR, name)) print() print(" ── 3. 语法检查 + 重启 ──") out = run( "cd %s && for f in relay.py admin.py landing.py showcase.py; do " "python3 -c \"import ast,sys;ast.parse(open(sys.argv[1]," "encoding='utf-8').read())\" $f || exit 1; done && echo PY_OK" % RELAY_DIR, timeout=120, quiet=True) print(" " + out[1].strip().splitlines()[-1]) out = run("sudo systemctl restart ykxmz-relay; sleep 4; " "systemctl is-active ykxmz-relay", timeout=180, quiet=True) print(" relay 状态: " + out[1].strip()) print() print(" ── 4. 复核 ──") script = ( 'printf " %-18s %s\\n" 本机 "' '$(curl -s -o /dev/null -w \'%{http_code}\' --max-time 8 ' 'http://127.0.0.1:' + PORT + '/v1/health)"\n' 'printf " %-18s %s\\n" 公网 "' '$(curl -s -o /dev/null -w \'%{http_code}\' --max-time 15 ' + PUBLIC + '/v1/health)"\n' 'printf " %-18s %s\\n" 公网首页 "' '$(curl -s -o /dev/null -w \'%{http_code}\' --max-time 15 ' + PUBLIC + '/)"\n' 'printf " %-18s %s\\n" 公网延迟 "' '$(curl -s -o /dev/null -w \'%{time_total}s\' --max-time 15 ' + PUBLIC + '/v1/health)"\n' 'printf " %-18s %s\\n" 角标 "' '$(curl -s --max-time 10 http://127.0.0.1:' + PORT + '/ | grep -o "最新版 [0-9.]*" | head -1)"\n' 'printf " %-18s %s\\n" 监听 "' '$(ss -tln 2>/dev/null | grep ":' + PORT + ' " | wc -l) 个"\n' 'printf " %-18s %s\\n" 监测 "' 'fails=$(grep -m1 ^fails= ' + RELAY_DIR + '/monitor_state.txt 2>/dev/null | cut -d= -f2); ' 'echo ${fails:-?} 次连续失败"\n' 'printf " %-18s %s\\n" 抢救报告 "' '$(ls ' + RELAY_DIR + '/rescue_reports 2>/dev/null | wc -l) 份"\n' ) out = run(script, timeout=180, quiet=True) for ln in out[1].strip().splitlines(): print(ln) print() print(" 公网地址:%s" % PUBLIC) return 0 if __name__ == "__main__": sys.exit(main())