Drupal, meet Hudson; Hudson, Drupal...

At $work, we use Hudson extensively, and it rocks. For those who don't know already, Hudson is an implementation of Continuous Integration that is remarkably easy to use. I wrote about my first impressions of Hudson previously. Hudson's original audience was Java developers using Ant or Maven, but with plugins and some hacking, we can make it do some things for us as module contributors to Drupal.

I've been cutting my Drupal developer teeth by working pretty intensively on a few modules for Drupal - Node Gallery and it's derivatives. We are hitting a crucial point in development where we are switching from the old way of defining fields on a node to using CCK. While the module is still in alpha, it's still in use by quite a few sites - as of this writing it's number 465 on the list of Drupal modules. Not exactly the spotlight, but we can't go breaking things without making people angry either. I figured this would be the perfect place for Hudson - it will let you know when you break something.

Pieces of the Puzzle

Here's the pieces you'll need:

  • A linux server with Java, a working Drupal install (that may get broken at times) and the cvs command-line utility.
  • These Drupal modules installed and enabled: drush, coder, and optionally simpletest.
  • Some time on your hands

The shell script

This is the most important piece of the setup. By utilizing Hudson's environment variables, we can make this as portable as possible. By using the same script for all Hudson jobs, changing the script automatically changes all of our jobs at once. Let's dive right in:

  1. #!/bin/bash
  2.  
  3. #set -x
  4.  
  5. PHP=/usr/bin/php
  6. DRUSH_PATH=/apps/drupal/drush
  7. DRUPAL_PATH=/apps/drupal/drupal_core
  8. MODULES_DIR=$DRUPAL_PATH/sites/ngdemo.sysadminsjourney.com/modules
  9. SITE="http://ngdemo.sysadminsjourney.com/"
  10.  
  11. DRUSH="$PHP $DRUSH_PATH/drush.php -n -r $DRUPAL_PATH -i $DRUPAL_PATH -l $SITE"
  12. EXITVAL=0
  13.  
  14. # Check our syntax
  15. PHP_FILES=`/usr/bin/find $WORKSPACE -type f -exec grep -q '<?php' {} \; -print`
  16. for f in $PHP_FILES; do
  17.   $PHP -l $f
  18.   if [ $? != 0 ]; then
  19.     let "EXITVAL += 1"
  20.     echo "$f failed PHP lint test, not syncing to ngdemo website."
  21.     exit $EXITVAL
  22.   fi
  23. done
  24.  
  25. #Install the files
  26. /usr/bin/rsync -a --delete $WORKSPACE/* $MODULES_DIR/$JOB_NAME
  27.  
  28. #Run update.php
  29. $DRUSH updatedb -q --yes
  30.  
  31. #Run coder
  32. CODER_OUTPUT=`$DRUSH coder no-empty`
  33. if [ -n "`echo $CODER_OUTPUT | grep $JOB_NAME`" ]; then
  34.   $DRUSH coder no-empty
  35.   echo "Coder module reported errors."
  36.   let "EXITVAL += 1"
  37. fi
  38.  
  39. #Run potx
  40. cd $MODULES_DIR/$JOB_NAME
  41. ../potx/potx-cli.php
  42. if [ $? != 0 ]; then
  43.   let "EXITVAL += 1"
  44.   echo "POTX failed."
  45. fi
  46. if [ -e $MODULES_DIR/$JOB_NAME/general.pot ]; then
  47.   cp $MODULES_DIR/$JOB_NAME/general.pot $MODULES_DIR/../files/$JOB_NAME.pot
  48. fi
  49.  
  50.  
  51. exit $EXITVAL

Lines 1 through 9 are likely the only ones you'll need to change. PHP is the path to your php binary. DRUSH_PATH is the path to where drush is installed, specifically drush.php. DRUPAL_PATH is the path to where Drupal is installed, and SITE is the URL of your Drupal site. Note that if Hudson is complaining about a build failing, and you can't figure out why, uncomment line 3. It will give you the gory details of what's going on with the script, and you can view it via the Console Output link in Hudson for your job.

Lines 11 and 12 setup the DRUSH command used by the script, and sets an EXITVAL of zero. If all goes well, our script returns 0 upon exit, and Hudson is happy. Anything goes wrong, and we'll exit with a code > 0, which signals Hudson to alert us.

Lines 14 through 23 find all files in $WORKSPACE (which is set by Hudson) that have the '<?php' begin tag in them, and then runs php -l against them. This is a very rudimentary syntax check. If any of the files fail the check, we do not copy the files from CVS to the site, and exit with a code of 1.

Line 26 is a simple rsync command to copy the files from the Hudson workspace over to our site's modules directory. Note that we use $JOB_NAME which is exported by Hudson, so this means that you must name your Hudson project the same as the module name. Also note that your Hudson user needs to have write access to the specific module directory that it's installing.

Line 29 runs drush so that it invokes update.php, and answers yes to all questions.

Lines 32 through 37 runs the default code review from the coder module. You will have had to set this up initially via the web interface. It then scans through that output looking for any complaints about our $JOB_NAME, and if found, prints it to stdout and increments our exit value by 1. Note we don't exit here, as it's a non-fatal error. However, Hudson will treat it as a failed build and email everyone about it.

Lines 40 through 48 runs the Translation Template Extractor command line utility against our module. It then copies the general.pot to the files directory. Again, the user running Hudson will need write access for this to work properly. If the potx-cli.php script should exit uncleanly, we increment our exit value by one.

Last in my script, we simply exit with whatever value we have ended up with at this point. Again, if Hudson sees anything other than a zero, it will email everyone about it.

Since the modules I'm working on don't have Simpletest tests ready yet, I don't run them in this script. However, it's on the horizon, and can be ran easily using run-tests.sh. Note that there is a patch that will cause run-tests.sh to output it's results in a JUnit style output, which Hudson understands fully. If you implement this, I strongly recommend applying that patch.

Hudson Setup

Now that we have our script ready, we need to setup our Hudson job. Note that installing Hudson itself is outside the scope of this article - it's refreshingly easy and doesn't need repeating here. There are two things you must do before creating the build task. First, setup your "E-mail Notification" section according to your mail server at http://myhudsonserver:8080/configure. Also, you will need to install the "URL Change Trigger" plugin by navigating to http://myhudsonserver:8080/pluginManager/available.

Once you install that plugin, create a new job. In my case, the job was named 'node_gallery', since that's the name of the Drupal module I was working with. Select 'Build a free-style software project' when asked.

Under the "Source Code Management" section, select "CVS", and then fill in the CVSROOT of the project you're working with. In my case, it was ':pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal-contrib'. Next, fill in the path to the module in the "Module(s)" form - for me it's 'contributions/modules/node_gallery/'. If you're working with CVS HEAD, leave the Branch field empty, otherwise type in the branch name there.

IMPORTANT: to avoid abusing Drupal.org's already overloaded CVS server with cvs logins once every 5 minutes, we will point Hudson instead to the RSS feed for the CVS log messages. Make sure "Poll SCM" is unchecked, and check "Build when a URL's content changes". To obtain the URL, you need the node id of your project. There's many ways to do this, but you can find it by going to the project's Drupal.org page and click on "View CVS Messages". From that page, click the orange RSS icon at the bottom left of that page. Copy and paste that URL into the URL field in Hudson.

Under the Build section, click "Add build step", and select "Execute Shell". In the resulting "Command" textarea, type the full path to the shell script we setup above.

The final section, "Post-build actions" is up to you, but you'll likely want to enable email notifications. Place a checkmark in the "Email Notification" box, and type in the email addresses of the desired recipients.

Click Save, and you're done! Hudson will start doing a CVS checkout of your project, and will start running tests on it. It will email you once anything goes wrong, and will notify you again when the problem is resolved. It will only run these tests after someone commits code to CVS, so you will likely need to hit the "Build Now" link in the left nav a few times.

We've really only scratched the surface of what Hudson can do. You can track performance using JMeter, add all kinds of crazy plugins, require logins, the list goes on and on. While this helps, it's still nowhere as helpful as a Ant/Maven job can be. Hopefully this article is enough to spark some interest from the Drupal community so that we can write some better continuous integration code in the future.

Also, I'm far from being an expert on either Drupal or Hudson. I wrote my first code for Drupal in November of 2009, and I only tinker with Hudson on occasion at work. Hudson works so well, it's one of those "set it and forget it" apps. I would love for readers to leave comments on any mistakes I might have made, or possible improvements I may have missed!

Your rating: None Average: 4 (8 votes)

Comments

Good Timing!

Hey, I'm just starting to collect best practices for all this here:

http://groups.drupal.org/node/47686

Love to have your participation. It's a brave new world!

Thanks!

Thanks for writing and sharing this nice overview about using Hudson to CI test Drupal. This is definitely one of the next big steps for Drupal and we're excited about setting this up as well.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <p> <span> <div> <h1> <h2> <h3> <h4> <h5> <h6> <img> <map> <area> <hr> <br> <br /> <ul> <ol> <li> <dl> <dt> <dd> <table> <tr> <td> <em> <b> <u> <i> <strong> <font> <del> <ins> <sub> <sup> <quote> <blockquote> <pre> <address> <code> <cite> <embed> <object> <param> <strike> <caption>
  • Lines and paragraphs break automatically.

More information about formatting options