DEV Community

Cover image for Who pushed failing tests again? Git Hooks with Husky 🐢
Leonardo Montini for This is Learning

Posted on • Originally published at leonardomontini.dev

Who pushed failing tests again? Git Hooks with Husky 🐢

Three things you do NOT want in your repository:

  1. Unformatted code
  2. Warnings/errors
  3. Failing tests

Is there an easy way to prevent these from happening? Sure, just make sure to format your code, run linters and launch your test suite right before pushing the code. And also, please tell your team to do the same.

What could go wrong? Everything :D

If a solution requires that people having to remember about doing something, you can be sure that it will not work.

Git Hooks

A possible solution to automate this process is with Git Hooks. They are basically scripts that run automatically every time a specific event occurs in a Git repository. For example, you can run a script before git commit (format and lint) or before git push (tests).

You can also make the action fail (commit or push, in this case) if the related hook fails.

This already sounds great but there is a problem: Git Hooks are not part of the repository. They are stored locally in the .git folder, which is not versioned. This means that every time you or a colleague clone a repository, they have to set up the hooks again.

Looks like we're still falling into the same trap, people have to remember to do something manually. Not gonna work.

Husky 🐢

Fear not! You can kindly ask a Husky to help you with that.

Husky

I mean, not the dog, but a really cool node package called Husky.

What husky does is mainly two things:

  1. You can define git hooks in your repository and keep them versioned, so that everyone will have the code when cloning the repo.
  2. It will automatically set up the hooks for you when you run npm install (or yarn), which means you can be faril sure that everyone will have them.

How cool is that? This actually covers the two main issues we had before!

How to use it

I recorded a video where I go step by step through the process of setting up Husky in a repository, with two hooks:

  • pre-commit: runs prettier and eslint on the staged files
  • pre-push: runs the test suite

No time to watch the video? You can add it on "Watch Later" on YouTube, there's some value to get from there.

In any case, here's a quick summary:

Prerequisites

In this example I will use prettier and eslint to format and lint the code. I will also use lint-staged to run the linters only on the staged files. If you don't have them yet, follow these simple steps:

npm install -D prettier lint-staged
Enter fullscreen mode Exit fullscreen mode

For eslint you can indeed manually install it as a dev dependency and paste a config file, or you can run this command and follow the instructions:

npm init @eslint/config
Enter fullscreen mode Exit fullscreen mode

This will run a simple CLI wizard that will end up generating a .eslintrc file for you and install the required dependencies.

Additionally, make sure to configure lint-staged to define what commands have to be executed on what files. An example:

{
  "./src/**/*.{js,ts}": ["prettier --write", "eslint --max-warnings 0"]
}
Enter fullscreen mode Exit fullscreen mode

This config runs prettier and eslint with the given arguments on all the js and ts files in the src folder.

Husky Setup

You can follow the official docs to set up Husky, but it's literally as easy as running this command:

npx husky-init && npm install
Enter fullscreen mode Exit fullscreen mode

or

yarn dlx husky-init --yarn2 && yarn
Enter fullscreen mode Exit fullscreen mode

This command will do everything for you, in partcular it will add on your package.json a prepare script that is what makes sure everyone has hooks installed after running npm install or yarn.

It will also create an example pre-commit hook in the .husky folder, which you can edit to add your own hooks.

Pre-commit hook

Husky already generated it for us, but it contains npm test as an example.

In our case we want to replace it with lint-staged. You can do it via the CLI but it's also as easy as opening the file in .husky/pre-commit and replacing the last line with npx lint-staged.

Make sure to run again npm install or yarn to install the hook and you're done!

Pre-push hook

This one is not generated by Husky, so we have to create it manually.

You can either manually create a file called .husky/pre-push or run this command:

npx husky add .husky/pre-push "npm test"
Enter fullscreen mode Exit fullscreen mode

This will create the file and add the npm test command to it. Once again, run npm install or yarn so that the hook is properly set in your repository.

Bypass Husky

Having too strict rules sometimes can be frustrating, especially when you're in a hurry and you just want to push your code and go home.

Husky allows you to bypass the hooks by adding the --no-verify flag to the command. For example:

git commit -m "My commit message" --no-verify
Enter fullscreen mode Exit fullscreen mode

This will skip the hooks and allow you to commit your code.

However, now all commands support --no-verify, including git push.

In this case, you can manually disable husky for that run by setting the HUSKY environment variable to 0:

HUSKY=0 git push
Enter fullscreen mode Exit fullscreen mode

This will let you run the command without the hooks. If you run git push without the HUSKY variable, it will trigger the hooks as usual.

Now that you know how to bypass hooks, please, please, please, don't do it unless strictly necessary.

Conclusion

If you cannot, or just don't want to, get the job done by pipelines, I highly recommend you to use this tool! It’s so simple to set up and it just works for everyone.

And that’s it! With husky you can make sure everyone in your team has all the required git hooks always set! Today we showcased only pre-commit and pre-push, but husky supports all available git hooks.

You can learn more about husky in the official docs and you should definitely leave a star to the GitHub repository.


Husky - Photo by Nataliia Kvitovska on Unsplash


Thanks for reading this article, I hope you found it interesting!

I recently launched my Discord server to talk about Open Source and Web Development, feel free to join: https://discord.gg/bqwyEa6We6

Do you like my content? You might consider subscribing to my YouTube channel! It means a lot to me ❀️
You can find it here:
YouTube

Feel free to follow me to get notified when new articles are out ;)

Top comments (2)

Collapse
 
theaccordance profile image
Joe Mainwaring

Excellent write up about Husky and how to leverage git hooks! My teams follow this implementation on projects with the exception of the stricter steps like linting & test validation.

Instead, we leverage a slightly different strategy:

  • No code is directly committed to our master/main branch on a project
  • Developers work in feature branches and have their code accepted through pull requests
  • Pull requests must pass CI/CD steps around linting and test validation before they can be merged.

We found this more flexible for our use case and ensures that all pushed commits still have prettier's formatting applied. Devs can push WIP code as a "save state" to their feature branches without always having to have their solutions fully complete.

Collapse
 
balastrong profile image
Leonardo Montini

Hey Joe, thanks for the comment!

Yes, going with CI pipelines is indeed a valid option, I also mentioned it in the conclusion, but if you cannot set them up (cost, private repos, internal policies) then git hooks might help :)