Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crash when when no platform specific matches exist and show a proper error #5168

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 34 additions & 29 deletions bundler/lib/bundler/resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ def search_for(dependency_proxy)
end
nested.reduce([]) do |groups, (version, specs)|
next groups if locked_requirement && !locked_requirement.satisfied_by?(version)
next groups unless specs.any? {|spec| spec.match_platform(platform) }

specs_by_platform = Hash.new do |current_specs, current_platform|
current_specs[current_platform] = select_best_platform_match(specs, current_platform)
Expand All @@ -145,7 +146,7 @@ def search_for(dependency_proxy)
next groups if @resolving_only_for_ruby

spec_group = SpecGroup.create_for(specs_by_platform, @platforms, platform)
groups << spec_group if spec_group
groups << spec_group

groups
end
Expand Down Expand Up @@ -262,30 +263,37 @@ def verify_gemfile_dependencies_are_found!(requirements)
"If you are updating multiple gems in your Gemfile at once,\n" \
"try passing them all to `bundle update`"
else
source = source_for(name)
specs = source.specs.search(name)
versions_with_platforms = specs.map {|s| [s.version, s.platform] }
cache_message = begin
" or in gems cached in #{Bundler.settings.app_cache_path}" if Bundler.app_cache.exist?
rescue GemfileNotFound
nil
end
message = String.new("Could not find gem '#{SharedHelpers.pretty_dependency(requirement)}' in #{source}#{cache_message}.\n")
message << "The source contains the following versions of '#{name}': #{formatted_versions_with_platforms(versions_with_platforms)}" if versions_with_platforms.any?
message = gem_not_found_message(name, requirement, source_for(name))
end
raise GemNotFound, message
end
end

def formatted_versions_with_platforms(versions_with_platforms)
version_platform_strs = versions_with_platforms.map do |vwp|
version = vwp.first
platform = vwp.last
version_platform_str = String.new(version.to_s)
version_platform_str << " #{platform}" unless platform.nil? || platform == Gem::Platform::RUBY
version_platform_str
def gem_not_found_message(name, requirement, source, extra_message = "")
specs = source.specs.search(name)
matching_part = name
requirement_label = SharedHelpers.pretty_dependency(requirement)
cache_message = begin
" or in gems cached in #{Bundler.settings.app_cache_path}" if Bundler.app_cache.exist?
rescue GemfileNotFound
nil
end
specs_matching_requirement = specs.select {| spec| requirement.matches_spec?(spec) }

if specs_matching_requirement.any?
specs = specs_matching_requirement
matching_part = requirement_label
requirement_label = "#{requirement_label} #{requirement.__platform}"
end
version_platform_strs.join(", ")

message = String.new("Could not find gem '#{requirement_label}'#{extra_message} in #{source}#{cache_message}.\n")

if specs.any?
message << "\nThe source contains the following gems matching '#{matching_part}':\n"
message << specs.map {|s| " * #{s.full_name}" }.join("\n")
end

message
end

def version_conflict_message(e)
Expand Down Expand Up @@ -357,19 +365,16 @@ def version_conflict_message(e)

metadata_requirement = name.end_with?("\0")

o << "Could not find gem '" unless metadata_requirement
o << SharedHelpers.pretty_dependency(conflict.requirement)
o << "'" unless metadata_requirement
if conflict.requirement_trees.first.size > 1
o << ", which is required by "
o << "gem '#{SharedHelpers.pretty_dependency(conflict.requirement_trees.first[-2])}',"
extra_message = if conflict.requirement_trees.first.size > 1
", which is required by gem '#{SharedHelpers.pretty_dependency(conflict.requirement_trees.first[-2])}',"
else
""
end
o << " "

