1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

[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 https://github.com/ruby/ruby/pull/2380 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:

ea3e6f194d/lib/rubygems/core_ext/kernel_require.rb (L77-L85)

* In order to do that, `rubygems` registers a map of default gems and
their files when it is first required:

ea3e6f194d/lib/rubygems.rb (L1247-L1276)

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.

https://github.com/rubygems/rubygems/commit/94df740c2b
This commit is contained in:
David Rodríguez 2020-03-29 21:45:39 +02:00 committed by Hiroshi SHIBATA
parent 8250000187
commit 4a417b08ae
Notes: git 2020-05-08 14:14:15 +09:00
2 changed files with 13 additions and 1 deletions

View file

@ -1217,7 +1217,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
def register_default_spec(spec)
extended_require_paths = spec.require_paths.map {|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("|")

View file

@ -1750,6 +1750,18 @@ class TestGem < Gem::TestCase
assert_nil Gem.find_unresolved_default_spec("README")
end
def test_register_default_spec_old_style_with_folder_starting_with_lib
Gem.clear_default_specs
old_style = Gem::Specification.new 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