0

I would like to ensure if my commit message contains a given string using pre-commit. I tried using a pygrep based hook, but it does not handle the multiline as expected.

For example, my commit message is:

My commit message

Changelog: trial

I want to validate that the commit message contains "Changelog:".

Does anyone have an idea?

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
RobinM
  • 1
  • 1

2 Answers2

0

with pygrep this is fairly easy -- but you'll need to invert the usual behaviour of pygrep (which is to error when present)

repos:
-   repo: local
    hooks:
    -   id: needs-changelog
        name: commit message needs "Changelog:"
        language: pygrep
        entry: '^Changelog:'
        args: [--multiline, --negate]
        stages: [commit-msg]

this also utilizes --multiline such that the regex can match anywhere in the message. --negate flips the usual pygrep behaviour

$ git commit -m "foo"
commit message needs "Changelog:"........................................Failed
- hook id: needs-changelog
- exit code: 1

.git/COMMIT_EDITMSG

$ git commit -m $'foo\nChangelog: whatever'
commit message needs "Changelog:"........................................Passed
[main (root-commit) 5d10868] foo Changelog: whatever
 1 file changed, 9 insertions(+)
 create mode 100644 .pre-commit-config.yaml


disclaimer: I created pre-commit

anthony sottile
  • 61,815
  • 15
  • 148
  • 207
  • What's the reason for not installing commit-msg stage configs during `pre-commit install`? The docs says you'd always run `pre-commit install` on repo checkout, but this wouldn't install all the specified config entries. I assume there's a valid reason for this, but doesn't this make it counter intuitive since the amazing thing about pre-commit is to easily manage a projects commit standards without complex and manual commit hook set ups? – Werner Smit Oct 14 '22 at 18:29
  • it can, if you configure [this](https://pre-commit.com/#top_level-default_install_hook_types) -- but pre-commit can't know what ones are what stages without downloading all the tools – anthony sottile Oct 14 '22 at 18:32
0

Loved the answer provided by @anthony-sottile above, and I adapted it to work for conventional commit messages! This is my first answer contribution too!

Solution

Create a .pre-commit-config.yaml file in your project's root directory, or just add this hook to your existing file.

# project_root/.pre-commit-config.yaml
repos:
- repo: local
  hooks:
  - id: commit-msg
    name: conventional commit messages
    language: pygrep
    entry: '^(chore|test|feat|fix|build|docs|refactor)!?: ((?!.*(ing))(?!.*(ed))).*$'
  args:
  - --multiline
  - --negate # fails if the entry is NOT matched
  stages:
  - commit-msg

Be sure to install the commit-msg hook as well or this pre-commit hook stage won't work. These commands can be run in a bash terminal individually.

pip install pre-commit && \
pre-commit install --hook-type commit-msg && \
git commit -m "failing commit message" --allow-empty

Adapting Guidance

Some clarification of the regex if anyone needs to repurpose this for their awesome use-case:

  1. Regex Cheat Sheet to understand the syntax
  2. Regex Simulator to help getting your text matched
DataWizard
  • 61
  • 5