pytorch/crcr-test
GitHub: pytorch/crcr-test
PyTorch 树外后端跨仓库 CI 中继系统的参考实现,用于演示下游仓库如何接收上游 PyTorch 事件并自动触发自身 CI。
Stars: 3 | Forks: 2
# 跨仓库 CI 中继 (CRCR) — 接入指南
本仓库用作 [跨仓库 CI 中继 (CRCR)](https://github.com/pytorch/rfcs/blob/master/RFC-0050-Cross-Repository-CI-Relay-for-PyTorch-Out-of-Tree-Backends.md) 系统的**参考实现与测试平台**。它演示了树外 (OOT) 后端如何接收上游 PyTorch 事件,并作为响应运行其自身的 CI。
使用本指南将您的下游仓库接入 CRCR pipeline。
## 工作原理
当 `pytorch/pytorch` 上开启 PR 或推送代码时,[pytorch-fdn-cross-repo-ci-relay](https://github.com/apps/pytorch-fdn-cross-repo-ci-relay) GitHub App 会向所有已批准的下游仓库发送 `repository_dispatch` 事件。您的仓库接收这些事件后,即可触发构建、测试或任何工作流作为响应。
```
pytorch/pytorch (PR / push)
│
▼
CRCR Relay Bot
(pytorch-fdn-cross-repo-ci-relay)
│
▼
repository_dispatch
│
├──► your-org/your-repo (your workflow runs)
├──► Ascend/pytorch
├──► riseproject-dev/pytorch-ci
└──► pytorch/crcr-test
```
## 信任级别
每个下游仓库都会被分配一个信任级别,该级别决定了它与 PyTorch CI 的集成深度:
| 级别 | 名称 | 描述 |
|-------|------|-------------|
| **L1** | 接入 (Onboarding) | 事件将转发至下游,但上游不会收到反馈。 |
| **L2** | 观察 (Observation) | 下游 CI 结果将显示在 [HUD](https://hud.pytorch.org) 页面上,但不会显示在 PR 上。 |
| **L3** | 稳定 (Stable) | 当应用 `ciflow/oot/` 标签时,会在 PR 上添加非阻塞性的检查运行 (check run)。 |
| **L4** | 成熟 (Mature) | 会在每个 PR 上添加阻塞性的检查运行;专为关键加速器保留。 |
所有新仓库均从 **L1** 开始。晋升到更高级别取决于其展现出的稳定性与可靠性。
## 接入步骤
### 步骤 1:安装 CRCR GitHub App
在您的下游仓库中安装 [pytorch-fdn-cross-repo-ci-relay](https://github.com/apps/pytorch-fdn-cross-repo-ci-relay) GitHub App。
如需批准安装,请联系 **[@albanD](https://github.com/albanD)** 或 **[@atalman](https://github.com/atalman)**。
### 步骤 2:将您的仓库添加至白名单
向 [`pytorch/pytorch`](https://github.com/pytorch/pytorch) 提交一个 PR,将您的仓库添加到白名单文件中:
**文件:** [`.github/allowlist.yml`](https://github.com/pytorch/pytorch/blob/main/.github/allowlist.yml)
将您的仓库添加到相应的级别下(新仓库从 L1 开始):
```
L1:
- pytorch/crcr-test
- Ascend/pytorch
- riseproject-dev/pytorch-ci
- your-org/your-repo # ← add your repo here
```
### 步骤 3:创建 Dispatch 接收器工作流
在您的仓库中创建一个 GitHub Actions 工作流,用于监听 `repository_dispatch` 事件。中继器会发送两种事件类型:
- **`push`** — 当代码推送到 `main` 或创建了 ciflow 标签时触发(例如,`refs/tags/ciflow/trunk/`)
- **`pull_request`** — 当 PR 被开启、重新开启、同步或关闭时触发
在您的仓库中创建 `.github/workflows/out-of-tree-ci.yml`:
```
name: PyTorch Out-of-Tree Dispatch
on:
repository_dispatch:
types:
- pull_request
- push
run-name: >-
Dispatch -
${{
github.event.client_payload.event_type == 'pull_request' &&
format(
'PR #{0} ({1})',
github.event.client_payload.payload.pull_request.number,
github.event.client_payload.payload.action
) ||
github.event.client_payload.payload.ref
}}
concurrency:
group: >-
oot-${{ github.event.client_payload.payload.repository.full_name }}-${{
github.event.client_payload.payload.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
actions: write
id-token: write # required for L2+ callback authentication (OIDC)
jobs:
cancel-workflow:
if: ${{ github.event.client_payload.payload.action == 'closed' }}
runs-on: ubuntu-latest
steps:
- run: echo "PR closed, canceling older runs in the same concurrency group"
build-and-test:
if: ${{ github.event.client_payload.payload.action != 'closed' }} # listen to the specific action types you need (opened, reopened, synchronize)
runs-on: ubuntu-latest
steps:
- name: Checkout your repository
uses: actions/checkout@v4
- name: Checkout PyTorch at dispatched SHA
uses: actions/checkout@v4
with:
repository: pytorch/pytorch
ref: ${{ github.event.client_payload.payload.pull_request.head.sha || github.event.client_payload.payload.after }}
path: pytorch
# Add your build and test steps here
- name: Build and test
run: |
echo "Building against PyTorch SHA: ${{ github.event.client_payload.payload.pull_request.head.sha || github.event.client_payload.payload.after }}"
# your build commands here
- name: Log event to step summary
if: always()
run: |
cat <<'SUMMARY' >> $GITHUB_STEP_SUMMARY
```json
${{ toJson(github.event) }}
```
SUMMARY
```
### 步骤 4:验证集成
步骤 1–3 完成后,您的工作流将开始接收 dispatch。您可以通过以下方式进行验证:
1. 检查您仓库的 **Actions** 选项卡中的 `repository_dispatch` 事件
2. 查找由 `pytorch-fdn-cross-repo-ci-relay[bot]` 触发的运行记录
3. 检查步骤摘要以获取完整的事件 payload
## Dispatch Payload 结构
中继器会将原始的 GitHub 事件封装在 `client_payload` 内部:
```
{
"event_type": "push",
"client_payload": {
"event_type": "push",
"payload": {
"ref": "refs/tags/ciflow/trunk/184534",
"after": "abc123...",
"deleted": false,
"base_ref": "refs/heads/main",
"repository": {
"full_name": "pytorch/pytorch"
}
}
}
}
```
对于 `pull_request` 事件:
```
{
"event_type": "pull_request",
"client_payload": {
"event_type": "pull_request",
"payload": {
"action": "opened",
"pull_request": {
"number": 184442,
"title": "My PR title",
"head": {
"sha": "abc123...",
"repo": {
"full_name": "user/pytorch"
}
}
},
"repository": {
"full_name": "pytorch/pytorch"
}
}
}
}
```
## 关键字段参考
| 字段 | 路径 | 描述 |
|-------|------|-------------|
| 事件类型 | `github.event.client_payload.event_type` | `push` 或 `pull_request` |
| 上游仓库 | `github.event.client_payload.payload.repository.full_name` | 始终为 `pytorch/pytorch` |
| Push ref | `github.event.client_payload.payload.ref` | Git ref(例如,`refs/tags/ciflow/trunk/12345`) |
| Push SHA | `github.event.client_payload.payload.after` | push 事件的 commit SHA |
| PR 编号 | `github.event.client_payload.payload.pull_request.number` | pull_request 事件的 PR 编号 |
| PR 操作 | `github.event.client_payload.payload.action` | `opened`、`reopened`、`synchronize`、`closed` |
| PR head SHA | `github.event.client_payload.payload.pull_request.head.sha` | PR 的 head commit SHA |
| PR head 仓库 | `github.event.client_payload.payload.pull_request.head.repo.full_name` | Fork 仓库(如适用) |
## 速率限制
CRCR 中继 Lambda 实施了服务端速率限制,以保护后端基础设施(Redis、ClickHouse)免受回调突发的影响。目前的限制为所有下游仓库**每分钟 60 个请求**。当多个下游仓库并发发送回调时——例如,在一次大型上游 PR 推送扇出到多个仓库期间——中继器可能会响应 **HTTP 429 (Too Many Requests)**。
### 429 发生时的情况
默认的回调操作 (`cross-repo-ci-relay-callback`) 使用 `curl --fail-with-body`,它将任何非 2xx 响应视为硬性失败。单次瞬时的 429 就会导致回调步骤失败并使任务标记为出错,即使速率限制是暂时的。
### 推荐:带退避机制的客户端重试
正如 [#8](https://github.com/pytorch/crcr-test/issues/8) 中所讨论的,首选的缓解措施是**带有指数退避 (exponential backoff) 的客户端重试**,而不是提高服务端的阈值。服务端限制器是一种保护机制,应当予以保留——任何固定限制都无法保证并发的突发流量永远不会超过它。
**为什么选择重试而不是提高限制**(根据 [@can-gaa-hou](https://github.com/can-gaa-hou)):
### 下游仓库的最佳实践
1. **为工作流步骤添加抖动 (jitter)** — 如果您的工作流中有多个发送回调的任务,请使用随机延迟(`sleep $((RANDOM % 10))`)错开执行,以避免同步突发。
2. **预见 429 响应** — 将您的回调步骤设计为能够容忍瞬时故障。在 `curl` 调用周围使用重试包装器可以防止不必要的任务失败。
3. **遵守 `Retry-After` 标头** — 如果中继器在 429 响应中包含 `Retry-After` 标头,请至少等待相应时间后再重试。
4. **限制并发回调任务** — 使用 GitHub Actions 并发组 (concurrency groups),避免因同一仓库的并行回调导致中继器过载。
### 计划中的改进
- 在 `cross-repo-ci-relay-callback` 操作中实现带有退避机制的客户端重试 ([pytorch/test-infra](https://github.com/pytorch/test-infra))
- 在中继 Lambda 中支持 `Retry-After` 标头
## 相关资源
- [RFC-0050: 跨仓库 CI 中继](https://github.com/pytorch/rfcs/blob/master/RFC-0050-Cross-Repository-CI-Relay-for-PyTorch-Out-of-Tree-Backends.md) — 完整的 CRCR 设计规范
- [白名单](https://github.com/pytorch/pytorch/blob/main/.github/allowlist.yml) — 当前已批准的下游仓库列表
- [CRCR 中继 GitHub App](https://github.com/apps/pytorch-fdn-cross-repo-ci-relay) — 中继机器人
- [追踪 Issue](https://github.com/pytorch/pytorch/issues/175022) — CRCR 追踪 Issue
## 联系人
如有关于接入、安装批准或晋升至更高信任级别的问题:
- **[@albanD](https://github.com/albanD)** — PyTorch 核心维护者
- **[@atalman](https://github.com/atalman)** — PyTorch 开发基础设施 (Dev Infra)
- **[@groenenboomj](https://github.com/groenenboomj)**
- **[@jewelkm89](https://github.com/jewelkm89)**
- **[@subinz1](https://github.com/subinz1)**
- **[@fffrog](https://github.com/fffrog)**
标签:PyTorch, 威胁情报, 开发者工具, 开源框架, 持续集成, 搜索引擎查询, 跨仓库协同