
Always Be Building
Why Hudson? Of the mainline Ruby CI apps I looked at, none works on Ruby 1.9.1. With some dedication I could set the server up to use Ruby 1.8.6 for the CI app, while running everything else on 1.9.1. Or I could get some work done. I chose the later (or so I thought) and after giving CC.rb its fair chance, Googling the state of 1.9 support in other CIs, I ended up with Hudson as the front runner.
So what’s it like?
It turns out that not all Java apps are designed by Enterprise SOA consultants. Hudson has no ceremonial setup. The only dependency is a JDK and it runs with a single command. I’ll take convenience over configuration any day.
In spite of that, I ended up spending the better half of the afternoon setting it up. More on that in a bit.
The nice thing about a well-designed UI, and Hudson’s UI shows a lot of positive brain activity, is that you don’t ever need to RTFM. That’s a good thing because, what Hudson calls documentation, is of the same caliber as comments left on a YouTube video. Unfortunately, I had to read through it to get past some installation woes.
Make It Work
I decided to go the easy way and install Hudson using apt-get. That created a hudson account, configured it to run an instance, and planted an init.d script I could use to start/stop that instance. Awesome, until I tried to actually start the instance.
To cut a longer-than-it-should-be story short, and to save you a trip to the Wiki, the pre-packaged /etc/init.d/hudson fires up Hudson as a daemon using this line:
$SU $HUDSON_USER -c "$DAEMON $DAEMON_ARGS -- $JAVA $JAVA_ARGS -jar $HUDSON_WAR $HUDSON_ARGS" || return 2
Some, I don’t know which because I spent too long hunting the general problem to obsess over details, but some environment variable is missing. Without it, the daemon doesn’t launch Hudson, and doesn’t spit error messages either. Since Hudson worked smoothly on every account but $HUDSON_USER, the quick and dirty solution I came up with was to let it inherit the environment:
$SU -m $HUDSON_USER -c "$DAEMON $DAEMON_ARGS -- $JAVA $JAVA_ARGS -jar $HUDSON_WAR $HUDSON_ARGS" || return 2
An hour and two characters later, I got Hudson firing up and ready to go. The next step was to put it behind Apache, a simple matter of setting up a virtual host to proxy into the Hudson standalone server:
<VirtualHost *> ServerName hudson.labnotes.org ProxyPass / http://localhost:8080/ ProxyPassReverse / http://localhost:8080/ </VirtualHost>
You’d think. The default configuration of mod_proxy I inherited from apt-get install apache2 denies all proxy access. I had to add this Proxy directive inside the VirtualHost:
<Proxy *> Order deny,allow Allow from hudson.labnotes.org </Proxy>
While none of this is rocket science, tinkering and failing and fixing takes time. Here’s a tip: when you change proxy configuration and /etc/init.d/apache reload, the next request might still behave based on the previous configuration. Give Apache some time to adjust its bearing after each configuration change.
Checking Out The Code
Before you can “build” the code, you need to check it out. Step one is easy, install the Git plugin. Hudson has a UI that lists all the available plugins, you can choose which one to install, hit a button and it will download and install them in the background. Awesome feature that every self-hosted app should have (I’m looking at you timing-out-on-updates WP).
Once installed, you can tell the project which Git repository to clone, which branch to use, how often to check for changes, and so on. In my case, accessing Git required SSH, SSH required private/public keys, SSH public must be authorized with Gitosis, and both keys go in ~hudson/.ssh. Not in /home/hudson/.ssh (since I installed with apt-get, home is somewhere in /var/lib).
Using Git submodules to vendor Rails and plugins? Then you also need to update the submodules after checking out the main repo.
Unpacking Gems, some of which include native extensions? Then you need to rebuild these or the app will refuse to run.
Using MySQL database for testing? Then you’ll want to upload the latest database schema before running the tests.
The first build task in my project looks like this:
git submodule init git submodule update rake gems:build env RAILS_ENV=test rake db:schema:load
Don’t forget RAILS_ENV=test, or it will default to modifying the development database.
What comes after that are rake specs and rake cucumber. With that proper care and feeding, you get continuous integration testing running like clock work.
Metric Fu(n)

The second project I created used the awesome metric_fu to measure and graph the stinkyness of my code. This one runs once a day. Here are some of the protips I learned while setting it up.
For starters you need to checkout the code, update all submodules, build all native extensions and update the database schema. Two different projects, so two different Git clones, both of which need to be managed the same way. I just copied the build task from the previous project over here.
Get metric_fu running in all its glory first, then create a Hudson project around it. You need to sort out the Gem dependencies and whatever metric_fu configuration options you like. That’s easier to do from a proper shell than by reconfiguring the project and hitting “Build Now”.
Here’s what my lib/tasks/metric_fu.rb looks like:
begin
gem 'jscruggs-metric_fu'
require 'metric_fu'
MetricFu::Configuration.run do |config|
#define which metrics you want to use
config.metrics = [:stats, :churn, :saikuro, :flay, :flog, :reek, :roodi, :rcov]
config.graphs = [:flog, :flay, :reek, :roodi, :rcov]
config.flay = { :dirs_to_flay => ['app', 'lib'] }
config.flog = { :dirs_to_flog => ['app', 'lib'] }
config.reek = { :dirs_to_reek => ['app', 'lib'] }
config.roodi = { :dirs_to_roodi => ['app', 'lib'] }
config.saikuro = { :output_directory => 'scratch_directory/saikuro', :input_directory => ['app', 'lib'],
:cyclo => "", :filter_cyclo => "0", :warn_cyclo => "5", :error_cyclo => "7",
:formater => "text"} #this needs to be set to "text"
config.churn = { :start_date => "1 year ago", :minimum_churn_count => 10}
config.rcov = { :test_files => ['spec/**/*_spec.rb', 'script/cucumber'],
:rcov_opts => ["--sort coverage", "--no-html", "--text-coverage",
"--no-color", "--profile", "--rails", "--exclude /gems/,/Library/,spec,features"]}
end
rescue LoadError
end
Enable the Archive the artifacts post-build action and set it to archive the files in tmp/metric_fu/output/. You’ll then see all the metric_fu files listed in the build status page. Click on the index.html link to navigate the metric_fu reports.
If you installed the Ruby plugin for Hudson, you can also enable the Publish Rails stats report post-build action, which will show rake stats report directly in the build status page.
If your app is called Fawesome, then FawesomeMetrics is a good project name (or just Metrics). On the other hand, Fawesome Metrics is a bad project name: the project’s directory name is based on the project name, and rake metrics:all does not like paths with spaces.
Follow these tips, and you can get Hudson up and running in under 25 minutes.