现象
必填检查 Sign-off 会在本 PR 的每个 commit 都已带 DCO trailer 的 PR 上失败,从而拦住合并。它看起来像"作者没有 sign-off",实际不是。
证据(PR #4640,run 35217870793)
BASE_SHA: e1a97c2fd2e4ff98d3e0a2bf997bd461c0314cee
HEAD_SHA: 45340f38ba2c4444971f51c6a8908d4d88cd01bd
该 run 报出的 9 个"缺少 Signed-off-by"的 commit:
897e9aedb 2d301ca15 1924c6f6c d9b317412 9ccbf80ac
0d5450654 f2c4a27af e12e05fff a96c9aa91
逐一核对当前 main:这 9 个全部已是 main 的祖先提交,不是本 PR 作者提交的内容。而本 PR 自己的两个 commit(88d2e0f17、45340f38)各自恰好带 1 条 Signed-off-by: trailer。检查结论与事实相反。
根因
.github/workflows/dco.yml 用事件负载里的 base 尖端作为范围起点:
git rev-list --reverse "${BASE_SHA}..${HEAD_SHA}" # BASE_SHA = github.event.pull_request.base.sha
github.event.pull_request.base.sha 是事件产生时记录下来的 base 分支尖端,之后 base 分支继续前进时它不会刷新;而 PR 的 head 里已经包含比它更新的 main 提交(分支 rebase/合并过更新的 main)。于是 BASE..HEAD 的范围把 main 上已经合入的提交也算了进来,其中由 GitHub 在 main 上生成的合并提交没有 trailer,检查因此失败。
这也解释了它的间歇性:base 恰好较新时通过(同日的 #4630、#4631、#4629、#4643 通过),base 落后时失败(#4640 失败)。
影响
Sign-off 是 ruleset protect main 声明的必需上下文,且该 ruleset 打开 strict_required_status_checks_policy。因此这个误判会直接让已经通过评审的 PR 无法合并,并且最常打在社区贡献者的 PR 上——他们最没有能力自行绕过或诊断 CI 语义。
建议修复(需要维护者授权,.github/workflows/** 属 lead-owned)
让范围基于当前的 base 尖端,而不是事件里的旧尖端:
git fetch --no-tags origin "${{ github.event.pull_request.base.ref }}"
git rev-list --reverse --no-merges \
"origin/${{ github.event.pull_request.base.ref }}..${HEAD_SHA}"
等价的最小改动是保留 BASE_SHA,但用当前 base 尖端把 main 上已合入的提交减去:
git rev-list --reverse "${BASE_SHA}..${HEAD_SHA}" \
--not "origin/${{ github.event.pull_request.base.ref }}"
两种写法都只保留作者为本 PR 新增的提交,同时仍然检查 PR 内自带的合并提交(若希望连 PR 内合并提交也检查,去掉 --no-merges 即可)。
相关观察(同一 ruleset,另一个"审批不被计入"的坑)
同一个 ruleset 打开 require_last_push_approval: true,因此最后推入 commit 的人不能提供计入的审批。当维护者在社区 PR 的分支上直接推入修复提交时,维护者自己的 APPROVE 就不再生效,PR 反而卡在 REVIEW_REQUIRED(#4640 当前正是这种状态:该 head 上有两条 APPROVED 记录,GitHub 仍显示 REVIEW_REQUIRED)。对应的操作约束是:需要调整他人 PR 的内容时,应改为在独立分支上提出并请作者采纳,而不是替作者推送;若已推送,则由另一位维护者审批,或由作者重新推入 head。
验收
- 一个 head 包含较新
main 提交、且自身提交都带 trailer 的 PR:Sign-off 通过。
- 一个 head 中任一"仅属于本 PR"的提交缺少 trailer:
Sign-off 仍然失败,并只列出该提交。
现象
必填检查
Sign-off会在本 PR 的每个 commit 都已带 DCO trailer 的 PR 上失败,从而拦住合并。它看起来像"作者没有 sign-off",实际不是。证据(PR #4640,run 35217870793)
该 run 报出的 9 个"缺少 Signed-off-by"的 commit:
逐一核对当前
main:这 9 个全部已是main的祖先提交,不是本 PR 作者提交的内容。而本 PR 自己的两个 commit(88d2e0f17、45340f38)各自恰好带 1 条Signed-off-by:trailer。检查结论与事实相反。根因
.github/workflows/dco.yml用事件负载里的 base 尖端作为范围起点:github.event.pull_request.base.sha是事件产生时记录下来的 base 分支尖端,之后 base 分支继续前进时它不会刷新;而 PR 的 head 里已经包含比它更新的main提交(分支 rebase/合并过更新的main)。于是BASE..HEAD的范围把main上已经合入的提交也算了进来,其中由 GitHub 在main上生成的合并提交没有 trailer,检查因此失败。这也解释了它的间歇性:base 恰好较新时通过(同日的 #4630、#4631、#4629、#4643 通过),base 落后时失败(#4640 失败)。
影响
Sign-off是 rulesetprotect main声明的必需上下文,且该 ruleset 打开strict_required_status_checks_policy。因此这个误判会直接让已经通过评审的 PR 无法合并,并且最常打在社区贡献者的 PR 上——他们最没有能力自行绕过或诊断 CI 语义。建议修复(需要维护者授权,
.github/workflows/**属 lead-owned)让范围基于当前的 base 尖端,而不是事件里的旧尖端:
等价的最小改动是保留
BASE_SHA,但用当前 base 尖端把main上已合入的提交减去:两种写法都只保留作者为本 PR 新增的提交,同时仍然检查 PR 内自带的合并提交(若希望连 PR 内合并提交也检查,去掉
--no-merges即可)。相关观察(同一 ruleset,另一个"审批不被计入"的坑)
同一个 ruleset 打开
require_last_push_approval: true,因此最后推入 commit 的人不能提供计入的审批。当维护者在社区 PR 的分支上直接推入修复提交时,维护者自己的 APPROVE 就不再生效,PR 反而卡在REVIEW_REQUIRED(#4640 当前正是这种状态:该 head 上有两条 APPROVED 记录,GitHub 仍显示REVIEW_REQUIRED)。对应的操作约束是:需要调整他人 PR 的内容时,应改为在独立分支上提出并请作者采纳,而不是替作者推送;若已推送,则由另一位维护者审批,或由作者重新推入 head。验收
main提交、且自身提交都带 trailer 的 PR:Sign-off通过。Sign-off仍然失败,并只列出该提交。