# -*- coding: utf-8 -*- r"""一条命令完成发布。 用法: python _build/release.py 1.7.0 # 升版本 + 构建 + 打包 + 推送 + 建发行版 python _build/release.py 1.7.0 --no-readme # 明说这版没改 README 它干这些事: 1. 升版本号(三端 + 文档 + 测试断言) 2. 重建 EXE 和 APK 3. 打三个成品 4. 跑一遍关键测试 5. git 提交 + 推送 6. 在 Gitee 建发行版,把三个文件传上去 7. 覆盖仓库简介(只在第一次需要,重复设成一样也无害) 令牌从 _build/.gitee_token 读(那个文件在 .gitignore 里,不会进仓库)。 第一次用:把令牌写进去就行。 """ from __future__ import annotations import os import subprocess import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") TOKEN_FILE = ROOT / "_build" / ".gitee_token" GIT = r"C:\Program Files\Git\bin\git.exe" GIT_SSH = r"C:\Program Files\Git\usr\bin\ssh.exe" def sh(args, cwd=ROOT, check=False, timeout=1800, env=None): merged = dict(os.environ) if env: merged.update(env) proc = subprocess.run(args, cwd=cwd, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout, env=merged) if check and proc.returncode != 0: raise RuntimeError(f"{args[0]} 失败:{proc.stdout}\n{proc.stderr}") return proc def step(n, title): print() print(f" {'=' * 56}") print(f" {n}. {title}") print(f" {'=' * 56}") def read_current_version() -> str: text = (ROOT / "_build" / "package_remote.py").read_text(encoding="utf-8") for line in text.splitlines(): if line.startswith("VERSION = "): return line.split('"')[1] return "0.0.0" def main() -> int: if len(sys.argv) < 2: print(" 用法: python _build/release.py <新版本号>") print(f" 当前版本: {read_current_version()}") return 1 new_version = sys.argv[1] old_version = read_current_version() note_readme = "--no-readme" in sys.argv if not TOKEN_FILE.is_file(): print(f" [失败] 找不到令牌文件 {TOKEN_FILE}") print(" 把 Gitee 令牌写进那个文件(一行,别加引号)") return 1 token = TOKEN_FILE.read_text(encoding="utf-8").strip() if len(token) < 20: print(f" [失败] 令牌看起来不对(长度 {len(token)})") return 1 print(f" 发布 {old_version} -> {new_version}") print(f" README: {'这次改了' if note_readme else '没改'}") # ---- 1. 升版本 ---- step(1, "升版本号") proc = sh([sys.executable, "_build/bump_version_generic.py", old_version, new_version]) print(proc.stdout[-1200:]) if proc.returncode != 0: print(proc.stderr[-500:]) return 1 # ---- 2. 重建 ---- step(2, "重建 EXE 和 APK") sh([GIT, "--version"]) # 探活,无关紧要 for args in (["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "android\\build_apk.ps1"], ["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "windows\\build_exe.ps1"]): proc = sh(args, timeout=2400) tail = (proc.stdout or "").strip().splitlines()[-2:] for line in tail: print(f" {line}") if proc.returncode != 0: print(f" [失败] {args[-1]}") print((proc.stderr or "")[-600:]) return 1 # ---- 3. 打包 ---- step(3, "打三个成品") proc = sh([sys.executable, "_build/package_remote.py"]) print(proc.stdout[-1400:]) if proc.returncode != 0: return 1 # ---- 4. 关键测试 ---- step(4, "跑关键测试") for test in ("audit_encoding.py", "test_net_tune.py", "test_chunked_body.py", "test_admin_js.py"): proc = sh([sys.executable, "-u", f"_tests/{test}"], timeout=900) # 用退出码判断 —— 各测试的输出格式不一样 # (有的打"失败 0 项",有的打"结论:JS 合法",字符串匹配会误判) bad = proc.returncode != 0 print(f" {test:28} {'✗' if bad else '✓'}") if bad: print((proc.stdout or "")[-800:]) print((proc.stderr or "")[-400:]) return 1 proc = sh([sys.executable, "-u", "_tests/test_package_remote.py"], timeout=1800) out = proc.stdout or "" last = [ln for ln in out.splitlines() if "通过" in ln or "失败" in ln] print(f" {'test_package_remote.py':28} " f"{'✓' if proc.returncode == 0 else '✗'} {last[-1] if last else ''}") if proc.returncode != 0: return 1 # ---- 5. 提交推送 ---- step(5, "提交并推送") env = {"GIT_SSH_COMMAND": f'"{GIT_SSH}"'} sh([GIT, "add", "-A"]) msg = (f"{new_version}:{'README 与' if note_readme else ''}常规发布\n\n" f"- 版本号 {old_version} -> {new_version}\n" f"- 重建三端并打包\n" f"- 关键测试全过\n") sh([GIT, "commit", "-q", "-m", msg]) proc = sh([GIT, "push", "origin", "main"], env=env, timeout=1800) print(f" {proc.stdout.strip()[-300:]}") if proc.returncode != 0: print(proc.stderr[-600:]) return 1 # ---- 6. 发行版 ---- step(6, "建 Gitee 发行版") proc = sh([sys.executable, "_build/gitee_release.py", token, new_version], timeout=2400) print(proc.stdout[-1200:]) if proc.returncode != 0: print(proc.stderr[-600:]) return 1 print() print(f" ✓ {new_version} 发布完成") print(f" https://gitee.com/xmz7891/ykxmz/releases/tag/v{new_version}") return 0 if __name__ == "__main__": sys.exit(main())