NLDClient-yudde/Tool/py_tools/version_code_manager.py

135 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env python3
# version_code_manager.py
# 用途:管理各渠道的 Android versionCode / iOS buildNumber 自动递增
#
# 逻辑(优先级从高到低):
# 1. 读取 Jenkins node 状态文件 ~/.nld_build_state/{channel}.json 中保存的上次值
# 2. 若状态文件不存在,则从 ChannelSetting.yaml 的 BuildCode 字段读取默认初始值
# 3. 将该值 +1 后:
# a. 写回状态文件(跨构建持久化,因为 Jenkins 每次构建是全新进程,环境变量不保留)
# b. 输出 shell export 语句供调用方 source
#
# 使用方式(在 build_unity.sh 中):
# source <(python3 .../version_code_manager.py <channel_name> <channel_yaml_path>)
# echo "VERSION_CODE=$NLD_CLIENT_<channel>_VERSION_CODE"
#
# 传入参数:
# argv[1]: ChannelName (e.g. "Dev")
# argv[2]: ChannelYamlPath (e.g. "/path/to/Tool/Channels/Dev/ChannelSetting.yaml")
#
# 退出码:
# 0: 成功
# 1: 参数不足
# 2: yaml 文件不存在
# 3: yaml 解析失败
# 4: BuildCode 字段不存在
import sys
import os
import json
import yaml
# Jenkins node 上存储各渠道 versionCode 的状态目录
# 优先使用 JENKINS_HOME 下的固定路径,避免 ~ 在不同用户身份下解析到不同目录
# 发布历史只保存在 Jenkins node 上,不进入 git 仓库
_jenkins_home = os.environ.get("JENKINS_HOME") or os.environ.get("HUDSON_HOME")
if _jenkins_home:
STATE_DIR = os.path.join(_jenkins_home, "nld_build_state")
else:
# 本地调试场景回退到 ~/.nld_build_state
STATE_DIR = os.path.expanduser("~/.nld_build_state")
def main():
if len(sys.argv) < 3:
print("Usage: python3 version_code_manager.py <ChannelName> <ChannelYamlPath>", file=sys.stderr)
sys.exit(1)
channel_name = sys.argv[1]
yaml_path = sys.argv[2]
# 环境变量名称,将渠道名中的特殊字符替换为下划线,保持规范
safe_channel = channel_name.replace("-", "_").replace(" ", "_")
env_var_name = f"NLD_CLIENT_{safe_channel}_VERSION_CODE"
# 1. 优先从状态文件读取(跨构建持久化存储)
current_code = read_state_file(safe_channel)
if current_code is not None:
print(f"[VersionCode] 从状态文件读取到 {safe_channel} 上次值: {current_code}", file=sys.stderr)
else:
# 2. 回退到 YAML 默认值
print(f"[VersionCode] 状态文件不存在,从 YAML 读取初始值", file=sys.stderr)
current_code = read_build_code_from_yaml(yaml_path)
# 2. 递增
new_code = current_code + 1
print(f"[VersionCode] 渠道={channel_name}, 旧值={current_code}, 新值={new_code}", file=sys.stderr)
# 3. 写回状态文件(跨构建持久化)
write_state_file(safe_channel, new_code)
# 4. 输出 export 语句供 shell source写到 stdout
print(f"export {env_var_name}={new_code}")
print(f"export NLD_CURRENT_VERSION_CODE={new_code}")
def read_state_file(safe_channel):
"""从 Jenkins node 状态文件读取渠道的上次 versionCode不存在时返回 None"""
state_file = os.path.join(STATE_DIR, f"{safe_channel}.json")
if not os.path.exists(state_file):
print(f"[VersionCode] 状态文件不存在: {state_file}", file=sys.stderr)
return None
try:
with open(state_file, 'r', encoding='utf-8') as f:
data = json.load(f)
code = int(data.get("version_code", ""))
return code
except Exception as e:
print(f"[VersionCode] 警告: 读取状态文件失败: {e}", file=sys.stderr)
return None
def write_state_file(safe_channel, new_code):
"""将递增后的 versionCode 写入 Jenkins node 状态文件,实现跨构建持久化"""
try:
os.makedirs(STATE_DIR, exist_ok=True)
state_file = os.path.join(STATE_DIR, f"{safe_channel}.json")
with open(state_file, 'w', encoding='utf-8') as f:
json.dump({"version_code": new_code, "channel": safe_channel}, f)
print(f"[VersionCode] ✅ 已将 {safe_channel} versionCode={new_code} 写入状态文件: {state_file}", file=sys.stderr)
except Exception as e:
print(f"[VersionCode] 警告: 写入状态文件失败: {e},不影响本次构建", file=sys.stderr)
def read_build_code_from_yaml(yaml_path):
"""从 ChannelSetting.yaml 读取 BuildCode 字段"""
if not os.path.exists(yaml_path):
print(f"[VersionCode] 错误: YAML 文件不存在: {yaml_path}", file=sys.stderr)
sys.exit(2)
try:
with open(yaml_path, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
except yaml.YAMLError as e:
print(f"[VersionCode] 错误: 解析 YAML 失败: {e}", file=sys.stderr)
sys.exit(3)
build_code = data.get("BuildCode")
if build_code is None:
print(f"[VersionCode] 错误: ChannelSetting.yaml 中不存在 BuildCode 字段", file=sys.stderr)
sys.exit(4)
try:
code = int(build_code)
except (ValueError, TypeError):
print(f"[VersionCode] 错误: BuildCode 值 '{build_code}' 无法解析为整数", file=sys.stderr)
sys.exit(4)
print(f"[VersionCode] 从 YAML 读取到 BuildCode 初始值: {code}", file=sys.stderr)
return code
if __name__ == "__main__":
main()