[前][次][番号順一覧][スレッド一覧]

ruby-changes:67397

From: David <ko1@a...>
Date: Tue, 31 Aug 2021 19:06:45 +0900 (JST)
Subject: [ruby-changes:67397] 9a25a98c6b (master): [rubygems/rubygems] Show all missing gems when using a bundle before installing it

https://git.ruby-lang.org/ruby.git/commit/?id=9a25a98c6b

From 9a25a98c6b43ef32a8d2e36ef9fa4f5b00ad283c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20Rodr=C3=ADguez?= <deivid.rodriguez@r...>
Date: Fri, 23 Jul 2021 23:49:13 +0200
Subject: [rubygems/rubygems] Show all missing gems when using a bundle before
 installing it

Not only the first one that's missing.

This also allows us to simplify things.

https://github.com/rubygems/rubygems/commit/69718a9509
---
 lib/bundler/definition.rb           | 38 ++++++++++++++++++-------------------
 lib/bundler/spec_set.rb             | 26 +++++++++----------------
 spec/bundler/install/yanked_spec.rb | 30 +++++++++++++++++++++++++++++
 3 files changed, 57 insertions(+), 37 deletions(-)

diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 06f73dd..3e357f7 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -138,7 +138,7 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/definition.rb#L138
         @unlock[:gems] ||= @dependencies.map(&:name)
       else
         eager_unlock = expand_dependencies(@unlock[:gems] || [], true)
-        @unlock[:gems] = @locked_specs.for(eager_unlock, false, false, false).map(&:name)
+        @unlock[:gems] = @locked_specs.for(eager_unlock, false, false).map(&:name)
       end
 
       @dependency_changes = converge_dependencies
@@ -191,14 +191,6 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/definition.rb#L191
     # @return [Bundler::SpecSet]
     def specs
       @specs ||= materialize(requested_dependencies)
-    rescue GemNotFound => e # Handle yanked gem
-      gem_name, gem_version = extract_gem_info(e)
-      locked_gem = @locked_specs[gem_name].last
-      raise if locked_gem.nil? || locked_gem.version.to_s != gem_version || !@remote
-      raise GemNotFound, "Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \
-                         "no longer be found in that source. That means the author of #{locked_gem} has removed it. " \
-                         "You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \
-                         "removed in order to install."
     end
 
     def new_specs
@@ -210,9 +202,7 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/definition.rb#L202
     end
 
     def missing_specs
-      missing = []
-      resolve.materialize(requested_dependencies, missing)
-      missing
+      resolve.materialize(requested_dependencies).missing_specs
     end
 
     def missing_specs?
@@ -498,6 +488,20 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/definition.rb#L488
 
     def materialize(dependencies)
       specs = resolve.materialize(dependencies)
+      missing_specs = specs.missing_specs
+
+      if missing_specs.any?
+        missing_specs.each do |s|
+          locked_gem = @locked_specs[s.name].last
+          next if locked_gem.nil? || locked_gem.version != s.version || !@remote
+          raise GemNotFound, "Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \
+                             "no longer be found in that source. That means the author of #{locked_gem} has removed it. " \
+                             "You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \
+                             "removed in order to install."
+        end
+
+        raise GemNotFound, "Could not find #{missing_specs.map(&:full_name).join(", ")} in any of the sources"
+      end
 
       unless specs["bundler"].any?
         bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last
@@ -735,7 +739,7 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/definition.rb#L739
             # if we won't need the source (according to the lockfile),
             # don't error if the path/git source isn't available
             next if @locked_specs.
-                    for(requested_dependencies, false, true, false).
+                    for(requested_dependencies, false, true).
                     none? {|locked_spec| locked_spec.source == s.source }
 
             raise
@@ -755,7 +759,7 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/definition.rb#L759
 
       resolve = SpecSet.new(converged)
       @locked_specs_incomplete_for_platform = !resolve.for(expand_dependencies(requested_dependencies & deps), true, true)
-      resolve = SpecSet.new(resolve.for(expand_dependencies(deps, true), false, false, false).reject{|s| @unlock[:gems].include?(s.name) })
+      resolve = SpecSet.new(resolve.for(expand_dependencies(deps, true), false, false).reject{|s| @unlock[:gems].include?(s.name) })
       diff    = nil
 
       # Now, we unlock any sources that do not have anymore gems pinned to it
