Skip to content

Instantly share code, notes, and snippets.

@rwilcox
Created May 25, 2012 14:48
Show Gist options
  • Save rwilcox/2788518 to your computer and use it in GitHub Desktop.
Save rwilcox/2788518 to your computer and use it in GitHub Desktop.
Capistrano, system wide RVM and creating gemsets
<h1>Introduction to the Problem</h1>
Capistrano is the standard way to deploy Rails apps. Yesterday I was using Capistrano to deploy to a machine where I had installed RVM (Ruby Version Manager) at the system level.
I manually set up Ruby 1.8.7 and Ruby 1.9.2, because I need to run two applications on that machine (one a Ruby 1.8.7 app and one a Ruby 1.9.2 app). Using RVM for production deploys is great for this.
My <code>cap deploy:setup</code> task, however, complained that Ruby 1.9.2 wasn't installed on the machine.
<em>That's funny, because I did install it</em>, I thought. After banging my head up against the problem for a few hours, I finally posted the question to Stackoverflow.com: (<a href="http://stackoverflow.com/questions/6949014/capistrano-deploying-to-system-wide-rvm-not-seeing-installed-rubies">Capistrano deploying to system wide RVM not seeing installed Rubies</a>)
I got my answer: the message about the Ruby not being installed was misleading, it <em>actually</em> meant that the <strong>gemset</strong> wasn't installed. Which it wasn't (I was planning on doing that as part of the <code>cap deploy:setup</code> task.
<h1>Creating gemsets in your <code>deploy:setup</code> step</h1>
Ideally I want <code>cap delpoy:setup</code> to take care of eveything for me: installing some rubies, creating the appropriate gemsets, you name it. Because automated deployments mean everything should be automated (amirite?).
But then I get errors like this when I'm trying to create the gemset I want to use!
It's non-obvious how to do this - and in fact the obvious way <em>will not work!</em>
<h2>Background</h2>
You see, <code>require 'rvm/capistrano'</code> hooks into the low levels of Capistrano's <code>run</code> function, meaning everything happens in the context of the ruby+gemset that you declared in your Capfile. (Technically <code>rvm/capistrano</code> uses a user shell called <code>rvm-shell</code>, instead of <code>bash</code> or <code>sh</code>. This shell knows enough to properly set your paths to Ruby etc etc.
Normally this is awesome - that means that Capistrano knows about your Gemset, and installs gems there etc etc. Capistrano's <code>run</code> command just does the right thing.
However, there are two cases where you want things to happen outside of <code>rvm-shell</code>:
<ol>
<li>Installing the Ruby</li>
<li>Creating the Gemset</li>
</ol>
If you try to do these things using <code>run</code>, Capistrano will give an error about Ruby not being installed, like it gave me. Even if RVM is trying to say, "I don't see that gemset", the error message will be about a missing Ruby.
<h1>The obvious solution, and why it doesn't work (as a conversation)</h1>
The obvious thing you might try in your Capfile is this command:
<code>run "rvm install 1.9.2"</code>
Except, as I explained above, that won't work. Here's what's going on, as a conversation.
<p><strong>You, to Capistrano:</strong> Run this command for me</p>
<p><strong>Capistrano, to remote machine:</strong> Hey, I want to log into this machine, using the <code>rvm-shell</code> command, using Ruby 1.9.2 and gemset MY_APP. When I'm logged in please execute <code>rvm install 1.9.2</code></p>
<p><strong>Remote machine, to Capistrano:</strong> Could not log you in, an error happened when firing up <code>rvm-shell</code>. I could not find the ruby/gemset you wanted, so I can't set the Ruby paths appropriately. I'm giving up and stopping because I can't possibly do whatever that command was that you wanted me to execute</p>
<p><strong>Capistrano, to you:</strong> I couldn't install that Ruby you wanted me to install because I can't activate that Ruby you want me to use for the gemset you want me to use - I don't think that Ruby is installed!</p>
<p><strong>You:</strong> Le sigh.</p>
<h1>The solution: avoid <code>rvm-shell</code> for Ruby installation AND gemset creation</h1>
You might think that you need to avoid <code>rvm-shell</code> just for the installation of your Ruby. In fact, you need to avoid <code>rvm-shell</code> for <strong>both</strong> the installation of Ruby <em>and the creation of your gemset!</em>
<h1>How to avoid <code>rvm-shell</code></h1>
Define this method in your Capfile.
<code style="white-space: pre">
def disable_rvm_shell(&block)
old_shell = self[:default_shell]
self[:default_shell] = nil
yield
self[:default_shell] = old_shell
end
</code>
Now, in the context of that block, <code>run</code> will execute commands by using sh/bash as a shell, instead of <code>rvm-shell</code>.
In your Capfile, install Ruby and your gemsets by:
<code style="white-space: pre">
disable_rvm_shell do
run "rvm install 1.9.2"
run "rvm use 1.9.2@MY_APP --create"
end
</code>
This installation process must come before any other command in your <code>deploy:setup</code> chain.
<h1>Conclusion</h1>
And that's that - I really hope this helps someone out there!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment