DEV Community

Cover image for Never Forget to Remember with Githooks and Husky
Michael Steichen for Focused Labs

Posted on • Originally published at focusedlabs.io

Never Forget to Remember with Githooks and Husky

Don’t forget to run the tests before you merge into main! Remember to run ESLint before pushing! Please include the ticket number in your commit message so {APP_INTEGRATION} can do its thing.

Agreeing to common conventions and processes frees up bandwidth for engineers to focus on tasks that are interesting, meaningful, and add value for our clients. Onboarding new engineers to a project takes time and energy and can be inconsistent depending on how often documentation is updated. Most importantly: executing manual tasks is a computer’s job!

With Husky + githooks, developers can automate boring day-to-day housekeeping tasks, easily share them with colleagues and choose whether to make them required or “opt-in” for all contributors. Moreover, we can document and enforce our repository’s standards with code rather than an SOP or checklist that quickly goes out of date. Below is one approach you could adopt on your own projects!

Installation and Setup

First we install husky as a dev dependency.

npm install husky --save-dev
Enter fullscreen mode Exit fullscreen mode

Now to finish setting up husky run:

husky install
Enter fullscreen mode Exit fullscreen mode

At this point you should see a .husky/ directory in your project. 🎉

[🐶 One thing I love about Husky’s latest version is that all of its magic is contained in that one, conspicuous directory.]

Does it work?

Let’s test our install and setup by adding a post-commit hook that logs an important affirmation to our fellow contributors. To do that we can use the husky add command:

npx husky add .husky/post-commit "echo You. Are. Enough."

Now we can take comfort in these wise words after we commit. 🧘

Run Tests

Affirmations are nice and all, but it would be much more practical to do something like run unit tests before each commit so I don’t break the build for my team over lunch. To do that, we’d pull out the husky add command again:

npx husky add .husky/pre-commit "npm run test"

If I attempt to commit code that breaks tests, the commit will abort and log failures to the console.

Linting

We can also lint and fix before we commit. If I have an existing script that does linting for me, i.e.,:

"scripts": {
        ...
    "lint-fix": "eslint ./ --ignore-path .gitignore --fix",
  },
Enter fullscreen mode Exit fullscreen mode

then I can run npx husky add .husky/pre-commit "npm run lint-fix". No more wrangling with built-in IDE/editor settings and extensions!

Other applications

Rinse. Wash. Repeat! At this point you’re all set to figure out which hooks and commands make the most sense on your project.

FAQ + Gotchas

Why do I get following error.git can't be found when trying to run husky install ?

Husky assumes that your package.json and .git directory are at the same level. This might not be the case in, say, a mono repo with distinct frontend/ backend/ packages. Husky supports this by means of a custom-directory. If I only really want to run use githooks to manage my frontend code, I can add this npm script to the scripts object in package.json:

"scripts": {
        ...
    "onetime-setup-husky": "cd .. && husky install frontend/.husky"
  },
Enter fullscreen mode Exit fullscreen mode

Now when we run npm run onetime-setup-husky we will first navigate to the directory that contains .git and then install the .husky directory in the frontend/ directory. This only needs to be run once!

I doubt I’m going to get folks on my team to set all this up and run all these commands in the right order. Is there a way to make this easier?

Yes! One approach would be to create a script that does, well, everything:

"scripts": {
        ...
    "onetime-setup-all-the-hooks-and-all-the-things": "cd .. && husky install frontend/.husky && npx husky add frontend/.husky/pre-commit \"npm run test\" && npx husky add frontend/.husky/post-commit \"echo you are doing great\""
  },
Enter fullscreen mode Exit fullscreen mode

Now all you’d have to do is ask your colleague to run npm run onetime-setup-all-the-hooks-and-all-the-things and they would be in business. I’d advise you rename it to something a little more reasonable, but this gets the point across 🙂.

Can I make all of this mandatory for all contributors? How do I make sure everyone else sets it up properly?

To make setup as painless and automatic as possible, ensure your .husky/ directory is tracked in your repository and then rename the onetime-setup* script to prepare in your package.json scripts, i.e.,

"scripts":{
    "prepare": "cd .. && husky install frontend/.husky"
  },
Enter fullscreen mode Exit fullscreen mode

The prepare script will be executed auto-magically after npm install executes. Check out the npm docs to learn more about it. And now that the .husky directory is tracked by git, you and your colleagues will be running the exact same scripts across machines so no need to inline them and pollute your package.json

I want to add husky + git hooks for myself, but my team isn’t totally convinced— can we meet in the middle?

Understandable. Nobody likes to be micro-managed and admittedly some tasks are less important than others. One dev might prefer to use their IDE’s built-in prettier/ESLint tooling. Another dev might elect to run unit tests constantly using Their Favorite Tool, in which case a pre-commit hook doing the same would be frustrating and redundant. That’s okay!

For one, be sure and add .husky/ to your .gitignore so it remains untracked and out of your colleagues’ hair.

To “opt-out” of these hooks, do nothing! If you don’t ever run npm run onetime-setup* script then you are all set. If you do and later regret it: delete the .husky/ directory.

Why didn’t you use husky’s built in super quick auto setup?
There is indeed an even quicker way to set up Husky. For the sake of learning, however, I thought it’d be more illustrative to setup husky the slightly longer way. Additionally, the above approach above works in the case of mono-repos where .git and package.json are located in different directories.

Why do you need Husky for this if githooks work out of the box?

You do not need Husky to leverage githooks. But I have found Husky quite useful for all of the reasons above: shareability, opt-in, enforcing code standards with code etc.

Top comments (0)