@@ -859,12 +863,6 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/definition.rb#L863
       current == proposed
     end
 
-    def extract_gem_info(error)
-      # This method will extract the error message like "Could not find foo-1.2.3 in any of the sources"
-      # to an array. The first element will be the gem name (e.g. foo), the second will be the version number.
-      error.message.scan(/Could not find (\w+)-(\d+(?:\.\d+)+)/).flatten
-    end
-
     def compute_requires
       dependencies.reduce({}) do |requires, dep|
         next requires unless dep.should_include?
diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
index 1a8906c..7d41a2b 100644
--- a/lib/bundler/spec_set.rb
+++ b/lib/bundler/spec_set.rb
@@ -11,7 +11,7 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/spec_set.rb#L11
       @specs = specs
     end
 
-    def for(dependencies, check = false, match_current_platform = false, raise_on_missing = true)
+    def for(dependencies, check = false, match_current_platform = false)
       handled = []
       deps = dependencies.dup
       specs = []
@@ -33,11 +33,6 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/spec_set.rb#L33
           end
         elsif check
           return false
-        elsif raise_on_missing
-          others = lookup[dep.name] if match_current_platform
-          message = "Unable to find a spec satisfying #{dep} in the set. Perhaps the lockfile is corrupted?"
-          message += " Found #{others.join(", ")} that did not match the current platform." if others && !others.empty?
-          raise GemNotFound, message
         end
       end
 
@@ -71,8 +66,8 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/spec_set.rb#L66
       lookup.dup
     end
 
-    def materialize(deps, missing_specs = nil)
-      materialized = self.for(deps, false, true, !missing_specs)
+    def materialize(deps)
+      materialized = self.for(deps, false, true)
 
       materialized.group_by(&:source).each do |source, specs|
         next unless specs.any?{|s| s.is_a?(LazySpecification) }
@@ -84,16 +79,9 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/spec_set.rb#L79
 
       materialized.map! do |s|
         next s unless s.is_a?(LazySpecification)
-        spec = s.__materialize__
-        unless spec
-          unless missing_specs
-            raise GemNotFound, "Could not find #{s.full_name} in any of the sources"
-          end
-          missing_specs << s
-        end
-        spec
+        s.__materialize__ || s
       end
-      SpecSet.new(missing_specs ? materialized.compact : materialized)
+      SpecSet.new(materialized)
     end
 
     # Materialize for all the specs in the spec set, regardless of what platform they're for
@@ -117,6 +105,10 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/spec_set.rb#L105
       end
     end
 
+    def missing_specs
+      select {|s| s.is_a?(LazySpecification) }
+    end
+
     def merge(set)
       arr = sorted.dup
       set.each do |set_spec|
diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb
index 6d3065a..b45a74c 100644
--- a/spec/bundler/install/yanked_spec.rb
+++ b/spec/bundler/install/yanked_spec.rb
@@ -70,4 +70,34 @@ RSpec.context "when using gem before installing" do https://github.com/ruby/ruby/blob/trunk/spec/bundler/install/yanked_spec.rb#L70
     expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.")
     expect(err).to_not include("You'll need to update your bundle to a different version of rack (0.9.1) that hasn't been removed in order to install.")
   end
+
+  it "does not suggest the author has yanked the gem when using more than one gem, but shows all gems that couldn't be found in the source" do
+    gemfile <<-G
+      source "#{file_uri_for(gem_repo1)}"
+      gem "rack", "0.9.1"
+      gem "rack_middleware", "1.0"
+    G
+
+    lockfile <<-L
+      GEM
+        remote: #{file_uri_for(gem_repo1)}
+        specs:
+          rack (0.9.1)
+          rack_middleware (1.0)
+
+      PLATFORMS
+        ruby
+
+      DEPENDENCIES
+        rack (= 0.9.1)
+        rack_middleware (1.0)
+    L
+
+    bundle :list, :raise_on_error => false
+
+    expect(err).to include("Could not find rack-0.9.1, rack_middleware-1.0 in any of the sources")
+    expect(err).to_not include("Your bundle is locked to rack (0.9.1), but that version could not be found in any of the sources listed in your Gemfile.")
+    expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.")
+    expect(err).to_not include("You'll need to update your bundle to a different version of rack (0.9.1) that hasn't been removed in order to install.")
+  end
 end
-- 
cgit v1.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]