PIYO - Tech & Life -

railsのサブコマンドが使えなくなる問題の原因はbinstubs

半年ほど前ですがアプリケーションをデプロイした先でコンソールを起動できない問題に遭遇しました。よくわからなかったのでQA@ITで質問を投げたのですが、結局解決には至らず。

AWSにデプロイしたRailsアプリケーションのコンソールを起動したいがUsageが出てしまう - QA@IT

その代わり同じような機能を実現するスクリプトを書いて回避したというところで僕としてはこの問題をスルーしていました。

デプロイ先のサーバーでrails consoleできないときの対応とCapistranoからの使い方 - ぴよログ

その原因が今更ながらわかったのでメモしておきます。

Rails4以降+binstubsで起こる問題

bundlerにbinstubsという機能があります。

$ bundle install --binstubs

とやると、実行ファイル系のgemを実行するのに便利なスクリプトを./binに生成してくれます。$ bundle exec cap ...などと実行していたところを$ ./bin/capとタイプするだけでいける、みたいな。

ところがこの--binstubsオプションがRails4が予め用意しているファイルを上書きしてしまいます。試しにRails4のアプリケーションを作って確認してみます。

$ rails new sample -T --skip-bundle 
$ cd sample/bin
$ ls 
bundle rails rake

このように3つのファイルが最初から用意されています。この状況でbundle install --binstubsを実行するとrailsrakeがbundlerが生成したものに上書きされてしまいrailsコマンドが正しく動かないという状況が生まれるようです。

元々あるものとbundlerが生成したものの違いはコードを読めばわかると思いますがまだ読んでません。。。

最近ではbinstubsが上書きされている場合に警告メッセージを出る機能が追加されたようです。新しいめのRails4を使用している人は気づくのかも。僕んときはベータ版だったのでそんなのなかったよ。

Rails 4 prints help for “rails new” when running “rails console” by indirect · Pull Request #9843 · rails/rails

なぜデプロイ先のサーバーで使えなかったか

僕はローカルではbinstubsを使っていなかったので、上記の問題はアプリケーションをデプロイしたときにのみ遭遇しました。原因がなかなかわからなかったのは、デプロイをCapistranoに任せていた上、デプロイ用設定ファイルの書き方をいろいろな記事からつまみ食いしたせいで、自分でもよくわかったいなかったからです。

さっきよく見たらこんな風に書いてありました。

# config/deploy.rb for Capistrano 2.15.5

# Bundler
require 'bundler/capistrano'
set :bundle_flags, "--deployment --binstubs"

--binstubs指定してる(´・ω・`)

特に必要なわけではないので--binstubsを外したらうまくいきました。

binstubsを使いたいときの回避策

$ bundle install --binstubs=.bin

このように--binstubs=ディレクトリとすると./binを上書きしなくて済みます。