o << if metadata_requirement
"is not available in #{relevant_source}"
if metadata_requirement
o << "#{SharedHelpers.pretty_dependency(conflict.requirement)}#{extra_message} is not available in #{relevant_source}"
else
"in #{relevant_source}.\n"
o << gem_not_found_message(name, conflict.requirement, relevant_source, extra_message)
end
end
end,
Expand Down
9 changes: 7 additions & 2 deletions bundler/spec/commands/exec_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,10 @@ def bin_path(a,b,c)
let(:expected) { "" }
let(:expected_err) { <<-EOS.strip }
Could not find gem 'rack (= 2)' in locally installed gems.
The source contains the following versions of 'rack': 0.9.1, 1.0.0

The source contains the following gems matching 'rack':
* rack-0.9.1
* rack-1.0.0
Run `bundle install` to install missing gems.
EOS

Expand All @@ -894,7 +897,9 @@ def bin_path(a,b,c)
let(:expected) { "" }
let(:expected_err) { <<-EOS.strip }
Could not find gem 'rack (= 2)' in locally installed gems.
The source contains the following versions of 'rack': 1.0.0

The source contains the following gems matching 'rack':
* rack-1.0.0
Run `bundle install` to install missing gems.
EOS

Expand Down
6 changes: 3 additions & 3 deletions bundler/spec/install/gemfile/git_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
gem "foo", "1.1", :git => "#{lib_path("foo-1.0")}"
G

expect(err).to include("The source contains the following versions of 'foo': 1.0")
expect(err).to include("The source contains the following gems matching 'foo':\n * foo-1.0")
end

it "complains with version and platform if pinned specs don't exist in the git repo" do
Expand All @@ -106,7 +106,7 @@
end
G

expect(err).to include("The source contains the following versions of 'only_java': 1.0 java")
expect(err).to include("The source contains the following gems matching 'only_java':\n * only_java-1.0-java")
end

it "complains with multiple versions and platforms if pinned specs don't exist in the git repo" do
Expand All @@ -128,7 +128,7 @@
end
G

expect(err).to include("The source contains the following versions of 'only_java': 1.0 java, 1.1 java")
expect(err).to include("The source contains the following gems matching 'only_java':\n * only_java-1.0-java\n * only_java-1.1-java")
end

it "still works after moving the application directory" do
Expand Down
51 changes: 51 additions & 0 deletions bundler/spec/install/gemfile/specific_platform_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,57 @@
bundle "install --verbose"
end

it "does not resolve if the current platform does not match any of available platform specific variants for a top level dependency" do
build_repo2 do
build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "x86_64-linux" }
build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "universal-darwin-20" }
end

gemfile <<~G
source "#{file_uri_for(gem_repo2)}"

gem "sorbet-static", "0.5.6433"
G

simulate_platform "arm64-darwin-21" do
bundle "install", :raise_on_error => false
end

expect(err).to include <<~ERROR.rstrip
Could not find gem 'sorbet-static (= 0.5.6433) arm64-darwin-21' in rubygems repository #{file_uri_for(gem_repo2)}/ or installed locally.

The source contains the following gems matching 'sorbet-static (= 0.5.6433)':
* sorbet-static-0.5.6433-universal-darwin-20
* sorbet-static-0.5.6433-x86_64-linux
ERROR
end

it "does not resolve if the current platform does not match any of available platform specific variants for a transitive dependency" do
build_repo2 do
build_gem("sorbet", "0.5.6433") {|s| s.add_dependency "sorbet-static", "= 0.5.6433" }
build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "x86_64-linux" }
build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "universal-darwin-20" }
end

gemfile <<~G
source "#{file_uri_for(gem_repo2)}"

gem "sorbet", "0.5.6433"
G

simulate_platform "arm64-darwin-21" do
bundle "install", :raise_on_error => false
end

expect(err).to include <<~ERROR.rstrip
Could not find gem 'sorbet-static (= 0.5.6433) arm64-darwin-21', which is required by gem 'sorbet (= 0.5.6433)', in rubygems repository #{file_uri_for(gem_repo2)}/ or installed locally.

The source contains the following gems matching 'sorbet-static (= 0.5.6433)':
* sorbet-static-0.5.6433-universal-darwin-20
* sorbet-static-0.5.6433-x86_64-linux
ERROR
end

private

def setup_multiplatform_gem
Expand Down