Help us understand the problem. What is going on with this article?

bundler 1.11.0 の変更で spring が起動しない件について

More than 3 years have passed since last update.

rails c で以下の様な例外が発生して起動しない事があります。これは、spring が子プロセスを立ち上げるところで発生する例外です。

/Users/koshigoe/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load such file -- bundler/setup (LoadError)
        from /Users/koshigoe/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/commands.rb:33:inv `<module:Spring>'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/commands.rb:4:in `<top (required)>'
        from /Users/koshigoe/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from /Users/koshigoe/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/application.rb:77:in `preload'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/application.rb:143:in `serve'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/application.rb:131:in `block in run'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/application.rb:125:in `loop'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/application.rb:125:in `run'
        from /Users/koshigoe/tmp/spring-debug/vendor/bundle/gems/spring-1.6.1/lib/spring/application/boot.rb:18:in `<top (required)>'
        from /Users/koshigoe/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from /Users/koshigoe/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        from -e:1:in `<main>'

bundler の変更によって、 Bundler.with_clean_env のスコープ(ブロック)では ENV['RUBYLIB'] から bundler のパス(path/to/bundler-x.y.z/lib) が取り除かれる様になりました。なお、bundler のパスが ENV['RUBYLIB'] に追加されているのは bundler の仕業です。

spring では、子プロセスが require 'bundler/setup' する場面があります。この子プロセスは Bundler.with_clean_env の中で Process.spawn されて生まれています。

ここで、bundler を gem install でインストールし、アプリケーションが依存する gem を bundle install --path vendor/bundle でインストールしている場合を考えます(BUNDLE_DISABLE_SHARED_GEMS: '1')。bundler 管理下の GEM_HOME (vendor/bundle) には bundler がいないため、vendor/bundle しか見ない場合は require 'bundle/setup'LoadError となってしまいます。

さて、vendor/bundle に bundler があれば例外は避けられるのでしょうか?

$ GEM_HOME=vendor/bundle gem install bundler --no-document
$ bundle exec rails c
Running via Spring preloader in process 45717
Loading development environment (Rails 4.2.5)
(001): >>

予想通り、 LoadError 例外は発生しなくなりました。

spring 側の変更で対応する場合はどうするのが良いのでしょうか。乱暴ですが ENV['RUBYLIB'] に path/to/bundler-x.y.z/lib を追加してしまうのが簡単でしょう。Bundler.with_clean_env で取り除いたものを戻すというあたりが気持ち悪いですね。

diff --git a/lib/spring/application_manager.rb b/lib/spring/application_manager.rb
index 8b34b9f..2699ba2 100644
--- a/lib/spring/application_manager.rb
+++ b/lib/spring/application_manager.rb
@@ -92,13 +92,18 @@ module Spring
     def start_child(preload = false)
       @child, child_socket = UNIXSocket.pair

+      bundler_lib_path = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR).grep(%r{/bundler-\d[^/]*/lib\z}).first
       Bundler.with_clean_env do
+        rubylib = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR)
+        rubylib.unshift bundler_lib_path if bundler_lib_path
+
         @pid = Process.spawn(
           {
             "RAILS_ENV"           => app_env,
             "RACK_ENV"            => app_env,
             "SPRING_ORIGINAL_ENV" => JSON.dump(Spring::ORIGINAL_ENV),
-            "SPRING_PRELOAD"      => preload ? "1" : "0"
+            "SPRING_PRELOAD"      => preload ? "1" : "0",
+            "RUBYLIB"             => rubylib.join(File::PATH_SEPARATOR)
           },
           "ruby",
           "-I", File.expand_path("../..", __FILE__),

spring 側で require 'bundler/setup' しているという事は、spring は bundler に依存しているという事だと思ってしまうのですが、RUNTIME DEPENDENCIES に bundler がないので、自分は何かを見落としている様な気がしています。

テストが書ければ PR を作ってみようかとも思うのですが、spring 本体のテストコードでは、テストコード(テスト用アプリ)が依存する gem を test/apps/gems/2.2.3/gems/ 以下に bundler も含めて配置する仕組みになっています。bundler とアプリケーションの依存 gem を分けて配置する構成のテストを書ける気がしません。

以下、まとめ。

  • 敗北を認めよう
  • bundler 1.10.6 を使ってれば大丈夫
  • bundler 1.11.x でも vendor/bundle に入れちゃえば大丈夫(--path vendor/bundle)
  • 多分 bundler install --system という選択肢もある

2016/02/07 追記

コメントにて、v1.6.3 で修正された事を教えていただきました。

-I オプションでパスを指定したんですね。

取り急ぎ、v1.6.3 を使って rails c が起動できる事が確認できました。

koshigoe
feedforce
『「働く」を豊かにする。』というミッションを掲げ、企業向けネットサービスを開発・提供しています。
https://www.feedforce.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away