135 lines
5.2 KiB
Python
135 lines
5.2 KiB
Python
#!/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()
|