Django continuous integration with Hudson and Nose

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:

[gist id=240285]

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.

[gist id=240291]

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

[gist id=240296]

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.

Config [Hudson].jpg

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

Config [Hudson]-1.jpg

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:

Dummy [Hudson].jpg

**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/

This entry was posted in Django, Programming, Python, Technology. Bookmark the permalink.

17 Responses to Django continuous integration with Hudson and Nose

  1. Joe Heck says:

    Nice writeup! I’ve been wondering about nose & django together, but hadn’t yet found the django-nose piece.

    Did you have to do much work on the system hosting Hudson to enable virtualenv? Would love to see/hear what you specific build steps look like for this setup.

  2. Chris says:

    Joe,

    Didn’t have to do much to get it to use the virtualenv.

    I’m running the tests via a shell job, so all I had to do was create the virtualenv and source it.

    This gist http://gist.github.com/240296 is pretty much our build command. Then we have one other build command for pylint using what you had in your tutorial.

  3. Stavros says:

    Hello,
    I love your guide (and the final result), but I don’t like having to install a fork of official packages (it’s a bit hard to maintain). Do you think your changes could ever see their way upstream?

  4. Chris Heisel says:

    Stavros,

    Thanks! Two thoughts, you can give the folks at Nose a ping, I sent the maintainer a pull request on BitBucket, but haven’t heard back.

    I’ll also look and see if I can make a plugin that’d do the same thing my patch is doing.

    Chris

  5. Chris says:

    Stavros,

    Give this plugin I whipped up a try, it should compliment nose’s built-in coverage plugin, adding the XML output: nose-xcover

  6. Stavros says:

    I just tried your plugin, it is much better and I have gotten everything working fine (after setting up your plugin nose couldn’t find it, but I copied the module manually in /site-packages/). The coverage and nosetests xml files are generated fine, but for some reason Hudson reports my tests as failed, even though nosetests.xml has failures=”0″…

    I’ll figure it out sooner or later, thanks for granting my request!

  7. Stavros says:

    Hmm, tests pass but Hudson reports a failed build. Could this be because I’m just running manage.py test?

  8. Stavros says:

    Word to the wise, don’t use the pypi version of django-nose, get the one from github.

  9. jacob says:

    Hi Chris,
    First, apologies, I’m a python/django noob. When you say to “create a test set­tings module for your appli­ca­tion,” what exactly does this mean? If I merely parse the statement word-by-word, given my rudimentary knowledge, I take it that I should create a file with a .py extension (i.e. a module) in (one of (or all of?)) the application directories in my django project. Is that correct? If so, does it matter what the module is named? Is something somewhere supposed to refer to it? Which of my django applications should contain it? Where exactly in the dir structure of the application should it go? Does this assume one Hudson project per django app?

    • Chris says:

      Jacob,

      The settings module is a regular python file that can live anywhere on your Python path. It’s documented on the “Django Web site”:http://docs.djangoproject.com/en/1.1/topics/settings/

      Often times folks will have a settings module in their project folder, or if you’re building a reusable application you might have a settings file for testing inside that application.

      My Hudson configuration doesn’t assume one application per Hudson project. You could run a large test suite containing many applications if you wanted.

  10. Jacob says:

    Thanks!

  11. As another resource, Colin, one of the guys I work with, just did a writeup of our Django/Hudson setup here at Caktus:

    http://www.caktusgroup.com/blog/2010/03/08/django-and-hudson-ci-day-1/

    It walks you through setting up a new Hudson installation to test a Django project – hope someone finds it useful!

  12. Tim says:

    Really good post but:

    “Assuming you have a properly constructed setup.py” – No I don’t, any pointers to what is a proper setup.py for a Django app?

  13. James English says:

    I wonder if you could help me out, I am trying to get this to work but I am running into a lot of problems

    So when hudson runs my build script (which is exactly the same as your except I renamed ve to trunk) I get:
    /var/lib/hudson/jobs/dummy/workspace/trunk/build-settings.sh: 3: source: not found

    Then when it tries to run:
    pip install -E ./trunk -r requirements.pip

    I get:
    IOError: [Errno 2] No such file or directory: ‘requirements.pip’

    Then I get some more errors, but those are the first 2 so I figured they probably impact the rest of the errors.

    Some help is greatly appreciated, I can’t figure out why source won’t work in the shell script. I’ve gone to the $WORKSPACE directory myself and run the shell script and it has worked fine!

    I even tried pasting the shell script into hudson and still got the same source not found error.

  14. James English says:

    I answered my own question. I had to go into the hudson settings and set “shell executable” to “bash” for some reason it wasn’t using the bash shell

  15. Hey, greate writeup! I have one problem with the cobertura plugin in a python project with nosetests: It seems to ignore files which are not tested at all, so I have 100% coverage on classes, files and conditionals (which, unfortunately, is just not ture ;)). Do you have any ideas what could be causing this?

Comments are closed.