Automating Conventional Commit Messages - Globally

I am constantly on the lookout for ways to better myself as a developer. This includes not only getting better at the actual art of coding but also improving the processes of coding and finding ways to streamline and/or automate these processes. One of the areas that I had already (mostly) improved on is writing useful commit messages, so my git log doesn't end up looking like this XKCD comic:

One bad habit that is easy to fall into as a solo developer (especially under a time constraint) is to rationalize ignoring or delaying proper documentation since you're the only one working in the codebase it is easy to think that documentation is unimportant. And it is. Until it isn't - and then it is really important.

So that is one area that I am constantly trying to improve in. A git commit message is an oft-overlooked and undervalued form of documentation, so how can we improve here? I had already been, for the most part, following The seven rules of a great Git commit message. They made sense and were easy enough to implement, and almost got me to where I wanted to be as far as git commit messages were concerned.

However, I was recently made aware of the Conventional Commits standard published by the Angular Team thanks to the Enhance your git log with conventional commits post on dev.to. I really liked the article and the standard so I bookmarked it for a more thorough reading. It was a couple of days later when I went back to the post and saw the following comment that really got my wheels spinning:

JavaScript Joel

I have added husky + commitizen + commitlint to our projects and it has been great.

Some benefits include an auto-generated change log. auto-incrementing semantic versioning. and increased visibility for breaking changes.

I would recommend it for every project.

Because we are using gitlab, we are using these packages semantic-release-gitlab and npm-publish-git-tag.

So I got to googling and found the How We Use Commitizen to Clean Up Commit Messages post that I used for a starting point on implementing and enforcing conventional commit messages.

The only problem was that the vast majority of my projects are PHP projects, and these are all npm packages. As much as I wanted to add this to my workflow, I wasn't keen on the idea of adding a package.json file to each project and adding unnecessary complexity to my dev process. So I went about discovering if these tools could be added globally so they could be added to my ~30 projects without configuring them individually for each project.

The Tools

There were three tools that I had to configure:

  • commitizen- Help write the conventional commit message
  • commitlint - Determine if the commit message is properly formatted
  • git global hooks - Fail the commit if the commit message is not properly formatted

Commitizen

commitizen is a handy little command line utility that provides prompts and hints for filling out each section for a conventional commit message.

The first step is to install it using npm:

npm install -g commitizen

Install your preferred commitizen adapter globally, for example cz-conventional-changelog

npm install -g cz-conventional-changelog

Create a .czrc file in your home directory, with path referring to the preferred, globally installed, commitizen adapter

echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc

After commitizen has been installed, instead of making a commit with git commit you make a commit with:

git cz

This launches commitzen and you'll be prompted to first select the type of commit you are making:


After the type of commit is selected, you'll be prompted to fill out the subject, body, breaking changes, and open issues sections:


Pro-tip:
For the body (`Provide a longer description of the change:`)
If you want to add newlines you can add them with the `\n` newline character.
 
 Does a thing that is important\n\nBy doing\n-Thing 1\n-Thing 2
 
The dashes create a bullet list.

Yes, this is an ugly work around, but the only way that I found that works, and not a deal breaker, for me.
Now that commitizen is installed and working, we can move on to the next step.

Commitlint

commitlint is a tool that will lint your commit message and let you know if it adheres to whatever standard you've chosen.

Global installation is as easy as commitizen, but it is not explained in the readme. I had to dig around in the github issues to find an example. I plan on making a pull request to remedy this, but here are the steps:

Install the cli utility and the configuration you wish to apply:

npm install -g @commitlint/cli @commitlint/config-conventional

Create a global config file:

echo "module.exports = {extends: ['@commitlint/config-conventional']}" > ~/commitlint.config.js

That is all there to it. You can test that it is installed and working by running the following command:

echo 'should fail' | commitlint

And to see a passing message you can try:

echo "fix(server): send cors headers" | commitlint

Now we're ready to tie it all together...

git hook

commitlint in and of itself does not really do much until it is placed into a git hook. It is from inside this hook that we can fail the commit if the message does not follow the standard.

Like the earlier npm packages, I wanted this applied to all of my projects, without configuring a git hook for each individual project. If you are unfamiliar with using git hooks globally, you can check out my previous post on git global hooks.

If you've got global git hooks configured already, you can add a file as /path/to/global/hooks/commit-msg. I use the fish shell for my scripting, but the bash shouldn't look too much different. The line commitlint < $argv[1] does the work, I think bash would be commitlint < $1

#! /usr/bin/fish

# run any local commit-msg hook first
if test -e ./.git/hooks/commit-msg
        sh ./.git/hooks/commit-msg
end

commitlint < $argv[1]

exit $status

In Conclusion

So there you have it, three easy steps to set up a conventional commit message workflow. This is most useful if you can get in the habit of making atomic commits. You can see the real beauty of this after making a few commits and then running git log --one-line. Give it a shot and see what you think!

If you liked this post, you can subscribe to the rss feed or follow me @ToddEidson on Twitter to be notified of future blog posts.

Date Published: 17 March, 2019

Tags: git

About Todd

Full stack application developer. Life-long learner. Pragmatic programmer. Believer in clean coding. Proponent for extensible and reusable code. Hobbies include (very) amateur photography, collecting old jazz records and going to live music performances.

North Central Ohio, US
toddeidson[dot]info

Obligatory Disclaimer

All opinions are my own, probably wrong, and subject to change without notice.

© 2017-2019 Todd Eidson. All content is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.

Hosted on linode