summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Rodríguez <deivid.rodriguez@riseup.net>2020-03-29 21:45:39 +0200
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2020-05-08 14:13:29 +0900
commit4a417b08aeaf296238059e6abdb465c324073e15 (patch)
tree8f8f0fc89db4b6a7366e6491405c80084cf3bc02
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 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: https://github.com/rubygems/rubygems/blob/ea3e6f194df500b028b52b3b64decbd3df1d5ab0/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: https://github.com/rubygems/rubygems/blob/ea3e6f194df500b028b52b3b64decbd3df1d5ab0/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
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/3092
-rw-r--r--lib/rubygems.rb2
-rw-r--r--test/rubygems/test_gem.rb12
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 = 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("|")
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")
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