Chicken florentine? More like chicken yum-entine. Get it with a couple of rolls for a delicious lunch!
I’ve been bumbling about with CruiseControl.rb lately and found it very enjoyable. The idea of continuous integration has intrigued me since I first learned about it, but I haven’t had the chance to put it into use until now. At first I was hideously confused about the difference between CC.rb and regular ol’ CruiseControl. It turns out that there is no commonality between the two except for the name, and that ThoughtWorks built both of them (although probably by separate teams). The full-blown CC is written in Java and seems to be an unwieldy beast that does everything you could think of. CC.rb is a complete from-scratch alternative written in Ruby that is very light and focused for Ruby on Rails applications.
When I say light, I mean light. It just sits there and runs rake whenever somebody checks something in to your repository. Out of the box, it does not support the ability to, say, run a task on a schedule. If you want this type of feature, you either need to find someone who has written it already or you need to write it yourself. The former option is unlikely because CC.rb seems to be relatively new and, true to the theme of RoR, few people seem to use it for anything other than exactly what it was built for. However, I needed to run potentially lots of tasks on a schedule or at intervals, as part of the build system. For these reasons cron itself wouldn’t work, and Petrik’s cron scheduler wouldn’t work either.
That left writing it myself. This turns out not to be nearly as intimidating as it first sounds, partly because it’s Ruby (and not Java), partly because CC.rb is well architected, and partly because CC.rb is so lightweight that there’s just not that much complexity to muddle through. Just tell CC.rb “don’t use your scheduler, use mine!” and you’re off and running.
Here’s the setup I have: CC.rb runs its normal cycle, i.e. check for changes, build, notify. I then use the post_build_action plugin to kick off my Capistrano task, which deploys everything to the unstable server. This is neat, because whenever you visit the unstable server, you are guaranteed to be looking at the very latest version of the code (that actually built :P). Unfortunately, if you have a long-running task which interacts with said unstable server, you would really prefer if the code didn’t change underneath you. In my case, every night I want to start a large set of Selenium browser compatibility tests (which the built-in scheduler doesn’t support) and I need to block the build so that it doesn’t deploy while the tests are running (which CC.rb doesn’t support at all). I would also like to take the results of those Selenium tests and report them through the CC.rb dashboard.
I’ve written a scheduler that does the trick pretty well. The actual task scheduling part leaves a lot to be desired, and the reporting is not great (it just dumps the output from the task into the build artifacts directory). It’s a decent first-shot attempt though.
To configure, you’ll need to add the following lines to your cruise_config.rb:
project.scheduler = CronScheduler.new(project)
project.scheduler.tasks < < CronTask.new(:name => 'Test Units', :at => 15.seconds, :task => 'rake test:units', :disable_build => true)
The first line tells CC.rb to use CronScheduler as the project’s scheduler. The second line is an example task. You may add as many tasks as you like. Tasks are forked and execed so they run as their own process.
You can disable the build with the "disable_build option. This means that CC.rb will not poll the repository or listen for explicit build requests from the dashboard until the process has finished running. You may safely have overlapping tasks disable the build; the scheduler tracks the number of tasks that have disabled the build.
You can schedule a task with either the :at or :every option. at runs the task at a certain time; for example at 15.seconds past the minute (every minute), 17.minutes past the hour (every hour), or 23.hours past the day (every day). every runs the task every so often; for example every 19.minutes since the builder started. This part has a lot of room for improvement.
Okay, enough from me. Get the code! And, of course, contact me with comments, suggestions, or (better yet) patches.