Introduction to the Problem
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.
cap deploy:setup task, however, complained that Ruby 1.9.2 wasn’t installed on the machine.
That’s funny, because I did install it, I thought. After banging my head up against the problem for a few hours, I finally posted the question to Stackoverflow.com: (Capistrano deploying to system wide RVM not seeing installed Rubies)
I got my answer: the message about the Ruby not being installed was misleading, it actually meant that the gemset wasn’t installed. Which it wasn’t (I was planning on doing that as part of the
cap deploy:setup task.
Creating gemsets in your
Ideally I want
cap delpoy:setup 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 will not work!
require 'rvm/capistrano' hooks into the low levels of Capistrano’s
run function, meaning everything happens in the context of the ruby+gemset that you declared in your Capfile. (Technically
rvm/capistrano uses a user shell called
rvm-shell, instead of
sh. 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
run command just does the right thing.
However, there are two cases where you want things to happen outside of
- Installing the Ruby
- Creating the Gemset
If you try to do these things using
run, 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.
The obvious solution, and why it doesn’t work (as a conversation)
The obvious thing you might try in your Capfile is this command:
run "rvm install 1.9.2"
Except, as I explained above, that won’t work. Here’s what’s going on, as a conversation.
You, to Capistrano: Run this command for me
Capistrano, to remote machine: Hey, I want to log into this machine, using the
rvm-shell command, using Ruby 1.9.2 and gemset MY_APP. When I’m logged in please execute
rvm install 1.9.2
Remote machine, to Capistrano: Could not log you in, an error happened when firing up
rvm-shell. 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
Capistrano, to you: 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!
You: Le sigh.
The solution: avoid
rvm-shell for Ruby installation AND gemset creation
You might think that you need to avoid
rvm-shell just for the installation of your Ruby. In fact, you need to avoid
rvm-shell for both the installation of Ruby and the creation of your gemset!
How to avoid
Define this method in your Capfile.
`def disablervmshell(&block) oldshell = self[:defaultshell] self[:default_shell] = nil yield self[:default_shell] = old_shell end
Now, in the context of that block,
run will execute commands by using sh/bash as a shell, instead of
In your Capfile, install Ruby and your gemsets by:
disable_rvm_shell do run "rvm install 1.9.2" run "rvm use 1.9.2@MY_APP --create" end
This installation process must come before any other command in your
And that’s that - I really hope this helps someone out there!