Bundlerの動作に疑問を持ったきっかけ
数か月前の話になりますが、@jnchitoさんの以下のQiita記事を読ませていただいて --path vendor/bundle
について改めて考える機会がありました。
bundle install時に--path vendor/bundleを付ける必要性は本当にあるのか、もう一度よく考えてみよう
https://qiita.com/jnchito/items/99b1dbea1767a5095d85
この記事で特に心動かされたのは、(--path vendor/bundle
を付けても付けなくても)どっちでもいいなら付けない方を好む理由として挙げられていた**「systemにインストールするのがBundlerのデフォルトで、pathを指定するのがオプションだから」**という一文でした。確かにデフォルト設定で問題が起きないならデフォルトのまま使うに越したことがないなぁと思います。
ただ、gemをグローバルにインストールした場合で1個だけ疑問に思った動作がありました。
上記記事にもありますが、「グローバルにインストールされる」というのはすなわち、Bundlerを使ってインストールしたgemが開発マシン内のどこでも使える状態になる(bundle installしたプロジェクト以外の場所でも使える)ことを意味します。
どこでも使えるとは、例えば静的解析ツールRuboCopをbundle install
でグローバルにインストールすれば、どこのディレクトリにいようがrubocop
コマンドを実行できる!ということですが、自分の環境ではそのような動作にはならなかったのです…。
$ echo "source 'https://rubygems.org'\n\ngem 'rubocop'" >>| Gemfile
$ bundle install
$ bundle exec rubocop -v # bundle execを付けるともちろん大丈夫
0.75.0
$ rubocop -v # bundle execを付けないとコマンドが見つからない…
zsh: command not found: rubocop
これについて、何故そうなるのかやっと理解できました。
先に結論
-
rbenvを使用している環境の場合、
bundle install
しただけではshims
ディレクトリが更新されない参考
https://github.com/rbenv/rbenv/pull/638#issuecomment-59375024 -
rbenv rehash
を明示的に行うことで、shims
ディレクトリが更新されて、どこのディレクトリにいようがrubocop
コマンドを実行できる! -
rbenvを使用していない環境(aptで直接インストール等)であれば、今回の事象はそもそも起きない
自分の環境
- Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-65-generic x86_64)
- rbenv 1.1.2-4-g577f046
- ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
- Bundler version 2.0.2
色々試したこと
gem install rubocop
したらどうなのか??
そもそもBundlerを経由しなかった場合どうなるのか知りたかったので試してみました。
$ gem install rubocop
$ rubocop -v # ちゃんとコマンドが見つかる
0.75.0
大丈夫そうです。ということはBundlerが原因…と思いましたが、そういえばrbenvを当たり前のように使っていたので、rbenvを使わなかったらどうなるのか試してみました。
sudo apt install ruby
で入れたRubyだとどうなのか??
厳密にはrbenvでインストールするrubyバージョンと合わせるべきですが、今回は横着しました…。
rbenvを使用していないまっさらな環境で以下を実行します。
$ sudo apt install -y ruby
$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]
$ sudo gem install bundler
$ echo "source 'https://rubygems.org'\n\ngem 'rubocop'" >>| Gemfile
$ bundle install # 本件と関係ないエラーが出る場合があるが、ruby-dev等 足りないライブラリをインストールすると解消する
$ bundle exec rubocop -v # bundle execを付けるともちろん大丈夫
0.75.0
$ rubocop -v # bundle execを付けなくても大丈夫!
0.75.0
なるほど、ということはrubyのバージョンに因る問題か、rbenvに何かあるのかな?
…詳細は書きませんが、rbenvでruby 2.5.1をインストールして試しても同様だったので、rbenvに何かありそうです。
rbenvの仕組み
rbenvのGitHubリポジトリを読むと、rbenvのshims
について記述があります。
rbenvはPATH
の先頭にshims
ディレクトリを挿入することで、rubyのバージョンを変更した際のコマンド切替をよしなに制御してくれています。
その他参考
rbenvの使い方と仕組みについて
https://qiita.com/Kodak_tmo/items/73147ed4f0eec54d6e94
古い記事だとrbenv install
実行後はshims
を更新する為にrbenv rehash
を実行すること!と書いてあったりするのですが、
5年ほど前のPull requestsでrbenv rehash
を自動で処理するように変更されています。
なので、普段rbenv rehash
を実行することはあんまりないんじゃないかなー…と思います。(違っていたらごめんなさい…)
今回の件、なんとなくshims
が更新されてないんじゃない?と思ったので明示的にrbenv rehash
したらどうだろうと思い試してみました。
$ echo "source 'https://rubygems.org'\n\ngem 'rubocop'" >>| Gemfile
$ bundle install
$ bundle exec rubocop -v # bundle execを付けるともちろん大丈夫
0.75.0
$ rubocop -v # bundle execを付けないとコマンドが見つからない…
zsh: command not found: rubocop
$ rbenv rehash # shimsディレクトリを更新すると…
$ rubocop -v # bundle execを付けなくても大丈夫!
0.75.0
思っていた動作になりました!
Pull requestsをよくよく読んだら書いてあった
先ほどのrbenv rehash
が不要になったPull requestsをよくよく読んだら書いてありました!
bundle install
した際 複数回rbenv rehash
が実行されるのを防ぐ為にそうなっているとのこと。Bundlerの並列性の為にrbenv rehash
を一回だけ処理するのは難しかったとのことです。
そもそもBundlerを使用している時点でbundle exec
を付けてコマンド実行すると思うので実用上何も問題は起きないと思います。ですが、引っかかっていた些細な疑問が解決して良かったです!