PyTorch 新引入的 Cross-Repository CI Relay,简称 CRCR,解决的是一个很现实的问题:当 pytorch/pytorch 的 PR 打开或提交更新时,树外后端仓库也需要尽早知道这次变化会不会把自己打坏。过去这类验证往往靠人工触发、定期同步或维护者事后排查;CRCR 的目标是把“主仓库变化”和“下游仓库 CI”连成一条自动链路,并能追踪结果。
为什么树外后端需要跨仓库 CI
PyTorch 的核心仓库变化频繁,后端生态又不都住在同一个仓库里。对 out-of-tree backend 来说,风险通常不是“代码无法编译”这么单一,而是接口、构建选项、测试假设、算子行为、依赖版本等细节在主仓库推进时发生偏移。
CRCR 的关键价值在于把反馈时间前移:PR 刚打开、commit 刚 push,下游仓库就能自动跑自己的验证。这样主仓库作者可以更早看到影响范围,下游维护者也不必等到破坏已经进入主干后才开始定位。
这里的重点不是把所有 CI 都塞进 pytorch/pytorch,而是让各个下游仓库保留自己的测试定义,同时由一个 relay 机制负责触发和汇总。这比集中式大流水线更适合多后端、多仓库的生态。
Relay 模式解决了两个协作断点
跨仓库 CI 最难的部分通常不是“跑一个 workflow”,而是事件和结果的归属。
一个 PR 发生在上游仓库,但测试逻辑在下游仓库;测试失败时,失败结果又需要回到上游 PR 的上下文里,才能被作者和 reviewer 看见。CRCR 正是在这个缝隙里工作:监听 pytorch/pytorch 的 PR 或 commit 事件,触发下游仓库 CI,并跟踪这些 CI 的状态。
这类设计带来几个直接收益:
- 下游仓库不用等待人工 ping 才知道上游变化。
- 上游 PR 可以更早暴露对树外后端的影响。
- 每个后端仍然在自己的仓库中维护测试矩阵、依赖和诊断日志。
- CI 结果可以作为跨团队协作的共同事实,而不是 Slack、issue、邮件里的零散描述。
边界也要看清:CRCR 能把信号传过去、把状态跟回来,但不能自动判断所有失败是否由当前 PR 引起。 flaky test、环境波动、下游仓库自身变更,仍然需要清晰的失败归因策略。
可以这样实践:用 GitHub Actions 模拟一个跨仓库 Relay
下面是一个可改造的最小示例,用来表达 CRCR 这类机制的工程形状。它不是 PyTorch CRCR 的内部实现细节,而是一个团队可以在自己的多仓库项目中实践的模式:上游仓库收到 PR 事件后,通过 GitHub API 触发下游仓库的 workflow。
你需要准备:
- 上游仓库:保存下面的 workflow。
- 下游仓库:有一个支持
workflow_dispatch的 workflow。 - 一个有权限触发下游 Actions 的 token,保存为上游仓库 secret:
DOWNSTREAM_RELAY_TOKEN。
上游仓库 .github/workflows/relay-downstream.yml:
name: Relay downstream CI
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [main]
jobs:
relay:
runs-on: ubuntu-latest
steps:
- name: Trigger downstream workflow
env:
GH_TOKEN: ${{ secrets.DOWNSTREAM_RELAY_TOKEN }}
DOWNSTREAM_OWNER: your-org
DOWNSTREAM_REPO: your-backend-repo
DOWNSTREAM_WORKFLOW: ci-from-upstream.yml
UPSTREAM_REPO: ${{ github.repository }}
UPSTREAM_SHA: ${{ github.sha }}
UPSTREAM_REF: ${{ github.ref }}
PR_NUMBER: ${{ github.event.pull_request.number || '' }}
run: |
set -euo pipefail
payload=$(jq -n \
--arg ref "main" \
--arg upstream_repo "$UPSTREAM_REPO" \
--arg upstream_sha "$UPSTREAM_SHA" \
--arg upstream_ref "$UPSTREAM_REF" \
--arg pr_number "$PR_NUMBER" \
'{
ref: $ref,
inputs: {
upstream_repo: $upstream_repo,
upstream_sha: $upstream_sha,
upstream_ref: $upstream_ref,
pr_number: $pr_number
}
}')
curl -fsS -X POST \
-H "Authorization: Bearer $GH_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$DOWNSTREAM_OWNER/$DOWNSTREAM_REPO/actions/workflows/$DOWNSTREAM_WORKFLOW/dispatches" \
-d "$payload"
下游仓库 .github/workflows/ci-from-upstream.yml:
name: CI from upstream relay
on:
workflow_dispatch:
inputs:
upstream_repo:
required: true
type: string
upstream_sha:
required: true
type: string
upstream_ref:
required: true
type: string
pr_number:
required: false
type: string
jobs:
test-against-upstream:
runs-on: ubuntu-latest
steps:
- name: Checkout downstream backend
uses: actions/checkout@v4
- name: Checkout upstream source
uses: actions/checkout@v4
with:
repository: ${{ inputs.upstream_repo }}
ref: ${{ inputs.upstream_sha }}
path: upstream
- name: Print relay context
run: |
echo "Testing downstream backend against ${{ inputs.upstream_repo }}@${{ inputs.upstream_sha }}"
echo "Upstream ref: ${{ inputs.upstream_ref }}"
echo "PR number: ${{ inputs.pr_number }}"
- name: Run downstream checks
run: |
set -euo pipefail
# Replace this with your backend build and test commands.
python -m pip install -U pip
python -m pytest tests -q
运行前要改的地方很少:把 DOWNSTREAM_OWNER、DOWNSTREAM_REPO、DOWNSTREAM_WORKFLOW 改成你的下游仓库信息,并把最后的 pytest 命令替换成真实的后端验证命令。
如果你还想把下游状态回写到上游 PR,可以继续扩展:由下游 workflow 使用 GitHub Commit Statuses 或 Checks API,把 success、failure、pending 写回 upstream_sha。这一步要谨慎处理权限,尤其是来自 fork 的 PR。
落地时要盯住的工程细节
跨仓库 CI 一旦规模变大,问题会从“能不能触发”变成“能不能稳定运营”。几个决策值得提前定清楚。
事件范围要收窄。不是所有 push 都需要触发所有下游仓库,按分支、路径、标签或 PR 标记过滤,可以减少无效队列和维护者噪音。
结果要可追踪。每次 relay 都应该携带上游 repo、SHA、ref、PR 编号;下游日志也要打印这些字段。否则失败发生后,很难把一次测试和一个上游变更准确对上。
权限要最小化。触发下游 workflow 的 token 不应该拥有无关仓库的写权限;如果需要回写状态,也应只允许写 checks/statuses,而不是给出宽泛的管理权限。
失败要分类。真正由上游 PR 引入的回归、下游测试 flaky、基础设施故障、依赖下载失败,处理路径完全不同。没有分类,跨仓库 CI 很快会变成“红了但没人信”的噪音源。
采用建议
CRCR 对 PyTorch 这样的生态尤其有意义:核心仓库高速演进,树外后端需要跟上,但又不适合把所有后端测试都合并进一个巨型仓库。它把协作协议变成自动化事件流,让上游变更在进入主线前更早接受下游现实检验。
团队要引入类似机制,可以从一个高价值下游仓库开始,不要一口气接入所有项目。先验证三件事:触发是否可靠、日志是否能定位问题、失败是否有人处理。等这些闭环跑顺,再增加仓库和测试矩阵。跨仓库 CI 的目标不是让流水线更热闹,而是让破坏更早、更准、更可修。