Skip to content

Automatic updates with post merge git hook

April 03, 2019Reynald Mandel2 min read

Have you ever looked for a bug that happens only locally ? That was due to a missing DB migration after a git pull ? Or a failing webpack compilation due to an inexisting node module ? Well, I have good news for you ;)

Install git hooks!

As specified in this article, git hooks are powerful tools. They trigger automated tasks each time you do git operations. You can run a linter before each commit, automatically add certain git tags before each push, run your migrations after a git pull, etc.

Unfortunately, they stand in a never-commited .git folder. To share them with your team members, install Husky with npm install husky --save-dev. Now you hooks should stand in the commited package.json file, so that you all benefit from the same checks:

"scripts": {
    "precommit": "./scripts/",
    "postmerge": "./scripts/",

What do those two lines mean?

  • For the first one, it's simple as the git hook is quite explicit: each time you run git commit, it runs the script. As the pre-commit is launched before, if the script fails, it prevents you from commiting. And that's the main purpose actually :p.
  • For the second one, it is triggered after a merge operation: of course after a git merge, but also after a git pull. Indeed, under the hood git pullis the combination of git fetch and git merge. Even a fast-forward merge triggers the post-merge hook.

What should I trigger after a merge ?

On my project, we trigger those 3 operations:

  • Update the vendors when the lock file changed (npm install on package-lock.json change and composer install on composer.lock change)
  • Run the newly received database migrations. This way, our local DB scheme is always up-to-date! We no longer loose 30 minutes to investigate on local bugs to finally see this SQL error "column [...] does not exist"
  • Update our API Manager with the new API swagger file (describing the available routes/methods/params of our API, cf. Swagger specs)

But feel free to improve it with your project needs! It works for anything requiring an update of your local dev environment.

The final code snippet

So here is my file:

#!/usr/bin/env bash
# This gist is correct (works with fast-forward merges)
# Even if this one is more readable

function changed {
    git diff --name-only HEAD@{2} HEAD | grep "^$1" > /dev/null 2>&1

if changed 'app/config/parameters.yml.dist' || changed 'composer.lock'; then
    echo -ne '\n\e[31mWARNING:\e[m \e[33mThe composer.lock and/or the parameters.yml.dist file changed, composer install needs to be executed.\e[m\n\n'
    composer install # or even better: make composer-install

if changed 'app/DoctrineMigrations'; then
    echo -ne '\n\e[31mWARNING:\e[m \e[33mThere are new migrations to be executed.\e[m\n\n'
    bin/console doctrine:migrations:migrate -n # or even better: make doctrine-migrate

if changed 'devops/swagger.json'; then
    echo -ne '\n\e[31mWARNING:\e[m \e[33mThe swagger changed, the API Manager needs to be updated.\e[m\n\n'
    python devops/scripts/ --env local # or even better: make apimanager-update

exit 0

As you can see, I wrote the entire commands directly inside. But as we have lots of them to remember with tricky options on my project, we grouped them in a Makefile. This way, the file is more readable and if the command changes, you only have one place to update it! Read this article describes many other advantages to use a makefile. Enjoy ^^