Tracking configuration changes in Jenkins

From my experience you want to put mostly everything in software projects under versioning. Not only your production code, but also build and deployment scripts. This doesn't always prove to be straightforward. With the SCM Sync configuration plugin there is a beautiful plugin for versioning all Jenkins configuration under Git (or SVN). Here are my quick notes on how I got it running on my newest project.

  1. Install the plugin through the normal Jenkins process
  2. As described here create a SSH key for the Jenkins box and register it as a "deploy key" with Github:
    $ sudo -u jenkins bash
    $ ssh-keygen
    Follow the steps and create a key without a passphrase. Now copy the contents from ~/.ssh/ to
  3. Set-up Git globally on the machine:
    $ sudo -u jenkins bash
    $ HOME=/Users/Shared/Jenkins/ git config –global YOUR_EMAIL
    $ HOME=/Users/Shared/Jenkins/ git config –global Jenkins
  4. Add as SCM root to "SCM Sync configuration" under http://YOUR_JENKINS/configure
The path under 3. should point to your Jenkins home folder. If your setup is similar to mine, you might not have a regular Jenkins user that you can log in with. Faking the HOME directory was the quickest way to get Git to accept the parameters.

This should be enough to have the given user now push to Github whenever somebody changes something in the configuration.

Hudson/Jenkins multi-configuration

In SuRF we changed a lot of str() calls to unicode() which made code fail under Python 2.6.2. This wasn’t found through unit tests as my tests ran with the newest Python version only.

Luckily Hudson/Jenkins has a multi-configuration (or matrix) mode that runs a test with several configurations. This lets you for example run your tests on different Python versions to check for compatibility.

When creating a new job you just choose “Build multi-configuration project” and receive a configuration page mostly similar to a general project.


A bit further down the page there’s a special section called “Configuration matrix”. Here you can set several axes of parameters with multiple values. In the example shown I chose a variable python_version with values 2.5, 2.6 & 2.7. Additional I added a value for the Python distributed with Ubuntu so I make sure to catch newer versions as well as distribution specific issues.


As SuRF tests run against an RDF store tests should run sequentially (checked). Also to keep Jenkins from running all configurations if something fails I defined a “touchstone build”. Only if this build succeeds all the other options will be tried.

I had one issue when setting up a Git repository – the same error I reported before: “FATAL: Could not apply tag jenkins-python_version=DISTRIB”. Setting the appropriate Git options did not work as explained in the simple set-up, and setting a default in Jenkin’s settings did not help either. I could only get the test to start by checking “Skip internal tag” which shows up when you click on “Advanced…” in section “Source code management”.

I now have two axes set-up for SuRF (one axis for different versions of rdflib), totally 16 distinct configurations which still complete pretty fast.


Setting up different Python versions

Here’s a short log on how I installed different Python versions in parallel on an Ubuntu system:

Install all build dependencies and get the Python source code

$ sudo aptitude build-dep python2.7
$ wget
$ tar -xjf Python-2.7.1.tar.bz2

Now configure and install the version in parallel with others to a directory under Jenkins:

$ cd Python-2.7.1
$ ./configure –prefix=/home/jetty/jenkins/python
$ make altinstall

Probably repeat these steps for 2.5, 2.6 (and 3.2?).


Add axis:


with values

2.5 2.6 2.7 DISTRIB

And now add a shell script before the actual test code:

rm -rf “.env”
echo “**> creating virtualenv”
if [ “$python_version” = “DISTRIB” ] ; then
virtualenv .env
virtualenv -p /home/jetty/jenkins/python/bin/python$python_version .env

PIP_DOWNLOAD_CACHE=/home/jetty/jenkins/pip_download_cache pip install -E .env nose coverage

Now add a second shell script running the actual test. Use the virtualenv as e.g. described in my former post. Voila.