diff options
authorDavid Rodríguez <>2020-03-29 21:45:39 +0200
committerHiroshi SHIBATA <>2020-05-08 14:13:29 +0900
commit4a417b08aeaf296238059e6abdb465c324073e15 (patch)
parent8250000187726b161e7491be6653dafeebad59b2 (diff)
[rubygems/rubygems] Fix incorrect bundler version being required
In ruby 2.7.0, there's a slight change in bundler's default gemspec file where the executable folder of the bundler gem is `libexec` instead of `exe`. I made that change in to try to simplify the integration of the `bundler` gem with upstream, minimizing the number of changes that need to be made to the gemspec to adapt to the structure of ruby-core. That worked ok, expected for this issue. The new name of the folder including the executable files uncovered a bug in rubygems, which is the following: * In order to be able to use newer versions of default gems, `rubygems` ships with a customized `require` that has knowledge about which files belong to which default gem. If one of these files is required, `rubygems` will detect that and activate its gem mechanism to choose the newest version of the corresponding default gem present in the system (unless a different version has already been activated). It's this part of the custom require: * In order to do that, `rubygems` registers a map of default gems and their files when it is first required: As explained in the method's header, two types of default gem specifications are supported. One of the formats is the style used by some ruby-core gemspec files, where paths inside the `spec.files` array don't include the `spec.require_paths` part. So in this "old style", if a gem ships with a `lib/bundler.rb` file, it will be registered in this array as `spec.files = ["bundler.rb"]`, not as `spec.files = ["lib/bundler.rb"]`. The `Gem.register_default_spec` method "detects" this style by making sure that none of the files in the `spec.files` array start with any of the `spec.require_paths`. * Since in ruby 2.7 the default specification file of the `bundler` gem includes a `libexec/bundle` file, this check would no longer work correctly, because even though the specification file is still "old style", it has one registered file which starts with "lib", one of the "require paths" of the gem. * This means that the gem is incorrectly detected as "new style", but since none of the paths start with "lib/", no files are actually registered, so the gem is not being considered a default gem, and thus the default version is always used with no possibility of being "upgraded". The fix of the problem is simple: check that no files start with `lib/` (or any other require paths), instead of with "lib" which doesn't exclude other potential "non requirable folder" starting with lib, like in the `bundler` case.
Notes: Merged:
2 files changed, 13 insertions, 1 deletions
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 2703525..dbb32b2 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -1217,7 +1217,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
def register_default_spec(spec)
extended_require_paths = {|f| f + "/"}
- new_format = spec.require_paths.any? {|path| spec.files.any? {|f| f.start_with? path } }
+ new_format = extended_require_paths.any? {|path| spec.files.any? {|f| f.start_with? path } }
if new_format
prefix_group = extended_require_paths.join("|")
diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb
index d64a253..6b18bb9 100644
--- a/test/rubygems/test_gem.rb
+++ b/test/rubygems/test_gem.rb
@@ -1750,6 +1750,18 @@ class TestGem < Gem::TestCase
assert_nil Gem.find_unresolved_default_spec("README")
+ def test_register_default_spec_old_style_with_folder_starting_with_lib
+ Gem.clear_default_specs
+ old_style = do |spec|
+ spec.files = ["libexec/bundle", "foo.rb", "bar.rb"]
+ end
+ Gem.register_default_spec old_style
+ assert_equal old_style, Gem.find_unresolved_default_spec("foo.rb")
+ end
def test_use_gemdeps
gem_deps_file = 'gem.deps.rb'.tap(&Gem::UNTAINT)
spec = util_spec 'a', 1