Keeping your repository clean with git hooks

Different developers have different styles, which can clash in a collaborate development environment. A growing trend amongst developers is to leverage their version control systems to block actions from affecting team members unless their code is valid, with respect to the codebase.

Git comes with a series of git hooks. Git hooks are events that can be configured to fire off scripts when the event occurs. This would allow you to setup your repository to make sure that you can only contribute code that is formatted valid. We will be using Husky to make configuring these hooks easier.

I will be demonstrating the configuration of this repository.

Husky

We will be installing Husky, a tool that makes configuring Git hooks easier. We will configure all our hooks to run through Husky, and we will store all of our tools locally so that anyone running the codebase can be ensured to use the same exact tooling, rather than relying on global installs of these tools.

~/Work/node-githooks-demo > master > npm install husky@next --save-dev

Prettier

The first tool we will be integrating into our workflow is Prettier. Prettier is a tool that will take your code, read it, interpret it, and output a formatted version of the code.

We must first install Prettier to our project.

~/Work/node-githooks-demo > master > npm install --save-dev --save-exact prettier

We will also install the pretty-quick module, which will allow us to easily select all our staged files and run prettier on them.

~/Work/node-githooks-demo > master > npm install --save-dev pretty-quick

To test this out, we're going to simply run pretty-quick on our ugly.js file:

let test = ["foo"
,"bar","bizz","buzz"];

console.log("this", "is", "a", "test", test);







We will first stage ugly.js, and then run npx pretty-quick, to trigger pretty-quick to run on our files. The resuly makes ugly.js formatted like so:

let test = ["foo", "bar", "bizz", "buzz"];

console.log("this", "is", "a", "test", test);

We can now register this with Husky in our package.json file, by adding a section for Husky (see bottom of file):

{
  "name": "githooks-demo",
  "version": "1.0.0",
  "description": " An example of using git hooks to keep a repository clean",
  "main": "ugly.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/philbarresi/node-githooks-demo.git"
  },
  "author": "Phil Barresi",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/philbarresi/node-githooks-demo/issues"
  },
  "homepage": "https://github.com/philbarresi/node-githooks-demo#readme",
  "devDependencies": {
    "husky": "^0.14.3",
    "prettier": "^1.10.2",
    "pretty-quick": "^1.4.1"
  },
  "husky": {
    "hooks": {
      "pre-commit": "pretty-quick --staged"
    }
  }
}

If we set our ugly.js to be its original content, and then stage and commit our code, we will find that it is automatically rewritten:

 ~/Work/node-githooks-demo > master > git add .
 ✘  ~/Work/node-githooks-demo > master > cat ugly.js
let test = ["foo"
,"bar","bizz","buzz"];

console.log("this", "is", "a", "test", test);







 ~/Work/node-githooks-demo > master > git add ugly.js
 ~/Work/node-githooks-demo > master > git commit -m "Demonstrating ugly.js"
husky > pre-commit (node v9.2.0)
🔍  Finding changed files since git revision adad22a.
🎯  Found 1 changed file.
✍️  Fixing up ugly.js.
✅  Everything is awesome!
[master e19b580] Demonstrating ugly.js
 1 file changed, 3 insertions(+)
 create mode 100644 ugly.js
 ~/Work/node-githooks-demo > master > git status
On branch master
nothing to commit, working tree clean
 ~/Work/node-githooks-demo > master > cat ugly.js
let test = ["foo", "bar", "bizz", "buzz"];

console.log("this", "is", "a", "test", test);

We're now rewritting the code to be formatted the same way, across the entirety of the team. You can choose to add prettier options, as well.

Running eslint pre-push

ESLint is an open source linting tool that allows you to detect stylistic errors in your project. We can configure ESLint to run before each push by registering another hook. It is entirely possible to run this pre-commit, however I personally prefer making commits often and don't always want to spend time refactoring just to save my progress when I get to a good stopping point with my changes.

Much like the other tools, ESLint can be installed through npm.

~/Work/node-githooks-demo > master > npm install eslint --save-dev

We can configure it from the command line, as well:

~/Work/node-githooks-demo > master > npx eslint --init

Which will take you through an interactive tutorial for adding style rules.

At this point, to register ESlint to run before we push, we simply go back to our package.json file and register eslint to run pre-push! Our hooks will now look like:

{
    "hooks": {
      "pre-commit": "pretty-quick --staged",
      "pre-push": "eslint ."
    }
}

Now, we can have ESLint check for issues!


 ~/Work/node-githooks-demo > master > git status
On branch master
nothing to commit, working tree clean
 ~/Work/node-githooks-demo > master > cat ugly.js
var test = ["foo", "bar", "bizz", "buzz"];

console.log("this", "is", "a", "test", test);
 ~/Work/node-githooks-demo > master > git push origin master
husky > pre-push (node v9.2.0)

/Users/xueye/Work/node-githooks-demo/ugly.js
  1:1   error    Unexpected var, use let or const instead  no-var

✖ 10 problems (9 errors, 1 warning)
  9 errors, 0 warnings potentially fixable with the `--fix` option.

husky > pre-push hook failed (add --no-verify to bypass)
error: failed to push some refs to 'https://github.com/philbarresi/node-githooks-demo.git'

We can now clean up our code, commit it (and have it automatically prettified), and then ensure that our next push is in line with our repository standards.

 ~/Work/node-githooks-demo > master > cat ugly.js
const test = ["foo", "bar", "bizz", "buzz"];

console.log("this", "is", "a", "test", test);
 ~/Work/node-githooks-demo > master > git push origin master
Phil Barresi

Phil Barresi

Love this programming thing, may want to do it as a career. Technical Lead, Senior Developer at Troy Web Consulting; Adjunct Instructor of Web Development classes at Stevens Institute of Technology.

Read More