[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.