Files
agent-desktop/scripts/sync_version.py
T
Pine 123ba0d0fa feat: 桌面端认证/通信/tauri 后端更新 + README
- auth/jwt/header 认证流程更新,会话与令牌处理增强
- 通信(ChatWindow/ConversationList/Network)更新
- tauri backend.rs/remote.rs、tauri.conf.json 更新
- pineagents app 路由(agent_gate/opc)与配置更新
2026-09-11 21:17:48 +08:00

234 lines
7.7 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
版本号同步脚本:从根目录 VERSION 文件读取版本号,同步到各端配置文件。
单一数据源:agent-desktop/VERSION
同步目标:
- console/src-tauri/tauri.conf.json (version 字段)
- console/src-tauri/Cargo.toml (version 字段)
- src/pineagents/branding.py (version 字段)
- src/pineagents/config/config.py (app_version 默认值)
用法:
uv run scripts/sync_version.py # 同步版本号
uv run scripts/sync_version.py --check # 仅检查是否一致,不修改
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
VERSION_FILE = ROOT / "VERSION"
def read_version() -> str:
"""从 VERSION 文件读取版本号。"""
if not VERSION_FILE.is_file():
print(f"错误:VERSION 文件不存在: {VERSION_FILE}", file=sys.stderr)
sys.exit(1)
version = VERSION_FILE.read_text(encoding="utf-8").strip()
if not version:
print("错误:VERSION 文件为空", file=sys.stderr)
sys.exit(1)
return version
def pep440_to_semver(version: str) -> str:
"""将 PEP 440 版本号转换为 semver 格式(Tauri 要求)。
转换规则:
0.0.1b3 -> 0.0.1-beta.3
0.0.1a1 -> 0.0.1-alpha.1
0.0.1rc1 -> 0.0.1-rc.1
0.0.1 -> 0.0.1
0.0.1.post1 -> 0.0.1-post.1
0.0.1.dev1 -> 0.0.1-dev.1
"""
# 已经是 semver 格式(包含 -),直接返回
if "-" in version:
return version
# 匹配预发布标签:a/b/rc + 数字
match = re.match(r"^(\d+\.\d+\.\d+)(a|b|rc|alpha|beta)(\d+)$", version, re.IGNORECASE)
if match:
base, label, num = match.groups()
label = label.lower()
if label == "a":
label = "alpha"
elif label == "b":
label = "beta"
return f"{base}-{label}.{num}"
# 匹配 .postN / .devN
match = re.match(r"^(\d+\.\d+\.\d+)\.(post|dev)(\d+)$", version, re.IGNORECASE)
if match:
base, label, num = match.groups()
return f"{base}-{label.lower()}.{num}"
# 纯版本号,直接返回
return version
def sync_tauri_conf(version: str, check_only: bool = False) -> bool:
"""同步版本号到 tauri.conf.json。
注意:Tauri 要求 version 字段必须是 semver 格式,
因此这里将 PEP 440 格式转换为 semver 格式后再写入。
例如:0.0.1b3 -> 0.0.1-beta.3
"""
path = ROOT / "console" / "src-tauri" / "tauri.conf.json"
if not path.is_file():
print(f"跳过:文件不存在 {path}")
return True
semver_version = pep440_to_semver(version)
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
old_version = data.get("version", "")
if old_version == semver_version:
print(f" ✓ tauri.conf.json 已是 {semver_version} (semver)")
return True
if check_only:
print(f" ✗ tauri.conf.json 不一致: {old_version}{semver_version} (semver)")
return False
data["version"] = semver_version
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
f.write("\n")
print(f" ✓ tauri.conf.json 已更新: {old_version}{semver_version} (semver)")
return True
def sync_cargo_toml(version: str, check_only: bool = False) -> bool:
"""同步版本号到 Cargo.toml。
注意:Cargo 要求 version 字段必须是 semver 格式,
因此这里将 PEP 440 格式转换为 semver 格式后再写入。
例如:0.0.1b3 -> 0.0.1-beta.3
"""
path = ROOT / "console" / "src-tauri" / "Cargo.toml"
if not path.is_file():
print(f"跳过:文件不存在 {path}")
return True
semver_version = pep440_to_semver(version)
content = path.read_text(encoding="utf-8")
# 匹配 [package] 下的 version = "x.x.x"
pattern = re.compile(r'^(\s*version\s*=\s*)"[^"]*"', re.MULTILINE)
match = pattern.search(content)
if not match:
print(f" ✗ Cargo.toml 未找到 version 字段")
return False
old_version = match.group(0).split('"')[1]
if old_version == semver_version:
print(f" ✓ Cargo.toml 已是 {semver_version} (semver)")
return True
if check_only:
print(f" ✗ Cargo.toml 不一致: {old_version}{semver_version} (semver)")
return False
new_content = pattern.sub(f'{match.group(1)}"{semver_version}"', content, count=1)
path.write_text(new_content, encoding="utf-8")
print(f" ✓ Cargo.toml 已更新: {old_version}{semver_version} (semver)")
return True
def sync_branding_py(version: str, check_only: bool = False) -> bool:
"""同步版本号到 branding.py。"""
path = ROOT / "src" / "pineagents" / "branding.py"
if not path.is_file():
print(f"跳过:文件不存在 {path}")
return True
content = path.read_text(encoding="utf-8")
pattern = re.compile(r'(\s*version\s*:\s*str\s*=\s*)"[^"]*"')
match = pattern.search(content)
if not match:
print(f" ✗ branding.py 未找到 version 字段")
return False
old_version = match.group(0).split('"')[1]
if old_version == version:
print(f" ✓ branding.py 已是 {version}")
return True
if check_only:
print(f" ✗ branding.py 不一致: {old_version}{version}")
return False
new_content = pattern.sub(f'{match.group(1)}"{version}"', content, count=1)
path.write_text(new_content, encoding="utf-8")
print(f" ✓ branding.py 已更新: {old_version}{version}")
return True
def sync_config_py(version: str, check_only: bool = False) -> bool:
"""同步版本号到 config.py 的 app_version 默认值。"""
path = ROOT / "src" / "pineagents" / "config" / "config.py"
if not path.is_file():
print(f"跳过:文件不存在 {path}")
return True
content = path.read_text(encoding="utf-8")
# 匹配 app_version: str = Field( default="x.x.x",
pattern = re.compile(r'(app_version\s*:\s*str\s*=\s*Field\(\s*default\s*=\s*)"[^"]*"')
match = pattern.search(content)
if not match:
print(f" ✗ config.py 未找到 app_version 字段")
return False
old_version = match.group(0).split('"')[1]
if old_version == version:
print(f" ✓ config.py 已是 {version}")
return True
if check_only:
print(f" ✗ config.py 不一致: {old_version}{version}")
return False
new_content = pattern.sub(f'{match.group(1)}"{version}"', content, count=1)
path.write_text(new_content, encoding="utf-8")
print(f" ✓ config.py 已更新: {old_version}{version}")
return True
def main():
parser = argparse.ArgumentParser(description="同步版本号到各端配置文件")
parser.add_argument("--check", action="store_true", help="仅检查是否一致,不修改")
args = parser.parse_args()
version = read_version()
print(f"{'检查' if args.check else '同步'}版本号: {version}")
print(f"数据源: {VERSION_FILE}")
print()
results = []
results.append(sync_tauri_conf(version, args.check))
results.append(sync_cargo_toml(version, args.check))
results.append(sync_branding_py(version, args.check))
results.append(sync_config_py(version, args.check))
print()
if all(results):
print("✓ 所有配置文件版本号一致")
sys.exit(0)
else:
print("✗ 部分配置文件版本号不一致" + ("(请运行 uv run scripts/sync_version.py 同步)" if args.check else ""))
sys.exit(1)
if __name__ == "__main__":
main()