A githooks example, and how to share it with the team
Tags: git, githooks, ci, linux
Published: 2021-03-14 17:56:32

## Introduction

Regarding githooks, I had 3 different goals:

1. :scroll: Verify that my commits respect the expected format (hook n°1)
2. :wrench: Check unit tests are green before every `git push` (hook n°2)
3. :busts_in_silhouette: Share these hooks with my team

## A bit of context

Just for the record, my project was made with Symfony/PHP, and launched via docker/docker-compose.

But since the main goal here is to use githooks, you can adapt following lines for your own context and needs :smiley:  

:information_source: **Note:** *Once a `git init` is made on your project folder, you can find githooks examples in `./.git/hooks` in folder as `.sample` files.*

## Let's do it

### Step 1 - :scroll: Verify commit-message format

With my team, we decided to use specific commit formats in order to facilitate the way to consult our git history.

Here are the formats:

```bash
## Example of a commit linked to a specific ticket
[PSBO-723] An amazing commit message

## Example of a commit linked to a hotfix
[hotfix] A hot message for a hot commit
```

First, I need to create a regex that matches only these formats:
```
^\[(hotfix|\w+\-[0-9]+)\]( \w+)+$
```

Then I have to create the githooks that will check the commit message format every time I will try to commit.

To do it I create the hook file as `.git/hooks/commit-msg` in my project folder:

```bash
#!/usr/bin/env bash

echo "Checking commit-message format..."

## the first arg is the path to the git commit temporary file
TEMPORARY_FILE_PATH=$1

## get commit-message from the temporary file
COMMIT_MSG=`head -n1 $TEMPORARY_FILE_PATH`

## init regex to match commit-message format
REGEX="^\[(hotfix|\w+\-[0-9]+)\]( \w+)+$"

## checking commit-message format
if ! [[ $COMMIT_MSG =~ $REGEX ]];then
    echo -e "Your commit-message format is not valid:\n$COMMIT_MSG\n"
    echo "Valid format examples:"
    echo "[PSBO-123] My commit message"
    echo "[hotfix] My commit message"
    exit 1
else
    echo "Well done! Your commit-message is valid."
    exit 0
fi

```

Once done, don't forget to give to the file the right to be executed.
```bash
sudo chmod +x .git/hooks/commit-msg
```
#### Let's test the hook

Now, if I try to commit with an invalid message:
```sh
git commit -m "Yehaaaa"
```
It returns:
```
Checking commit-message format...
Your commit-message format is not valid:
Yehaaaa

Valid format examples:
[PSBO-123] My commit message
[hotfix] My commit message
```
And the commit is canceled, as expected! :tada: 

If I try to commit with a valid message:
```sh
git commit -m "[PSBO-456] It's a valid one this time"
```

It returns:
```
Checking commit-message format...
Well done! Your commit-message is valid.
```
And the commit is done! :heavy_check_mark: 

### Step 2 - :wrench: Launch unit tests before each `git push`

Let's do it again!
Since I want to check unit tests **before** pushing my code, I will create a hook file as `.git/hooks/pre-push`:

```bash
#!/usr/bin/env bash

echo "Checking unit-tests..."

## checking unit-tests
cd ./infra || exit
if sudo docker-compose run php bin/phpunit;then
    echo "Well done, unit-tests are green!"
    echo "'git push' is now done."
    exit 0
else
    echo "Unit-tests failed, therefore the push is canceled."
    echo "Please fix them before pushing again."
    exit 1
fi

```

Once again, I need to give this hook file the right to be executed.
```bash
sudo chmod +x .git/hooks/pre-push
```

So, let's check if it works in case my unit tests are failing.

First, I changed the code of a unit test so that it fails.
Now I try to git push to trigger the hook:
```sh
git add .
git commit -m "[hotfix] Breaking unit-tests"
git push origin develop
```
It returns
```sh
Checking unit-tests...
Creating infra_php_run ... done

Testing Project Test Suite
F                                                                   1 / 1 (100%)

There was 1 failure:

1) App\Tests\Util\CalculatorTest::testCalculate
Failed asserting that 15 is identical to 16.

/var/www/tests/Util/CalculatorTest.php:31

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
ERROR: 1
Unit-tests failed, therefore the push is canceled.
Please fix them before pushing again.
```

Tests failed, so the push is canceled, seems good! :+1: 

OK, this time I fixed the code of the unit test.
Then I push to trigger the hook once again:
```sh
git add .
git commit -m "[hotfix] Fixing unit-tests"
git push origin develop
```
It returns
```sh
Checking unit-tests...
Creating infra_php_run ... done

Testing Project Test Suite
.                                                                   1 / 1 (100%)

OK (1 test, 1 assertion)
Well done, unit-tests are green!
'git push' is now done.
```

Unit-tests are green, so the push was validated and done! This hook works as planned! :dancer:

### Step 3 - :busts_in_silhouette: Share githooks with the team

Now, my hooks are ready! 

But unfortunately I can't share them with the team.

Why that?
Because githooks are ignored by git (as every other file in `.git` folder), therefore they can't be added to the index or be commited in anyway...

Unless we do some little magic! :tophat: :rabbit:

:information_source: **Note:** *There are multiple solutions to solve this issue, I picked this one because I found it smart and easy to set up.*

So to do the magic, I'm going to create my own githooks folder in the root path of the project (not in the `.git` folder):

```sh
## I create my own githooks folder
mkdir .githooks

## Then, move the githooks I created in this new folder
mv .git/hooks/commit-msg .githooks/
mv .git/hooks/pre-push .githooks/

## Don't forget to give "execute" right to the files, if not already done
sudo chmod +x .githooks/*
```

Now, I just need to tell git where to find my githooks files:
```sh
## Specify a folder for hooks (only available for git version >= 2.9)
git config core.hooksPath .githooks
```

And it's done!

From now one, I can commit these hooks since there are no longer ignored by git, so my teammates will be able to use them.

## Conclusion
My githooks work, and I can share them with my team, it's all good! :tada: :boom:

Hope this helped you find out how githooks work and how powerful they can be.