Taming the husky

Easy and shareable Git Hooks for JS projects

David Barral
Trabe
3 min readJan 21, 2019

--

Photo by Hybrid on Unsplash

Husky is a JS library that eases the handling of client side Git hooks in JS projects. A Git hook is a script that Git executes before or after some event (a commit, a push, etc.). You can create side-effects within a hook or check some condition to abort the current Git operation. We use a pre-push hook in our projects to run sanity checks before pushing a branch, to avoid broken builds in our Continuous Integration.

To setup a hook you add the corresponding script inside .git/hooks. There are some *.sample scripts in the folder to get you started.

Hooks have a problem though: the .git directory is not versioned. It’s not easy to share custom hooks.

Enter the husky

There are several approaches to overcome the shareable hooks problem. One is using a tool like Husky.

After installing husky with npm install husky --save-dev all your Git hooks will be setup to invoke the Husky binary. Then, you define your hooks in the project’s package.json (or in a .huskyrc.js file).

Our package.json looks like this (redacted for the sake of clarity):

Each time we push, we check the package by running the test suite and linting the code.

This approach has some advantages:

  • Shareable and versioned hooks.
  • Hooks are defined with npm scripts like any other task in the project.
  • Hooks are ready after cloning and installing the dependencies. There’s no extra step.
  • No need to know the internals of .git/hooks.

Taming the husky

Sometimes we don’t want our hooks to run. I know. They are meant to be run everytime we do a push but life it’s hard and, sometimes, we just don’t want to.

To handle those cases we take advantage of two env vars: HUSKY_GIT_PARAMS and HUSKY_GIT_STDIN. Hooks receive parameters via command line and stdin and they are stored in those env vars by Husky. Each hook receives different data (take a look at the Git docs to learn more).

In our case we define a custom script that runs before the pre-push hook. If the script has a 0 exit code we skip the hook.

The skip-husky script looks like this:

Let’s break it into pieces:

  • We can skip the hook by setting the NO_HUSKY env var. Useful if we don’t want to wait for the push. Handy for projects with heavy checks. The CI will handle the possible failure. We also use it when we are sure that we won’t break the build (we updated the docs, fixed some typos, etc).
  • We skip the hook when pushing to an alternate origin. In our case it’s usually one of our customers repos with their own CI environment. Only a few can push there and we only push if our CI says that we can do it. Due to network security we cannot automate the sync.
  • We definitely don’t want to check the code when deleting remote branches. That would be silly.

Summing up

A husky dog owner told me once: “In the list of the 100 most trainable dog breeds, the huskies stand at the 98th position from the top”. It was a made up number but it really stressed her point. Luckily for us we are talking computers here and not real dogs.

Git hooks are quite useful but it’s a shame that they aren’t shareable. Libraries like husky allow us to overcome this. Husky is easy to setup and customize. We like it and use it in our projects. If you haven’t already, you should try it.

--

--

David Barral
Trabe

Co-founder @Trabe. Developer drowning in a sea of pointless code.