---
date: 2026-05-11
tags: [git, branch-protection, gitea, security, policy]
---

# 禁止 PR 作者自合：彻底删脚本绕过通道而不是优化它

## 背景

`hongwei.zhang` / `chentao.jia` 都不是 admin，Gitea 默认规则下作者不能批/合自己 PR。
为了让 AI 节奏（10+ PR/天）能跑下去，长期持有一个脚本 `scripts/ops/gitea-pr-merge.py`：
**临时 PATCH 分支保护把 `required_approvals=0` + `block_on_outdated_branch=false` → merge → restore**。

## 不直观的事

这个 "relax dance" 一直被视为**自审批必要工具**，issue #284 F7 还在讨论"改用 AIBot
token + 删 dance"作为优化方向。但优化方向走的还是"绕过自审批限制"。

真正问题：**绕过通道本身就是漏洞**——restore 失败时分支保护停在"宽松"状态，
任何人（包括恶意账号 / 被盗 token）能 push 不需要 approval，安全窗口暴露到下次
人工恢复。脚本会 exit 3 警告但事故已发生。

## 解法

**直接禁止自合，彻底删脚本**：

- 分支保护规则保留 `required_approvals=1` + `block_on_outdated_branch=true`
- 删除 `scripts/ops/gitea-pr-merge.py` 整个文件（PR #304）
- CLAUDE.md / AGENTS.md「PR 合并规则」段重写：禁止脚本临时放宽分支保护
- 合并路径只有两条：
  1. **人手合**：另一位团队成员在 Gitea web UI 点 Approve → Merge
  2. **未来 AI 自动合**：等 ai-review 升 required check 后（issue #259），
     新增 `auto-merge.yml`，用 AIBot token 走 **standard** merge——AIBot 不是 PR
     作者，正常 approve+merge，不需要绕过

## 反例：为什么不"留薄壳"

discussion 里有过保留 `gitea-pr-merge.py` 作为"标准 merge 调用 wrapper"的提议。
被否决理由：
1. 标准 merge 就是 1 个 API 调用，写到 ops 文档或 workflow 里直接用 curl 即可，
   不需要脚本封装
2. 保留薄壳的成本：未来"加个 flag 让 dance 复活"的诱惑随时存在；删干净最稳
3. 入口收敛：人手合走 Gitea web UI Merge 按钮，AI 合走 workflow → standard API。
   第三条命令行通道是反模式

## 适用范围

任何团队"为了流程顺畅"长期持有"绕过安全规则"的脚本：根因是组织规则跟工具
设计错位。**修工具不是解，删工具+改规则才是**。

类似的"绕过型脚本"还要警惕：
- 紧急部署绕过 CI（`--bypass-ci`）
- 跳过 pre-commit hook（`--no-verify`）
- 临时降低数据库迁移校验（`prisma migrate resolve --applied`）

每一条都该问：根因是流程错了还是工具不够好？如果是流程错了，先改流程。

## 验证

- `find scripts -name "gitea-pr-merge*"` 应为空
- Gitea web UI → Settings → Branches → develop / staging / production:
  `Require approvals: ✓` + `Block merge if pull request is outdated: ✓`
- `grep -r "gitea-pr-merge" --include="*.md" --include="*.py" --include="*.sh"` 应只在
  历史 .learnings 文件里出现（记录过去状态，正常）

## 相关

- 落地 PR：#304
- 跟踪 issue：#284（PR #281 follow-up F6-F9 收尾）
- 后续阻塞：issue #259（ai-review 升 required check + auto-merge workflow）
- 反面 learnings（保留作为历史记录，不修改）：
  - `.learnings/2026-04-29-branch-protection-stale-required-checks.md`
  - `.learnings/2026-04-29-batch-merge-runner-cancellation.md`
  - `.learnings/ERRORS/ERR-20260425-010.md` / `ERR-20260427-011.md` / `ERR-20260510-001-*`
