diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 06f73dd1bd..3e357f76d7 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -138,7 +138,7 @@ module Bundler @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 # @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 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 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 # 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 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 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 1a8906c47e..7d41a2bc92 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -11,7 +11,7 @@ module Bundler @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 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 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 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 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 6d3065a836..b45a74cf8b 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 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