Deploying CruiseControl.rb with Custom Tasks
More and more work is being done by geographically disparate teams these days, so a continuous integration tool like CruiseControl.rb is quite useful. Andre and I could have used this when we were heads down with GeoKit. But better late than never, right?
I decided I would make an attempt to deploy CruiseControl.rb to one of the hosts that I have — and do so with Capistrano. For the most part, it was pretty painless, but I did run into a gotcha or two.
First, the host I deployed to uses the Apache / Lighttpd (fastcgi) style of deployment. Unfortunately, Lighttpd just would never start up for me! After way too long, I finally figured out that I had DOS line breaks in my file that I could not see. So I zapped them with dos2unix and all was well. Unfortunately, this ate up a lot of my time as I’m not very skilled at debugging fastcgi issues.
CruiseControl.rb is a Rails app, but not your typical Rails app. It uses no Rails app, but rather stores your project information in a projects directory underneath the app root. Further, its more than just a Rails app, it utilizes additional processes as project builders.
To deploy with Capistrano, you have to preserve the projects directory so that it will persist across your releases. That’s pretty easy to do with symlinks, so that’s a piece of cake. You can use the after_update_code callback to do this.
There are two other use cases that I could see that I would need. Adding a project and starting a builder for that project. To enable those, I added Capistrano tasks for each. Both tasks rely on environment variables and both tasks could probably benefit from a bit more error-checking, but they are good enough for me.
So to start a project, I use:
NAME=GeoKit URL=svn://rubyforge.org/var/svn/geokit cap add_project
to start its builder, I use:
NAME=GeoKit cap start_builder
I added my custom tasks under config/recipes/cruisecontrol.rb and required this in deploy.rb. The custom tasks code is below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
Capistrano.configuration(:must_exist).load do desc “Update symlink for projects directory.“ task :after_update_code do run <<-CMD rm -rf #{release_path}/projects && ln -nfs #{shared_path}/projects #{release_path}/projects CMD end desc “Add a project to cruise control.“ task :add_project do unless ENV[‘NAME‘] && ENV[‘URL‘] raise ArgumentError, “***** You must specify the NAME and URL parameters to add a project. *****“ end run “#{current_release}/cruise add #{ENV[‘NAME‘]} –url #{ENV[‘URL‘]}“ end desc “Start a builder for a project.“ task :start_builder do raise ArgumentError, “***** You must specify the NAME of the project to build. *****“ unless ENV[‘NAME‘] run “#{current_release}/cruise build #{ENV[‘NAME‘]} &“ end end |
And with that, any of my projects can now be monitored with CruiseControl.rb.
A few caveats.
- I had to physically edit the cruise_config.rb file (on the server) for the project to include my email address and polling interval. No big deal, its a one-time thing.
- If your code is proprietary, you may want to protect the site with
basic authentication to keep unwanted eyes from seeing your project assets. - Since your project data is on your server’s file system, you may want to consider a backup strategy if you value your build history.
Suggestions for ThoughtWorks:
- Consider including Capistrano tasks to make it easier — mine are admittedly rough, I’m sure.
- Consider adding some element of authorization by project. For example, I have public plugins, but I also have proprietary code that I do not wish to share. Obviously, its easy enough to deploy a second site, but that’s mildly inconvenient.
I hope this is helpful to continuous integration fans out there trying to get set up!