ci(commits): enforce Conventional Commits via commit-msg hook and PR gate (#70) #93
No reviewers
Labels
No labels
area:auth
area:ci
area:db
area:infra
area:native
area:pwa
area:service
epic
feature
foundation
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
james/carol!93
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "70-conventional-commits"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #70. Resolves ADR-0014's "convention isn't enforced via a commit-msg hook today" caveat.
What lands
lefthook.yml— newcommit-msghook (conventional). Inline regex (zero new dep). Lenient locally: allowsfixup!/squash!/amend!autosquash markers andRevert "..."shapes; skips duringgit merge/git rebase. Rejects with a helpful message that quotes the offending subject, lists allowed types, and points at theLEFTHOOK_EXCLUDE=conventionalescape..forgejo/workflows/commits.yml— new PR-only workflow. Walksgit rev-list --no-merges BASE..HEAD, applies the stricter regex (no fixup/squash). Catches contributors who skipped local hooks (--no-verify, no lefthook installed,LEFTHOOK_EXCLUDE).CLAUDE.md— Commit messages follow Conventional Commits bullet updated to point at both enforcement layers; Local git hooks bullet generalized so it doesn't get stale every time a new hook lands.docs/ci.md— new Conventional Commits section explaining the two layers, the type list (mirroringcliff.tomlexactly), why regex vs commitlint, and the bypass.Why a regex, not commitlint
commitlintis the obvious tool but pulls ~125 transitives onto the install graph and would need a newlavamoat.allowScriptsentry under ADR-0010. The regex above covers every variationcliff.toml'scommit_parsersrecognise; the per-commit subject is the only thing being gated, so the cost/benefit favoured zero new deps. The ticket explicitly OK'd this option. Documented indocs/ci.mdas a Reconsider if condition.Why local is lenient, CI is strict
git commit --fixup=<sha>creates afixup! <subject>commit during autosquash workflows — these are valid in-progress commits, expected to be rebased out before push. Blocking them locally would break the workflow. Locally the hook allows them; the CI regex does not, so a fixup that leaks into a PR fails the gate.Acceptance criteria
git commit -m "did a thing"is rejected locally with a message pointing at the convention doc — verified, output captured below.git commit -m "feat(release): ..."-style messages succeed — verified.docs/ci.mdupdated to point at the enforcement, not just the convention.lavamoat.allowScriptsentry needed — regex approach, zero new deps.Local verification
Rejection path:
Regex matrix (allowed forms):
Composes with
LEFTHOOK_EXCLUDEescape pattern as gitleaks and actionlint.commit-msghook is the third entry in the same lefthook config; the CI workflow follows the samesecrets.yml-style file-per-concern pattern.Test plan
ci(commits): ...so it should).cliff.toml,lefthook.yml,commits.yml) are documented indocs/ci.md.