Skip to content

feat(commits): add breaking-change and commit-msg hook support - #204

Open
iamlasse wants to merge 1 commit into
marcus:mainfrom
iamlasse:feat/commit-normalize-v2
Open

feat(commits): add breaking-change and commit-msg hook support#204
iamlasse wants to merge 1 commit into
marcus:mainfrom
iamlasse:feat/commit-normalize-v2

Conversation

@iamlasse

@iamlasse iamlasse commented Aug 1, 2026

Copy link
Copy Markdown

Summary

Completes the Conventional Commits normalizer (base in a local-only commit) into a fully spec-compliant, git-hook-ready normalizer, addressing iteration-1 review feedback.

Changes

  • Breaking-change support (Conventional Commits 1.0.0): the ! header indicator (feat!:, fix(api)!:) and the BREAKING CHANGE: / BREAKING-CHANGE: footer.
  • --check validate-only: prints nothing on success, prints the error and exits non-zero on non-conformance (distinct from default mode, which prints the rewritten message).
  • In-place --file rewrite: writes the normalized message back to the file so a real commit-msg hook can enforce the format; only writes when the result differs (mtime-preserving no-op on canonical input).
  • scripts/commit-msg.sh hook (diagnostics captured in a per-invocation mktemp file, not a fixed shared path) plus README + Makefile (make install-hooks now installs both hooks) documentation.
  • Tests for feat!:/fix(scope)!:/BREAKING CHANGE: preservation, idempotency of the breaking form, --check rejection, and trailer preservation.

Iteration-2 review fixes

  • Trailer preservation (blocker): footer handling generalized beyond BREAKING CHANGE:. Every paragraph whose first line looks like a git trailer (Signed-off-by:, Co-authored-by:, Fixes #..., this project's own Nightshift-Task:/Nightshift-Ref:, etc.) is emitted verbatim instead of being joined into a wrapped prose paragraph. Verified at runtime — the project's own required trailers now round-trip unchanged.
  • Subject length is validated after the trailing period is stripped, so a 71-rune subject ending in . (canonical form 70) is accepted.
  • Body wrapping measures width in runes (utf8.RuneCountInString) so multi-byte subjects cannot overshoot BodyWrapWidth, matching MaxSubjectLength.
  • commit-msg hook writes diagnostics to a per-invocation temp file (mktemp) instead of a fixed, world-writable, non-unique /tmp path.

Verification

gofmt, go vet ./..., go build ./..., and go test ./internal/commits/... ./cmd/nightshift/commands/... all pass. The local pre-commit and commit-msg hooks both ran on this commit.


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

Adds Conventional Commits 1.0.0 breaking-change support (the "!" header
indicator and the BREAKING CHANGE: footer), fixes the no-op --check flag so
it validates-only with non-zero exit on non-conformance, makes --file mode
rewrite the message in place (idempotent, mtime-preserving), and ships a
scripts/commit-msg.sh hook plus README/Makefile install docs.

Footer handling is generalized beyond BREAKING CHANGE: every paragraph whose
first line looks like a git trailer (Signed-off-by:, Co-authored-by:,
Fixes #..., Nightshift-Task:, BREAKING CHANGE:, ...) is emitted verbatim, so
the commit-msg hook never collapses trailers into prose.

Subject length is validated after the trailing period is stripped, and body
wrapping measures width in runes so multi-byte subjects cannot overshoot.

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