Posted at

Ruby 2.6.1 に含まれる Bundler 1.17.2 に不具合があるという話


不具合

先日知った以下の件。

この問題は gem update --system によって default gems としての Bundler を 1.17.3 にすれば回避可能でしょうか?

Ruby 2.6.1 は Ruby 2.5.3 と比較して、メモリ使用量が大きく(数百 MB 単位で)削減される(ケースがある)ことが確認できているため、簡単な Workaround で回避可能であれば個人的には積極的に利用したいところです。


比較

default gems としての Bundler のバージョンと、通常の gem としての Bundler のバージョンの組み合わせについて、実行に問題がなさそうなのかを確認。確認内容があっているのか、あまり自信がない。


Bundler 1.17.3 で作った Gemfile.lock

#
default gems
gem install
ruby -rbundler/setup
bundle exec ruby -rbundler/setup

A
1.17.2
None
指定バージョンを利用可能
default gems のものを利用してしまう

B
1.17.2
1.17.3
bundler の取り違い
指定バージョンを利用可能

C
1.17.3
None
指定バージョンを利用可能
指定バージョンを利用可能

D
1.17.3
2.0.1
指定バージョンを利用可能
指定バージョンを利用可能


Bundler 2.0.1 で作った Gemfile.lock

#
default gems
gem install
ruby -rbundler/setup
bundle exec ruby -rbundler/setup

E
1.17.2
None

bundle install --deployment 不可

bundle install --deployment 不可

F
1.17.2
2.0.1
You must use Bundler 2 or greater with this lockfile.
指定バージョンを利用可能

G
1.17.3
2.0.1
指定バージョンを利用可能
指定バージョンを利用可能


補足(1): Bundler 1.17.3 で作った Gemfile.lock

$ bundle -v

Bundler version 1.17.3
$ bundle init
$ echo 'gem "csv", "3.0.3"' >> Gemfile
$ bundle install
$ cat Gemfile.lock
GEM
remote: https://rubygems.org/
specs:
csv (3.0.3)

PLATFORMS
ruby

DEPENDENCIES
csv (= 3.0.3)

BUNDLED WITH
1.17.3


A

$ docker run --rm -it -v(pwd):/mnt centos:7 bash

# yum install -y https://github.com/feedforce/ruby-rpm/releases/download/2.6.1/ruby-2.6.1-1.el7.centos.x86_64.rpm
# ruby -v
ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-linux]
# gem -v
3.0.1
# bundle -v
Bundler version 1.17.2
# gem list bundler

*** LOCAL GEMS ***

bundler (default: 1.17.2)
# cp /mnt/Gemfile* .
# bundle install --deployment
# ruby -rbundler/setup -rcsv -e 'puts CSV::VERSION'
3.0.3
# bundle exec ruby -rbundler/setup -rcsv -e 'puts CSV::VERSION'
3.0.4


B

$ docker run --rm -it -v(pwd):/mnt centos:7 bash

# yum install -y https://github.com/feedforce/ruby-rpm/releases/download/2.6.1/ruby-2.6.1-1.el7.centos.x86_64.rpm
# gem install bundler -v 1.17.3 --no-document
# bundle -v
Bundler version 1.17.3
# gem list bundler

*** LOCAL GEMS ***

bundler (1.17.3, default: 1.17.2)
# cp /mnt/Gemfile* .
# bundle install --deployment
# ruby -rbundler/setup -rcsv -e 'puts CSV::VERSION'
Traceback (most recent call last):
9: from /usr/lib64/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
8: from /usr/lib64/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
7: from /usr/lib64/ruby/2.6.0/bundler/setup.rb:10:in `<top (required)>'
6: from /usr/lib64/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler.rb:107:in `setup'
5: from /usr/lib64/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/runtime.rb:26:in `setup'
4: from /usr/lib64/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/runtime.rb:26:in `map'
3: from /usr/lib64/ruby/2.6.0/forwardable.rb:230:in `each'
2: from /usr/lib64/ruby/2.6.0/forwardable.rb:230:in `each'
1: from /usr/lib64/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/runtime.rb:31:in `block in setup'
/usr/lib64/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/runtime.rb:319:in `check_for_activated_spec!': You have already activated bundler 1.17.3, but your Gemfile requires bundler 1.17.2. Prepending `bundle exec` to your command may solve this. (Gem::LoadError)
# bundle exec ruby -rbundler/setup -e 'puts Bundler::VERSION'
1.17.3
# bundle exec ruby -rbundler/setup -rcsv -e 'puts CSV::VERSION'
3.0.3


