fix jsonDataValidation.yml

This commit is contained in:
秋云
2025-04-04 03:51:39 +08:00
parent 0999b7369a
commit c58350892c

View File

@@ -1,11 +1,10 @@
name: JSON Data Validation name: JSON Data Validation
on: on:
pull_request: pull_request_target:
types: [opened, synchronize, reopened, edited] types: [opened, synchronize, reopened, edited]
branches: branches:
- main - main
# 修改路径匹配,使其更宽松
paths: paths:
- 'repo/pathing/*.json' - 'repo/pathing/*.json'
@@ -15,14 +14,16 @@ jobs:
permissions: permissions:
contents: write contents: write
pull-requests: write pull-requests: write
id-token: write
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
ref: ${{ github.head_ref }} ref: ${{ github.event.pull_request.head.sha }}
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
@@ -38,36 +39,41 @@ jobs:
GITHUB_ACTOR: ${{ github.actor }} GITHUB_ACTOR: ${{ github.actor }}
PR_NUMBER: ${{ github.event.pull_request.number }} PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_REF: ${{ github.head_ref }} HEAD_REF: ${{ github.event.pull_request.head.ref }}
PR_REPO: ${{ github.event.pull_request.head.repo.full_name }}
run: | run: |
cat << 'EOF' > validate.py cat << 'EOF' > validate.py
import json import json
import os import os
import sys import sys
import subprocess import subprocess
import re
from packaging.version import parse from packaging.version import parse
from semver import VersionInfo from semver import VersionInfo
# ==================== 配置和常量 ====================
# 定义有效的 type 和 move_mode 值 # 定义有效的 type 和 move_mode 值
VALID_TYPES = ["teleport", "path", "target", "orientation"] VALID_TYPES = ["teleport", "path", "target", "orientation"]
VALID_MOVE_MODES = ["swim", "walk", "fly", "climb", "run", "dash", "jump"] VALID_MOVE_MODES = ["swim", "walk", "fly", "climb", "run", "dash", "jump"]
# 定义 action 和 action_params 的最低兼容版本 # 定义 action 和 action_params 的最低兼容版本
ACTION_VERSION_MAP = { ACTION_VERSION_MAP = {
"stop_flying": "0.42.0",
"force_tp": "0.42.0",
"nahida_collect": "0.42.0",
"pick_around": "0.42.0",
"hydro_collect": "0.42.0",
"electro_collect": "0.42.0",
"anemo_collect": "0.42.0",
"pyro_collect": "0.43.0",
"up_down_grab_leaf": "0.42.0",
"fight": "0.42.0", "fight": "0.42.0",
"combat_script": "0.42.0", "mining": "0.43.0",
"log_output": "0.42.0",
"fishing": "0.43.0", "fishing": "0.43.0",
"mining": "0.43.0" "force_tp": "0.42.0",
"log_output": "0.42.0",
"anemo_collect": "0.42.0",
"combat_script": "0.42.0",
"hydro_collect": "0.42.0",
"pick_around": "0.42.0",
"pyro_collect": "0.43.0",
"stop_flying": "0.42.0",
"normal_attack": "0.42.0",
"electro_collect": "0.42.0",
"nahida_collect": "0.42.0",
"up_down_grab_leaf": "0.42.0"
} }
# 定义 action_params 的最低兼容版本和正则表达式验证 # 定义 action_params 的最低兼容版本和正则表达式验证
@@ -79,38 +85,93 @@ jobs:
"params": {"version": "0.42.0", "regex": r"^\d+$"} "params": {"version": "0.42.0", "regex": r"^\d+$"}
}, },
"combat_script": { "combat_script": {
"params": {"version": "0.36.4", "regex": r"^.+$"} # 任意非空字符串 "params": {"version": "0.42.0", "regex": r"^.+$"} # 任意非空字符串
}, },
"log_output": { "log_output": {
"params": {"version": "0.40.0", "regex": r"^.+$"} # 任意非空字符串 "params": {"version": "0.42.0", "regex": r"^.+$"} # 任意非空字符串
} }
# 其他 action 类型没有明确的 action_params 格式要求 # 其他 action 类型没有明确的 action_params 格式要求
} }
# 默认版本号
DEFAULT_BGI_VERSION = "0.42.0"
DEFAULT_VERSION = "1.0"
# ==================== 文件操作 ====================
def get_original_file(file_path): def get_original_file(file_path):
"""从 git 仓库获取原始文件内容,如果失败则尝试从本地备份获取"""
# 返回值增加一个来源标识: "git", "backup", "current", None
# 首先尝试从 git 仓库获取
try: try:
# 添加编码参数解决中文路径问题
result = subprocess.run(['git', 'show', f'origin/main:{file_path}'], result = subprocess.run(['git', 'show', f'origin/main:{file_path}'],
capture_output=True, text=True, encoding='utf-8') capture_output=True, text=True, encoding='utf-8')
return json.loads(result.stdout) if result.returncode == 0 else None if result.returncode == 0:
print("从 git 仓库成功获取原始文件")
return json.loads(result.stdout), "git"
except Exception as e: except Exception as e:
print(f"获取原始文件失败: {str(e)}") print(f"从 git 仓库获取原始文件失败: {str(e)}")
return None
# 如果 git 获取失败,尝试从本地备份目录获取
try:
# 假设有一个备份目录,存放原始文件
backup_dir = os.path.join(os.path.dirname(os.path.dirname(file_path)), "backups")
backup_file = os.path.join(backup_dir, os.path.basename(file_path))
if os.path.exists(backup_file):
print(f"从本地备份获取原始文件: {backup_file}")
with open(backup_file, 'r', encoding='utf-8') as f:
return json.load(f), "backup"
except Exception as e:
print(f"从本地备份获取原始文件失败: {str(e)}")
# 如果都失败了,尝试使用当前文件的副本作为原始文件
try:
print("尝试使用当前文件作为原始文件")
with open(file_path, 'r', encoding='utf-8') as f:
current_data = json.load(f)
# 创建一个副本,避免引用相同的对象
return json.loads(json.dumps(current_data)), "current"
except Exception as e:
print(f"使用当前文件作为原始文件失败: {str(e)}")
print("无法获取任何形式的原始文件")
return None, None
def load_json_file(file_path):
"""加载 JSON 文件"""
try:
with open(file_path, encoding='utf-8') as f:
return json.load(f), None
except Exception as e:
return None, f"❌ JSON 格式错误: {str(e)}"
def save_json_file(file_path, data):
"""保存 JSON 文件"""
try:
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
return True
except Exception as e:
print(f"保存文件失败: {str(e)}")
return False
# ==================== 版本处理 ====================
def process_version(current, original, is_new): def process_version(current, original, is_new):
"""处理版本号更新逻辑"""
if is_new: if is_new:
return "1.0" return DEFAULT_VERSION
try:
# 修改版本号处理逻辑,确保总是增加版本号
if not original: if not original:
return "1.0" return DEFAULT_VERSION
try: try:
cv = parse(current) cv = parse(current)
ov = parse(original) ov = parse(original)
# 强制更新版本号,无论当前版本是否大于原始版本 # 强制更新版本号,无论当前版本是否大于原始版本
return f"{ov.major}.{ov.minor + 1}" return f"{ov.major}.{ov.minor + 1}"
except: except Exception:
# 如果解析失败,尝试简单的数字处理 # 如果解析失败,尝试简单的数字处理
parts = original.split('.') parts = original.split('.')
if len(parts) >= 2: if len(parts) >= 2:
@@ -118,16 +179,41 @@ jobs:
major = int(parts[0]) major = int(parts[0])
minor = int(parts[1]) minor = int(parts[1])
return f"{major}.{minor + 1}" return f"{major}.{minor + 1}"
except: except ValueError:
pass pass
return f"{original}.1" return f"{original}.1"
except Exception as e:
print(f"处理版本号失败: {str(e)}") def extract_required_version(compatibility_issues):
return "1.0" if not original else f"{original}.1" """从兼容性问题中提取所需的最高版本号"""
required_versions = []
for issue in compatibility_issues:
parts = issue.split(">=")
if len(parts) > 1:
version_part = parts[1].split(",")[0].strip()
version_match = re.search(r'(\d+\.\d+\.\d+)', version_part)
if version_match:
required_versions.append(version_match.group(1))
if not required_versions:
return None
try:
return max(required_versions, key=lambda v: VersionInfo.parse(v))
except ValueError:
return None
def parse_bgi_version(version_str):
"""解析 BGI 版本号"""
try:
# 确保删除 v 前缀
return VersionInfo.parse(version_str.lstrip('v'))
except ValueError:
return None
# ==================== 字段验证 ====================
def check_action_compatibility(action_type, action_params, bgi_version): def check_action_compatibility(action_type, action_params, bgi_version):
"""检查 action 和 action_params 与 BGI 版本的兼容性""" """检查 action 和 action_params 与 BGI 版本的兼容性"""
import re
issues = [] issues = []
validation_issues = [] validation_issues = []
@@ -135,13 +221,21 @@ jobs:
if not action_type: if not action_type:
return issues, validation_issues return issues, validation_issues
# 确保 bgi_version 是有效的格式
bgi_ver = parse_bgi_version(bgi_version)
if not bgi_ver:
validation_issues.append(f"无效的 bgiVersion 格式: {bgi_version}")
return issues, validation_issues
# 检查 action 兼容性 # 检查 action 兼容性
if action_type in ACTION_VERSION_MAP: if action_type in ACTION_VERSION_MAP:
min_version = ACTION_VERSION_MAP[action_type] min_version = ACTION_VERSION_MAP[action_type]
if VersionInfo.parse(bgi_version.lstrip('v')) < VersionInfo.parse(min_version): try:
if bgi_ver < VersionInfo.parse(min_version):
issues.append(f"action '{action_type}' 需要 BGI 版本 >= {min_version},当前为 {bgi_version}") issues.append(f"action '{action_type}' 需要 BGI 版本 >= {min_version},当前为 {bgi_version}")
except ValueError:
validation_issues.append(f"无法比较版本: {min_version} 与 {bgi_version}")
else: else:
# 未知的 action 类型
validation_issues.append(f"未知的 action 类型: '{action_type}',已知类型: {', '.join(sorted(ACTION_VERSION_MAP.keys()))}") validation_issues.append(f"未知的 action 类型: '{action_type}',已知类型: {', '.join(sorted(ACTION_VERSION_MAP.keys()))}")
# 检查 action_params 兼容性和格式 # 检查 action_params 兼容性和格式
@@ -151,8 +245,11 @@ jobs:
regex_pattern = param_info["regex"] regex_pattern = param_info["regex"]
# 版本兼容性检查 # 版本兼容性检查
if VersionInfo.parse(bgi_version.lstrip('v')) < VersionInfo.parse(min_version): try:
if bgi_ver < VersionInfo.parse(min_version):
issues.append(f"action '{action_type}' 的参数需要 BGI 版本 >= {min_version},当前为 {bgi_version}") issues.append(f"action '{action_type}' 的参数需要 BGI 版本 >= {min_version},当前为 {bgi_version}")
except ValueError:
validation_issues.append(f"无法比较版本: {min_version} 与 {bgi_version}")
# 参数格式验证 # 参数格式验证
if not re.match(regex_pattern, str(action_params)): if not re.match(regex_pattern, str(action_params)):
@@ -160,21 +257,22 @@ jobs:
return issues, validation_issues return issues, validation_issues
def validate_file(file_path): def process_coordinates(positions):
try: """统一处理坐标保留两位小数逻辑"""
with open(file_path, encoding='utf-8') as f: # 明确指定 UTF-8 编码 coord_changed = False
data = json.load(f) for pos in positions:
except Exception as e: for axis in ['x', 'y']:
print(f"❌ JSON 格式错误: {str(e)}") if axis in pos and isinstance(pos[axis], (int, float)):
sys.exit(1) original = pos[axis]
pos[axis] = round(float(pos[axis]), 2)
if original != pos[axis]:
coord_changed = True
return coord_changed
original_data = get_original_file(file_path) def ensure_required_fields(info, filename):
is_new = not original_data """统一处理必要字段检查逻辑"""
info = data["info"]
filename = os.path.splitext(os.path.basename(file_path))[0]
# 自动修正逻辑
corrections = [] corrections = []
if info["name"] != filename: if info["name"] != filename:
info["name"] = filename info["name"] = filename
corrections.append(f"name 自动修正为 {filename}") corrections.append(f"name 自动修正为 {filename}")
@@ -184,47 +282,144 @@ jobs:
corrections.append("type 自动修正为 collect") corrections.append("type 自动修正为 collect")
if not info["author"]: if not info["author"]:
info["author"] = os.getenv("GITHUB_ACTOR") info["author"] = os.getenv("GITHUB_ACTOR", "未知作者")
corrections.append(f"author 自动设置为 {info['author']}") corrections.append(f"author 自动设置为 {info['author']}")
# 处理坐标保留两位小数 return corrections
coord_changed = False
for pos in data["positions"]:
if "x" in pos and isinstance(pos["x"], (int, float)):
original_x = pos["x"]
pos["x"] = round(float(pos["x"]), 2)
if original_x != pos["x"]:
coord_changed = True
if "y" in pos and isinstance(pos["y"], (int, float)): def check_position_fields(positions):
original_y = pos["y"] """检查位置字段的有效性
pos["y"] = round(float(pos["y"]), 2)
if original_y != pos["y"]:
coord_changed = True
if coord_changed: 自动修复功能:
corrections.append("坐标值自动保留两位小数") 1. 缺少 type 字段时,自动设置为 'path'
2. type 字段无效时,自动修正为 'path'
# 检查 action 和 action_params 兼容性 3. 当 type 为 'path' 或 'target' 且缺少 move_mode 时,自动设置为 'walk'
bgi_version = info["bgiVersion"] 4. move_mode 字段无效时,自动修正为 'walk'
compatibility_issues = [] """
validation_issues = [] validation_issues = []
notices = []
corrections = [] # 添加修正列表
for idx, pos in enumerate(positions):
# 检查必需字段
required_fields = ["x", "y", "type"]
missing_fields = [field for field in required_fields if field not in pos]
if missing_fields:
validation_issues.append(f"位置 {idx+1} 缺少必需字段: {', '.join(missing_fields)}")
# 自动添加缺失的 type 字段
if "type" in missing_fields:
pos["type"] = "path" # 自动修复:缺少 type 字段时设置为 path
corrections.append(f"位置 {idx+1} 缺少 type 字段,已设置为默认值 'path'")
# 如果添加了 path 类型,也需要添加 move_mode
if "move_mode" not in pos:
pos["move_mode"] = "walk" # 自动修复:为 path 类型添加默认 move_mode
corrections.append(f"位置 {idx+1} 缺少 move_mode 字段,已设置为默认值 'walk'")
# 移除 continue确保后续检查能够执行
# continue
for idx, pos in enumerate(data["positions"]):
# 验证 type 字段 # 验证 type 字段
if "type" in pos: if "type" in pos:
pos_type = pos["type"] pos_type = pos["type"]
if pos_type not in VALID_TYPES: if pos_type not in VALID_TYPES:
validation_issues.append(f"位置 {idx+1}: type '{pos_type}' 无效,有效值为: {', '.join(VALID_TYPES)}") validation_issues.append(f"位置 {idx+1}: type '{pos_type}' 无效,有效值为: {', '.join(VALID_TYPES)}")
# 自动修正无效的 type 字段
pos["type"] = "path" # 自动修复:无效 type 修正为 path
corrections.append(f"位置 {idx+1} 的 type '{pos_type}' 无效,已修正为 'path'")
pos_type = "path" # 更新 pos_type 以便后续检查
# 当 type 为 path 或 target 时,验证 move_mode # 当 type 为 path 或 target 时,验证 move_mode
if pos_type in ["path", "target"]: if pos_type in ["path", "target"]:
if "move_mode" not in pos: if "move_mode" not in pos:
validation_issues.append(f"位置 {idx+1}: type 为 '{pos_type}' 时必须指定 move_mode") validation_issues.append(f"位置 {idx+1}: type 为 '{pos_type}' 时必须指定 move_mode")
# 自动添加缺失的 move_mode
pos["move_mode"] = "walk" # 自动修复:缺少 move_mode 时设置为 walk
corrections.append(f"位置 {idx+1} 缺少 move_mode 字段,已设置为默认值 'walk'")
elif pos["move_mode"] not in VALID_MOVE_MODES: elif pos["move_mode"] not in VALID_MOVE_MODES:
validation_issues.append(f"位置 {idx+1}: move_mode '{pos['move_mode']}' 无效,有效值为: {', '.join(VALID_MOVE_MODES)}") validation_issues.append(f"位置 {idx+1}: move_mode '{pos['move_mode']}' 无效,有效值为: {', '.join(VALID_MOVE_MODES)}")
# 自动修正无效的 move_mode
pos["move_mode"] = "walk" # 自动修复:无效 move_mode 修正为 walk
corrections.append(f"位置 {idx+1} 的 move_mode '{pos['move_mode']}' 无效,已修正为 'walk'")
# 验证 action 兼容性 # 检查第一个位置是否为 teleport
if idx == 0 and pos.get("type") != "teleport":
notices.append("⚠️ 第一个 position 的 type 不是 teleport")
return validation_issues, notices, corrections
def check_bgi_version_compatibility(bgi_version, auto_fix=False):
"""检查 BGI 版本兼容性"""
corrections = []
# 删除可能存在的 v 前缀
if bgi_version.startswith('v'):
bgi_version = bgi_version.lstrip('v')
corrections.append(f"bgiVersion 前缀 'v' 已删除")
bgi_ver = parse_bgi_version(bgi_version)
if not bgi_ver:
if auto_fix:
corrections.append(f"bgiVersion {bgi_version} 格式无效,自动更新为 {DEFAULT_BGI_VERSION}")
return DEFAULT_BGI_VERSION, corrections
return bgi_version, []
if bgi_ver < VersionInfo.parse(DEFAULT_BGI_VERSION):
if auto_fix:
corrections.append(f"bgiVersion {bgi_version} 自动更新为 {DEFAULT_BGI_VERSION} (原版本低于要求)")
return DEFAULT_BGI_VERSION, corrections
return bgi_version, corrections
# ==================== 主验证逻辑 ====================
def initialize_data(data, file_path):
"""初始化数据结构,确保必要字段存在"""
messages = []
if "info" not in data:
data["info"] = {}
messages.append(f"⚠️ 文件缺少 info 字段,已添加默认值")
info = data["info"]
filename = os.path.splitext(os.path.basename(file_path))[0]
# 检查并添加必要的字段
if "name" not in info:
info["name"] = filename
messages.append(f"⚠️ 文件缺少 name 字段,已设置为文件名: {info['name']}")
if "type" not in info:
info["type"] = "collect"
messages.append(f"⚠️ 文件缺少 type 字段,已设置为默认值: collect")
if "author" not in info:
info["author"] = os.getenv("GITHUB_ACTOR", "未知作者")
messages.append(f"⚠️ 文件缺少 author 字段,已设置为: {info['author']}")
if "version" not in info:
info["version"] = DEFAULT_VERSION
messages.append(f"⚠️ 文件缺少 version 字段,已设置为默认值: {DEFAULT_VERSION}")
if "bgiVersion" not in info:
info["bgiVersion"] = DEFAULT_BGI_VERSION
messages.append(f"⚠️ 文件缺少 bgiVersion 字段,已设置为默认值: {DEFAULT_BGI_VERSION}")
if "positions" not in data:
data["positions"] = []
messages.append(f"⚠️ 文件缺少 positions 字段,已添加空数组")
for message in messages:
print(message)
return data
def check_actions_compatibility(positions, bgi_version):
"""检查所有位置的 action 兼容性"""
compatibility_issues = []
validation_issues = []
for idx, pos in enumerate(positions):
action_type = pos.get("action", "") action_type = pos.get("action", "")
action_params = pos.get("params", "") action_params = pos.get("params", "")
@@ -237,128 +432,185 @@ jobs:
for issue in valid_issues: for issue in valid_issues:
validation_issues.append(f"位置 {idx+1}: {issue}") validation_issues.append(f"位置 {idx+1}: {issue}")
# 根据兼容性问题更新 bgiVersion return compatibility_issues, validation_issues
if compatibility_issues:
required_versions = []
for issue in compatibility_issues:
# 从错误信息中提取版本号
parts = issue.split(">=")
if len(parts) > 1:
version_part = parts[1].split(",")[0].strip()
required_versions.append(version_part)
if required_versions: def update_bgi_version_for_compatibility(info, compatibility_issues, auto_fix):
max_required = max(required_versions, key=lambda v: VersionInfo.parse(v)) """根据兼容性问题更新 BGI 版本"""
current_bgi = VersionInfo.parse(bgi_version.lstrip('v')) corrections = []
if current_bgi < VersionInfo.parse(max_required):
info["bgiVersion"] = f"v{max_required}"
corrections.append(f"bgiVersion 自动更新为 v{max_required} 以兼容所有功能")
compatibility_issues = [] # 清空兼容性问题,因为已经更新了版本
original_version = original_data["info"]["version"] if original_data and "info" in original_data else None # 首先检查并删除 v 前缀
print(f"原始版本号: {original_version}, 当前版本号: {info['version']}, 是否新文件: {is_new}") if info["bgiVersion"].startswith('v'):
info["bgiVersion"] = info["bgiVersion"].lstrip('v')
corrections.append(f"bgiVersion 前缀 'v' 已删除")
if auto_fix and compatibility_issues:
max_required = extract_required_version(compatibility_issues)
if max_required:
# 确保 max_required 没有 v 前缀
max_required = max_required.lstrip('v')
try:
current_bgi = parse_bgi_version(info["bgiVersion"])
if current_bgi and current_bgi < VersionInfo.parse(max_required):
info["bgiVersion"] = max_required
corrections.append(f"bgiVersion {info['bgiVersion']} 自动更新为 {max_required} 以兼容所有功能")
return [], corrections
except ValueError as e:
print(f"警告: 版本号解析失败 - {e}")
info["bgiVersion"] = DEFAULT_BGI_VERSION
corrections.append(f"bgiVersion 自动更新为 {DEFAULT_BGI_VERSION} (版本解析失败)")
return [], corrections
return compatibility_issues, corrections
def validate_file(file_path, auto_fix=False):
"""验证并修复 JSON 文件"""
# 加载文件
data, error = load_json_file(file_path)
if error:
print(error)
return []
# 获取原始文件
original_data, source = get_original_file(file_path) if auto_fix else (None, None)
is_new = not original_data if auto_fix else True
# 初始化数据结构
data = initialize_data(data, file_path)
info = data["info"]
filename = os.path.splitext(os.path.basename(file_path))[0]
# 收集所有修正 - 修复:添加了这一行来定义 all_corrections 变量
all_corrections = []
# 检查必要字段
corrections = ensure_required_fields(info, filename)
all_corrections.extend(corrections)
# 检查并删除 bgiVersion 的 v 前缀
if "bgiVersion" in info and info["bgiVersion"].startswith('v'):
info["bgiVersion"] = info["bgiVersion"].lstrip('v')
all_corrections.append("bgiVersion 前缀 'v' 已删除")
# 处理坐标
coord_changed = process_coordinates(data["positions"])
if coord_changed:
all_corrections.append("坐标值自动保留两位小数")
# 检查 BGI 版本兼容性
bgi_version, corrections = check_bgi_version_compatibility(info["bgiVersion"], auto_fix)
if corrections:
info["bgiVersion"] = bgi_version
all_corrections.extend(corrections)
# 检查位置字段 - 修改为接收三个返回值
position_issues, notices, pos_corrections = check_position_fields(data["positions"])
if auto_fix and pos_corrections:
all_corrections.extend(pos_corrections)
# 检查 action 兼容性
compatibility_issues, action_validation_issues = check_actions_compatibility(data["positions"], info["bgiVersion"])
position_issues.extend(action_validation_issues)
# 根据兼容性问题更新 BGI 版本
compatibility_issues, corrections = update_bgi_version_for_compatibility(info, compatibility_issues, auto_fix)
all_corrections.extend(corrections)
# 更新版本号 - 只有从 git 获取的文件才更新版本号
if auto_fix:
has_original_version = False
original_version = None
if original_data and "info" in original_data and "version" in original_data["info"]:
original_version = original_data["info"]["version"]
has_original_version = True
print(f"成功获取原始版本号: {original_version}")
else:
print("未找到原始版本号,将视为新文件处理")
# 只有在没有原始版本号时才视为新文件
is_new = not has_original_version
print(f"原始版本号: {original_version}, 当前版本号: {info['version']}, 是否新文件: {is_new}, 来源: {source}")
# 只有当文件来源是 git 时才更新版本号
if source == "git":
new_version = process_version(info["version"], original_version, is_new) new_version = process_version(info["version"], original_version, is_new)
if new_version != info["version"]: if new_version != info["version"]:
info["version"] = new_version info["version"] = new_version
corrections.append(f"version 自动更新为 {new_version}") all_corrections.append(f"version 自动更新为 {new_version}")
print(f"版本号已更新: {info['version']}") print(f"版本号已更新: {info['version']}")
else: else:
print(f"版本号未变化: {info['version']}") print(f"版本号未变化: {info['version']}")
try:
bgi_ver = VersionInfo.parse(info["bgiVersion"].lstrip('v'))
if not (bgi_ver > VersionInfo.parse("0.42.0")):
print(f"❌ bgiVersion {info['bgiVersion']} 必须大于 0.42")
sys.exit(1)
except ValueError:
print(f"❌ 无效的 bgiVersion 格式: {info['bgiVersion']}")
sys.exit(1)
# 检查 bgiVersion 并自动修正
bgi_version = info["bgiVersion"]
try:
bgi_ver = VersionInfo.parse(bgi_version.lstrip('v'))
if bgi_ver < VersionInfo.parse("0.42.0"):
# 自动修正为 v0.42.0
info["bgiVersion"] = "v0.42.0"
corrections.append(f"bgiVersion {bgi_version} 自动更新为 v0.42.0 (原版本低于要求)")
bgi_version = "v0.42.0"
except ValueError:
# 格式无效时自动修正
info["bgiVersion"] = "v0.42.0"
corrections.append(f"bgiVersion {bgi_version} 格式无效,自动更新为 v0.42.0")
bgi_version = "v0.42.0"
# 校验 positions
notices = []
for idx, pos in enumerate(data["positions"]):
if not all(key in pos for key in ["x", "y", "type"]):
# 改为自动添加缺失字段而不是退出
missing = [k for k in ["x", "y", "type"] if k not in pos]
for m in missing:
if m == "type":
pos[m] = "teleport"
else: else:
pos[m] = 0.0 print(f"本地文件,保持版本号不变: {info['version']}")
corrections.append(f"position {idx+1} 自动补全缺失字段: {', '.join(missing)}")
if idx == 0 and pos["type"] != "teleport":
notices.append("⚠️ 第一个 position 的 type 不是 teleport")
# 添加兼容性问题和验证问题到通知 # 合并所有通知
for issue in compatibility_issues: for issue in compatibility_issues:
notices.append(issue) notices.append(issue)
# 添加验证问题到通知中 for issue in position_issues:
for issue in validation_issues:
notices.append(issue) notices.append(issue)
# 保存修正 # 保存修正
if corrections: if auto_fix:
with open(file_path, 'w', encoding='utf-8') as f: # 保存时也使用 UTF-8 编码 # 无论是否有问题,都打印所有自动修正项
json.dump(data, f, indent=2, ensure_ascii=False) if all_corrections:
subprocess.run(['git', 'add', file_path]) print("🔧 自动修正:")
print("🔧 自动修正:", ", ".join(corrections)) for correction in all_corrections:
print(f" - {correction}")
else:
print("✅ 没有需要自动修正的项目")
# 只有在有修正或问题时才保存文件
if all_corrections or position_issues:
if save_json_file(file_path, data):
print("✅ 文件已保存")
else:
notices.append("❌ 保存文件失败")
return notices return notices
# 主流程 def main():
changed_files = subprocess.run(['git', 'diff', '--name-only', 'origin/main...HEAD'], import argparse
capture_output=True, text=True).stdout.splitlines()
print(f"检测到的变更文件: {changed_files}") parser = argparse.ArgumentParser(description='校验 BetterGI 脚本文件')
parser.add_argument('path', help='要校验的文件或目录路径')
parser.add_argument('--fix', action='store_true', help='自动修复问题')
args = parser.parse_args()
path = args.path
auto_fix = args.fix
if os.path.isfile(path) and path.endswith('.json'):
print(f"\n🔍 校验文件: {path}")
notices = validate_file(path, auto_fix)
if notices:
print("\n校验注意事项:")
for notice in notices:
print(f"- {notice}")
else:
print("✅ 校验完成,没有发现问题")
elif os.path.isdir(path):
all_notices = [] all_notices = []
for f in [f for f in changed_files if f.endswith('.json')]: for root, _, files in os.walk(path):
print(f"\n🔍 校验文件: {f}") for file in files:
notices = validate_file(f) if file.endswith('.json'):
all_notices.extend([f"{f}: {n}" for n in notices]) file_path = os.path.join(root, file)
print(f"\n🔍 校验文件: {file_path}")
notices = validate_file(file_path, auto_fix)
if notices:
all_notices.extend([f"{file_path}: {n}" for n in notices])
# 提交自动修正 if all_notices:
if subprocess.run(['git', 'diff', '--staged', '--quiet']).returncode: print("\n所有校验注意事项:")
print("检测到需要提交的修改") for notice in all_notices:
subprocess.run(['git', 'config', 'user.name', 'GitHub Actions']) print(f"- {notice}")
subprocess.run(['git', 'config', 'user.email', 'actions@github.com'])
commit_result = subprocess.run(['git', 'commit', '-m', '🛠 自动校验修正'], capture_output=True, text=True)
print(f"提交结果: {commit_result.stdout} {commit_result.stderr}")
# 修改 push 命令,指定远程分支名称
head_ref = os.getenv('HEAD_REF')
if not head_ref:
print("⚠️ 无法获取 HEAD_REF 环境变量")
head_ref = "HEAD:refs/heads/" + subprocess.run(['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
capture_output=True, text=True).stdout.strip()
print(f"使用当前分支: {head_ref}")
push_result = subprocess.run(['git', 'push', 'origin', f'HEAD:{head_ref}'], capture_output=True, text=True)
print(f"推送结果: {push_result.stdout} {push_result.stderr}")
if push_result.returncode == 0:
print("✅ 自动修正已提交")
else: else:
print(f"❌ 推送失败: {push_result.stderr}") print("\n✅ 所有文件校验完成,没有发现问题")
else: else:
print("没有需要提交的修改") print(f"❌ 无效的路径: {path}")
# 生成提醒信息 # 生成提醒信息
if all_notices: if all_notices: