Skip to content

feat: add commit-msg hook enforcing conventional commits#2

Open
joaopcm wants to merge 1 commit intomainfrom
nightshift/commit-normalize
Open

feat: add commit-msg hook enforcing conventional commits#2
joaopcm wants to merge 1 commit intomainfrom
nightshift/commit-normalize

Conversation

@joaopcm
Copy link
Owner

@joaopcm joaopcm commented Feb 22, 2026

Summary

  • Add .githooks/commit-msg hook validating Conventional Commits format
  • Add .gitmessage commit template with format hint
  • Wire commit template globally via git/config
  • Set core.hooksPath scoped to dotfiles repo only in setup.sh (not global — avoids breaking hooks in other repos)

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

Summary by CodeRabbit

  • Chores
    • Enforces Conventional Commits formatting for commit messages, with helpful guidance on failures.
    • Adds a default commit message template to guide commit content and examples.
    • Configures Git to use the repository template and custom hooks path by default.
    • Provides a setup step to install and activate the commit-msg hook automatically.

@coderabbitai
Copy link

coderabbitai bot commented Feb 22, 2026

📝 Walkthrough

Walkthrough

Adds Conventional Commits enforcement: a commit-msg hook validating commit messages, a .gitmessage template, git config pointing to the template, and setup script changes to install and enable the hook and template.

Changes

Cohort / File(s) Summary
Commit-msg hook & installer
.githooks/commit-msg, setup.sh
Introduces a commit-msg hook that validates Conventional Commits format (type or type(scope): description), allows merge commits and alias messages (WIP, SAVEPOINT, WIPE SAVEPOINT), prints usage on failure. setup.sh symlinks template, makes hook executable, and sets hooksPath.
Commit message template & git config
.gitmessage, git/config
Adds .gitmessage with Conventional Commits template/examples and updates git config to use the template (template = ~/.gitmessage).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Developer as Developer
    participant Git as Git Client
    participant Hook as ".githooks/commit-msg"
    Developer->>Git: git commit (with message)
    Git->>Hook: run commit-msg with message file
    Hook->>Hook: check for merge commit or aliases (WIP/SAVEPOINT)
    alt allowed
        Hook-->>Git: exit 0 (allow commit)
        Git-->>Developer: commit succeeds
    else invalid format
        Hook-->>Git: print usage, exit 1 (reject)
        Git-->>Developer: commit aborted, show hook output
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 I hopped into the tree of commits,
I trimmed the brambles of vague bits,
Now type and scope lead every line,
Clean messages, neat and fine —
Hop on, commit with glittery wits! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add commit-msg hook enforcing conventional commits' directly and accurately describes the main change: adding a commit-msg hook that enforces Conventional Commits formatting. It is concise, specific, and clearly communicates the primary purpose of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch nightshift/commit-normalize

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

.githooks/commit-msg validates format, allows WIP/SAVEPOINT exceptions.
hooksPath set locally (not globally) to avoid breaking other repos.

Nightshift-Task: commit-normalize
Nightshift-Ref: https://github.com/marcus/nightshift
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.githooks/commit-msg:
- Around line 16-27: Update the commit-msg validation to enforce the allowed
Conventional Commit types and support the breaking-change "!" indicator: replace
the loose pattern /^[a-z]+(\([a-z0-9_-]+\))?: .+/ with a pattern that enumerates
types and makes the scope and trailing "!" optional, e.g.
/^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9_-]+\))?(!)?:
.+/ applied to the same $msg variable, and keep the special-case acceptance for
exact messages like WIP, SAVEPOINT, and "WIPE SAVEPOINT" (either via a separate
equality check before the regex or by expanding the regex with alternation) so
those exceptions still pass.
- Around line 11-13: The commit-msg hook currently allows WIP/SAVEPOINT messages
but blocks auto-generated git revert messages; update the guard so revert
subjects like Revert "..." are accepted—either add "Revert" to the existing grep
pattern (e.g. grep -qE '^(WIP|SAVEPOINT|WIPE SAVEPOINT|Revert)') or add a
separate conditional that checks if the commit subject starts with "Revert" and
exits 0; modify the if that uses echo "$msg" | grep -qE ... to include this new
case so git revert messages are not rejected.

