| title | DCO Auto-Fix Workflow — /fix-dco Command Design & Implementation | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| domain | devops | ||||||||
| tags |
|
||||||||
| status | published | ||||||||
| source | codewhale | ||||||||
| created | 2026-06-13 00:00:00 UTC | ||||||||
| updated | 2026-06-14 00:00:00 UTC | ||||||||
| domain_expert | codewhale | ||||||||
| verified_date | 2026-06-14 |
贡献者提交 PR 后 DCO(Signed-off-by)检查失败是最常见的阻塞原因之一。尤其是 AI Agent 自动提交的 PR,经常缺签。需要在 PR 评论区提供一键自动修复能力。
Error message from DCO check:
Commit sha: abc1234, Author: user, Committer: user; Expected "Signed-off-by: user <user@example.com>", but got ""
Root cause: GitHub 的 DCO 检查要求每个 commit 都包含 Signed-off-by: 行。当贡献者使用 git commit 时忘记加 -s 标志,或者 AI Agent 自动生成的 commit 缺少签名,DCO 检查就会失败。手动修复需要 git rebase --signoff,这对新手贡献者来说门槛较高。
项目向 pre-commit/pre-commit-hooks 官方上游提交原生 check-dco 钩子(PR #1262),通过了全部 CI(flake8、mypy、tox 矩阵、pre-commit.ci),但仍被核心维护者以"可用内置 pygrep 替代"为由拒绝合入。
依赖单一上游的审核周期来驱动自身核心合规门禁是危险的。上游的拒绝是对供应链哲学的一次检验——它暴露的不是技术缺陷,而是信任模型的风险。
当原生提交被拒后,应立刻执行以下步骤:
- 提取核心资产:将 5 个关键文件(
.pre-commit-hooks.yaml、setup.cfg、setup.py、check_dco.py、tests/)从 Fork 仓库抽离,推送到全新独立仓库。 - 锁定版本:使用固定 Commit SHA(而非分支名)作为
.pre-commit-config.yaml中的rev值,确保供应链可控。 - 补全 ADR:记录决策过程,供后续审计。
- 发布公告:在追踪 Issue 中向所有节点交代迁移方案。
| 维度 | 独立 Python 钩子 | pygrep 方案 |
|---|---|---|
| 报错体验 | 精准 stderr 引导 | 粗暴整行匹配 |
| 多签名支持 | 原生 Python | 跨行正则脆弱 |
| 测试覆盖 | 16 个 pytest 用例 | 无测试 |
| 供应链可控 | 自主仓库 | 依赖上游 |
利用 issue_comment 事件 + pull_request_target 安全模型,实现 /fix-dco 命令。
on:
issue_comment:
types: [created]
jobs:
fix-dco:
if: |
github.event.issue.pull_request &&
contains(github.event.comment.body, '/fix-dco') &&
(github.event.comment.user.login == github.event.issue.user.login ||
contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), ...))同仓库 PR(head repo = 目标仓库):
- 检出 PR 头部分支(
refs/pull/<num>/head) - 执行
git rebase --signoff origin/main或git rebase --signoff HEAD~N - Force-push 回原分支
- 回复 ✅ 成功
Fork PR(head repo ≠ 目标仓库):
- GITHUB_TOKEN 无权 push 到 fork
- 回复手动修复指引:
git rebase --signoff HEAD~N git push --force
- 仅允许 PR 作者、MEMBER、OWNER、COLLABORATOR 触发
- 最多自动修复 10 个 commit(超过则要求手动修复)
- 需要
contents: write+pull-requests: write权限
- name: Get PR info
run: |
PR_JSON=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }} \
--jq '{headRef: .head.ref, headRepo: .head.repo.full_name, baseRef: .base.ref}')
echo "isFork=$([ "$headRepo" != "${{ github.repository }}" ] && echo true || echo false)"在 PR 评论区输入 /fix-dco,等待 30 秒内 bot 回复修复结果。
- Open a test PR with a commit missing
Signed-off-by:— confirm the/fix-dcocomment triggers the workflow - Run the workflow on a same-repo PR — confirm it auto-amends and force-pushes with sign-off
- Run the workflow on a fork PR — confirm it posts manual instructions instead of force-pushing
- Verify the
pr-welcome.ymloutput includes the copy-pastegit rebase --signoffcommands
# Test: Create a PR without Signed-off-by
git commit -m "test: missing signoff" # no -s flag
git push origin test-branch
gh pr create --title "Test DCO" --body "Testing DCO fix"
# Trigger the fix
gh pr comment <PR_NUM> --body "/fix-dco"
# Verify: Check if signoff was added
gh api repos/OWNER/REPO/pulls/<PR_NUM>/commits --jq '.[].commit.message' | grep "Signed-off-by"- 配套措施:
pr-welcome.yml欢迎语中已预置 DCO 修复命令 - 兜底方案:如自动修复失败,需贡献者本地手动执行
git commit --amend --signoff