Being nice to colleagues with git pre-commit hooks

For my current contract I'm doing a huge amount of testing on a system that is very fun to hack on. I've also been given a lot of rope leeway in how I test. Thus, I use Test::Most quite heavily, but I've a tiny problem:

use Test::Most 'die';

That should halt the test file at first failure, a feature I use quite a bit when developing to ensure that test failures don't scroll past when I'm actively hacking on code. However, when I added subtests to Test::Builder a few years ago, I made sure that a fatal failure in a subtest would cause the subtest to fail, but not the entire test program. Well, darn. That means use Test::Most 'die' doesn't quite do what I want it to do in this case.

Being pragmatic, I did the simplest thing which can possibly work. I used the testing equivalent of weapons of mass destruction:

use Test::Most 'bail';

Thus, any test failure will cause the test suite to bail out, immediately, no questions asked! I don't mind this behavior at all when I'm hacking on a single test program, but I don't want to commit that, so I must remember to remove that before I commit.

Well, of course I committed that; the more manual steps you make anyone take, the more likely they are to forget one. Time to make sure I don't do that again.

One of the lovely features of git is how easy it is to modify its behavior at various stages. In this case, I didn't want to commit my 'bail' hack, so I wrote the following and saved it as .git/hooks/pre-commit:

#!/bin/bash

RESULT=$(git grep -l 'Test::Most.*bail' t | wc -l)
if [ $RESULT -ne 0 ]; then
    echo -e "\e[01;31m$0 failed. Cannot commit:\e[0m" >&2
    git grep 'Test::Most.*bail' t
    exit 1
fi
exit 0

For this tiny hack, I check to see if I've committed my nuclear option and if I have, I get output similar to this:

$ git commit
.git/hooks/pre-commit failed. Cannot commit:
t/import/merger.t:use Test::Most 'bail';
t/import/parser_errors.t:use Test::Most 'bail';

I knew about parser_errors.t having 'bail' because I added that for testing my pre-commit hook, but I didn't know that merger.t had the same problem!

I'm sure my colleagues would have been most unhappy had I merged this to integration.

Next up is stopping myself from committing $DB::single = 1;.

12 Comments

Thx a lot for the tip with "die" and "bail". Didn't know about those... I think they will help me a lot in a current project.

I misread the title as meaning "How you can be nice to your colleagues who are afflicted with git pre-commit hooks." lol.

+1 with Boris on die and bail.

Don't ever put 'use Test::Most' in a .t file - just invoke it from the command line, when you need it:

perl -Ilib -MTest::Most=die t/mytest.t

or

prove -lr -MTest::Most=die t

What's the nuance with that? I use Test::Most myself on a test script, and I read on the perldoc that I can invoke prove or the script itself with the {BAIL,DIE}ONFAIL=1 environment variable set to get similar behavior to the Test::Most explicit imports.

Aren't you using it to get the die-on-fail functionality? Perhaps I missed something.

You know, I always wondered why is that behaviour not controllable by environment variables: During development I'd always want bailonfail but that should not go out to production. Not even the repository.

Ovid, so either I can't read or it was added after I read the docs :) Thanks for pointing those out.

I also use the $BAIL_ON_FAIL environment variable instead of the 'bail' module parameter. In fact, I have a script (named t, for brevity) that runs prove with this variable set. That way, when I want to do TDD, I use t, and when I'm ready for full unit testing, I can use make test or whatnot.

And if you work with multiple git repos and want to always be using the same pre-commit safety hook, you can just do something like:

DIR=~/.git-template/
mkdir $DIR
cp .git/hooks/pre-commit $DIR
# Then in your ~/.bashrc
export GIT_TEMPLATE_DIR=$DIR
# Then any subsequent clone ...
git clone https:/.... 
# ... now also includes your hook

About Ovid

user-pic Freelance Perl/Testing/Agile consultant and trainer. See http://www.allaroundtheworld.fr/ for our services. If you have a problem with Perl, we will solve it for you. And don't forget to buy my book! http://www.amazon.com/Beginning-Perl-Curtis-Poe/dp/1118013840/