Stuff that works, part 5: Continuous Integration with Hudson

Last year, when we were writing a series of posts titled ”Stuff that works” (part 1, part 2, part 3, part 4), we were using a home-grown set of shell scripts to automatically build our software. Initially the scripts were run from cron every night, then later every hour and even later the script was triggered for every commit.

The autobuilder script worked, but we were missing features like IRC notifications, triggering builds from a web page and IRC and statistics. We would also have liked to have a history of builds combined with the test results of every build.

We were already setting up a MySQL database for builds and test results when we discovered that Continuous Integration is actually a hot topic these days and there is plenty of software available to help you with it. We eventually decided on software called Hudson.

Hudson is open source software and it is under active development by a large and growing community.
Hudson is written in Java and packaged so it can be set up easily and quickly. Basically you download the hudson.war file and run it: java -jar hudson.war

That’s it! Then you point your browser to port 8080 on that host and you have a pretty Web UI for setting up, managing and monitoring of various build jobs.

In a nutshell a job consists of following steps:

    Poll a version control repository for changes.
    Check out the source to a workspace directory.
    Execute a build script.
    Archive build results from workspace (or build artifacts like Hudson calls them.)
    (Optional) pick up test results from XML files in workspace.

Each build of each job has a nice web page showing if the build was successful, when it was run and how long it took. The build artifacts can be downloaded from the same page and you can also inspect the test results.
You can configure a job to trigger one or more other jobs. For example, when the job that builds F-Secure Mac Protection finishes successfully, we have set it to trigger 2 other jobs: Installtest and Smoketest.

A job does not have to build software. Hudson does not care what the build script does. The build script in Installtest downloads the last successful build artifact from the job that launched it and installs it on a Mac mini running on a side table in the corner of the team room. Then it makes couple of simple checks and uninstalls the software. The result of this run is recorded in an XML file in the JUnit format. When the build script exists successfully, Hudson reads the XML file and stores the test results.

Hudson executes the Installtest parallel with the Smoketest job. Smoketest takes much longer to run (about 7-8 minutes), which is why we have the Installtest to provide us fast feedback in case we break something. Smoketest is similar to Installtest, except it runs many more tests.

We have also a number of other jobs, which are triggered by the Smoketest: Fulltest (runs all the tests taking about an hour), Upgradetest (runs upgrade from previous version and all tests from fulltest) and Performancetest (runs a set of common user tasks, measuring the time it takes.)

As you see, it is a good practice to split long jobs into multiple smaller jobs that run quicker. The quicker a job runs, the faster you get to know if you broke something and the faster you can fix it.

Hudson also has a whole lot of plugins that can be used to enhance and extend the basic functionality. There are plugins for supporting most of the popular version control systems and plugins to analyze source code and create reports and lots more. (And there is a plugin for IRC too.)

If you are doing continuous integration you could do a lot worse than use Hudson to help you. I definitely recommend you check it out.