multistage 環境で capistrano-rbenv を使うときは rbenv_path の扱いに注意!

タイトル読んで納得した人は読み飛ばしてください。
VM の設定が完了するのを待ちつつ Fulentd Casual Talks #2 の ust 聞きながらこれを書いてます。

capistrano-rbenv 使ってますか?

デプロイ先で rbenv を使う場合は capistrano-rbenv が非常に便利です。

config/deploy.rb に

# rbenv setting
require 'capistrano-rbenv'
set :rbenv_ruby_version, '1.9.3-p374'

set :default_environment, {
  'RBENV_ROOT' => "#{rbenv_path}",
  'PATH' => "#{rbenv_path}/shims:#{rbenv_path}/bin:$PATH"
}

と書いておくだけで rbenv を利用し始めることができます。

まだ rbenv をインストールしていないのであれば

$ cap deploy:setup

とするだけで、rbenv もインストールしてくれます。すばらしいですね。

multistage と capistrano-rbenv の組み合わせ

でも、一個だけ罠があります。
capistrano を使うときに、ほぼ必須とも言える multistage プラグイン(capistrano/ext/multistage)と
この capistrano-rbenv を同時に使うとハマる箇所がひとつあるのです。
それは先ほどの設定で出てきた rbenv_path という変数です。
よく見ると :default_environment を設定するときに利用しているやつです。

実はこの rbenv_path、値を参照する際にデプロイ先と通信する(インストールされているか確認する)ため、
multistage を有効にした状態で先ほどの設定を行うと、エラーで失敗します。

$ bundle exec cap dev deploy:setup 
  * executing "echo $HOME/.rbenv"
no servers found to match {:once=>true, :eof=>true}

multistage プラグインがデプロイ先ホストを決定する前に rbenv_path が通信しようとしてしまうためです。
これを防ぐためには :default_environment を決めるタイミングを multistage の実行後にずらします。

namespace :rbenv do
  task :setup_shellenv do
    set :default_environment, {
      'RBENV_ROOT' => "#{rbenv_path}",
      'PATH' => "#{rbenv_path}/shims:#{rbenv_path}/bin:$PATH"
    }
  end
  after 'multistage:ensure', 'rbenv:setup_shellenv'
end

そうすると、以下のように実行タイミングが調整されてうまく rbenv を利用することができます。

bundle exec cap dev deploy:setup                                                                                      ~/Dropbox/gnavi/gnavi-gauth-infra
    triggering load callbacks
  * 2013-02-18 11:49:28 11:49:28 == Currently executing `dev'
    triggering start callbacks for `deploy:setup'
  * 2013-02-18 11:49:28 11:49:28 == Currently executing `multistage:ensure'
    triggering after callbacks for `multistage:ensure'
  * 2013-02-18 11:49:28 11:49:28 == Currently executing `rbenv:setup_shellenv'
  * executing "echo $HOME/.rbenv"
    servers: ["localhost"]
    [localhost] executing command
    command finished in 73ms
(以下略)


Web で探したら、rbenv_path を使わず直接値を書いている人もいましたが、
ユーザや OS が変わると必ずしも /home/foo/.rbenv になるとは限らず、
いずれコピペで失敗しそうなのでこんな風にしてみました。

この部分も capistrano-rbenv が吸収してくれると幸せなんですけどねえ。