🐛 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>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# CI 接线(CI Wiring)
|
||||
|
||||
配置或排查 CI 上的提交信息校验时读本文件。交互式起草提交信息不需要它。
|
||||
|
||||
## 唯一实现
|
||||
|
||||
`scripts/validate_commit_message.py` 是提交信息规则的唯一实现,策略来自
|
||||
`references/commit_policy.json`。CI 入口只负责定位并调用它:
|
||||
|
||||
```bash
|
||||
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.title` 加 `base.sha..head.sha` 的完整提交范围 |
|
||||
| `push` | `before..after` 完整范围;`before` 为零对象时按分支图计算新增提交 |
|
||||
| `workflow_run` | 见下节 |
|
||||
| 其它 / 本地 | 回退 `HEAD` 一条 |
|
||||
|
||||
已识别事件缺失或损坏 payload 时失败;shallow repository 一律失败。不能用可能截断的
|
||||
payload `commits` 数组或计数字段证明范围完整。
|
||||
|
||||
## workflow_run 编排
|
||||
|
||||
`workflow_run` payload 不含 PR 标题,也不含 `before`/`after`,`workflow_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=2` 且 `ERROR: ...policy...` | 部署或配置问题,不是提交信息不合规;检查 policy 路径与 schema |
|
||||
| `rc=2` 且 `ERROR: ...payload...` | 事件 payload 缺失或损坏;核对 `GITHUB_EVENT_PATH` |
|
||||
| `rc=2` 且 `requires complete ... history` | checkout 是 shallow;改为完整 fetch |
|
||||
| `checks: 1 subject(s)` 但期望是一个范围 | 读 stderr 的 `WARN`:集成分支未解析,或上游不是 PR |
|
||||
| `validator: ... not found` | wrapper 的候选路径与实际部署布局不符 |
|
||||
Reference in New Issue
Block a user