At [work][lincoln-loop] we’ve decided to use [Hudson][hudson] for our continuous integration server.

I started off using [Joe Heck’s great Python and Hudson writeup][python-hudson] as a guide.

But to get really good reporting, including a coverage report, you’re going to want to use [Nose][nose], [django-nose][django-nose], and my [nose-xcover][nose-xcover] plugin.

Before you get [Hudson][hudson] up and running, you’ll first want to create a test settings module for your application. Here’s a sample:

With that in place you’ll want to set up a separate test requirements file for [pip][pip]. If you’re not using [pip][pip], I can’t be held responsible if the [central committee][pip-propaganda] gets ahold of you.

Why use [my fork of nose][heisel-nose]? We’ll get to that in a second. First let’s set up our build script.

1. We change into Hudson workspace set up for this build

2. Set up a [virtualenv][virtualenv] for our project

3. Activate it

4. Install our application’s requirements

5. Install our application’s test requirements

6. Assuming you have a properly constructed setup.py, this command will symlink it into your virtualenv’s site packages

7. Finally run our tests, using our test settings.

The –with-coverage option tells nose we want to capture coverage information. The –cover-package option tells nose we only want reporting on our application. The –with-xunit option will generte a nosetests.xml file in our workspace with the results of the test run.

Finally, the –with-cover-xml option is an option I added to nose’s coverage plugin, and the reason why I’ve got [my fork][heisel-nose] listed in our test-requirements.pip. Finally, the –with-xcoverage option activates my [nose-xcover][nose-xcover] plugin. It outputs an XML coverage report that Hudson can use, and it’ll honor the –cover-package option you specified earlier, so your coverage percentage won’t be artificially lowered, or inflated, by third-party code you use.

Now let’s configure Hudson to use the two XML reports we’re generating.

First the test pass/fail report.

And then our coverage report, you’ll need the [Cobertura plugin][cobertura] for this:

That’s right, you’ll also want the [Chuck Norris plugin][chuck]. Why? Because Chuck Norris **can** divide by zero. That’s why.

Also, I’d highly recommend the [Green balls][green] plugin, because Hudson’s default of blue == pass just doesn’t fly with me, or Chuck.

While we’ve been setting up Hudson, I had another build in the oven baking using the recipe above. Let’s see how it turned out:

**Editor’s note:** I updated this post to use my [nose-xcover][nose-xcover] plugin and not my [fork of nose][heisel-nose].

[lincoln-loop]: http://lincolnloop.com

[hudson]: http://hudson-ci.org/

[python-hudson]: http://www.rhonabwy.com/wp/2009/11/04/setting-up-a-python-ci-server-with-hudson/

[nose]: http://somethingaboutorange.com/mrl/projects/nose/

[django-nose]: http://github.com/jbalogh/django-nose

[pip]: http://pypi.python.org/pypi/pip

[pip-propaganda]: http://s3.pixane.com/python_comrades.png

[virtualenv]: http://pypi.python.org/pypi/virtualenv

[heisel-nose]: http://bitbucket.org/cmheisel/nose/src/

[cobertura]: http://wiki.hudson-ci.org/display/HUDSON/Cobertura+Plugin

[chuck]: http://wiki.hudson-ci.org/display/HUDSON/ChuckNorris+Plugin

[green]: http://wiki.hudson-ci.org/display/HUDSON/Green+Balls

[nose-xcover]: http://github.com/cmheisel/nose-xcover/