Using Git hooks for easier development (2020)

Share on facebook
Share on google
Share on twitter
Share on linkedin

Do you have a set of routines you do before you commit and publish code? These routines could be linting, formatting, testing and building the code. Imagine if there was a way to automate all of this for your whole team? The solution is to use Git hooks.

In this post, we will go through my recommended way to use Git hooks, using lint-staged, TSLint and Prettier. We will also answer the question: “What should run as Git hooks vs. on the CI?”.

 

Working with Git Hooks

Git hooks enable us to hook into Git events and add code to be executed at events such as:

• Commit-msg

• Pre-commit

• Pre-push

Git hooks are set up in the .git folder in a Git project. These are written as bash scripts. Unless you lack social skills and wear square glasses you wouldn’t like to write bash scripts (joke).

Husky enables you to specify Node scripts to execute on the different hooks. This can be used eg. to run npm scripts and will make it very easy to hook your Node scripts into the Git events.

Install Husky

Husky can be installed with:

npm i --save-dev husky

Or for YARN users:

yarn add husky -D

Configuring Husky

Husky can be configured in package.json with a property called husky and supports the Git hooks.

You can also create a .huskyrc file for the husky config (recommended).

From here you specify regular NPM  scripts that will be executed on the specific Git events.

My recommended setup with Git hooks for Angular apps

After having used Git hooks for a while I have developed a couple of preferences for scripts I would like to run before I commit code. My husky config looks like this:

Commit-msg

The commit message hook is triggered before pre-commit. It is used to evaluate the commit message using commitlint.

My recommended commitlint configuration is:

This will make sure to enforce conventional commits ala in the Angular repo. The benefit of this is clear intent in every commit message as well as it can be used to generate CHANGELOG files.

When using this, make sure you install:
Commitlint:
npm i -D commitlint

And config conventional:
npm i -D @commitlint/config-conventional

Pre-commit

Pre-commit is used for linting and formating the code before it is committed. This ensures that you avoid seeing extra commits such as:

chore: fixed lint

Pre-commit hooks should be fast, as they are running on a developer’s machine that otherwise will be idle.

My recommended approach for fast pre-commit hooks is to do:

  1. Prettier formatting of all supported files
  2. TSLint linting of Typescript
  3. stylelint linting of css/scss files

All of this is triggered through the lint-staged command.

My recommended lint-staged configuration looks like this:

Lint-staged is applying the given commends for every staged file (appending them to each command) that matches the glob expressions.

Lint-staged is installed with:

npm i -D lint-staged

The first part, is formating Typescript files, JSON, markdown SCSS and HTML with Prettier.

The second part is linting Typescript files with TSLint and fixing errors.

The last part is linting and fixing style with stylelint.

 

For my recommended Prettier and TSLint configuration check this post.

Post-merge

The post-merge git hooks will be triggered AFTER we have merged two branches together using either eg. git pull origin master or git merge master. For this hook, we want to use post-npm-install for checking if package.json has gotten new dependencies after the merge, and do an npm install if it has. This will free you from the “works on my machine” syndrome and make sure you always will get the node modules installed as you catch up with the destination branch.

To do this we first install npm-post-install:

npm i -D npm-post-install

And then we added this line to the .huskyrc file:

"post-merge": "post-npm-install"

And we will now automatically do npm install after a merge if necessary.

Git hooks vs. running scripts on the CI

You might be wondering, what should run as Git hooks vs. on the CI. I have a clear take on this:

If it is related to the styling and formating on the code, use Git commit hooks. Otherwise let the CI handle it (test, build, SonarCube etc.)

You might be wondering, what about the post-commit hooks? My recommendation is to (normally) let the CI handle that, eg. e2e tests, tests and build, if you are not short on CI resources.

Git hooks should always be fast and they make the Git versioning cleaner by cleaning up before the commits are happening and are building automatic enforcement of good code practices into the workflow.

Concluding remarks

In this post, we saw how Git hooks can ease the development process as you are automatically running trivial commit-msg, pre-commit and post-merge scripts. Also, I gave a recommendation on how to set up Git hooks for Angular apps that I have found very beneficial myself.

Thanks for reading. Remember to follow me on Twitter and leave a comment.

Do you want to become an Angular architect? Check out Angular Architect Accelerator.

Related Posts and Comments

How to Set up a CI pipeline with Azure Pipelines and Nx

It goes without saying that having a CI pipeline for your Angular apps is a must. Setting one up for regular Angular apps is fairly straightforward but when you have an Nx monorepo there are certain other challenges that you have to overcome to successfully orchestrate a “build once, deploy many” pipeline. This post will

Read More »

How to Set Up Git Hooks in an Nx Repo

Git hooks can be used to automate tasks in your development workflow. The earlier a bug is discovered, the cheaper it is to fix (and the less impact it has). Therefore it can be helpful to run tasks such as linting, formatting, and tests when you are e.g. committing and pushing your code, so any

Read More »