# -*- coding: utf-8 -*- r"""通用版本号升级(1.5.0 -> 1.6.0)。 只改**用户能看到版本号的地方**,不动那些当数据用的 "1.4.0/1.5.0": * HTTP User-Agent "LanRemote/1.4.0" —— 协议标识,不是产品版本, 而且 20 多个测试在断言它,动它要同步改一大片 * _tests 里当测试数据用的版本串 * _build 下的一次性脚本(历史记录) 改这些: android/AndroidManifest.xml versionCode / versionName android/build_apk.ps1 APK 文件名 + manifest 参数 _build/package_remote.py 三个产物名 windows/version_info.txt EXE 的文件版本 windows/lanremote/__init__.py __version__ docs/*.txt 三份文档 relay/relay.py VERSION _tests/test_package_remote.py 断言成品名 _tests/test_apk_contents.py 断言 APK 名 """ from __future__ import annotations import re import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") OLD = sys.argv[1] if len(sys.argv) > 1 else "1.5.0" NEW = sys.argv[2] if len(sys.argv) > 2 else "1.6.0" OLD_CODE = str(int(OLD.split(".")[0]) * 100 + int(OLD.split(".")[1]) + 1) NEW_CODE = str(int(NEW.split(".")[0]) * 100 + int(NEW.split(".")[1]) + 1) def swap(path: Path, pairs, note: str) -> bool: if not path.is_file(): print(f" [跳过] {path.name} 不存在") return False raw = path.read_bytes() had_bom = raw.startswith(b"\xef\xbb\xbf") text = raw.decode("utf-8-sig") original = text for old, new in pairs: text = text.replace(old, new) if text == original: print(f" [跳过] {path.name}: 没有 {OLD}") return False path.write_bytes((b"\xef\xbb\xbf" if had_bom else b"") + text.replace("\r\n", "\n").encode("utf-8")) print(f" {path.name}: {note}") return True def main() -> int: print(f" 版本号 {OLD} -> {NEW}(versionCode {OLD_CODE} -> {NEW_CODE})") print() changed = 0 # ---- Android manifest ---- manifest = ROOT / "android" / "AndroidManifest.xml" if swap(manifest, [(f'android:versionCode="{OLD_CODE}"', f'android:versionCode="{NEW_CODE}"'), (f'android:versionName="{OLD}"', f'android:versionName="{NEW}"')], f"versionCode={NEW_CODE} versionName={NEW}"): changed += 1 # ---- APK 构建脚本 ---- apk_ps1 = ROOT / "android" / "build_apk.ps1" if swap(apk_ps1, [(f"ykxmz-watch-{OLD}.apk", f"ykxmz-watch-{NEW}.apk"), (f"--version-code {OLD_CODE}", f"--version-code {NEW_CODE}"), (f'--version-name "{OLD}"', f'--version-name "{NEW}"')], "APK 名 + 版本"): changed += 1 # ---- 打包脚本 ---- if swap(ROOT / "_build" / "package_remote.py", [(f'VERSION = "{OLD}"', f'VERSION = "{NEW}"')], f"VERSION={NEW}"): changed += 1 # ---- Windows EXE 版本信息 ---- # ⚠ 原来用的是 f'"{OLD}.0"',一旦 OLD 和文件里对不上就静默跳过 # (version_info.txt 就是这么从 1.7.1 一直漏到现在的)。 # 改成正则匹配任意四段版本。 import re as _re _vi = ROOT / "windows" / "version_info.txt" if _vi.is_file(): _raw = _vi.read_bytes() _bom = _raw.startswith(b"\xef\xbb\xbf") _txt = _raw.decode("utf-8-sig") _new = _re.sub( r'(StringStruct\("(?:File|Product)Version",\\s*")\\d+\\.\\d+\\.\\d+\\.\\d+("\))', r'\\g<1>' + NEW + r'.0\\g<2>', _txt) if _new != _txt: _vi.write_bytes((b"\xef\xbb\xbf" if _bom else b"") + _new.replace("\\r\\n", "\\n").encode("utf-8")) print(f" version_info.txt: EXE 文件版本 {NEW}.0") changed += 1 else: print(" [跳过] version_info.txt") if False: changed += 1 # ---- __init__ ---- if swap(ROOT / "windows" / "lanremote" / "__init__.py", [(f'"{OLD}"', f'"{NEW}"')], f"__version__={NEW}"): changed += 1 # ---- 中转 VERSION ---- if swap(ROOT / "relay" / "relay.py", [(f'VERSION = "{OLD}"', f'VERSION = "{NEW}"')], f"VERSION={NEW}"): changed += 1 # ---- 安装包版本 ---- # ⚠ 原来漏了这三处,导致 version_info.txt 从 1.7.1 起就一直没跟上 # (用户在"文件属性 -> 详细信息"里看到一个错三个版本的号)。 if swap(ROOT / "installer" / "ykxmz.iss", [(f'#define MyAppVersion "{OLD}"', f'#define MyAppVersion "{NEW}"'), (f'ykxmz-setup-{OLD}.exe', f'ykxmz-setup-{NEW}.exe')], f"安装包版本 {NEW}"): changed += 1 # ---- 安装许可页上的版本 ---- if swap(ROOT / "installer" / "license-combined.txt", [(f"版本:{OLD}", f"版本:{NEW}")], f"许可页版本 {NEW}"): changed += 1 # ---- 三份文档 ---- for path in sorted((ROOT / "docs").glob("*.txt")): raw = path.read_bytes() had_bom = raw.startswith(b"\xef\xbb\xbf") text = raw.decode("utf-8-sig") new_text = text.replace(OLD, NEW) if new_text != text: path.write_bytes((b"\xef\xbb\xbf" if had_bom else b"") + new_text.replace("\r\n", "\n").encode("utf-8")) print(f" docs/{path.name}") changed += 1 # ---- 会断言成品名的测试 ---- for name in ("test_package_remote.py", "test_apk_contents.py"): path = ROOT / "_tests" / name if not path.is_file(): continue text = path.read_text(encoding="utf-8") new_text = (text.replace(f"ykxmz-agent-{OLD}", f"ykxmz-agent-{NEW}") .replace(f"ykxmz-server-{OLD}", f"ykxmz-server-{NEW}") .replace(f"ykxmz-watch-{OLD}", f"ykxmz-watch-{NEW}")) if new_text != text: path.write_text(new_text, encoding="utf-8", newline="\n") print(f" _tests/{name}") changed += 1 print() print(f" 共改了 {changed} 处") print(f" 注意:HTTP UA 仍然是 LanRemote/1.4.0(协议标识,不是产品版本)") # 复核 print() print(" 复核:") checks = [ (manifest, f'versionName="{NEW}"'), (apk_ps1, f"ykxmz-watch-{NEW}.apk"), (ROOT / "_build" / "package_remote.py", f'VERSION = "{NEW}"'), ] for path, needle in checks: ok = needle in path.read_text(encoding="utf-8-sig") print(f" {path.name}: {'✓' if ok else '✗'} {needle}") return 0 if __name__ == "__main__": sys.exit(main())