C

$ docker run --rm -it -v(pwd):/mnt centos:7 bash

# yum install -y https://github.com/feedforce/ruby-rpm/releases/download/2.6.1/ruby-2.6.1-1.el7.centos.x86_64.rpm
# gem update --system
# bundle -v
Bundler version 1.17.3
# gem list bundler

*** LOCAL GEMS ***

bundler (default: 1.17.3)
# cp /mnt/Gemfile* .
# bundle install --deployment
# ruby -rbundler/setup -e 'puts Bundler::VERSION'
1.17.3
# ruby -rbundler/setup -rcsv -e 'puts CSV::VERSION'
3.0.3
# bundle exec ruby -rbundler/setup -e 'puts Bundler::VERSION'
1.17.3
# bundle exec ruby -rbundler/setup -rcsv -e 'puts CSV::VERSION'
3.0.3


D

$ docker run --rm -it -v(pwd):/mnt centos:7 bash

# yum install -y https://github.com/feedforce/ruby-rpm/releases/download/2.6.1/ruby-2.6.1-1.el7.centos.x86_64.rpm
# gem install bundler --no-document
# gem update --system
# gem -v
3.0.2
# bundle -v
Bundler version 2.0.1
# gem list bundler

*** LOCAL GEMS ***

bundler (2.0.1, default: 1.17.3)
# cp /mnt/Gemfile* .
# bundle install --deployment
# ruby -rbundler/setup -e 'puts Bundler::VERSION'
1.17.3
# ruby -rbundler/setup -rcsv -e 'puts CSV::VERSION'
3.0.3
# bundle exec ruby -rbundler/setup -e 'puts Bundler::VERSION'
1.17.3
# bundle exec ruby -rbundler/setup -rcsv -e 'puts CSV::VERSION'
3.0.3


補足: Bundler 2.0.1 で作った Gemfile.lock

$ bundle -v

Bundler version 2.0.1
$ bundle init
$ echo 'gem "csv", "3.0.3"' >> Gemfile
$ bundle install
$ cat Gemfile.lock
GEM
remote: https://rubygems.org/
specs:
csv (3.0.3)

PLATFORMS
ruby

DEPENDENCIES
csv (= 3.0.3)

BUNDLED WITH
2.0.1


E

$ docker run --rm -it -v(pwd):/mnt centos:7 bash

# yum install -y https://github.com/feedforce/ruby-rpm/releases/download/2.6.1/ruby-2.6.1-1.el7.centos.x86_64.rpm
# ruby -v
ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-linux]
# gem -v
3.0.1
# bundle -v
Bundler version 1.17.2
# gem list bundler

*** LOCAL GEMS ***

