Skip to content

feat(cli): add commit message normalizer - #237

Open
gregggreg wants to merge 1 commit into
marcus:mainfrom
gregggreg:commit-normalize-normalizer-v3
Open

feat(cli): add commit message normalizer#237
gregggreg wants to merge 1 commit into
marcus:mainfrom
gregggreg:commit-normalize-normalizer-v3

Conversation

@gregggreg

Copy link
Copy Markdown

What

Standardizes the commit message format this repository uses, going forward.

Scope assumption: this enforces one format on new commits. It does not rewrite history — that is destructive and out of scope.

New internal/commitmsg package (stdlib only) parses, validates and normalizes Conventional Commits messages. A single canonical spec string in spec.go is the source of truth, quoted by all four consumers:

  • nightshift commit-msg CLI — --check / --fix / --print-spec / --quiet, over a file or stdin
  • scripts/commit-msg.sh git hook, installed by make install-hooks
  • README and website/docs/cli-reference.md
  • the orchestrator's plan and implement prompts, so autonomous agents are told exactly what the hook enforces

Format: type(scope)!: subject, 72-character header cap, blank line before the body, body wrapped at 72 columns (code blocks, lists, indented text and URLs left alone), and one trailing block of git trailers with Nightshift-Task / Nightshift-Ref / Co-Authored-By deduped.

Review fixes in this iteration

Supersedes #236 (same task, earlier iteration). Three defects from that review are fixed, each with tests:

  1. The git-generated exemption was over-broad. It matched a bare ^Merge prefix, so a human subject like "Merge duplicate config loaders into one" silently skipped validation. It now matches only what git actually writes — Merge branch|branches|remote-tracking branch|pull request|tag|commit , Revert ", fixup! , squash! , amend! .
  2. Length checks counted bytes, not runes. A 65-rune accented subject measures 120+ bytes and was rejected with a false subject-length error, hard-blocking a valid commit via the hook. subject-length and body-wrap now use utf8.RuneCountInString, and wrapParagraph fills by characters.
  3. --fix relocated body prose. splitTrailers hoisted any known-trailer-key line from anywhere in the body, tearing a mid-paragraph sentence out and welding the surrounding text together — silent content loss, since --fix rewrites COMMIT_EDITMSG in place. Hoisting is now restricted to a paragraph made entirely of trailers, or the run of known trailers ending the final block. A trailing prose paragraph (really: this is prose / not: a trailer) is no longer absorbed as trailers.

Two smaller items folded in:

  • wrapParagraph no longer breaks See <very-long-url> into a short line plus a still-over-limit line; a token that cannot fit on any line stays on the line it started, and such lines are exempt from body-wrap.
  • The hook prefers this repo's binary, then go run ./cmd/nightshift, and falls back to a globally installed nightshift only if it actually supports commit-msg — so an older install elsewhere on PATH cannot fail the hook with a confusing error.

Verification

gofmt -l . clean, go vet ./..., go build ./... and go test ./... all pass across the repo.

End-to-end in a scratch repo with the hook installed: git merge --no-ff succeeds, git commit --fixup succeeds, git rebase --autosquash succeeds, and a human Merge duplicate config loaders into one subject is correctly rejected (the iteration-1 blocker and the iteration-2 regression, both covered).

CLI smoke test: printf 'Fixed the thing.' | nightshift commit-msg --check - exits 1 with actionable issues; --fix produces fix: fixed the thing.

Note on overlapping work

Draft PR #186, branch chore/commit-message-normalizer, and PRs #231/#232 cover overlapping scope with different designs. Only one of these should land.

Nightshift-Task: commit-normalize
Nightshift-Ref: https://github.com/marcus/nightshift

🤖 Generated with Claude Code

Add internal/commitmsg, a stdlib-only package that parses, validates and
normalizes Conventional Commits messages, and expose it three ways from
one canonical spec string: a nightshift commit-msg subcommand, a
commit-msg git hook installed by make install-hooks, and the
orchestrator's plan and implement prompts.

The exemption for git's own headers matches only what git writes
(Merge branch/branches/tag/commit/pull request/remote-tracking branch,
Revert ", fixup!, squash!, amend!), so merges and rebase --autosquash
are never blocked while a hand-written "Merge ..." subject is still
checked. Length limits count runes rather than bytes, and --fix takes
trailers only from a trailer paragraph or the end of the final block,
never from the middle of prose.

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