Skip to content

feat(commits): add Conventional Commits message normalizer - #222

Open
iamlasse wants to merge 3 commits into
marcus:mainfrom
iamlasse:feat/commit-normalize-20260821
Open

feat(commits): add Conventional Commits message normalizer#222
iamlasse wants to merge 3 commits into
marcus:mainfrom
iamlasse:feat/commit-normalize-20260821

Conversation

@iamlasse

Copy link
Copy Markdown

Summary

Standardizes commit messages across the repo with a small, dependency-free normalizer plus tooling around it.

  • internal/commits — pure Normalize(msg) (string, error) and Validate(msg) error enforcing Conventional Commits:
    • header type(scope)?: subject restricted to a known type set: feat, fix, docs, style, refactor, test, chore, perf, build, ci, revert
    • auto-fixes: lowercase type and subject, whitespace trimming, trailing-period removal, blank-line collapsing, 72-column body wrapping
    • rejects with actionable errors: empty message, missing/unknown type, missing subject, subject over 72 chars
    • idempotent: Normalize(Normalize(m)) == Normalize(m)
  • nightshift commit normalize — cobra subcommand reading the message from a positional arg, --file, or stdin; with --file the normalized message is written back in place, otherwise printed to stdout. --check validates only, printing a diff-style report and exiting non-zero without modifying anything.
  • scripts/commit-msg.sh — installable git commit-msg hook that normalizes the message file in place and rejects unfixable messages; make install-hooks now installs it alongside the pre-commit hook:
    ln -s ../../scripts/commit-msg.sh .git/hooks/commit-msg
  • Docsdocs/commit-messages.md covers the format rules, examples, CLI usage, and hook installation; the README links to it.

Testing

  • gofmt -l . clean, go vet ./... clean, go build ./... succeeds
  • go test ./internal/commits/... ./cmd/nightshift/... passes (table-driven tests: idempotency, casing, wrapping, all error cases, Validate)
  • Smoke-tested stdin/--file/--check flows and the hook itself (this commit was normalized by it)

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

Add internal/commits with pure Normalize/validate functions enforcing the project's Conventional Commits rules (known type set, lowercase type, lowercase subject, 72-char subject limit, whitespace trimming, and 72-column body wrapping). Wire it into the CLI as 'nightshift commit normalize' (positional, --file, and stdin sources; --check to validate only), ship a commit-msg git hook under scripts/, and document the format and installation in docs/commit-messages.md.

Nightshift-Task: commit-normalize
Nightshift-Ref: https://github.com/marcus/nightshift
Add internal/commits with pure Normalize/Validate functions enforcing the
project's Conventional Commits rules: a known type set (feat, fix, docs,
style, refactor, test, chore, perf, build, ci, revert), lowercase type and
subject, a 72-character subject limit, whitespace trimming, and 72-column
body wrapping. Trivially fixable issues are rewritten automatically; missing
or unknown types, missing subjects, and overlong subjects are rejected with
actionable errors.

Wire the package into the CLI as 'nightshift commit normalize' (positional,
--file, and stdin sources; --check validates with a diff-style report and
exits non-zero without modifying anything; --file rewrites the message file
in place). Ship an installable scripts/commit-msg.sh hook, extend 'make
install-hooks' to install it, and document the format, command, and hook in
docs/commit-messages.md with a pointer from the README.

Nightshift-Task: commit-normalize
Nightshift-Ref: https://github.com/marcus/nightshift
The commit-msg hook invoked its go-run fallback as a single quoted
string, so the exec failed and every commit was rejected with a
misleading formatting error. The hook now stores the command as an
array, probes that the tool runs before blaming the message, uses a
mktemp'd error file cleaned up by a trap, and distinguishes tooling
failures from format rejections.

The normalizer now accepts and preserves Conventional Commits
breaking-change markers (feat!:, feat(scope)!:), checks the 72-char
subject limit after trailing-period trimming, measures body wrap width
in runes rather than bytes, and keeps trailing trailer/footer blocks
verbatim instead of re-wrapping them.

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

Copy link
Copy Markdown
Author

Review fixes pushed (35b5ed6):

Hook fallback bug (confirmed): scripts/commit-msg.sh now stores the nightshift command as a bash array so the multi-word go run ... fallback expands correctly, probes that the tool actually runs before inspecting the message, and clearly distinguishes "failed to run nightshift (tooling problem)" from "message does not follow Conventional Commits". Verified live: with no nightshift on PATH, the hook builds via go run and normalizes the message file; a broken binary reports the tooling error, not a format rejection.

Minor items:

  • The fixed /tmp/nightshift-commit-msg.err is replaced by mktemp + trap cleanup.
  • The 72-char subject limit is now checked after trailing-period trimming, so a 73-char subject that shrinks to 72 is accepted.
  • Conventional Commits breaking-change markers (feat!:, feat(scope)!:) are accepted and preserved; documented in docs/commit-messages.md.
  • Body wrap width is measured in runes, not bytes.
  • Bonus fix found while committing through the hook: trailing trailer/footer blocks (Reviewed-by:, BREAKING CHANGE:, Nightshift-Task:, ...) are now preserved verbatim instead of being re-wrapped and corrupted.

gofmt/vet/build/tests all pass; new table-driven tests cover each fix.

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