Capistrano で Bundler 1.0 を使う
現在、Capistrano で Rails アプリケーション 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 は無視されます。
[参考資料]
- http://yehudakatz.com/2010/07/26/whats-new-in-bundler-1-0-0-rc-1/
- http://d.hatena.ne.jp/mirakui/20100703/1278165723
- http://blog.nicolasblanco.fr/2010/07/30/capistrano-task-for-bundler-1-0/
[UPDATE]
2010-08-09のコミットにより、自分で deploy:bundle タスクを書く必要はなくなりました。単に、deploy.rb の中で
と書くだけで、deploy:update の後で deploy install コマンドが自動的に実行されるようになります。
require 'bundler/capistrano'
なお、この方法を使う場合、デフォルトで #{shared_path}/bundle に gems がインストールされます。インストール先を変更したい場合は、deploy.rb の中で
のように書いてください。
set :bundle_dir, "#{shared_path}/lib"