Categories
git Software

Build and push to upstream git repository in the background

[Updated 11 Feb 2011 – I’m not sure the original version actually worked properly.]

Quite often I’ll make small changes to a project’s code which I’m 99% sure won’t break the build. Of course thanks to Murphy’s Law that probability falls to about 10% if I decide to risk it push the changes upstream without running the full build locally.

Now even if the build only takes a few minutes to run, that’s a few minutes which I can’t spend doing anything else with the code, because if I save a file I can’t be sure whether the original or changed version will be used in the tests. What I’d like to be able to do is to run the build in the background, with changes automatically pushed upstream if everything’s green, or an alert shown if either the build fails or the push is rejected (because someone else has pushed a change while the build was running).

Fortunately with a distributed version control system like git, this is fairly easy.

Firstly, clone the local working repository and set up a remote pointing to the clone:

git clone . ../myproject-staging
git remote add staging ../myproject-staging

Now set up the cloned copy:

cd ../myproject-staging

# Allow pushing into the checked-out branch
git config receive.denyCurrentBranch ignore

# Create a remote for the upstream server you want to push to (change the URI!)
git remote add upstream git@your.upstream.server/your/repository/path.git

Do anything that needs doing to allow the cloned project to build (if you have a test database, create a separate instance to avoid tests interacting with your main workspace), and verify that the build runs.

Now add the following to .git/hooks/post-receive:

#!/bin/bash

cd ..
export GIT_DIR=.git
git reset --hard
nohup .git/hooks/post-reset 1> build_output 2>&1 &

This will reset your checked-out working copy to the pushed master, then run .git/hooks/post-reset in the background. Create that file with the following content:

#!/bin/bash

function error {
  growlnotify -s -t `basename $PWD` -m "$1"
  exit 1
}

# Replace this with your build command
bundle install --local && rake || error "Rake failed"

git push upstream master || error "Git push failed"
growlnotify -s -t `basename $PWD` -m "Built and pushed successfully"

Make sure both these hooks are executable:

chmod u+x .git/hooks/post*-receive

Now, back in your normal working repository you should just be able to type git push staging (add --force if you’ve rebased your normal master and get an error about the push not being a fast-forward), and the project will build in the staging repo. If everything works, it’ll push upstream and you’ll get a Growl notification (assuming you have Growl and growlnotify, or the equivalents for your OS, installed). If it fails, you’ll also get a Growl notification. I’ve made the alerts sticky, so I don’t miss them – if you don’t want that, just remove the -s flag. The build errors will be in the build_output file.