4

I am trying to write a pre-receive hook for git that will pull the latest version of the code being pushed and run unit tests against it. My code is below, but when it gets to "git checkout $newrev", I get:

remote: fatal: reference is not a tree: 188de39ca68e238bcd7ee9842a79397f39a5849e

What do I need to do to get a checkout of the code being pushed before the receive has happened?

#!/bin/bash
while read oldrev newrev refname
do
  echo "Preparing to run unit tests for $newrev"
  TEST_DIR=/opt/git/sommersault-push-tests/sommersault

  # check out this version of the code
  unset GIT_DIR
  echo $refname
  cd $TEST_DIR
  git checkout $newrev

  ...do more stuff...
done
Nicolás Ozimica
  • 9,481
  • 5
  • 38
  • 51
  • 2
    Are you sure you can do that? How can it checkout code it hasn't received? I would think you'd need to do a post-receive hook and then have it rollback (reset) if the tests fail. – wadesworld Aug 20 '12 at 18:06
  • I think @wadesworld gave you the correct advice. I could just point this link as a solution for a similar problem: http://stackoverflow.com/questions/2087216/commit-in-git-only-if-tests-pass – Nicolás Ozimica Aug 20 '12 at 18:51
  • Ah, okay. I was starting out looking at this post - http://codeutopia.net/blog/2011/06/30/how-to-automatically-run-unit-tests-from-a-git-push/ - which seems to imply it's possible to get the code. (It uses "git archive" rather than "git checkout", though I couldn't get either to work.) But it makes sense that it wouldn't have access to the code yet. – Maria Gullickson Aug 20 '12 at 19:21
  • Actually, I take that back. The githooks manpage says `This hook is invoked by git-receive-pack on the remote repository` which indicates it happens after the new content is received. Note however that it receives a line of input for each ref to be updated.Not sure the cause of the error yet though.. – wadesworld Aug 20 '12 at 21:02

2 Answers2

5

Despite what other people suggested the commit is already received, but it's not written yet.

I would even go as far as saying, that pre-receive hooks are better for deployment that post receive hooks. That why Heroku uses pre-receive hooks to deploy. If you're deployment doesn't go through, you can just reject the commit.

Here's some code that should do the trick for you:

#!/bin/bash
while read oldrev newrev refname
do
    echo "Preparing to run unit test for $newrev ... "
    git archive $newrev | tar -x -C /tmp/newrev
    cd /tmp/newrev

    echo "Running unit test for $newrev ... "
    # execute your test suite here

    rc=$?

    cd $GIT_DIR
    rm -rf /tmp/newrev
    if [[ $rc != 0 ]] ; then
        echo "Push rejected: Unit test failed on revision $newrev."
        exit $rc
    fi
done

exit 0
codingjoe
  • 1,210
  • 15
  • 32
0

I'm using this script based on this howto. Only executes phpunit in selected branches

#!/bin/sh

while read oldrev newrev refname
do
    # Only run this script for the dev branch. You can remove this
    # if block if you wish to run it for others as well.
    if [ $refname = "refs/heads/dev" ] ; then
        # Anything echo'd will show up in the console for the person
        # who's doing a push
        echo "Preparing to run phpunit for $newrev ... "

        # Since the repo is bare, we need to put the actual files someplace,
        # so we use the temp dir we chose earlier
        mkdir -p /tmp/$refname
        git archive $newrev | tar -x -C /tmp/$refname

        echo "Running phpunit for $newrev ... "

        # This part is the actual code which is used to run our tests
        # In my case, the phpunit testsuite resides in the tests directory, so go there
        cd /tmp/$refname
        composer install > /dev/null

        # And execute the testsuite, phpunit will send a response error if tests fail
        phpunit --group no-db

        # Clean temp dir
        rm -rf /tmp/$refname
    fi
done

# Everything went OK so we can exit with a zero
exit 0

Customize at will...

Quim Calpe
  • 124
  • 4