Comment on lines +16 to +27
if ! echo "$msg" | grep -qE '^[a-z]+(\([a-z0-9_-]+\))?: .+'; then
echo "ERROR: commit message must follow Conventional Commits format"
echo ""
echo " type: description"
echo " type(scope): description"
echo ""
echo " Allowed types: feat fix docs style refactor perf test build ci chore revert"
echo " Example: feat(auth): add login endpoint"
echo ""
echo " Exceptions: WIP, SAVEPOINT, WIPE SAVEPOINT"
exit 1
fi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Regex misses the breaking-change ! indicator and is more permissive than documented

Two related gaps in the validation regex ^[a-z]+(\([a-z0-9_-]+\))?: .+:

  1. Breaking change ! not handledfeat!: description and feat(scope)!: description are valid Conventional Commits syntax, but would be rejected because ! is not represented in the pattern.
  2. Type allow-list not enforced — the regex accepts any [a-z]+ string (e.g., banana: fix), yet the error message advertises a specific list of allowed types. A user following the error message exactly is fine, but typos like fetaure: … silently pass.
🐛 Proposed fix — enumerate allowed types and support breaking-change `!`
-if ! echo "$msg" | grep -qE '^[a-z]+(\([a-z0-9_-]+\))?: .+'; then
+if ! echo "$msg" | grep -qE '^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9_-]+\))?!?: .+'; then
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.githooks/commit-msg around lines 16 - 27, Update the commit-msg validation
to enforce the allowed Conventional Commit types and support the breaking-change
"!" indicator: replace the loose pattern /^[a-z]+(\([a-z0-9_-]+\))?: .+/ with a
pattern that enumerates types and makes the scope and trailing "!" optional,
e.g.
/^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9_-]+\))?(!)?:
.+/ applied to the same $msg variable, and keep the special-case acceptance for
exact messages like WIP, SAVEPOINT, and "WIPE SAVEPOINT" (either via a separate
equality check before the regex or by expanding the regex with alternation) so
those exceptions still pass.

@joaopcm joaopcm force-pushed the nightshift/commit-normalize branch from 48fe0dd to 17ab5b4 Compare February 22, 2026 01:30
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
git/config (1)

7-7: Heads-up: template path depends on setup.sh running successfully, and its scope is intentionally broader than the hook's.

Two things worth being aware of:

  1. ~/.gitmessage must exist before this setting takes effect. If the dotfiles are bootstrapped (e.g., git/config is linked first) but setup.sh hasn't been run yet to create the symlink, every git commit that opens an editor across all repos will emit a warning about the missing template file. Consider documenting the required bootstrap order, or guard the template setting in setup.sh the same way core.hooksPath is set there.

  2. Template is global; hook enforcement is repo-scoped. The [commit] template here propagates to every git repo on the machine, so contributors will see the Conventional Commits hint everywhere. The core.hooksPath (and thus the validation) is intentionally limited to the dotfiles repo (per setup.sh). This is a deliberate design choice that the PR description calls out explicitly — just worth confirming that's the intended UX, since users in other repos will see the prompt but experience no enforcement.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@git/config` at line 7, The global commit template setting "template =
~/.gitmessage" can warn across all repos if the file doesn't exist and exposes
the hint without enforcement; update setup.sh to only set the commit.template
when the file exists (same guard used for setting core.hooksPath) or document
the required bootstrap order in the repo README; specifically, in setup.sh check
for the presence of ~/.gitmessage (or create the symlink) before running git
config --global commit.template, and ensure core.hooksPath handling remains
repo-scoped as intended.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@git/config`:
- Line 7: The global commit template setting "template = ~/.gitmessage" can warn
across all repos if the file doesn't exist and exposes the hint without
enforcement; update setup.sh to only set the commit.template when the file
exists (same guard used for setting core.hooksPath) or document the required
bootstrap order in the repo README; specifically, in setup.sh check for the
presence of ~/.gitmessage (or create the symlink) before running git config
--global commit.template, and ensure core.hooksPath handling remains repo-scoped
as intended.

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