Installing git hooks in a dockerized environment

Written by aherve | Published 2017/05/31
Tech Story Tags: docker | git | devops | programming

TLDRvia the TL;DR App

Git hooks can be very convenient to keep your commit log clean. Here’s a way to install and share git hooks across your team when you are working in a dockerized environment

TL;DR: a demo project on github is available

1. Git hooks in a nutshell

Git provides a way to run commands before you do something. A common usage would be to run your linters before you try to commit something, for instance. If the linter detects any errors, then you are asked to fix it before you actually commit your work. This would typically clean your git log from commits such as “fixed lint”.

If you are new to this concept, you should at least know that:

  • git hooks are script that are automatically found by git if present under the .git/hooks directory
  • a git hook is run locally by your computer
  • When a git hook fails, then your git command is not run.

You might want to take a look at git samples that are already present in any git project:

What you get after any `git init`

Simply copy pre-commit.sample to pre-commit, make it executable, and here you are, you just installed your first working git hook !

2. Using githooks as a team

Now you probably know that if you put some scripts in your .git/hooks directory, then your teammates won’t be able to take advantage of it. You’re basically the only one to know about your script, and you can’t guarantee than any commit of your team passes the githook test you just wrote.

Tools such as husky can help you to automatically install githooks when working directly with npm/yarn. But this is 2017 and we want to work with docker, don’t we ?

Here’s what we are going to do:

  • Create a hooks directory at the root of the project, put some scripts inside, and commit them.
  • Create a docker container whose purpose is to create a symlink of the official hook script into your local .git/hooks directory
  • Declare this container in your main docker-compose.yml.
  • When anyone run docker-compose up, the hook container will ensure that the official hooks are properly installed in your configuration.

Now let’s do this.

First, we create a docker container, defined by some Dockerfile.githook:

The idea behind this is quite simple. It basically creates a container that will:

  • cd /tmp/hooks && ls | xargs chmod +x : go to some /tmp/hooks (explanations about this particular directory will come further), find scripts, and make them executable
  • cd /tmp/.git/hooks && find ../../hooks -type f -exec ln -sf {} /tmp/.git/hooks/\; Now go to some /tmp/.git/hooks dir, find the executables, and create symlinks to this directory

So this container is basically creating symlinks from its own /tmp/hooks to its own /tmp/.git/hooks. The last thing we need is to share our own .git/hooks and hooks directories with the container. This way, when the container creates its own symlinks, then it will actually be doing this on our computer. Which is quite exactly what we are looking for.

This can be achieved by using volumes in your docker-compose.yml file:

This will:

  • declare a githook_installer container in your main project.
  • use busybox: a tiny image that know about basic unix commands such as ls, xargs, find, ln...
  • Mount your local .git directory into the container’s /tmp.git directory
  • Mount your local ./hooks directory into the container’s /tmp/hooks directory
  • boot everytime you run docker-compose up. Note that this will actually create the symlinks every single time you run your project. But creating a bunch of symlinks that already exist is absolutely not an issue; you won’t even notice it.

3. Now let’s have a demo, shall we ?

I’ve created a demo on github for those who are interested by the result

Let’s create a lunatic pre-commit script in the ./hooks directory:

Now run docker-compose up, as anyone would do when working with docker.

And try to commit something. You’ll either get:

pre-commit hook startingOkay, I will accept your commitOn branch masternothing to commit, working directory clean

Or

pre-commit hook startingMeh. Maybe another time

Not that if your commit is rejected, then the git log remains untouched.

Perhaps you’ll want to write a more useful pre-hook now. docker-compose run linter could surely be a good idea, provided you created some linter container in your configuration !


Published by HackerNoon on 2017/05/31