Capistrano で Bundler 1.0 を使う

現在、CapistranoRails アプリケーション foo を、リモートサーバの /var/rails/foo に配置していると仮定します。

ただし、これまでは Bundler を使っていないとします。

RubyGems のアップデート作業を自動化するため、Bundler 1.0 を導入してみましょう。

※ この記事は Bundler 1.0.0.rc.1 に基づいています。

まず、deploy.rb に deploy:bundle タスクを追加します。


namespace :deploy do
(省略)

desc "Run bundle install"
task :bundle do
run "cd #{release_path} && bundle install #{shared_path}/lib"
end
end

#{release_path} は /var/rails/foo/current に、#{shared_path}/lib は /var/rails/foo/shared/lib に展開されます。

要するに、Rails.root に cd して bundle install /var/rails/foo/shared/lib というコマンドを実行するというタスクです。

bundle install を引数無しで実行すると、システム全体の gem として /usr/local/lib/ruby/gems/1.8/gems 辺りにインストールされてしまいます。

複数の Rails アプリケーションが動いているサーバでこれをやるのはとても危険です。

そこで foo のための gem は /var/rails/foo/shared/lib に隔離するわけです。このやり方は、Bundler の作者 Yehuda Katz も推奨しています。

続いて、次のようなコードを deploy.rb に追加します。


after "deploy:update", :roles => :app do
deploy.bundle
end

ただし、既に after "deploy:update" が定義されている場合は、その中に deploy.bundle を追加します。

このコードは、deploy:update タスクを実行した直後に実行されます。

したがって、新しいアプリケーションをリモートサーバに配置するたびに、Rails.root で bundle install が実行され、古くなった gem が更新されます。

これは、便利ですね!

さて、ひとつ厄介な問題があります。

Bundler は Gemfile に記述された gem を単にインストールしていくだけなので、インストール時にオプションが必要な場合や、rubygems.org からダウンロードできない場合は使えません。

例えば、今 foo アプリケーションが bar という架空の gem を必要としているが、そのパッケージは rubygems.org にはなく、すでにダウンロードしてある ~/src/bar-1.2.3/bar-1.2.3.gem をインストールしなくてはならない、という状況を想定します。

ここで、bundle install /var/rails/foo/shared/lib を実行しても、次のようなエラーが出て止まってしまうでしょう。


Could not find gem 'bar (>= 0, runtime)' in any of the gem sources.

このようなときは、gem コマンドであらかじめ /var/rails/foo/shared/lib に bar をインストールすればOKです。


$ gem install ~/src/bar-1.2.3/bar-1.2.3.gem -i /var/rails/foo/shared/lib/ruby/1.8

末尾の /ruby/1.8 を忘れないこと!

なお、root 権限でシステム全体に bar をインストールしてしまうという方法はダメです。

特定のディレクトリに対して bundle install する場合、システム全体の gem は無視されます。

[参考資料]

[UPDATE]
2010-08-09のコミットにより、自分で deploy:bundle タスクを書く必要はなくなりました。単に、deploy.rb の中で


require 'bundler/capistrano'
と書くだけで、deploy:update の後で deploy install コマンドが自動的に実行されるようになります。

なお、この方法を使う場合、デフォルトで #{shared_path}/bundle に gems がインストールされます。インストール先を変更したい場合は、deploy.rb の中で


set :bundle_dir, "#{shared_path}/lib"
のように書いてください。