Skip to content

Commit adf1983

Browse files
committed
ci(jsonDataValidation): 完善 JSON 数据验证工作流,支持 Fork PR 处理
- 新增 Fork PR 检测逻辑与相关环境变量 - 优化 checkout 步骤,适配同仓库 PR、手动触发与 Fork PR 场景 - 新增 Fork PR 的目标 JSON 文件同步步骤 - 完善自动修复回推的权限与密钥校验逻辑 - 更新工作流的环境变量传递与条件判断
1 parent b27f3a3 commit adf1983

1 file changed

Lines changed: 120 additions & 10 deletions

File tree

‎.github/workflows/jsonDataValidation.yml‎

Lines changed: 120 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,22 @@ jobs:
3737
run: |
3838
# 根据触发类型设置环境变量
3939
if [ "${{ github.event_name }}" = "pull_request_target" ]; then
40+
IS_FORK=false
41+
if [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then
42+
IS_FORK=true
43+
fi
4044
echo "触发类型: PR触发"
4145
echo "trigger_type=pr" >> $GITHUB_OUTPUT
4246
echo "validate_path=pr_files" >> $GITHUB_OUTPUT
4347
echo "auto_fix=true" >> $GITHUB_OUTPUT
4448
echo "pr_number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
4549
echo "head_ref=${{ github.event.pull_request.head.ref }}" >> $GITHUB_OUTPUT
4650
echo "head_repo=${{ github.event.pull_request.head.repo.full_name }}" >> $GITHUB_OUTPUT
51+
echo "head_sha=${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
4752
echo "base_ref=${{ github.event.pull_request.base.ref }}" >> $GITHUB_OUTPUT
4853
echo "base_sha=${{ github.event.pull_request.base.sha }}" >> $GITHUB_OUTPUT
54+
echo "maintainer_can_modify=${{ github.event.pull_request.maintainer_can_modify }}" >> $GITHUB_OUTPUT
55+
echo "is_fork=$IS_FORK" >> $GITHUB_OUTPUT
4956
else
5057
echo "触发类型: 手动触发"
5158
echo "trigger_type=manual" >> $GITHUB_OUTPUT
@@ -54,18 +61,31 @@ jobs:
5461
echo "pr_number=${{ github.event.inputs.pr_number }}" >> $GITHUB_OUTPUT
5562
echo "head_ref=" >> $GITHUB_OUTPUT
5663
echo "head_repo=" >> $GITHUB_OUTPUT
64+
echo "head_sha=" >> $GITHUB_OUTPUT
5765
echo "base_ref=main" >> $GITHUB_OUTPUT
5866
echo "base_sha=" >> $GITHUB_OUTPUT
67+
echo "maintainer_can_modify=false" >> $GITHUB_OUTPUT
68+
echo "is_fork=false" >> $GITHUB_OUTPUT
5969
fi
6070
61-
- name: Checkout code
71+
- name: Checkout PR code for same-repo PR or manual run
72+
if: ${{ steps.set_env.outputs.trigger_type != 'pr' || steps.set_env.outputs.is_fork != 'true' }}
6273
uses: actions/checkout@v4
6374
with:
6475
fetch-depth: 0
6576
ref: ${{ github.event.pull_request.head.sha || github.sha }}
6677
token: ${{ secrets.GITHUB_TOKEN }}
6778
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
6879

80+
- name: Checkout base repository for fork PR
81+
if: ${{ steps.set_env.outputs.trigger_type == 'pr' && steps.set_env.outputs.is_fork == 'true' }}
82+
uses: actions/checkout@v4
83+
with:
84+
fetch-depth: 0
85+
ref: ${{ github.event.pull_request.base.sha }}
86+
token: ${{ secrets.GITHUB_TOKEN }}
87+
repository: ${{ github.repository }}
88+
6989
- name: Set up Python
7090
uses: actions/setup-python@v5
7191
with:
@@ -81,7 +101,10 @@ jobs:
81101
echo "触发类型: ${{ steps.set_env.outputs.trigger_type }}"
82102
echo "验证路径: ${{ steps.set_env.outputs.validate_path }}"
83103
echo "自动修复: ${{ steps.set_env.outputs.auto_fix }}"
104+
echo "是否为fork PR: ${{ steps.set_env.outputs.is_fork }}"
84105
echo "PR号: ${{ steps.set_env.outputs.pr_number }}"
106+
echo "Head SHA: ${{ steps.set_env.outputs.head_sha }}"
107+
echo "允许维护者修改: ${{ steps.set_env.outputs.maintainer_can_modify }}"
85108
echo "Current directory: $(pwd)"
86109
echo "List files in root:"
87110
ls -la
@@ -95,6 +118,7 @@ jobs:
95118
96119
- name: Setup Git and repositories
97120
run: |
121+
BOT_TOKEN="${{ secrets.PR_FIX_PAT }}"
98122
git config --global user.name "GitHub Actions Bot"
99123
git config --global user.email "actions@github.com"
100124
# 设置Git处理中文等特殊字符
@@ -116,8 +140,13 @@ jobs:
116140
117141
# 确保origin指向PR的fork仓库
118142
if [ "$PR_REPO" != "$MAIN_REPO" ] && [ "${{ steps.set_env.outputs.trigger_type }}" = "pr" ]; then
119-
ORIGIN_REPO="https://github.com/${PR_REPO}.git"
120-
echo "PR来自fork仓库,设置origin指向: $ORIGIN_REPO"
143+
if [ -n "$BOT_TOKEN" ] && [ "${{ steps.set_env.outputs.maintainer_can_modify }}" = "true" ]; then
144+
ORIGIN_REPO="https://x-access-token:${BOT_TOKEN}@github.com/${PR_REPO}.git"
145+
echo "PR来自fork仓库,已配置自动修复推送目标"
146+
else
147+
ORIGIN_REPO="https://github.com/${PR_REPO}.git"
148+
echo "PR来自fork仓库,但缺少 PR_FIX_PAT 或未允许维护者修改,后续将无法自动回推"
149+
fi
121150
else
122151
ORIGIN_REPO=$UPSTREAM_REPO
123152
echo "PR来自同一仓库或非PR触发,origin与upstream相同: $ORIGIN_REPO"
@@ -136,12 +165,10 @@ jobs:
136165
137166
# 检查是否处于PR环境并切换到正确的分支
138167
if [ "${{ steps.set_env.outputs.trigger_type }}" = "pr" ] && [ -n "${{ steps.set_env.outputs.head_ref }}" ]; then
139-
echo "检测到PR,切换到PR分支: ${{ steps.set_env.outputs.head_ref }}"
168+
echo "检测到PR,准备工作分支"
140169
if [ "$PR_REPO" != "$MAIN_REPO" ]; then
141-
# fork仓库的PR,需要先创建本地分支追踪fork的远程分支
142-
git checkout -b "${{ steps.set_env.outputs.head_ref }}" --track "origin/${{ steps.set_env.outputs.head_ref }}" || \
143-
git checkout -b "${{ steps.set_env.outputs.head_ref }}" --no-track && \
144-
git push --set-upstream origin "${{ steps.set_env.outputs.head_ref }}"
170+
# fork仓库的PR不要直接检出fork代码,避免在受信上下文执行不受信工作区
171+
git checkout -B "pr-fix-${{ steps.set_env.outputs.pr_number }}" "${{ steps.set_env.outputs.base_sha }}"
145172
else
146173
# 同一仓库的PR
147174
git checkout "${{ steps.set_env.outputs.head_ref }}" || git checkout -b "${{ steps.set_env.outputs.head_ref }}"
@@ -159,6 +186,63 @@ jobs:
159186
git checkout -b temp-validation-branch
160187
fi
161188
189+
- name: Apply fork PR JSON files to workspace
190+
if: ${{ steps.set_env.outputs.trigger_type == 'pr' && steps.set_env.outputs.is_fork == 'true' }}
191+
uses: actions/github-script@v6
192+
with:
193+
github-token: ${{ secrets.GITHUB_TOKEN }}
194+
script: |
195+
const fs = require('fs');
196+
const path = require('path');
197+
198+
const owner = context.repo.owner;
199+
const repo = context.repo.repo;
200+
const pull_number = Number('${{ steps.set_env.outputs.pr_number }}');
201+
const headOwner = '${{ github.event.pull_request.head.repo.owner.login }}';
202+
const headRepo = '${{ github.event.pull_request.head.repo.name }}';
203+
const headSha = '${{ steps.set_env.outputs.head_sha }}';
204+
205+
const files = await github.paginate(github.rest.pulls.listFiles, {
206+
owner,
207+
repo,
208+
pull_number,
209+
per_page: 100
210+
});
211+
212+
const targetFiles = files.filter(file => /^repo\/pathing\/.*\.json$/.test(file.filename));
213+
core.info(`需要同步到工作区的 JSON 文件数: ${targetFiles.length}`);
214+
215+
for (const file of targetFiles) {
216+
if (file.status === 'removed') {
217+
if (fs.existsSync(file.filename)) {
218+
fs.unlinkSync(file.filename);
219+
core.info(`已删除文件: ${file.filename}`);
220+
}
221+
continue;
222+
}
223+
224+
if (file.status === 'renamed' && file.previous_filename && fs.existsSync(file.previous_filename)) {
225+
fs.unlinkSync(file.previous_filename);
226+
core.info(`已删除旧文件: ${file.previous_filename}`);
227+
}
228+
229+
const response = await github.rest.repos.getContent({
230+
owner: headOwner,
231+
repo: headRepo,
232+
path: file.filename,
233+
ref: headSha
234+
});
235+
236+
if (Array.isArray(response.data) || !response.data.content) {
237+
throw new Error(`无法读取文件内容: ${file.filename}`);
238+
}
239+
240+
const content = Buffer.from(response.data.content, 'base64').toString('utf8');
241+
fs.mkdirSync(path.dirname(file.filename), { recursive: true });
242+
fs.writeFileSync(file.filename, content);
243+
core.info(`已同步文件: ${file.filename}`);
244+
}
245+
162246
- name: Prepare validation script
163247
run: |
164248
# 检查build目录和validate.py文件是否存在
@@ -282,9 +366,12 @@ jobs:
282366
GITHUB_ACTOR: ${{ github.actor }}
283367
PR_NUMBER: ${{ steps.set_env.outputs.pr_number }}
284368
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
369+
PR_FIX_PAT: ${{ secrets.PR_FIX_PAT }}
285370
HEAD_REF: ${{ steps.set_env.outputs.head_ref }}
286371
PR_REPO: ${{ steps.set_env.outputs.head_repo || github.repository }}
287372
CHANGED_FILES_B64: ${{ steps.changed_files.outputs.changed_files }}
373+
IS_FORK: ${{ steps.set_env.outputs.is_fork }}
374+
MAINTAINER_CAN_MODIFY: ${{ steps.set_env.outputs.maintainer_can_modify }}
288375
run: |
289376
set -e
290377
# 使用base64解码文件列表
@@ -358,6 +445,26 @@ jobs:
358445
359446
# 检查是否有文件被修改
360447
if [ -n "$(git status --porcelain)" ]; then
448+
if [ "$IS_FORK" = "true" ] && [ "$MAINTAINER_CAN_MODIFY" != "true" ]; then
449+
echo "Fork PR 需要开启“允许维护者修改”才能自动回推"
450+
{
451+
echo "### ❌ 自动修复无法回推"
452+
echo "当前 PR 来自 fork,但未开启“允许维护者修改(Allow edits by maintainers)”。"
453+
echo "请开启后重新运行工作流。"
454+
} > validation_notes.md
455+
exit 1
456+
fi
457+
458+
if [ "$IS_FORK" = "true" ] && [ -z "$PR_FIX_PAT" ]; then
459+
echo "缺少 PR_FIX_PAT,无法将修复推回 fork PR"
460+
{
461+
echo "### ❌ 自动修复无法回推"
462+
echo "当前 PR 来自 fork,工作流需要仓库 Secret `PR_FIX_PAT` 才能自动推回修复结果。"
463+
echo "请配置该 Secret 后重新运行工作流。"
464+
} > validation_notes.md
465+
exit 1
466+
fi
467+
361468
echo "发现修改,提交更改"
362469
git add .
363470
git commit -m "自动修复 JSON 格式和版本号 [ci skip]"
@@ -366,7 +473,10 @@ jobs:
366473
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
367474
echo "当前分支: ${CURRENT_BRANCH}"
368475
369-
if [ "$CURRENT_BRANCH" = "HEAD" ]; then
476+
if [ "$IS_FORK" = "true" ]; then
477+
echo "推送修复到 fork 分支: ${HEAD_REF}"
478+
git push origin HEAD:${HEAD_REF}
479+
elif [ "$CURRENT_BRANCH" = "HEAD" ]; then
370480
# 如果在detached HEAD状态,使用正确的方式推送
371481
if [ -n "${HEAD_REF}" ]; then
372482
echo "在detached HEAD状态,使用HEAD_REF推送: ${HEAD_REF}"
@@ -500,7 +610,7 @@ jobs:
500610
fi
501611
502612
- name: Add PR comment
503-
if: ${{ always() && ((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')) }}
613+
if: ${{ always() && (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')) }}
504614
continue-on-error: true
505615
uses: actions/github-script@v6
506616
with:

0 commit comments

Comments
 (0)