I recently wanted to make it easier for contributors to ActiveHash to test their changes against multiple versions of Rails, with multiple versions of Ruby. My stories looked like this:
As a contributor
I want to be able to run `bundle install`, then quickly run the suite spec suite against the latest released version of rails
So that I can develop quickly using a familiar workflow
As a gem maintainer
I want to be able to run the spec suite against 3 different versions of ruby, each with 5 different versions of rails
So that I can release the gem with confidence that I'm not going to break people's apps
In this post I’ll explain how I did that with a (relatively) simple shell script.
The final script looks like this:
#!/bin/sh
set -e
if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
source "$HOME/.rvm/scripts/rvm"
elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
source "/usr/local/rvm/scripts/rvm"
else
printf "ERROR: An RVM installation was not found.n"
fi
function run {
gem list --local bundler | grep bundler || gem install bundler --no-ri --no-rdoc
echo 'Running bundle exec rspec spec against activesupport / activerecord 2.3.2...'
ACTIVE_HASH_ACTIVERECORD_VERSION=2.3.2 bundle update activerecord
bundle exec rspec spec
echo 'Running bundle exec rspec spec against activesupport / activerecord 2.3.5...'
ACTIVE_HASH_ACTIVERECORD_VERSION=2.3.5 bundle update activerecord
bundle exec rspec spec
echo 'Running bundle exec rspec spec against activesupport / activerecord 2.3.11...'
ACTIVE_HASH_ACTIVERECORD_VERSION=2.3.11 bundle update activerecord
bundle exec rspec spec
echo 'Running bundle exec rspec spec against the latest released version of activesupport / activerecord...'
ACTIVE_HASH_ACTIVERECORD_VERSION="" bundle update activerecord
bundle exec rspec spec
echo 'Running bundle exec rspec spec against edge activesupport / activerecord...'
ACTIVE_HASH_ACTIVERECORD_VERSION=edge bundle update activerecord activesupport
bundle exec rspec spec
}
rvm use ruby-1.8.7@active_hash --create
run
rvm use ree-1.8.7@active_hash --create
run
rvm use ruby-1.9.2@active_hash --create
run
echo 'Success!'
Here’s a breakdown of what’s happening:
First the script tells the shell to exit immediately if any of the commands in the script fail:
set -e
This is helpful for continuous integration servers especially, so they fail fast. Next, the script loads RVM, looking first in the home directory, then for a system install:
if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
source "$HOME/.rvm/scripts/rvm"
elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
source "/usr/local/rvm/scripts/rvm"
else
printf "ERROR: An RVM installation was not found.n"
fi
The next section defines a function which, when invoked, runs the spec suite against 5 different versions of activerecord/activesupport:
function run {
gem list --local bundler | grep bundler || gem install bundler --no-ri --no-rdoc
echo 'Running bundle exec rspec spec against activesupport / activerecord 2.3.2...'
ACTIVE_HASH_ACTIVERECORD_VERSION=2.3.2 bundle update activerecord
bundle exec rspec spec
echo 'Running bundle exec rspec spec against activesupport / activerecord 2.3.5...'
ACTIVE_HASH_ACTIVERECORD_VERSION=2.3.5 bundle update activerecord
bundle exec rspec spec
echo 'Running bundle exec rspec spec against activesupport / activerecord 2.3.11...'
ACTIVE_HASH_ACTIVERECORD_VERSION=2.3.11 bundle update activerecord
bundle exec rspec spec
echo 'Running bundle exec rspec spec against the latest released version of activesupport / activerecord...'
ACTIVE_HASH_ACTIVERECORD_VERSION="" bundle update activerecord
bundle exec rspec spec
echo 'Running bundle exec rspec spec against edge activesupport / activerecord...'
ACTIVE_HASH_ACTIVERECORD_VERSION=edge bundle update activerecord activesupport
bundle exec rspec spec
}
(I know, it’s not particularly DRY – pulling out the duplication into other functions is an exercise for the reader…)
This function sets an environment variable to describe the intended gem versions, then updates bundler and runs the suite. To make this work, I added the following to my Gemfile:
source :gemcutter
gemspec
group :development do
activerecord_version = ENV['ACTIVE_HASH_ACTIVERECORD_VERSION']
if activerecord_version == "edge"
git "git://github.com/rails/rails.git" do
gem "activerecord"
gem "activesupport"
end
elsif activerecord_version && activerecord_version.strip != ""
gem "activerecord", activerecord_version
else
gem "activerecord"
end
# other gems...
end
The Gemfile checks the ACTIVE_HASH_ACTIVERECORD_VERSION
environment variable, and sets the correct gem version/git repo accordingly. If it gets a blank ACTIVE_HASH_ACTIVERECORD_VERSION
it does not set the version in the Gemfile, so when the bash script calls bundle update activerecord
it conveniently fetches the latest released version.
If you execute bundle update
, it won’t have a ACTIVE_HASH_ACTIVERECORD_VERSION
so it will fetch the latest, so the normal workflow still works as expected.
Finally, it runs the function in each of 3 rubies:
rvm use ruby-1.8.7@active_hash --create
run
rvm use ree-1.8.7@active_hash --create
run
rvm use ruby-1.9.2@active_hash --create
run
echo 'Success!'
RVM allows you to run a command with all of your installed rubies, but in this case I wanted to target 3 particular versions, and ensure that they work. RVM will fail if you don’t have the latest version of each ruby installed, and prompt you to install it. And since set -e
was called at the top of the file, there’s no need to check the return code of any of the functions because the script will fail if any individual command fails.
If you are a gem maintainer, this is an easy way to ensure that your gem is highly compatible with standard Ruby / Rails setups.
About the Author