GitHub PR 自动审查
一句话版:GitHub 的 PR 事件通过独立 webhook 入口进入规则,匹配后创建 DSH 审查会话;默认结果留在会话里,不自动回写 PR。
源码基线:0.1.5-alpha.1 / 5dda764ed3,2026-09-09 核验。 npm latest / next 为 0.1.2-rc.1(alpha 通道 0.1.5-alpha.1),请先按 升级指南 准备匹配的源码构建。手工发起审查见 代码审查教程。
1. 你会得到什么
入站 202 发生在规则完成之前;它不是“模型已经开始 / 审查已经通过”的凭证。
2. 前置条件
- 已构建的官方源码、可用的模型路由与额度。
- 一个专用测试仓库的本地 checkout,以及配置该仓库 webhook 的权限。
- 一个新的 DSH home;示例先在测试环境验收。
- 一个能把 GitHub 请求转发到专用 webhook 端口的 HTTPS 入口。Web UI 继续保持本地访问。
示例默认绑定 127.0.0.1:3081、路径 /github、最大请求体 1048576 字节。它在独立的 WebServer realm 中,和 3080 上的 Web UI / RPC 分开。
3. 复制示例并指定仓库
以下 shell 命令适用于 macOS / Linux。将路径换成自己的源码和测试 checkout;示例 home 应为新目录。
export DSH_SOURCE="/absolute/path/to/deepseek-harness"
export DSH_HOME="$HOME/.dsh-github-review-demo"
export DSH_GITHUB_REVIEW_WORKSPACE="/absolute/path/to/test-repo"
export DSH_GITHUB_WEBHOOK_PORT=3081
umask 077
cd "$DSH_SOURCE"
pnpm dsh web --dump-default-config >/dev/null
PROFILE_DIR="$DSH_HOME/profiles/web"
mkdir -p "$PROFILE_DIR"
cp apps/cli/config/examples/github-review/github-ready-review-rule.mjs \
"$PROFILE_DIR/github-ready-review-rule.mjs"
cp apps/cli/config/examples/github-review/cordis.yml \
"$PROFILE_DIR/github-review.patch.yml"
编辑复制后的 github-review.patch.yml 中规则的 config.repository,填精确的 OWNER/REPO。官方示例写的是 deepseek-harness/deepseek-harness,应替换成自己的目标,而不是照抄示例仓库名。保留 .mjs 与 patch 同目录,以便相对插件路径正确解析。
关键项如下;这是原 patch 中的片段,不是完整 overlay:
- id: github-ready-review-rule
name: './github-ready-review-rule.mjs'
config:
source: primary-github
repository: OWNER/REPO
workspacePath: !!js process.env.DSH_GITHUB_REVIEW_WORKSPACE ?? process.cwd()
agentPreset: standard
permissionPreset: read-only
规则与 adapter 的 source 都应为 primary-github。首次生成高熵 secret;后续重启使用同一值,并让 GitHub 端保持一致:
export DSH_GITHUB_WEBHOOK_SECRET="$(openssl rand -hex 32)"
pnpm dsh web --patch "$PROFILE_DIR/github-review.patch.yml"
在 Web UI 配好模型凭据。妥善保存 webhook secret,别提交到仓库、贴入日志或放进 URL。启动前生成新的 secret 会让旧 GitHub 配置的签名失效。
4. 只暴露 webhook 入口
将 HTTPS 入口的 /github 转发到上述本地端口,保留原始请求体与 GitHub 签名头;不要把整个 Web UI 端口一起转发出去。
在测试仓库的 webhook 配置中填:
| 配置项 | 值 |
|---|---|
| Payload URL | https://HOOK_HOST/github |
| Content type | application/json |
| Secret | 与运行时的 DSH_GITHUB_WEBHOOK_SECRET 相同 |
| 事件 | Pull requests |
这个 secret 只验证入站事件,不授予 Agent 读取私有 GitHub 仓库或发表评论的权限。需要读取私有 PR 时,应另配必要的出站凭据。默认规则的输出位置是 DSH Session,不是 GitHub 评论。
5. 分两层验证
A. 本地入口检查:不调用模型
在另一个终端设置与服务器相同的 secret 和端口,然后发送签名正确的 ping。不要在服务器正占用的终端里输入后续命令。
node --input-type=module <<'JS'
import { createHmac, randomUUID } from 'node:crypto';
const secret = process.env.DSH_GITHUB_WEBHOOK_SECRET;
if (!secret) throw new Error('Set DSH_GITHUB_WEBHOOK_SECRET first');
const body = JSON.stringify({ zen: 'local ingress check' });
const signature = createHmac('sha256', secret).update(body).digest('hex');
const port = process.env.DSH_GITHUB_WEBHOOK_PORT || '3081';
const response = await fetch('http://127.0.0.1:' + port + '/github', {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-hub-signature-256': 'sha256=' + signature,
'x-github-delivery': randomUUID(),
'x-github-event': 'ping',
},
body,
});
console.log('HTTP', response.status);
if (response.status !== 202) process.exitCode = 1;
JS
预期 HTTP 202,且不产生 review Session:ping 不符合规则过滤条件。这一步只覆盖端口、签名与入站接收,不验证公网转发或模型执行。
B. 真正的 PR → Session
- 在配置的测试仓库创建一个 draft PR,再转为 ready for review。
- 在 GitHub 的 delivery 详情检查返回状态与投递 ID。
- 在 DSH Workspace 中查找
Review OWNER/REPO#编号会话。 - 确认会话提示中的 head SHA 对应这次事件;审查应刷新 PR 当前元数据,并针对明确的提交检查。
- 等待真实模型结果,并确认文件、分支及 PR 状态未被修改。
默认只匹配 pull_request + ready_for_review;普通 opened、追加提交的 synchronize 或其他仓库的事件即使收到 202 也不触发该规则。
6. 投递与排障边界
| 现象 | 先检查 |
|---|---|
401 | 两端 secret 是否一致;代理是否改变原始 body;签名头是否完整 |
415 / 405 | 是否为 JSON、是否使用 POST |
503 | secret 引用是否解析成功、webhook runtime 是否就绪 |
202 但没有会话 | source、仓库全名、事件及 action 四个过滤条件;随后查看规则日志 |
| 会话存在但审查失败 | 模型路由、额度、仓库访问与只读策略;它们不由 webhook secret 解决 |
| 多个相同审查会话 | 官方 runtime 没有持久投递去重,redelivery 会再次执行规则 |
runtime 不保存投递 / 执行队列状态;进程崩溃会丢失尚未完成 prompt 准入的规则调用。prompt 准入后,才由普通 Session 持久化和 Agent 生命周期接管。
需要可靠去重或重试时,应在上游投递层或自己的规则持久层明确实现,别把 deliveryId 的存在误当成内置去重。也不要用浏览器登录 token 代替 GitHub webhook secret。
7. 扩展时保留的约束
官方规则把 PR 标题、作者等字段标为不可信元数据,并要求只读检查及 Session 内报告;保留这些边界。修改 action 过滤或增加自动评论属于额外行为,需要单独验收权限、重复执行与失败恢复。
本例通过 --patch 启用,普通 dsh web 不会自动读取这个文件。需要长期启用时,可把原 overlay 的条目合并进同目录的 cordis.patch.yml;同一组条目只挂载一次,避免又合并又传 --patch。