Setting up Buildbox when you run your own Git server
I’ve been trying to get a CI server for 88 Miles sorted out for ages. I tried to cobble something together before, but lost interest trying to keep track of previous builds and Ruby environments and other stuff. Me being me, I run my own Git server, so many of the existing CI servers out there won’t work for me (They assume GitHub), and I don’t really feel comfortable sending out source on a private project to a third party server. I also have a VM in my office that runs various dev machines, so I have the hardware to do it. Well, it turns out that a buddy of mine has been building a CI server that is a perfect fit for my purposes! It’s called Buildbox, and it comprises of an agent that you run on your hardware that manages builds for you. This is roughly what I did to get it running.
The Build Server
I setup a VM running Gentoo, with 1Gb of RAM and two virtual CPUs. It is as close to my prod environment as I can get it, running RVM – this is important, as there is a bit of a trick to this. I also set up a specific user (called build), that will do the work. I initiate the Buildbox daemon using monit, so if it ever dies, it should get restarted automatically.
Note: I’ve omitted the Buildbox setup instructions – The website does a better job than I could.
The wrapper script
Because I’m running RVM, and because monit is fairly dumb in setting up bash environments, I wrote a small wrapper script that will start and stop the Buildbox executable:
#!/bin/bash case $1 in start) if [[ -s "$HOME/.rvm/scripts/rvm" ]]; then source "$HOME/.rvm/scripts/rvm" elif [[ -s "/usr/local/rvm/scripts/rvm" ]]; then source "/usr/local/rvm/scripts/rvm" else printf "ERROR: An RVM installation was not found.\n" exit -1 fi rvm use ruby-2.0.0-p247 buildbox agent:start & echo $! > /var/run/buildbox/buildbox.pid ;; stop) kill `cat /var/run/buildbox/buildbox.pid` rm /var/run/buildbox/buildbox.pid ;; *) echo "Usage: buildbox-wrapper {start|stop}";; esac exit 0
If you call buildbox-wrapper start it sets up a RVM environment, then runs the buildbox agent and then saves the PID to /var/run/buildbox/buildbox.pid (Make sure the /var/run/buildbox directory is writable by the build user). Calling buildbox-wrapper stop reads the pid file and kills the agent.
The monit config
Add the following to your monitrc:
check process buildbox with pidfile /var/run/buildbox/buildbox.pid start program = "/bin/su - build /bin/bash --login -c '/home/build/scripts/buildbox-wrapper start'" stop program = "/bin/su - build /bin/bash --login -c '/home/build/scripts/buildbox-wrapper stop'"
This will change to the build user, then run the wrapper script.
My build script
You enter this into the code section of the web UI (I was a little confused by this – I didn’t realise it was editable!)
#!/bin/bash set -e set -x if [ ! -d ".git" ]; then git clone "$BUILDBOX_REPO" . -q fi git clean -fd git fetch -q git checkout -qf "$BUILDBOX_COMMIT" bundle install bundle exec rake db:schema:load bundle exec rake minitest:all
This is pretty much a cut and paste from the Buildbox website, except I run Minitest. I also tmp/screenshots/**/* and coverage/**/* to the artifacts section. Artifacts are get uploaded after a build is complete. I use them to upload my screenshots from all of my integration tests, as well as my coverage reports.
Git post-receive
This script belongs on your Git server in the hooks directory. Name is post-receive
#!/bin/bash while read oldrev newrev ref do branch=`echo $ref | cut -d/ -f3` author=`git log -1 HEAD --format="format:%an"` email=`git log -1 HEAD --format="format:%ae"` message=`git log -1 HEAD --format="format:%B"` echo "Pushing to Buildbox..." curl "https://api.buildbox.io/v1/projects/[username]/[projectname]/builds?api_key=[apikey]" \ -s \ -X POST \ -F "commit=${newrev}" \ -F "branch=${branch}" \ -F "author[name]=${author}" \ -F "author[email]=${email}" \ -F "message=${message}" echo "" done
Replace username, projectname and apikey with your own details. Make the file executable, and then push a change, and a build should start!