Ninjas on a Penny Farthing

  posted  

RVM - System Wide Installs & Capistrano Integration


Following on from the last few posts about rvm, today I’m going to talk about two related but different aspects I’ve been working on – one that makes life easier and one that standardises a process that has been possible for a while but never officially documented (as far as I know). Both are related to making deployment with RVM easier.

System Wide Installs

One of the simplest (yet, hopefully, most useful) things I’ve worked on is the formalization of a way to have a single rvm install for multiple users. It’s been possible for a while but required a little bit of configuration. Now, we’ve made it so that it is as simple to install rvm system wide as it is for a single user. Instead of running:

bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )

And doing all the configuration work yourself, you will soon be able to run:

bash < <( curl -L http://bit.ly/rvm-install-system-wide )

Which will handle almost all it for you. Note that the url before didn’t work – the new one should work correctly (and we plan on hopefully to moving it somewhere more verifiable than bit.ly). If you’re uncomfortable running the above command directly, I suggest doing the alternative (that allows you to inspect the file first):

curl -L http://bit.ly/rvm-install-system-wide > rvm-install-system-wide
# Inspect the file here
bash < ./rvm-install-system-wide

Lastly, there is a nice handy helper added in /usr/local/lib/rvm so users can simply add the following to the end of their profile:

[[ -s '/usr/local/lib/rvm' ]] && source '/usr/local/lib/rvm'

And it will automatically load rvm from ~/.rvm/ if available, otherwise from the system wide install at /usr/local/rvm

The Slightly More Technical Explanation

This approach is very similar to that of rvm-install-head except that it also does the following:

  1. Installs it into /usr/local
  2. Sets up a group (either set and export rvm_group_name before hand, or use the default of rvm) and adds root as a member
  3. Sets up directory permissions correctly for rvm.
  4. Sets up /etc/rvmrc correctly for a system-wide install

Please note that this is currently primarily tested on Ubuntu and will not work on OSX. Patches are, as always, welcome. Also, a lot of credit goes to an excellent blog post by Lee Jarvis that has served as the reference up until now and still serves as a reference in terms of the actual implementation.

Simpler Capistrano Support

The second of the production related changes is better capistrano support out of the box with rvm. First, before I discuss what’s actually involved, please note that this currently requires the head version of rvm but it will be included in future versions – so either append ~/.rvm/lib to your load path in the example or require by the absolute path. Once the next gem version is released, the below code should work as is.

So, without further ado, in new versions of rvm integrating with capistrano has gone from manually specifying GEM_HOME, GEM_PATH, BUNDLE_PATH, PATH and so on (with a fixed ruby version) in :default_environment to something as simple as adding the following to your Capfile:

require 'rvm/capistrano'
set :rvm_ruby_string, 'some-ruby' # Defaults to 'default'

And you’re done. Once that’s setup, any command you run in capistrano will be run with the correct environment variables set. Optionally, you also have the following variables you can set that will affect the capistrano script’s behaviour:

  • :rvm_type:system or :user – user looks for rvm in $HOME/.rvm where as system uses the /usr/local as set for system wide installs.
  • :rvm_bin_path – the full path to your rvm install’s bin folder if the above dont work for your setup.

The Slightly More Technical Explanation

This works by taking advantage of the ability to set a default shell for the commands capistrano calls – in recent commits to rvm, $rvm_bin_path (aka ~/.rvm/bin for most people) now contains rvm-shell – a thin wrapper around bash that does the following:

  • If the first argument doesn’t match ^-, sets rvm_shell_ruby_string to hold the value of it.
  • If rvm_shell_ruby_string is present, uses the given ruby via rvm (e.g. ree@rails3 and the like are supported)
  • exec bash with the rest of the arguments.

In essence, it means you can startup a bash instance with the environment variables set (but without rvm itself) by doing something similar to:

  • ~/.rvm/bin/rvm-shell rbx -c 'which ruby' – should point to rubinius
  • rvm_shell_ruby_string=ree ~/.rvm/bin/rvm-shell -c 'which ruby' – should point to ree
  • ~/.rvm/bin/rvm-shell -c 'which ruby' – should act like a normal bash, system ruby.

The rvm/capistrano basically just tells rvm to override the default capistrano shell to be rvm-shell.

Conclusion

All of the docs here will be added to the rvm site in the very near future – it’s more as a way to show the work that’s been going in to rvm. It’s worth noting also that this is just the start of the sorts of integration we’ll be working on to making using rvm ith other existing tools even easier.