Files
ykxmz/_build/bump_version_generic.py
T
xmz7891 de5018bbbe 按用户规矩升到 1.9.0(有新内容+重构,不是只修 bug)+ 加版本号一致性检查
用户定的规矩:
    只修 bug        -> 最后一位 +1    1.8.2 -> 1.8.3
    加新内容/重构    -> 第二位 +1,最后一位清 0    1.8.2 -> 1.9.0

这一轮既有新东西(设置窗口的中转服务器字段、允许新设备接入开关、
双击开设置窗口)又有重构(手表面板切换收成 showOnly()),
所以是 **1.9.0**,不是我之前说的 1.8.3。

== 顺手加了个「版本号一致性」检查(回归阶段 3a8)==

版本号散落在 8 个地方,靠脚本+人工替换很容易漏。实测漏了两个,
而且**漏了不报错**,只是用户看到一个对不上的版本号:

  * windows/version_info.txt 写着 1.7.1
    -> 用户在「文件属性 -> 详细信息」里看到的版本,错了三个版本
  * docs/LICENSE.txt 写着 1.7.1
    -> 安装时许可页上显示 1.7.1

第二个的根因值得记:installer/build_installer.py 会从 docs/ 里三份
文档**重新生成** license-combined.txt,所以光改生成物没用 ——
下次构建又变回去(我第一次就是这么被骗的)。改成在 build_license()
里**统一注入当前版本**,源文档写什么都不影响。

新增:
  _tests/check_version_consistency.py   检查 8 处版本号一致
  _build/add_verify_stage.py            给套件加阶段(不经 shell,
                                        不会被 PowerShell 吃掉 \$ 变量)
  _build/fix_license_version.py         按行精确修 license

== 另外修了两个我自己的坑 ==

1. verify_all.ps1 被 PowerShell heredoc 吃掉了 \ / \,
   整个脚本语法错。**同一个坑踩了两次**(3a7 和 3a8)。
   以后给 .ps1 加含 \$ 的内容一律写 Python 脚本文件再跑。

2. build_exe.ps1 被 edit 工具吃掉了 UTF-8 BOM
   (PowerShell 5.1 会按 GBK 读,中文注释乱码)。

全量回归 43 个阶段全部通过。
2026-10-07 22:10:14 +08:00

183 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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())