# -*- coding: utf-8 -*- r'''把某个版本的成品和官网代码传到中转服务器。 用法: python _build/deploy_release.py 1.9.0 python _build/deploy_release.py 1.9.0 --code-only # 只更新官网代码 python _build/deploy_release.py 1.9.0 --files-only # 只传成品文件 == 为什么写成脚本文件而不是 python -c == 这一轮我在 PowerShell 里用 `python -c @"..."@` 写带引号和 $ 的 Python 代码,失败了四五次(PowerShell 会插值、会吃掉 $、嵌套引号 转义一层套一层)。凡是超过两行的 Python,一律写成 .py 文件再跑。 == 官网上"最新版"是怎么来的 == relay.py 的 _list_downloads() 按 **mtime 倒序** 返回文件, landing.py 的 _version() 取**第一个**能匹配到 x.y.z 的文件名。 所以"上传哪个版本,官网就显示哪个版本" —— 不用改任何代码。 按顺序传 1.9.0、再传 2.0.0,官网就会先显示 1.9.0、再显示 2.0.0。 ''' from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "_build")) from devssh import upload, run # noqa: E402 REMOTE_DIR = "/root/lanremote-relay" DL_DIR = REMOTE_DIR + "/downloads" # 要传的成品(和 dist/ 里的文件名一致) ARTIFACTS = [ "ykxmz-setup-{v}.exe", "ykxmz-watch-{v}.apk", "ykxmz-watch-{v}-说明.txt", "ykxmz-server-{v}.zip", ] # 官网/服务端代码 CODE = ["relay.py", "admin.py", "landing.py", "showcase.py"] 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 到中转服务器" % version) print("=" * 62) # ---------------- 成品 ---------------- if not code_only: print() print(" ── 1. 上传成品到 downloads/ ──") 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/%s" % (DL_DIR, name)) print(" ✓ %-34s %10d 字节" % (name, local.stat().st_size)) # ---------------- 代码 ---------------- 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" % (REMOTE_DIR, name)) print(" ✓ %s" % name) print() print(" ── 3. 语法检查 + 重启 ──") out = run( "cd %s && python3 - <<'PY'\n" "import ast\n" "for f in %r:\n" " ast.parse(open(f, encoding='utf-8').read())\n" "print('语法 OK')\n" "PY" % (REMOTE_DIR, tuple(CODE)), timeout=90) print(" " + out[1].strip().splitlines()[-1]) out = run("systemctl restart lanremote-relay && sleep 4 && " "systemctl is-active lanremote-relay && " "grep -m1 '^VERSION' %s/relay.py" % REMOTE_DIR, timeout=120) for ln in out[1].strip().splitlines(): print(" " + ln) # ---------------- 复核 ---------------- print() print(" ── 4. 复核:官网现在显示哪个版本 ──") out = run( "curl -s -H 'Cache-Control: no-cache' http://127.0.0.1:8080/v1/downloads", timeout=60) print(" " + out[1].strip()[:400]) out = run("curl -s -H 'Cache-Control: no-cache' http://127.0.0.1:8080/ " "| grep -o '最新版 [0-9.]*' | head -1", timeout=60) print(" 官网首页角标: " + out[1].strip()) print() print(" 提示:官网 / 走 max-age=60,公网可能需要等一分钟才刷新;") print(" 下载链接带 604800 缓存,换版本号正好会换 URL。") return 0 if __name__ == "__main__": sys.exit(main())