bundler (default: 1.17.2)
# cp /mnt/Gemfile* .
# bundle install --deployment
Traceback (most recent call last):
2: from /usr/bin/bundle:23:in `<main>'
1: from /usr/lib64/ruby/2.6.0/rubygems.rb:302:in `activate_bin_path'
/usr/lib64/ruby/2.6.0/rubygems.rb:283:in `find_spec_for_exe': Could not find 'bundler' (2.0.1) required by your /Gemfile.lock. (Gem::GemNotFoundException)
To update to the latest version installed on your system, run `bundle update --bundler`.
To install the missing version, run `gem install bundler:2.0.1`


F

$ docker run --rm -it -v(pwd):/mnt centos:7 bash

# yum install -y https://github.com/feedforce/ruby-rpm/releases/download/2.6.1/ruby-2.6.1-1.el7.centos.x86_64.rpm
# gem install bundler -v 2.0.1 --no-document
# bundle -v
Bundler version 2.0.1
# gem list bundler

*** LOCAL GEMS ***

bundler (2.0.1, default: 1.17.2)
# cp /mnt/Gemfile* .
# bundle install --deployment
# ruby -rbundler/setup -e 'puts Bundler::VERSION'
You must use Bundler 2 or greater with this lockfile.
# ruby -rbundler/setup -rcsv -e 'puts CSV::VERSION'
You must use Bundler 2 or greater with this lockfile.
# bundle exec ruby -rbundler/setup -e 'puts Bundler::VERSION'
2.0.1
# bundle exec ruby -rbundler/setup -rcsv -e 'puts CSV::VERSION'
3.0.3


G

$ docker run --rm -it -v(pwd):/mnt centos:7 bash

# yum install -y https://github.com/feedforce/ruby-rpm/releases/download/2.6.1/ruby-2.6.1-1.el7.centos.x86_64.rpm
# gem install bundler --no-document
# gem update --system
# gem -v
3.0.2
# bundle -v
Bundler version 2.0.1
# gem list bundler

*** LOCAL GEMS ***

bundler (2.0.1, default: 1.17.3)
# cp /mnt/Gemfile* .
# bundle install --deployment
# ruby -rbundler/setup -e 'puts Bundler::VERSION'
2.0.1
# ruby -rbundler/setup -rcsv -e 'puts CSV::VERSION'
3.0.3
# bundle exec ruby -rbundler/setup -e 'puts Bundler::VERSION'
2.0.1
# bundle exec ruby -rbundler/setup -rcsv -e 'puts CSV::VERSION'
3.0.3


おまけ

gem install bundlergem update --system の実行順序によって、実行ファイル(ラッパー) bundle が conflict する様です。


bundler → rubygems

特に警告が出ることなく、また /usr/bin/bundle の内容が変わることはなかった。

# gem install bundler --no-document

...
# gem update --system
...


rubygems → bundler

gem update --system によって /usr/bin/bundle が更新され、 gem install bundler で上書きすると Ruby 同梱同等の内容に戻った。

# gem update --system

...
# gem install bundler --no-document
...
bundler's executable "bundle" conflicts with /usr/bin/bundle
Overwrite the executable? [yN] y
...


RPM でインストールした /usr/bin/bundle

#!/usr/bin/ruby

#
# This file was generated by RubyGems.
#
# The application 'bundler' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'

version = ">= 0.a"

str = ARGV.first
if str
str = str.b[/\A_(.*)_\z/, 1]
if str and Gem::Version.correct?(str)
version = str
ARGV.shift
end
end

if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('bundler', 'bundle', version)
else
gem "bundler", version
load Gem.bin_path("bundler", "bundle", version)
end


gem update --system で暗黙的に上書きされた /usr/bin/bundle

Bundler の exe/bundle とほぼ同等。違いは shebang のみ。

#!/usr/bin/ruby

# frozen_string_literal: true

# Exit cleanly from an early interrupt
Signal.trap("INT") do
Bundler.ui.debug("\n#{caller.join("\n")}") if defined?(Bundler)
exit 1
end

require "bundler"
# Check if an older version of bundler is installed
$LOAD_PATH.each do |path|
next unless path =~ %r{/bundler-0\.(\d+)} && $1.to_i < 9
err = String.new
err << "Looks like you have a version of bundler that's older than 0.9.\n"
err << "Please remove your old versions.\n"
err << "An easy way to do this is by running `gem cleanup bundler`."
abort(err)
end

require "bundler/friendly_errors"
Bundler.with_friendly_errors do
require "bundler/cli"

# Allow any command to use --help flag to show help for that command
help_flags = %w[--help -h]
help_flag_used = ARGV.any? {|a| help_flags.include? a }
args = help_flag_used ? Bundler::CLI.reformatted_help_args(ARGV) : ARGV

Bundler::CLI.start(args, :debug => true)
end


gem install bundler で上書きした /usr/bin/bundle

#!/usr/bin/ruby

#
# This file was generated by RubyGems.
#
# The application 'bundler' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'

version = ">= 0.a"

str = ARGV.first
if str
str = str.b[/\A_(.*)_\z/, 1]
if str and Gem::Version.correct?(str)
version = str
ARGV.shift
end
end

if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('bundler', 'bundle', version)
else
gem "bundler", version
load Gem.bin_path("bundler", "bundle", version)
end


rbenv install 2.6.1 で作られた versions/2.6.1/bin/bundle

#!/home/koshigoe/.rbenv/versions/2.6.1/bin/ruby

#
# This file was generated by RubyGems.
#
# The application 'bundler' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'

version = ">= 0.a"

str = ARGV.first
if str
str = str.b[/\A_(.*)_\z/, 1]
if str and Gem::Version.correct?(str)
version = str
ARGV.shift
end
end

if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('bundler', 'bundle', version)
else
gem "bundler", version
load Gem.bin_path("bundler", "bundle", version)
end