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:
- Prettier formatting of all supported files
- TSLint linting of Typescript
- 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.
9 thoughts on “Using Git hooks for easier development (2020)”
I’ve tried all sorts of tools, but at the end, I wrote something that you may find interesting. https://pypi.org/project/hooks4git
It is suposed to be language agnostic, easy to configure, simple to understand, and flexible.
Pingback: Angular Automation: 6 Things That Should Be Automated in Enterprises – Christian Lüdemann IT
A very useful article as always, Christian. Thanks!
One thing I want to mention (in case anyone follows my steps): In Prettier configuration, the
"parser": "typescript"
line should be deleted. If you don’t, Prettier tries to parse non-typescript files (e.g. package.json) as TypeScript and raises an error. If you delete that line, Prettier respects the file extension and parse accordingly (e.g. Javascript for js files, TypeScript for ts files, etc.).Good point! I will remove that.
Good point, thanks. Will fix that.
That is actually in another blog post but thanks! Just updated it.
Pingback: The Ten Commandments of Angular Development – Christian Lüdemann
“post-merge”: “post-npm-install”
npm-post-install?
Pingback: The Most Common Cypress Mistakes – Christian Lüdemann