Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions lib/rubygems/commands/fetch_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

require_relative "../command"
require_relative "../local_remote_options"
require_relative "../remote_fetcher"
require_relative "../resolver"
require_relative "../version_option"

class Gem::Commands::FetchCommand < Gem::Command
Expand Down Expand Up @@ -79,31 +81,59 @@ def fetch_gems
platform = Gem.platforms.last
gem_names = get_all_gem_names_and_versions

# A BestSet reads a source's compact index when it serves one, the same way
# gems are looked up when installing them.
remote_set = Gem::Resolver::BestSet.new

gem_names.each do |gem_name, gem_version|
gem_version ||= version
dep = Gem::Dependency.new gem_name, gem_version
dep.prerelease = options[:prerelease]
suppress_suggestions = !options[:suggest_alternate]

specs_and_sources, errors =
Gem::SpecFetcher.fetcher.spec_for_dependency dep
remote_specs, errors = find_remote_specs dep, remote_set

if platform
filtered = specs_and_sources.select {|s,| s.platform == platform }
specs_and_sources = filtered unless filtered.empty?
filtered = remote_specs.select {|s| s.platform == platform }
remote_specs = filtered unless filtered.empty?
end

spec, source = specs_and_sources.max_by {|s,| s }
remote_spec = remote_specs.max_by {|s| [s.version, Gem::Platform.sort_priority(s.platform)] }

if spec.nil?
if remote_spec.nil?
show_lookup_failure gem_name, gem_version, errors, suppress_suggestions, options[:domain]
exit_code |= 2
next
end
source.download spec

spec = remote_spec.spec
remote_spec.source.download spec
say "Downloaded #{spec.full_name}"
end

exit_code
end

# Find specs in +set+ that match +dep+ and can be used on this platform,
# along with the reasons any other spec was rejected.

def find_remote_specs(dep, set)
set.prerelease = dep.prerelease?

request = Gem::Resolver::DependencyRequest.new dep, nil

matching, mismatched = set.find_all(request).partition do |spec|
Gem::Platform.match_spec? spec
end

[matching, set.errors + platform_mismatches(mismatched)]
end

def platform_mismatches(specs)
specs.group_by {|spec| [spec.name, spec.version] }.map do |(name, version), group|
mismatch = Gem::PlatformMismatch.new name, version
group.each {|spec| mismatch.add_platform spec.platform.to_s }
mismatch
end
end
end
62 changes: 61 additions & 1 deletion test/rubygems/test_gem_commands_fetch_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def test_execute_platform

@cmd.options[:args] = %w[a]

@fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] = util_gzip(Marshal.dump([
@fetcher.data["#{@gem_repo}specs.#{Gem.marshal_version}.gz"] = util_gzip(Marshal.dump([
Gem::NameTuple.new(a2_spec.name, a2_spec.version, a2_spec.platform),
Gem::NameTuple.new(a2_universal_darwin_spec.name, a2_universal_darwin_spec.version, a2_universal_darwin_spec.platform),
]))
Expand All @@ -100,6 +100,66 @@ def test_execute_platform
"#{a2_universal_darwin_spec.full_name} not fetched")
end

def test_execute_compact_index
specs = spec_fetcher do |fetcher|
fetcher.gem "a", 1
fetcher.gem "a", 2
end

util_setup_compact_index(*specs.values)

@cmd.options[:args] = %w[a]

execute_with_exit_code

a2 = specs["a-2"]

assert_path_exist(File.join(@tempdir, a2.file_name),
"#{a2.full_name} not fetched")

assert_includes @fetcher.paths, "#{@gem_repo}info/a"
refute_includes @fetcher.paths, "#{@gem_repo}specs.#{Gem.marshal_version}.gz"
end

def test_execute_compact_index_platform
specs = spec_fetcher do |fetcher|
fetcher.gem "a", 2
fetcher.gem("a", 2) {|s| s.platform = "universal-darwin" }
end

util_setup_compact_index(*specs.values)

@cmd.options[:args] = %w[a]

util_set_arch "arm64-darwin20" do
execute_with_exit_code
end

a2_universal_darwin = specs["a-2-universal-darwin"]

assert_path_exist(File.join(@tempdir, a2_universal_darwin.file_name),
"#{a2_universal_darwin.full_name} not fetched")
end

def test_execute_compact_index_platform_mismatch
specs = spec_fetcher do |fetcher|
fetcher.spec("a", 2) {|s| s.platform = "java" }
end

util_setup_compact_index(*specs.values)

@cmd.options[:args] = %w[a]

execute_with_term_error

expected = <<-EXPECTED
ERROR: Could not find a valid gem 'a' (>= 0), here is why:
Found a (2), but was for platform java
EXPECTED

assert_equal expected, @ui.error
end

def test_execute_specific_prerelease
specs = spec_fetcher do |fetcher|
fetcher.gem "a", 2
Expand Down