Saving time with git hooks

Git hooks provide a way to trigger actions on different points in a git workflow. This is done by placing executable scripts in the $YOUR_PROJECT/.git/hooks directory and naming them according to the hooks they should trigger on. Git hooks are extremely useful to solve small problems that can cause quite some pain. So far, we’ve found three use cases for them here at Yoast.

PHP 5.2 autoloading madness

One of the characteristics of our PHP 5.2 compatible Composer setup, is that autoloading is done through a classmap that is generated by composer. A side effect of this is that you will get autoloading errors every time you switch to a branch that includes new classes. The reaction to such errors is usually threefold; you cry a little, you scratch your head and then you composer dump-autoload to regenerate the classmap to include the new classes. To prevent this annoyance from recurring we simply added that command to a post-checkout git hook. This makes sure the autoloading classmap is now regenerated automatically every time a new branch is checked out and the autoloading errors are history.

Codestyle nonsense

At Yoast we have a history of being strict on code style. However boring, I must admit a universal code style benefits code readability and collaboration. Still, there are few things I find less annoying than code style discussions. Code review should be focused around logic and architecture, it should not get lost into minor details like spaces and brackets. Fortunately, we’ve automated code style checks by using tools like phpcs, jshint and jslint. They run automatically on Travis CI after code has been pushed to one of our GitHub repositories. However, this is still not ideal because nothing actually prevents us from committing code to GitHub that contains code style errors and we end up doing just that a little too often.

A pre-commit hook that checks for code style errors solves this problem. Here’s the script:

if ! grunt check

echo "Error: Your commit was canceled due to a failed Grunt check. Please make sure all checks pass..."
say "Commit canceled. The command 'grunt check' failed."
exit 1

This script will check for code style violations every time I try to do a commit. If my code fails the check, the commit simply doesn’t succeed. Thereby I’m forced to make sure the code style is valid before I can even commit to GitHub. For audio feedback I am using say, the text-to-speech engine on OSX. This way I am notified even if I am not paying attention to the screen and didn’t see the failure. If you are on Windows or Linux, you could use espeak instead.

Unminified embarrassment

On November 19th, we released patch 3.0.2 for Yoast SEO which was supposed to fix most of the major open issues users were still experiencing after the big 3.0 release earlier that week. However, one of the last Pull Requests that got merged into the release contained unminified JavaScript, which caused the patch to actually break the snippet preview functionality for all users that updated to 3.0.2.

Even though the issue was discovered and patch 3.0.3 released within two hours, this issue gave us quite a headache. The checks and balances we had in place to prevent this sort of disaster from ever happening still appeared to leave a little too much space for human error. Again, the solution would be to extend the pre-commit hook with code that makes sure the assets are always minified before changes are even committed.

if ! grunt build

echo "Error: Your commit was canceled due to a failed Grunt build. Please make sure your code can be built..."
say "Commit canceled. The command 'grunt build' failed."
exit -1
git add **/*.min.js **/*.min.css

The code will simply always be built before commit. If the build step fails, the commit fails too and if minified files are changed in the process, they’ll simply be added to the commit. Thereby the hook actually prevents me from committing unminified assets ever again.

Git hooks as part of CI?

Git hooks can help us save time, fail earlier and focus more on things that really need our attention. They might figure as an extended part of my CI pipeline. The main thing that stops me from doing that, is that I currently have no way to generalize the experience for all developers on a repo without asking them to insert a custom script into their $YOUR_PROJECT/.git/hooks directory. If you have any ideas on how that could be automated or if you have experience with using git hooks yourself, please drop us a line in the comments below. We’d love to hear your ideas!

Coming up next!