Files
bettergi-scripts-list/.github/workflows/jsonDataValidation.yml
2025-05-17 05:53:39 +08:00

429 lines
18 KiB
YAML
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.

name: JSON Data Validation
on:
pull_request_target:
types: [opened, synchronize, reopened, edited]
branches:
- main
paths:
- 'repo/pathing/**/*.json'
workflow_dispatch:
inputs:
path:
description: '要验证的路径'
required: true
default: 'repo/pathing'
type: string
auto_fix:
description: '是否自动修复问题'
required: false
default: true
type: boolean
pr_number:
description: '关联的 PR 号'
required: false
type: string
jobs:
validate-json:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Set environment variables based on trigger type
id: set_env
run: |
# 根据触发类型设置环境变量
if [ "${{ github.event_name }}" = "pull_request_target" ]; then
echo "触发类型: PR触发"
echo "trigger_type=pr" >> $GITHUB_OUTPUT
echo "validate_path=pr_files" >> $GITHUB_OUTPUT
echo "auto_fix=true" >> $GITHUB_OUTPUT
echo "pr_number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
echo "head_ref=${{ github.event.pull_request.head.ref }}" >> $GITHUB_OUTPUT
echo "head_repo=${{ github.event.pull_request.head.repo.full_name }}" >> $GITHUB_OUTPUT
echo "base_ref=${{ github.event.pull_request.base.ref }}" >> $GITHUB_OUTPUT
echo "base_sha=${{ github.event.pull_request.base.sha }}" >> $GITHUB_OUTPUT
else
echo "触发类型: 手动触发"
echo "trigger_type=manual" >> $GITHUB_OUTPUT
echo "validate_path=${{ github.event.inputs.path }}" >> $GITHUB_OUTPUT
echo "auto_fix=${{ github.event.inputs.auto_fix }}" >> $GITHUB_OUTPUT
echo "pr_number=${{ github.event.inputs.pr_number }}" >> $GITHUB_OUTPUT
echo "head_ref=" >> $GITHUB_OUTPUT
echo "head_repo=" >> $GITHUB_OUTPUT
echo "base_ref=main" >> $GITHUB_OUTPUT
echo "base_sha=" >> $GITHUB_OUTPUT
fi
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha || github.sha }}
token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
pip install packaging semver
- name: Debug file structure
run: |
echo "触发类型: ${{ steps.set_env.outputs.trigger_type }}"
echo "验证路径: ${{ steps.set_env.outputs.validate_path }}"
echo "自动修复: ${{ steps.set_env.outputs.auto_fix }}"
echo "PR号: ${{ steps.set_env.outputs.pr_number }}"
echo "Current directory: $(pwd)"
echo "List files in root:"
ls -la
echo "List files in build directory (if exists):"
if [ -d "build" ]; then
ls -la build/
else
echo "build directory does not exist"
mkdir -p build
fi
- name: Setup Git and repositories
run: |
git config --global user.name "GitHub Actions Bot"
git config --global user.email "actions@github.com"
# 设置Git处理中文等特殊字符
git config --global core.quotepath false
# 确保远程仓库设置正确
echo "当前远程仓库配置:"
git remote -v
# 设置变量
MAIN_REPO="${{ github.repository }}"
PR_REPO="${{ github.event.pull_request.head.repo.full_name || github.repository }}"
# 设置上游仓库(upstream)指向主仓库
UPSTREAM_REPO="https://github.com/${MAIN_REPO}.git"
echo "设置upstream指向主仓库: $UPSTREAM_REPO"
git remote remove upstream 2>/dev/null || true
git remote add upstream $UPSTREAM_REPO
# 确保origin指向PR的fork仓库
if [ "$PR_REPO" != "$MAIN_REPO" ] && [ "${{ steps.set_env.outputs.trigger_type }}" = "pr" ]; then
ORIGIN_REPO="https://github.com/${PR_REPO}.git"
echo "PR来自fork仓库设置origin指向: $ORIGIN_REPO"
else
ORIGIN_REPO=$UPSTREAM_REPO
echo "PR来自同一仓库或非PR触发origin与upstream相同: $ORIGIN_REPO"
fi
git remote set-url origin $ORIGIN_REPO 2>/dev/null || git remote add origin $ORIGIN_REPO
# 获取最新的主仓库和分支
echo "获取远程分支信息"
git fetch upstream
git fetch origin
# 显示远程仓库配置
echo "更新后的远程仓库配置:"
git remote -v
# 检查是否处于PR环境并切换到正确的分支
if [ "${{ steps.set_env.outputs.trigger_type }}" = "pr" ] && [ -n "${{ steps.set_env.outputs.head_ref }}" ]; then
echo "检测到PR切换到PR分支: ${{ steps.set_env.outputs.head_ref }}"
if [ "$PR_REPO" != "$MAIN_REPO" ]; then
# fork仓库的PR需要先创建本地分支追踪fork的远程分支
git checkout -b "${{ steps.set_env.outputs.head_ref }}" --track "origin/${{ steps.set_env.outputs.head_ref }}" || \
git checkout -b "${{ steps.set_env.outputs.head_ref }}" --no-track && \
git push --set-upstream origin "${{ steps.set_env.outputs.head_ref }}"
else
# 同一仓库的PR
git checkout "${{ steps.set_env.outputs.head_ref }}" || git checkout -b "${{ steps.set_env.outputs.head_ref }}"
fi
elif [ -n "${{ github.ref_name }}" ]; then
echo "切换到分支: ${{ github.ref_name }}"
if [[ "${{ github.ref_name }}" == "main" ]]; then
# main分支需要明确指定
git checkout upstream/main -b main
else
git checkout "${{ github.ref_name }}" || git checkout -b "${{ github.ref_name }}"
fi
else
echo "创建临时分支"
git checkout -b temp-validation-branch
fi
- name: Prepare validation script
run: |
# 检查build目录和validate.py文件是否存在
mkdir -p build
if [ ! -f "build/validate.py" ]; then
echo "build/validate.py不存在跳过获取步骤"
else
echo "build/validate.py已存在检查文件头部"
head -n 5 build/validate.py
fi
- name: Get PR information for workflow_dispatch
if: ${{ steps.set_env.outputs.trigger_type == 'manual' && steps.set_env.outputs.pr_number != '' }}
id: pr_info
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
try {
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: parseInt(${{ steps.set_env.outputs.pr_number }})
});
core.setOutput('head_sha', pr.data.head.sha);
core.setOutput('head_ref', pr.data.head.ref);
core.setOutput('head_repo', pr.data.head.repo.full_name);
core.setOutput('found', 'true');
console.log(`找到 PR #${{ steps.set_env.outputs.pr_number }}`);
console.log(`Head SHA: ${pr.data.head.sha}`);
console.log(`Head Ref: ${pr.data.head.ref}`);
console.log(`Head Repo: ${pr.data.head.repo.full_name}`);
// 如果找到PR切换到PR分支
const exec = require('child_process').execSync;
if (pr.data.head.ref) {
console.log(`切换到PR分支: ${pr.data.head.ref}`);
exec(`git checkout ${pr.data.head.ref} || git checkout -b ${pr.data.head.ref}`);
}
} catch (error) {
console.log(`获取 PR #${{ steps.set_env.outputs.pr_number }} 信息失败: ${error.message}`);
core.setOutput('found', 'false');
}
- name: Get changed files for PR trigger
id: changed_files
if: ${{ steps.set_env.outputs.trigger_type == 'pr' }}
run: |
# 输出分支信息便于调试
echo "当前分支: $(git branch --show-current)"
echo "HEAD指向: $(git rev-parse HEAD)"
echo "PR基础分支: ${{ steps.set_env.outputs.base_ref }}"
# 确保有upstream/main分支
git fetch upstream main
echo "Upstream/main SHA: $(git rev-parse upstream/main)"
# 创建临时变量来存储修改的文件列表
CHANGED_FILES=""
# 方法1尝试使用git diff检测变化
echo "检测方法1: 使用git diff检测"
FILES_METHOD_1=$(git diff --name-only upstream/main HEAD | grep -E '^repo/pathing/.*\.json$' || true)
if [ -n "$FILES_METHOD_1" ]; then
echo "方法1找到的JSON文件:"
echo "$FILES_METHOD_1"
CHANGED_FILES="$FILES_METHOD_1"
else
echo "方法1未找到修改的JSON文件"
fi
# 方法2如果方法1失败尝试直接查询PR API获取修改的文件
if [ -z "$CHANGED_FILES" ] && [ -n "${{ steps.set_env.outputs.pr_number }}" ]; then
echo "检测方法2: 使用GitHub API检测"
PR_FILES=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/pulls/${{ steps.set_env.outputs.pr_number }}/files" | \
jq -r '.[] | select(.filename | test("^repo/pathing/.*\\.json$")) | .filename')
if [ -n "$PR_FILES" ]; then
echo "方法2找到的JSON文件:"
echo "$PR_FILES"
CHANGED_FILES="$PR_FILES"
else
echo "方法2未找到修改的JSON文件"
fi
fi
# 方法3如果前两种方法都失败列出所有repo/pathing中的JSON文件但限制在最近修改的
if [ -z "$CHANGED_FILES" ]; then
echo "检测方法3: 列出最近修改的JSON文件"
# 列出过去5次提交中修改的JSON文件
RECENT_FILES=$(git log -n 5 --name-only --pretty=format: | grep -E '^repo/pathing/.*\.json$' | sort -u || true)
if [ -n "$RECENT_FILES" ]; then
echo "最近修改的JSON文件:"
echo "$RECENT_FILES"
CHANGED_FILES="$RECENT_FILES"
echo "⚠️ 警告: 使用最近修改的文件作为回退方案"
else
echo "方法3未找到最近修改的JSON文件"
fi
fi
# 最后回退方案:直接指定验证目录
if [ -z "$CHANGED_FILES" ]; then
echo "⚠️ 警告: 所有方法均未检测到修改的JSON文件将验证整个repo/pathing目录"
CHANGED_FILES="repo/pathing"
fi
# 输出结果
echo "最终找到的修改文件:"
echo "$CHANGED_FILES"
# 使用base64编码保存文件列表避免特殊字符问题
echo "changed_files=$(echo "$CHANGED_FILES" | base64 -w 0)" >> $GITHUB_OUTPUT
- name: Run validation for PR trigger
if: ${{ steps.set_env.outputs.trigger_type == 'pr' }}
env:
GITHUB_ACTOR: ${{ github.actor }}
PR_NUMBER: ${{ steps.set_env.outputs.pr_number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_REF: ${{ steps.set_env.outputs.head_ref }}
PR_REPO: ${{ steps.set_env.outputs.head_repo || github.repository }}
CHANGED_FILES_B64: ${{ steps.changed_files.outputs.changed_files }}
run: |
# 使用base64解码文件列表
CHANGED_FILES=$(echo "$CHANGED_FILES_B64" | base64 --decode)
echo "PR 触发模式,验证修改的 JSON 文件"
if [ -z "$CHANGED_FILES" ]; then
echo "没有找到修改的 JSON 文件,跳过验证"
exit 0
fi
# 检查Python解释器编码设置
echo "Python编码设置:"
python -c "import sys; print(sys.getdefaultencoding())"
# 检查CHANGED_FILES是否包含整个目录
if [ "$CHANGED_FILES" = "repo/pathing" ]; then
echo "验证整个目录: repo/pathing"
python build/validate.py "repo/pathing" --fix
else
# 创建一个临时文件来存储文件列表
echo "$CHANGED_FILES" > temp_file_list.txt
# 单独验证每个修改的文件使用while读取避免文件名中的空格和特殊字符问题
while IFS= read -r file; do
echo "验证文件: $file"
if [ -f "$file" ]; then
python build/validate.py "$file" --fix
else
echo "警告: 文件不存在 - $file"
fi
done < temp_file_list.txt
rm temp_file_list.txt
fi
# 检查是否有文件被修改
if [ -n "$(git status --porcelain)" ]; then
echo "发现修改,提交更改"
git add .
git commit -m "自动修复 JSON 格式和版本号 [ci skip]"
# 确定当前分支
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
echo "当前分支: ${CURRENT_BRANCH}"
if [ "$CURRENT_BRANCH" = "HEAD" ]; then
# 如果在detached HEAD状态使用正确的方式推送
if [ -n "${HEAD_REF}" ]; then
echo "在detached HEAD状态使用HEAD_REF推送: ${HEAD_REF}"
git push origin HEAD:${HEAD_REF}
else
echo "无法确定目标分支,跳过推送"
fi
else
# 常规推送
echo "推送到分支: ${CURRENT_BRANCH}"
git push origin ${CURRENT_BRANCH}
fi
else
echo "没有文件被修改,无需提交"
fi
- name: Run validation for manual trigger
if: ${{ steps.set_env.outputs.trigger_type == 'manual' }}
env:
GITHUB_ACTOR: ${{ github.actor }}
PR_NUMBER: ${{ steps.set_env.outputs.pr_number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_REF: ${{ steps.pr_info.outputs.head_ref || '' }}
PR_REPO: ${{ steps.pr_info.outputs.head_repo || github.repository }}
VALIDATE_PATH: ${{ steps.set_env.outputs.validate_path }}
AUTO_FIX: ${{ steps.set_env.outputs.auto_fix }}
run: |
echo "手动触发模式,验证路径: ${VALIDATE_PATH}"
# 使用引号包裹路径,处理特殊字符
python build/validate.py "${VALIDATE_PATH}" $([[ "${AUTO_FIX}" == "true" ]] && echo "--fix")
# 如果关联了PR尝试提交更改
if [ -n "$PR_NUMBER" ] && [ -n "$HEAD_REF" ] && [ -n "$(git status --porcelain)" ]; then
echo "发现修改提交更改到PR: #$PR_NUMBER"
git add .
git commit -m "自动修复 JSON 格式和版本号 [ci skip]"
# 确定当前分支
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
echo "当前分支: ${CURRENT_BRANCH}"
if [ "$CURRENT_BRANCH" = "HEAD" ]; then
# 如果在detached HEAD状态使用正确的方式推送
echo "在detached HEAD状态使用HEAD_REF推送: ${HEAD_REF}"
git push origin HEAD:${HEAD_REF}
else
# 常规推送
echo "推送到分支: ${CURRENT_BRANCH}"
git push origin ${CURRENT_BRANCH}
fi
elif [ -n "$(git status --porcelain)" ]; then
echo "发现修改但未关联PR或无法确定分支跳过提交"
else
echo "没有文件被修改,无需提交"
fi
- name: Add PR comment
if: ${{ (steps.set_env.outputs.trigger_type == 'pr') || (steps.set_env.outputs.trigger_type == 'manual' && steps.set_env.outputs.pr_number != '' && steps.pr_info.outputs.found == 'true') }}
continue-on-error: true
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const pr_number = ${{ steps.set_env.outputs.pr_number }};
if (fs.existsSync('validation_notes.md')) {
const message = fs.readFileSync('validation_notes.md', 'utf8');
await github.rest.issues.createComment({
issue_number: pr_number,
owner: context.repo.owner,
repo: context.repo.repo,
body: message
});
} else {
console.log("没有发现 validation_notes.md 文件");
// 检查是否有文件被修改并提交
const { execSync } = require('child_process');
let commitMessage = '';
try {
const lastCommit = execSync('git log -1 --pretty=%B').toString().trim();
if (lastCommit.includes('自动修复')) {
commitMessage = '✅ 校验完成并自动修复了一些问题。修改已提交到PR中。';
} else {
commitMessage = '✅ 校验完成,没有发现需要修复的问题';
}
} catch (error) {
commitMessage = '✅ 校验完成,没有发现需要修复的问题';
}
await github.rest.issues.createComment({
issue_number: pr_number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commitMessage
});
}