Skip to content

[Bug] DCO 的 Sign-off 检查用事件里的旧 base SHA 取范围,导致已全部签名的 PR 被判失败 #4665

Description

@huangruiteng

现象

必填检查 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 仍然失败,并只列出该提交。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions