Installing ruby with Capistrano & rbenv
While we’re supposed to create docker(y) images and deploy these to the cloud, I’m still comfortable deploying and maintaining quite a range of applications using Capistrano (this builds on the battle tested server management process that I outlined more than 7 years ago). But Capistrano and its plugins are typically aimed at performing application level tasks, and not so much about configuring the environment.
I typically install ruby using rbenv
. To deploy ruby apps using rbenv a Capistrano plugin exist (capistrano/rbenv) but it is missing the commands to install and/or update the ruby installation.
This snippet presented here adds a few commands:
-
cap rbenv:install
# installs rbenv -
cap rbenv:update
# updates rbenv & installs the desired ruby version
Additionally it reimplements the cap rbenv:validate
method, as by default it prevents running any capistrano task when the ruby version doesn’t match.
# Capistrano deploy fragment that adds rbenv:install, rbenv:update and reimplements rbenv:validate with a non-exiting warning.
#
# this assumes "capistrano/rbenv" is `require`d in Capfile
#
set :rbenv_ruby, File.read(File.expand_path("../.ruby-version", __dir__)).strip
Rake::Task["rbenv:validate"].clear_actions
namespace :rbenv do
desc "Install rbenv"
task :install do
on roles(:setup) do
begin
execute "git clone https://github.com/rbenv/rbenv.git ~/.rbenv"
rescue SSHKit::Command::Failed
puts "rbenv already installed, updating..."
execute "cd ~/.rbenv && git pull"
end
# execute "~/.rbenv/bin/rbenv init"
execute "mkdir -p ~/.rbenv/plugins"
begin
execute "git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build"
rescue SSHKit::Command::Failed
puts "rbenv/ruby-build plugin already installed, updating..."
execute "cd ~/.rbenv/plugins/ruby-build && git pull"
end
execute "~/.rbenv/bin/rbenv install -s #{fetch(:rbenv_ruby)}"
execute "~/.rbenv/bin/rbenv global #{fetch(:rbenv_ruby)}"
execute "~/.rbenv/bin/rbenv local #{fetch(:rbenv_ruby)}"
execute "export PATH=\"$HOME/.rbenv/bin:$PATH\" && eval \"$(rbenv init -)\" && ruby -v"
execute "export PATH=\"$HOME/.rbenv/bin:$PATH\" && eval \"$(rbenv init -)\" && gem install bundler --no-document"
if fetch(:rbenv_ruby).nil?
puts "\nPlease uncomment the line `# set :rbenv_ruby, File.read('.ruby-version').strip` to enable capistrano rbenv"
end
execute :echo, "'export PATH=\"$HOME/.rbenv/bin:$PATH\"'", ">>", "~/.bashrc"
execute :echo, "'eval \"$(rbenv init -)\"'", ">>", "~/.bashrc"
end
end
task :validate do
on release_roles(fetch(:rbenv_roles)) do |host|
rbenv_ruby = fetch(:rbenv_ruby)
if rbenv_ruby.nil?
info 'rbenv: rbenv_ruby is not set; ruby version will be defined by the remote hosts via rbenv'
end
# don't check the rbenv_ruby_dir if :rbenv_ruby is not set (it will always fail)
unless rbenv_ruby.nil? || (test "[ -d #{fetch(:rbenv_ruby_dir)} ]") || ARGV.include?("rbenv:install")
warn "rbenv: #{rbenv_ruby} is not installed or not found in #{fetch(:rbenv_ruby_dir)} on #{host}"
exit 1
end
end
end
desc "update ruby"
task :update do
on roles(:app), in: :sequence do
execute "git -C ~/.rbenv/plugins/ruby-build pull"
execute "RBENV_ROOT=~/.rbenv ~/.rbenv/bin/rbenv install #{fetch(:rbenv_ruby)} -s -k"
execute "RBENV_ROOT=~/.rbenv ~/.rbenv/bin/rbenv global #{fetch(:rbenv_ruby)}"
execute "RBENV_ROOT=~/.rbenv RBENV_VERSION=#{fetch(:rbenv_ruby)} ~/.rbenv/bin/rbenv exec gem install -N bundler"
end
end
end