RVM - Improved support for Hudson
For the last three months, I’ve been working on rvm along side wayneeseguin, the creator of rvm, in order help make rvm easier to use for server deployments and continuous integration. As part a of this, one of my focuses was to develop a preferred CI platform.
The Back Story
About mid way through the program, Nick Quaranto posted an excellent article on the thoughtbot blog, Sailing down the Hudson with RVM. Nick’s post not only introduced a great starting point for a simple rvm-based ci setup but also highlighted hudson as a great choice for continuous integration.
With this in mind, Wayne and I chatted about the available options and settled on making hudson the preferred choice for rvm-based continuous integration. It met our requirements in terms of being both simple to use and highly flexible whilst also being well recommended throughout the community. With this in mind, I then set out to make it ever simpler to use rvm with hudson.
Part 1: rvm-shell
The first step in this process was to make working with rvm inside general
command / shell scripts inside hudson easier (like in Nick’s post).
To achieve this, I implemented rvm-shell – a simple wrapper around bash
that preloads the correct ruby environment from rvm before exec'ing to
become a bash shell environment. The idea being you can set the shebang
line in a hudson step to point to rvm-shell (optionally passing a few arguments
if you want a specific ruby) and rvm will attempt to setup the correct environment
for you. As an example, the following commands will let you run hudson with a ruby
and gemset combination:
To detect the ruby from a .rvmrc, falling back to your default interpeter:
#!/usr/bin/env rvm-shell
echo "If rvm is found, will load it and set the gemset"
gem env # Should show ruby the .rvmrcOr, for a specific ruby:
#!/usr/bin/env rvm-shell ree
echo "If rvm is found, will use ree"
gem env # Should show ruby the .rvmrcIf you want to set the ruby an alternate way, rvm-shell will
use the value of the shell variable $rvm_shell_ruby_string
if available, e.g:
#!/usr/bin/env rvm-shell
echo "If rvm is found, will load it and set the gemset"
gem env # Should show ruby And specifying the rvm_shell_ruby_string to be ree@rails3
will automatically run the script under ree with the rails3 gemset.
The biggest advantage of this approach is that you can use it with
Hudson Matrix Builds,
specifying the name of the variable as rvm_shell_ruby_string and
then specifying a list of rubies to run it against.
Part 2: Forking Hudson Plugins for fun and profit
With the first step simplified (e.g. for general shell commands) my next step was to make rvm work fluidly with the existing ruby plugins (or, as needed, to fork them and add rvm support). In this case, I took the three existing plugins:
Which required specifying information such as full paths to rake installations, and forked them to rvm-specific versions:
Now, instead of specifying lots of individual ruby install information, the tasks simple take a ruby string, like so:
By default, if you leave the ruby string blank rvm will attempt to
load the .rvmrc in a given directory, falling back to using your default
ruby to execute the ruby script / rake task. Please note that if
you’re on head, rvm will implicity trust your .rvmrc file in rvm-shell,
but in older versions it may have errors unless rvm_trust_rvmrcs=1
is set in ~/.rvmrc.
The catch here is that you need to make sure your rvm_bin_path (for example ~/.rvm/bin)
is in PATH before loading hudson. Depending on how you installed hudson, this
may involve manually editing an init.d file or two to add rvm_bin_path
to the path. If you’re using a system wide install, it should hopefully work
for you out of the box. Otherwise, hudson will give an error related to
being unable to find the rvm executable.
For most users, these plugins should work fine along side the existing ruby / rake / rubyMetrics plugins, but I suggest that if possible you remove the old style of plugins if you plan on using my rvm-specific versions (to avoid potential conflicts).
Installing them is a simple matter of downloading the plugins (either from the contrib directory in the rvm repository or from the download page in their respective repositories), uploading them via hudson and restarting your hudson server.
Part 3: ‘gem install hudson-with-rvm’
The next step is simple – Essentially, a fork maintained in parallel to
the fantastic hudson.rb project that
adds the rvm support out of the box, making getting hudson with rvm support
a simple matter of gem install hudson-with-rvm for most users.
Expect this in the next few days.
Part 4: Hudson and Ubuntu CI Virtual Appliance
With all of the above work done, the final steps to finishing my Ruby Summer of Code project are simple (the work is done, it’s now a matter of packaging it up) – a simple virtual appliance that can be easily deployed for people to get started with hudson and rvm (built on Ubuntu).
The hard work is done, I just need to update the image and find a place to host it. Once that’s pushed, expect a blog post here on it (and, if you’re interested in hosting virtual appliance images (each appx. 400MB to 600MB) for us, let us know).
Up Next: More Ruby Summer of Code updates
I’m at the end of my Ruby Summer of Code project at this point and almost all of the work is done. The last few things I have to do now are cleaning up and packaging the last bits of work. I plan on posting a follow up summarising all of my work and my experiences in the next few days.
Along with this, expect a nice and detailed guide for getting an Ubuntu 10.04 based server up and running with rvm and passenger (it’s written, I just need to edit it now).
Cheers!