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
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
directoryYou 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 !
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:
hooks
directory at the root of the project, put some scripts inside, and commit them..git/hooks
directorydocker-compose.yml.
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 executablecd /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 directorySo 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:
githook_installer
container in your main project.ls, xargs, find, ln...
.git
directory into the container’s /tmp.git
directory./hooks
directory into the container’s /tmp/hooks
directorydocker-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.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 !