Skip to content

chore: standardize commit messages on conventional commits - #215

Open
gregggreg wants to merge 3 commits into
marcus:mainfrom
gregggreg:chore/commit-message-standard
Open

chore: standardize commit messages on conventional commits#215
gregggreg wants to merge 3 commits into
marcus:mainfrom
gregggreg:chore/commit-message-standard

Conversation

@gregggreg

Copy link
Copy Markdown

What

Standardizes commit messages on Conventional Commits, enforced at two points:

  1. A local commit-msg hook (scripts/commit-msg.sh) that mechanically normalizes safe deviations and rejects what it cannot fix.
  2. A CI job (.github/workflows/commit-lint.yml) that validates every commit in a pull request.

Everything is dependency-free POSIX shell — no commitlint, no husky, no new runtime dependencies. Hook installation stays opt-in via make install-hooks, matching how scripts/pre-commit.sh is already installed; no developer git config is changed silently. make uninstall-hooks removes both hooks.

Stated assumptions

  • Conventional Commits is the target format. Chosen because it is the dominant convention and machine-parseable, which keeps generated changelogs/release notes on the table (this repo has changelog-synth and release-notes tasks).
  • The allowed type list is derived from this repo's own historyfeat, fix, docs, chore, test, refactor all appear in the last 200 subjects — plus the rest of the standard set so the vocabulary is not artificially narrow.
  • Merge, revert, fixup, squash and amend commits are exempt, since git generates or rewrites those itself.
  • A trailing (#123) is not counted toward the 72-character limit, because GitHub appends it at squash-merge time and authors never type it.

Deliberately out of scope

  • No rewriting of existing git history. The standard applies going forward. Roughly 9 of the last 15 subjects on main would fail the validator today; that is expected and is not something a hook should retroactively fix.
  • No automatic hook installation. Installing must be an explicit developer action.

Conservative by design

The normalizer only applies fixes it can make with certainty: trailing whitespace and periods, a capitalized type token, spacing after the colon, a capitalized leading word only when it is on a small allowlist of imperative verbs, and the blank line between subject and body. It copies the body, interior blank lines and trailers through verbatim, never guesses a missing type, and leaves anything it cannot parse alone so the validator can produce a specific error instead. It cannot corrupt a body, drop a trailer, or turn a valid message into an invalid one — e.g. docs: Nightshift now … keeps its proper noun.

Verification

make check (go tests + commit message tests + golangci-lint) passes, and make test-commit-msg runs 39 shell assertions covering both paths:

  • conforming message unchanged; trailing period stripped; capitalized type and verb lowercased; missing space inserted; scope and ! preserved; proper noun left alone; blank line inserted and extra blank lines collapsed; body, interior blank lines and Nightshift-Task/Co-Authored-By trailers preserved verbatim; merge/fixup/Revert "…" and git comments untouched
  • rejection: missing type, unknown type, malformed prefix, empty description, 73-char subject, trailing period, missing space, body without a blank line, empty message

The tests pass under dash, bash and zsh. The hook was also exercised end-to-end in a scratch repository: a fixable message is corrected in place, an unfixable one is rejected with an actionable error, and --no-verify bypasses it. The three commits on this branch conform to the new standard.

Note

There are older open PRs from earlier passes at this task (#212, #202). This one supersedes them; close whichever you do not want.

🤖 Generated with Claude Code

Adds a dependency-free POSIX shell commit-msg hook. The normalizer fixes
only what it can fix with certainty — trailing whitespace and periods, a
capitalized type or leading imperative verb, spacing after the colon, and
the blank line before the body — and copies the body and trailers through
verbatim. Anything it cannot parse is left alone for the validator to
reject with a specific error.

Installation stays opt-in: make install-hooks symlinks both hooks into
.git/hooks, matching how the existing pre-commit hook is installed, and
make uninstall-hooks removes them. No git config is changed implicitly.

Nightshift-Task: commit-normalize
Nightshift-Ref: https://github.com/marcus/nightshift
Runs the validator over every commit in the pull request range so a
message committed with --no-verify still has to be reworded before the
branch is mergeable, and runs the hook's own shell tests.

Nightshift-Task: commit-normalize
Nightshift-Ref: https://github.com/marcus/nightshift
Spells out the format, the allowed type list and where it came from, the
exemptions, worked good and bad examples, exactly what the normalizer will
and will not rewrite, and when --no-verify is legitimate. Adds a
CONTRIBUTING.md, which the repository did not have, and links it from the
README alongside the updated hook instructions.

Existing history is deliberately not rewritten.

Nightshift-Task: commit-normalize
Nightshift-Ref: https://github.com/marcus/nightshift
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant