Files
playbook/skills/commit-message/references/ci-wiring.md
T
cshandClaude Fable 5 7408c532f0 🐛 fix(commit-message): enforce one rule owner across skill and CI
CI ran .gitea/ci/commit_message_lint.py, an independent reimplementation
that parsed the type/emoji mapping out of docs/common/commit_message.md.
Its scope pattern accepted fix(-a), fix(a-), fix(a--b) and fix(_), and it
had no subject length check, so the gate that actually blocks merges
enforced weaker rules than the Validation this skill reports.

The CI entry is now a wrapper that locates the skill validator and
delegates to it with no arguments; commit_policy.json becomes the only
rule source and explanatory docs stop being a machine policy input.

The validator also learns the workflow_run event, whose payload carries
neither a PR title nor a before/after range. An upstream pull_request now
validates <integration-branch>..head_sha instead of HEAD alone, taking
the branch name from COMMIT_LINT_MAIN_BRANCH, and degrades to the
upstream head commit with a WARN rather than guessing a base.

Alongside: --help now documents the no-argument CI mode it had always
supported silently, CI wiring detail moves to references/ci-wiring.md,
and the description gains negative boundaries.

test/test_commit_message_policy.py asserts policy/spec-table equality and
uses ast to assert the CI entry imports no regex and reads no file, so a
second implementation cannot reappear unnoticed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 15:21:37 +08:00

4.2 KiB
Raw Blame History

CI 接线(CI Wiring

配置或排查 CI 上的提交信息校验时读本文件。交互式起草提交信息不需要它。

唯一实现

scripts/validate_commit_message.py 是提交信息规则的唯一实现,策略来自 references/commit_policy.json。CI 入口只负责定位并调用它:

python <skill-root>/scripts/validate_commit_message.py

无参数即进入 CI 输入校验模式。退出码原样透传:0 全部通过、1 存在不合规主题、 2 策略/参数/输入错误。

CI 入口不得自行实现校验,不得从 docs/common/commit_message.md 之类的说明性文档 解析 type/emoji 映射。说明性文档是给人读的;把它当机器策略来源会让 CI 与 commit_policy.json 各自演进,而 validator 报告的 Validation 就不再代表实际 拦合并的那道门。本仓库的 .gitea/ci/commit_message_lint.py 是薄 wrapper 范例, 其纪律由 test/test_commit_message_policy.py 机器保证。

事件覆盖范围

validate_commit_message.py --help 的当前输出为权威;下表是形状说明。

事件 取回的主题
pull_request* pull_request.titlebase.sha..head.sha 的完整提交范围
push before..after 完整范围;before 为零对象时按分支图计算新增提交
workflow_run 见下节
其它 / 本地 回退 HEAD 一条

已识别事件缺失或损坏 payload 时失败;shallow repository 一律失败。不能用可能截断的 payload commits 数组或计数字段证明范围完整。

workflow_run 编排

workflow_run payload 不含 PR 标题,也不含 before/afterworkflow_run.head_sha 是唯一锚点。因此:

  • 上游为 pull_request*:取 <集成分支>..head_sha 的完整范围。集成分支名读 $COMMIT_LINT_MAIN_BRANCH(默认 main),依次尝试 refs/remotes/origin/<名字>refs/heads/<名字>
  • 其它上游事件:只校验 head_sha 一条。
  • 集成分支无法解析、上游 head_branch 就是集成分支、或计算出的范围为空时,退化为 只校验 head_sha 并打印 WARN不猜测 base。默认分支的 push 因此只校验被推 上去的那一条,这是可接受的:进入默认分支的路径由 PR 那道门守。

该架构下 PR 标题不参与校验,这是 payload 决定的,不是配置项。完整提交范围校验取代 它,且强度更高——标题一直只是历史的代理,范围校验管的是真正留在历史里的东西。若确实 需要校验 PR 标题,只能在直接响应 pull_request 的那个 workflow 里做。

可信编排

workflow 编排必须来自可信 base/default branch。两条路:

  1. 直接触发用 pull_request_target
  2. 先由 pull_request 准备输入,再由默认分支的 workflow_run 执行。

普通 pull_request 中来自 PR head 的 workflow 即使切到 base worktree,仍可被待审改动 删除或绕过。可信 workflow 必须从可信 base 运行 wrapper、validator 和 policy;只能读取 PR head 的 Git 对象,不能在持有 secret 的步骤 checkout 或执行 PR head 内容。

排查

现象 处理
rc=2ERROR: ...policy... 部署或配置问题,不是提交信息不合规;检查 policy 路径与 schema
rc=2ERROR: ...payload... 事件 payload 缺失或损坏;核对 GITHUB_EVENT_PATH
rc=2requires complete ... history checkout 是 shallow;改为完整 fetch
checks: 1 subject(s) 但期望是一个范围 读 stderr 的 WARN:集成分支未解析,或上游不是 PR
validator: ... not found wrapper 的候选路径与实际部署布局不符