Using Jenkins With Rails

I finally set up a continuous deployment (CD) server for Ducky Guidance. I decided to use Jenkins since we already have a Windows Jenkins server for our Sitecore projects.

Setting up the project

Since we use RVM for our development and production enviroments, I installed RVM for the Jenkins user. Then I set Jenkins to use /bin/bash for shell commands. I created a .bashrc file to load RVM. With that, I was ready to create the build step for our project.

The build task

source ~/.bashrc                              # Loads RVM
cd .                                          # Loads the RVM environment set in the .rvmrc file
cp ~/duckyg-database.yml config/database.yml  # Copies the database.yml locally
bundle install                                # Installs gems
rake db:schema:load                           # Loads all the database schema
rake                                          # Runs RSpec and Cucumber

I use the source ~/.bashrc line to load RVM for the build task. After that, I use cd . to load the Ruby version and gemset we use for Ducky Guidance.

Since I made sure to not store our database.yml in our git repository, I created one in the Jenkins user home folder. I copy it from the Jenkins user's home directory with every build.

The rake db:schema:load and the bundle install commands ensure that all the migrations are loaded and all the gems are installed. From there, we can call rake, which will call RSpec and Cucumber.

RSpec and Cucumber HTML Formatted output

RSpec and Cucumber both have HTML results formatters. Since Jenkins will serve up HTML output, I thought it would be useful to have both tools output their results to an HTML file in the workspace. That way, I can quickly scan the full color, formatted HTML instead of digging through the console output. To accomplish this, I have to call RSpec and Cucumber separately, so that I can redirect their outputs to separate files.

It was easy to tell Cucumber to use the HTML formatter, as Cucumber looks at the CUCUMBER_FORMAT environment variable. I added the following line to my build task:

CUCUMBER_FORMAT=html rake cucumber > jenkins/cucumber.html

RSpec looks at the SPEC_OPTS environment variable, so you can call RSPEC in a similar way with the following line:

SPEC_OPTS="--format html" rake rspec > jenkins/rspec.html

My build task now looks slightly different than it did when I first set up the project:

source ~/.bashrc 
cd .
cp ~/duckyg-database.yml config/database.yml
[ -d jenkins ] && rm -rf jenkins
mkdir jenkins
bundle install > jenkins/bundler.txt
rake db:schema:load > jenkins/schema_load.txt
SPEC_OPTS="--format html" rake rspec > jenkins/rspec.html
CUCUMBER_FORMAT=html rake cucumber > jenkins/cucumber.html

As you can see, I decided to redirect the rake db:schema:load and bundle install commands to files as well. I stored all the files in a jenkins folder in the workspace group the output of the various commands called in my build tasks. I also added a link to the jenkins folder in the workspace to my job description, so that I can easily get to the output files when I view the job.

Capistrano

So far, I have Ducky Guidance set up as a Continuous Integration project. It is testing the project with every new commit, but it is not deploying it. The next step was to add another build task which would call Capistrano after all the RSpec and Cucumber tests passed. The contents of that build task are below:

source ~/.bashrc
cd .
cap development deploy:migrations > jenkins/deploy.txt
curl https://demo.duckyg.com --insecure > /dev/null

I use curl to warm up the app after it is deployed, so the first person to visit the site is no longer penalized with longer load times.

Before using Jenkins for deploys, I was invoking capistrano locally. I was utilizing SSH Agent Forwarding for authenticating code fetches on our staging and production servers. So when I used the above build task initally, it was erroring out when git would fetch the code. It seems that the way that Jenkins invokes shell commands prevents ssh from respecting the .ssh/config settings. To solve the problem, I created an SSH key on my deployment targets. Now capistrano uses the private key on the staging and production servers to authenticate git commands.

Conclusion

It wasn't too hard to fit Jenkins into my workflow. There were a couple of hiccups (like explicitly loading RVM, and placing SSH keys on our staging and production servers), but no show stoppers.