Vlad, RVM and Bundler sittin’ in a tree


Vlad the Deployer(Thanks to Chad for nudging me to write this post 🙂

Up until now, my only experience with deploying Rails or any other Ruby-based web application

has been to use the “standard” Capistrano.  For the main Rails 3 application I’ve been building

the past few months, Capistrano has been fine and was mostly a set it and forget it kinda thing.

One of my clients asked me to do a small side project to perform a simple task and naturally I chose

the most excellent Sinatra framework. I could go on and on about the awesomeness of Sinatra, but that’s

not the point of this post. Suffice it to say when I finished this small Sinatra application, I needed to

deploy it somehow. Manual deployments are for the birds, so my first knee-jerk reaction was to get

Capistrano wired up to do the automated deployment. But after taking a glance at my existing Capistrano

deployment script for my Rails 3 application, it seemed like overkill for this tiny Sinatra ditty.

The first alternative I ran across was a deployment tool named Vlad the Deployer

(which clearly gets an award for the best name ever). What attracted me right off the bat was the simplicity of its usage

and configuration. I also liked the fact that it uses pure Rake for everything, instead of some of the

“magic” that Capristrano does under the hood.

Getting Vlad up and running is pretty straightforward, but I did have a couple issues getting it integrated

into my particular deployment process. Like all good Ruby developers 2 of my must use tools are RVM

& Bundler.  Bundler is great for managing gem dependencies and RVM keeps you sane when working with many different versions

of Ruby and gems at the same time.

One thing I like to do during deployments is make sure “bundle install” gets run on the server.  This ensures that any new gems I’ve introduced in my Gemfile get automatically installed on the server

during deployment. Usually this wouldn’t be much of an issue, but I’m a pretty big fan of RVM, so much

so that I actually run it on my staging/production servers as well. RVM does some serious magic under

the hood, including altering various environment variables affecting the path. Here is the series of

steps I had to take to get Vlad to properly run “bundle install” during deployment. (NOTE: I’m mainly

posting this to hopefully get feedback on a better way. I really don’t like my solution too much.)

Minor server preparation

Before I show the deploy script itself, you do need to make sure your server is set up properly with

RVM and Bundler installed in the global gemset. Here are 2 great articles that describe this process:

 

Extend Vlad’s update Rake task

Since Vlad uses simple Rake tasks for everything, it’s easy to “tack on” steps before or after the

built in Vlad tasks. In this case I wanted to run my Bundler command right after the built in update

process was complete. Here’s one easy way to do that:</p>

FYI, if you want to run something before the update process starts, you can simply add a dependency

to the built in update task like so:</p>

Create a Bundler task

Next I created a separate task (see next section about remote_task) to perform the Bundler command I

needed to run on the server and invoked it inside of Vlad’s built in update task:</p>

Remotely running commands via SSH

Before I show the commands necessary, it’s important to understand that all of this will be run in

the context of an SSH session. Thankfully, there was a nice feature of Vlad that was extracted out

into its own gem known as remote_task.  This is a handy way to run Rake tasks in the context of remote

servers and is used heavily under the hood with Vlad. We’re also using it here for our custom Bundler

task and a “run” method can be called with whatever commands you want to be run on the remote server in

an SSH session.

Step by step

For clarity I put each command into its own local variable, each of which I’ll describe below.

Initialize RVM

When you login to a server via SSH, you have set of environment variables which include how paths

are resolved when running commands. Luckily, RVM takes care of all of that for us. Usually when using

RVM you simply load it via your .bashrc, but for some reason I couldn’t get this working in the

context of the SSH session used as part of the remote_task. I’m sure this is due to my lack of bash and

*nix skills which I’m actively trying to beef up. But to work around it for now, I just manually source

the RVM bootstrap script myself:</p>

Trust your RVM gemset

I’m not going to dive into RVM gemsets as part of this post, but just think of it as a way to manage gems

in isolation from other applications and environments. I like to use project-specific gemsets for everything

I do to keep things nice and clean. A nice companion to gemsets is the use of an .rvmrc file to

automatically switch to the correct gemset when navigating to your application’s directory. Creating a

.rvmrc is stupidly simple:</p>

Starting in version 1.0 of RVM,

there was a security measure put in place to force you to “trust” .rvmrc

files when changing into a directory with a .rvmrc for the first time. Normally this is fine, but it

presented an issue in the context of an automated script. This security measure can be disabled by this

next command:</p>

This tells RVM that I explicity trust the .rvmrc located in my release_path which is the root of my

application on the server.

Run bundle install – take 1

With RVM all loaded up, we can now issue our bundle command to install any new dependencies if necessary.

So naturally I tried the command below:</p>

But this blew up in my face with a nasty exception:</p>

Run bundle install – take 2

I had read somewhere previously (sorry, can’t remember exactly where) about sometimes needing to

explicitly specify the target path for the “bundle install” command. In this case I can just use

the $BUNDLE_PATH environment variable that RVM manages for me:</p>

This seems to fix the exception above, but I’ll be honest, I’m not exactly sure why yet. (And yes,

that does bug the heck out of me)

Putting it all together

Now that we have all of our commands ready to go, we can simply call the built in “run” method

and pass in each command concatenated one after another:</p>

If all is well, you should see a nice green message from Bundler saying your bundle is complete.

</post>

As I mentioned before I’m not all that happy with this solution, as it seems like there is probably

a better way to get Vlad, RVM and Bundler all working nicely together. I’d be really interested

to know of a better way.

Anyways, I hope this post benefits somebody in the future. Even if it’s myself a year from now. 🙂

RVM, Bash Scripting and Rails 3 Edge