Prhub

#2403 fix(ci): unblock ROCm fork PRs at checkout

原始 PR 作者 indianspeedster 合并时间 2026-08-12 13:45 文件变更 4 提交数 2 评论 0 代码增减 +18 / -56

执行摘要

ROCm fork PR 改走低信任 pull_request,修复 checkout 失败

在 PR #2347 中,带有 run-ci-amd 标签的 fork PR 在 actions/checkout 阶段即失败,报错 Refusing to check out fork pull request code from a 'pull_request_target' workflow。根因是 pr-test-rocm.yml 使用 pull_request_target 事件并从 fork 显式 checkout refs/pull//merge,而 actions/checkout 在 2026-07-20 新增守卫后拒绝对 fork 代码在可信上下文中检出。本 PR 将 ROCm 工作流迁移到与 NVIDIA 工作流一致的 pull_request 低信任模型,让 checkout 默认选择 PR merge commit,并移除 target-only 授权输出与不安全开关。

值得精读。该 PR 展示了 GitHub Actions 信任模型变更下的最佳实践:不要用 allow-unsafe-pr-checkout 绕过守卫,而是迁移到 pull_request 低信任事件,让 secrets 保护与 merge commit 检出由平台接管。同时可参照其测试策略(用字符串断言锁定 workflow 的关键属性)来防控类似 CI 回归。

讨论亮点

仓库内无独立 review 评论,但提交历史揭示了方案迭代:首个提交 e6de779 选择在 checkout 中显式设置 allow-unsafe-pr-checkout 快速解封;最终提交 6e331ac 则完全改用 pull_request 低信任事件模型,与 NVIDIA 的 pr-test.yml 保持一致,并删除授权步骤与会话开关。reviewer guapisolo 直接批准最终方案,且最终提交由 guapisolo 本人完成重写,说明团队更倾向于避免使用不安全开关、统一信任边界。

实现拆解

  1. 触发事件切换:.github/workflows/pr-test-rocm.ymlon.pull_request_target 改为 on.pull_requestresolve-ci-policyEVENT_NAME 直接使用 github.event_name,并删除 Authorize self-hosted PR execution 步骤及 allow_self_hosted 输出。
  2. stage 门禁简化:stage-c-4-gpu-mi300x 删除 if: needs.resolve-ci-policy.outputs.allow_self_hosted == 'true',不再传 checkout_refWANDB_API_KEY 从条件表达式改为 ${{ secrets.WANDB_API_KEY }},由 GitHub 对 fork 自动隐藏。
  3. 可复用工作流收敛:.github/workflows/_run-ci-rocm.yml 删除 checkout_ref 输入定义及 checkout 步骤的 ref,让 actions/checkout@v4 默认解析 PR merge commit,保留 persist-credentials: false
  4. 测试与文档同步:tests/ci/test/test_run_suite.pyTestRocmWorkflowScopeSeam 断言改为验证 pull_request 事件、禁止 pull_request_target、确认 allow_self_hostedcheckout_ref 消失;docs/ci/00-stage.md 更新信任模型说明。
  5. 验证:pytest tests/ci/test/test_run_suite.py -k RocmWorkflowScopeSeam 通过 2 项,pre-commit run check-yaml 通过。
文件 模块 状态 重要度
.github/workflows/pr-test-rocm.yml CI 工作流 modified 5.18
.github/workflows/_run-ci-rocm.yml 复用工作流 modified 3.39
tests/ci/test/test_run_suite.py 测试套件 modified 4.75
docs/ci/00-stage.md CI 文档 modified 2.1

关键符号

test_pr_nightly_and_dispatch_share_policy test_stage_consumes_policy_and_preserves_manual_full_scope

关键源码片段

tests/ci/test/test_run_suite.py test-coverage

测试配套更新,锁定新的工作流信任模型,防止回归。

class TestRocmWorkflowScopeSeam:
    @staticmethod
    def _workflow() -> str:
        return (Path(__file__).resolve().parents[3]
                / '.github' / 'workflows' / 'pr-test-rocm.yml').read_text()
​
    def test_pr_nightly_and_dispatch_share_policy(self):
        workflow = self._workflow()
        # pull_request 事件必须存在,且不允许残留 pull_request_target
        assert 'pull_request:' in workflow
        assert 'pull_request_target:' not in workflow
        policy_block = workflow.split('resolve-ci-policy:', 1)[1].split('resolve-ci-image:', 1)[0]
        # 自建授权步骤已移除,allow_self_hosted 门禁不再存在
        assert 'allow_self_hosted' not in policy_block
        assert 'EVENT_NAME: ${{ github.event_name }}' in policy_block
​
    def test_stage_consumes_policy_and_preserves_manual_full_scope(self):
        workflow = self._workflow()
        stage = workflow.split('  stage-c-4-gpu-mi300x:', 1)[1]
        command = stage.split('execute_command:', 1)[1].split('secrets:', 1)[0]
        assert 'needs: [resolve-ci-policy, resolve-ci-image]' in stage
        assert 'allow_self_hosted' not in stage
        assert 'checkout_ref:' not in stage
        assert 'WANDB_API_KEY: ${{ secrets.WANDB_API_KEY }}' in stage
        reusable = (Path(__file__).resolve().parents[3]
                    / '.github' / 'workflows' / '_run-ci-rocm.yml').read_text()
        assert 'checkout_ref:' not in reusable
        assert 'persist-credentials: false' in reusable
        assert 'allow-unsafe-pr-checkout' not in reusable

评论区精华

修复方案:allow-unsafe-pr-checkout 还是 pull_request 模型? 设计

首个提交 e6de779 选择在 checkout 中显式设置 allow-unsafe-pr-checkout 快速解封;最终提交 6e331ac 改为彻底使用 pull_request 低信任事件模型,与 NVIDIA workflow 对齐,移除授权步骤。reviewer guapisolo 直接批准最终方案,且最终提交由 guapisolo 完成重写。

结论:采用 pull_request 事件 + 默认 merge commit checkout,由 GitHub 统一管理 fork 的 secrets 隐藏。 · 已解决

风险与影响

1) 自托管 runner 暴露面未变:fork PR 代码在获得标签后仍会在 MI300X 自托管 runner 上执行,依赖维护者加标签的审批流程;
2) secrets 处理依赖 GitHub 保证:WANDB_API_KEY 直接引用 secrets,fork 运行会被 GitHub 置空,若测试逻辑假设 key 存在可能产生行为差异,但不会泄漏;
3) 事件字段兼容性:pull_requestpull_request_targetgithub.event.pull_request 结构一致,github.event.schedule 仅用于 schedule 触发,workflow_dispatch 分支保持原样,回归风险低;
4) 文档与测试同步,未引入新的部署配置。

对 fork 贡献者:ROCm 相关 PR 不再因 checkout 拒绝而阻塞,只要维护者打上 run-ci-amd 等标签即可在 MI300X 上运行测试;对维护者:CI 安全模型与 NVIDIA 工作流统一,减少自建授权步骤,降低维护成本;对系统:pr-test-rocm.yml 事件触发语义变化,今后无法在 pull_request_target 可信上下文中使用仓库 secrets,但当前阶段不需要。整体影响范围限于 CI 基础设施与 AMD/ROCm 测试路径。

安全模型变更 自托管 runner 暴露 secrets 依赖平台保证

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论