From 339227363ce0cf967fa17efa4489d823932ddabd Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 23 Dec 2020 08:45:19 +0900 Subject: [PATCH] Merge RubyGems 3.2.3 and Bundler 2.2.3 --- lib/bundler.rb | 9 +- lib/bundler/cli/update.rb | 2 +- lib/bundler/compact_index_client/cache.rb | 18 +-- .../compact_index_client/gem_parser.rb | 28 ++++ lib/bundler/definition.rb | 40 ++++-- lib/bundler/gem_helpers.rb | 52 ++++--- lib/bundler/lazy_specification.rb | 21 ++- lib/bundler/resolver/spec_group.rb | 30 ++-- lib/bundler/rubygems_integration.rb | 5 - lib/bundler/spec_set.rb | 13 +- lib/bundler/version.rb | 2 +- lib/rubygems.rb | 2 +- lib/rubygems/dependency_installer.rb | 1 + lib/rubygems/installer.rb | 23 --- lib/rubygems/remote_fetcher.rb | 2 +- lib/rubygems/request_set.rb | 15 +- lib/rubygems/resolver.rb | 7 +- lib/rubygems/resolver/api_set.rb | 47 ++++--- lib/rubygems/resolver/api_set/gem_parser.rb | 20 +++ lib/rubygems/resolver/api_specification.rb | 7 +- lib/rubygems/resolver/best_set.rb | 2 +- lib/rubygems/resolver/index_specification.rb | 15 ++ lib/rubygems/resolver/installer_set.rb | 64 ++++++++- lib/rubygems/resolver/spec_specification.rb | 14 ++ lib/rubygems/resolver/specification.rb | 12 ++ lib/rubygems/source.rb | 16 ++- spec/bundler/commands/exec_spec.rb | 26 ++++ spec/bundler/commands/install_spec.rb | 43 ++++++ spec/bundler/commands/lock_spec.rb | 84 +++++++++++ .../install/gemfile/specific_platform_spec.rb | 50 +++++++ spec/bundler/support/artifice/vcr.rb | 2 +- .../test_gem_commands_install_command.rb | 131 ++++++++++++++++++ test/rubygems/test_gem_installer.rb | 66 +-------- test/rubygems/test_gem_resolver_api_set.rb | 78 ++++------- .../test_gem_resolver_api_specification.rb | 6 +- test/rubygems/test_gem_resolver_best_set.rb | 6 +- test/rubygems/test_gem_source.rb | 4 +- .../test_gem_source_subpath_problem.rb | 4 +- 38 files changed, 671 insertions(+), 296 deletions(-) create mode 100644 lib/bundler/compact_index_client/gem_parser.rb create mode 100644 lib/rubygems/resolver/api_set/gem_parser.rb diff --git a/lib/bundler.rb b/lib/bundler.rb index 7a01de5ddb..c72ad27c40 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -212,13 +212,10 @@ module Bundler end end - def locked_bundler_version - return nil unless defined?(@definition) && @definition + def most_specific_locked_platform?(platform) + return false unless defined?(@definition) && @definition - locked_gems = definition.locked_gems - return nil unless locked_gems - - locked_gems.bundler_version + definition.most_specific_locked_platform == platform end def ruby_scope diff --git a/lib/bundler/cli/update.rb b/lib/bundler/cli/update.rb index ae908be65e..94699484d4 100644 --- a/lib/bundler/cli/update.rb +++ b/lib/bundler/cli/update.rb @@ -82,7 +82,7 @@ module Bundler locked_spec = locked_info[:spec] new_spec = Bundler.definition.specs[name].first unless new_spec - if Bundler.rubygems.platforms.none? {|p| locked_spec.match_platform(p) } + unless locked_spec.match_platform(Bundler.local_platform) Bundler.ui.warn "Bundler attempted to update #{name} but it was not considered because it is for a different platform from the current one" end diff --git a/lib/bundler/compact_index_client/cache.rb b/lib/bundler/compact_index_client/cache.rb index 8f73298fbe..c2cd069ec1 100644 --- a/lib/bundler/compact_index_client/cache.rb +++ b/lib/bundler/compact_index_client/cache.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative "gem_parser" + module Bundler class CompactIndexClient class Cache @@ -92,19 +94,9 @@ module Bundler header ? lines[header + 1..-1] : lines end - def parse_gem(string) - version_and_platform, rest = string.split(" ", 2) - version, platform = version_and_platform.split("-", 2) - dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest - dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : [] - requirements = requirements ? requirements.map {|r| parse_dependency(r) } : [] - [version, platform, dependencies, requirements] - end - - def parse_dependency(string) - dependency = string.split(":") - dependency[-1] = dependency[-1].split("&") if dependency.size > 1 - dependency + def parse_gem(line) + @dependency_parser ||= GemParser.new + @dependency_parser.parse(line) end def info_roots diff --git a/lib/bundler/compact_index_client/gem_parser.rb b/lib/bundler/compact_index_client/gem_parser.rb new file mode 100644 index 0000000000..e7bf4c6001 --- /dev/null +++ b/lib/bundler/compact_index_client/gem_parser.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Bundler + class CompactIndexClient + if defined?(Gem::Resolver::APISet::GemParser) + GemParser = Gem::Resolver::APISet::GemParser + else + class GemParser + def parse(line) + version_and_platform, rest = line.split(" ", 2) + version, platform = version_and_platform.split("-", 2) + dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest + dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : [] + requirements = requirements ? requirements.map {|d| parse_dependency(d) } : [] + [version, platform, dependencies, requirements] + end + + private + + def parse_dependency(string) + dependency = string.split(":") + dependency[-1] = dependency[-1].split("&") if dependency.size > 1 + dependency + end + end + end + end +end diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index fdd093fbb3..b22363d119 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -118,7 +118,7 @@ module Bundler end @unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version) - add_current_platform unless Bundler.frozen_bundle? + add_current_platform unless current_ruby_platform_locked? || Bundler.frozen_bundle? converge_path_sources_to_gemspec_sources @path_changes = converge_paths @@ -157,7 +157,7 @@ module Bundler end def resolve_remotely! - raise "Specs already loaded" if @specs + return if @specs @remote = true sources.remote! specs @@ -269,9 +269,8 @@ module Bundler else # Run a resolve against the locally available gems Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}") - platforms_for_resolve = platforms.one? {|p| generic(p) == Gem::Platform::RUBY } ? platforms : platforms.reject{|p| p == Gem::Platform::RUBY } - expanded_dependencies = expand_dependencies(dependencies + metadata_dependencies, @remote, platforms_for_resolve.map {|p| generic(p) }) - last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms_for_resolve) + expanded_dependencies = expand_dependencies(dependencies + metadata_dependencies, @remote) + last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms) end # filter out gems that _can_ be installed on multiple platforms, but don't need @@ -507,15 +506,11 @@ module Bundler end def validate_platforms! - return if @platforms.any? do |bundle_platform| - Bundler.rubygems.platforms.any? do |local_platform| - MatchPlatform.platforms_match?(bundle_platform, local_platform) - end - end + return if current_platform_locked? raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \ - "but your local platforms are #{Bundler.rubygems.platforms.map(&:to_s)}, and " \ - "there's no compatible match between those two lists." + "but your local platform is #{Bundler.local_platform}. " \ + "Add the current platform to the lockfile with `bundle lock --add-platform #{Bundler.local_platform}` and try again." end def add_platform(platform) @@ -528,6 +523,12 @@ module Bundler raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}" end + def most_specific_locked_platform + @platforms.min_by do |bundle_platform| + platform_specificity_match(bundle_platform, local_platform) + end + end + def find_resolved_spec(current_spec) specs.find_by_name_and_platform(current_spec.name, current_spec.platform) end @@ -549,6 +550,18 @@ module Bundler private + def current_ruby_platform_locked? + return false unless generic_local_platform == Gem::Platform::RUBY + + current_platform_locked? + end + + def current_platform_locked? + @platforms.any? do |bundle_platform| + MatchPlatform.platforms_match?(bundle_platform, Bundler.local_platform) + end + end + def add_current_platform add_platform(local_platform) end @@ -871,8 +884,7 @@ module Bundler end end - def expand_dependencies(dependencies, remote = false, platforms = nil) - platforms ||= @platforms + def expand_dependencies(dependencies, remote = false) deps = [] dependencies.each do |dep| dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name) diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb index 2a097375c0..b271b8d229 100644 --- a/lib/bundler/gem_helpers.rb +++ b/lib/bundler/gem_helpers.rb @@ -35,41 +35,33 @@ module Bundler def platform_specificity_match(spec_platform, user_platform) spec_platform = Gem::Platform.new(spec_platform) - return PlatformMatch::EXACT_MATCH if spec_platform == user_platform - return PlatformMatch::WORST_MATCH if spec_platform.nil? || spec_platform == Gem::Platform::RUBY || user_platform == Gem::Platform::RUBY - PlatformMatch.new( - PlatformMatch.os_match(spec_platform, user_platform), - PlatformMatch.cpu_match(spec_platform, user_platform), - PlatformMatch.platform_version_match(spec_platform, user_platform) - ) + PlatformMatch.specificity_score(spec_platform, user_platform) end module_function :platform_specificity_match def select_best_platform_match(specs, platform) - specs.select {|spec| spec.match_platform(platform) }. - min_by {|spec| platform_specificity_match(spec.platform, platform) } + matching = specs.select {|spec| spec.match_platform(platform) } + exact = matching.select {|spec| spec.platform == platform } + return exact if exact.any? + + sorted_matching = matching.sort_by {|spec| platform_specificity_match(spec.platform, platform) } + exemplary_spec = sorted_matching.first + + sorted_matching.take_while{|spec| same_specificity(platform, spec, exemplary_spec) && same_deps(spec, exemplary_spec) } end module_function :select_best_platform_match - PlatformMatch = Struct.new(:os_match, :cpu_match, :platform_version_match) class PlatformMatch - def <=>(other) - return nil unless other.is_a?(PlatformMatch) + def self.specificity_score(spec_platform, user_platform) + return -1 if spec_platform == user_platform + return 1_000_000 if spec_platform.nil? || spec_platform == Gem::Platform::RUBY || user_platform == Gem::Platform::RUBY - m = os_match <=> other.os_match - return m unless m.zero? - - m = cpu_match <=> other.cpu_match - return m unless m.zero? - - m = platform_version_match <=> other.platform_version_match - m + os_match(spec_platform, user_platform) + + cpu_match(spec_platform, user_platform) * 10 + + platform_version_match(spec_platform, user_platform) * 100 end - EXACT_MATCH = new(-1, -1, -1).freeze - WORST_MATCH = new(1_000_000, 1_000_000, 1_000_000).freeze - def self.os_match(spec_platform, user_platform) if spec_platform.os == user_platform.os 0 @@ -100,5 +92,19 @@ module Bundler end end end + + def same_specificity(platform, spec, exemplary_spec) + platform_specificity_match(spec.platform, platform) == platform_specificity_match(exemplary_spec.platform, platform) + end + module_function :same_specificity + + def same_deps(spec, exemplary_spec) + same_runtime_deps = spec.dependencies.sort == exemplary_spec.dependencies.sort + return same_runtime_deps unless spec.is_a?(Gem::Specification) && exemplary_spec.is_a?(Gem::Specification) + + same_metadata_deps = spec.required_ruby_version == exemplary_spec.required_ruby_version && spec.required_rubygems_version == exemplary_spec.required_rubygems_version + same_runtime_deps && same_metadata_deps + end + module_function :same_deps end end diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index 7b1d28b0c3..1081910816 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -4,7 +4,7 @@ require_relative "match_platform" module Bundler class LazySpecification - Identifier = Struct.new(:name, :version, :source, :platform, :dependencies) + Identifier = Struct.new(:name, :version, :platform) class Identifier include Comparable def <=>(other) @@ -108,7 +108,7 @@ module Bundler end def identifier - @__identifier ||= Identifier.new(name, version, source, platform, dependencies) + @__identifier ||= Identifier.new(name, version, platform) end def git_version @@ -131,17 +131,16 @@ module Bundler end # - # Bundler 2.2.0 was the first version that records the full resolution - # including platform specific gems in the lockfile, which means that if a - # gem with RUBY platform is recorded, the RUBY platform version of the gem - # should be installed. Previously bundler would record only generic versions - # in the lockfile and then install the most specific platform variant if - # available. + # For backwards compatibility with existing lockfiles, if the most specific + # locked platform is RUBY, we keep the previous behaviour of resolving the + # best platform variant at materiliazation time. For previous bundler + # versions (before 2.2.0) this was always the case (except when the lockfile + # only included non-ruby platforms), but we're also keeping this behaviour + # on newer bundlers unless users generate the lockfile from scratch or + # explicitly add a more specific platform. # def ruby_platform_materializes_to_ruby_platform? - locked_bundler_version = Bundler.locked_bundler_version - - locked_bundler_version.nil? || Gem::Version.new(locked_bundler_version) >= Gem::Version.new("2.2.0") + !Bundler.most_specific_locked_platform?(Gem::Platform::RUBY) end end end diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index 38dc175ff9..34780f9528 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -25,11 +25,15 @@ module Bundler def to_specs @activated_platforms.map do |p| - next unless s = @specs[p] - lazy_spec = LazySpecification.new(name, version, s.platform, source) - lazy_spec.dependencies.replace s.dependencies - lazy_spec - end.compact.uniq + specs = @specs[p] + next unless specs.any? + + specs.map do |s| + lazy_spec = LazySpecification.new(name, version, s.platform, source) + lazy_spec.dependencies.replace s.dependencies + lazy_spec + end + end.flatten.compact.uniq end def copy_for(platforms) @@ -42,12 +46,8 @@ module Bundler copied_sg end - def spec_for(platform) - @specs[platform] - end - def for?(platform) - !spec_for(platform).nil? + @specs[platform].any? end def to_s @@ -58,7 +58,7 @@ module Bundler def dependencies_for_activated_platforms dependencies = @activated_platforms.map {|p| __dependencies[p] } metadata_dependencies = @activated_platforms.map do |platform| - metadata_dependencies(@specs[platform], platform) + metadata_dependencies(@specs[platform].first, platform) end dependencies.concat(metadata_dependencies).flatten end @@ -94,7 +94,8 @@ module Bundler def __dependencies @dependencies = Hash.new do |dependencies, platform| dependencies[platform] = [] - if spec = @specs[platform] + specs = @specs[platform] + if spec = specs.first spec.dependencies.each do |dep| next if dep.type == :development next if @ignores_bundler_dependencies && dep.name == "bundler".freeze @@ -106,10 +107,7 @@ module Bundler end def metadata_dependencies(spec, platform) - return [] unless spec - # Only allow endpoint specifications since they won't hit the network to - # fetch the full gemspec when calling required_ruby_version - return [] if !spec.is_a?(EndpointSpecification) && !spec.is_a?(Gem::Specification) + return [] unless spec && spec.is_a?(Gem::Specification) dependencies = [] if !spec.required_ruby_version.nil? && !spec.required_ruby_version.none? dependencies << DepProxy.new(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform) diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index c577001043..d060e21f50 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -110,11 +110,6 @@ module Bundler obj.to_s end - def platforms - return [Gem::Platform::RUBY] if Bundler.settings[:force_ruby_platform] - Gem.platforms - end - def configuration require_relative "psyched_yaml" Gem.configuration diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index aef854ce0c..a0b9552c18 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -22,10 +22,11 @@ module Bundler break unless dep = deps.shift next if !handled.add?(dep) || skip.include?(dep.name) - if spec = spec_for_dependency(dep, match_current_platform) - specs << spec + specs_for_dep = spec_for_dependency(dep, match_current_platform) + if specs_for_dep.any? + specs += specs_for_dep - spec.dependencies.each do |d| + specs_for_dep.first.dependencies.each do |d| next if d.type == :development d = DepProxy.new(d, dep.__platform) unless match_current_platform deps << d @@ -184,11 +185,7 @@ module Bundler def spec_for_dependency(dep, match_current_platform) specs_for_platforms = lookup[dep.name] if match_current_platform - Bundler.rubygems.platforms.reverse_each do |pl| - match = GemHelpers.select_best_platform_match(specs_for_platforms, pl) - return match if match - end - nil + GemHelpers.select_best_platform_match(specs_for_platforms, Bundler.local_platform) else GemHelpers.select_best_platform_match(specs_for_platforms, dep.__platform) end diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index c730934258..8db404377c 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "2.2.2".freeze + VERSION = "2.2.3".freeze def self.bundler_major_version @bundler_major_version ||= VERSION.split(".").first.to_i diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 5da8b6904b..fe52d72cc3 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -8,7 +8,7 @@ require 'rbconfig' module Gem - VERSION = "3.2.2".freeze + VERSION = "3.2.3".freeze end # Must be first since it unloads the prelude from 1.9.2 diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb index fb555a46d4..400a5de5cf 100644 --- a/lib/rubygems/dependency_installer.rb +++ b/lib/rubygems/dependency_installer.rb @@ -286,6 +286,7 @@ class Gem::DependencyInstaller installer_set = Gem::Resolver::InstallerSet.new @domain installer_set.ignore_installed = (@minimal_deps == false) || @only_install_dir + installer_set.force = @force if consider_local? if dep_or_name =~ /\.gem$/ and File.file? dep_or_name diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 2c583743b9..3ec6d743a2 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -638,27 +638,6 @@ class Gem::Installer end end - def ensure_required_ruby_version_met # :nodoc: - if rrv = spec.required_ruby_version - ruby_version = Gem.ruby_version - unless rrv.satisfied_by? ruby_version - raise Gem::RuntimeRequirementNotMetError, - "#{spec.name} requires Ruby version #{rrv}. The current ruby version is #{ruby_version}." - end - end - end - - def ensure_required_rubygems_version_met # :nodoc: - if rrgv = spec.required_rubygems_version - unless rrgv.satisfied_by? Gem.rubygems_version - rg_version = Gem::VERSION - raise Gem::RuntimeRequirementNotMetError, - "#{spec.name} requires RubyGems version #{rrgv}. The current RubyGems version is #{rg_version}. " + - "Try 'gem update --system' to update RubyGems itself." - end - end - end - def ensure_dependencies_met # :nodoc: deps = spec.runtime_dependencies deps |= spec.development_dependencies if @development @@ -914,8 +893,6 @@ TEXT return true if @force - ensure_required_ruby_version_met - ensure_required_rubygems_version_met ensure_dependencies_met unless @ignore_dependencies true diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 8ebe6acc70..285c80a95f 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -215,7 +215,7 @@ class Gem::RemoteFetcher case response when Net::HTTPOK, Net::HTTPNotModified then - response.uri = uri if response.respond_to? :uri + response.uri = uri head ? response : response.body when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther, Net::HTTPTemporaryRedirect then diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb index a8a06d0b95..5190cfc904 100644 --- a/lib/rubygems/request_set.rb +++ b/lib/rubygems/request_set.rb @@ -195,19 +195,8 @@ class Gem::RequestSet yield req, installer if block_given? end rescue Gem::RuntimeRequirementNotMetError => e - recent_match = req.spec.set.find_all(req.request).sort_by(&:version).reverse_each.find do |s| - s = s.spec - s.required_ruby_version.satisfied_by?(Gem.ruby_version) && - s.required_rubygems_version.satisfied_by?(Gem.rubygems_version) && - Gem::Platform.installable?(s) - end - if recent_match - suggestion = "The last version of #{req.request} to support your Ruby & RubyGems was #{recent_match.version}. Try installing it with `gem install #{recent_match.name} -v #{recent_match.version}`" - suggestion += " and then running the current command again" unless @always_install.include?(req.spec.spec) - else - suggestion = "There are no versions of #{req.request} compatible with your Ruby & RubyGems" - suggestion += ". Maybe try installing an older version of the gem you're looking for?" unless @always_install.include?(req.spec.spec) - end + suggestion = "There are no versions of #{req.request} compatible with your Ruby & RubyGems" + suggestion += ". Maybe try installing an older version of the gem you're looking for?" unless @always_install.include?(req.spec.spec) e.suggestion = suggestion raise end diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb index e1c0d2dd0a..71c35ea3d3 100644 --- a/lib/rubygems/resolver.rb +++ b/lib/rubygems/resolver.rb @@ -261,7 +261,12 @@ class Gem::Resolver end def requirement_satisfied_by?(requirement, activated, spec) - requirement.matches_spec? spec + matches_spec = requirement.matches_spec? spec + return matches_spec if @soft_missing + + matches_spec && + spec.spec.required_ruby_version.satisfied_by?(Gem.ruby_version) && + spec.spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version) end def name_for(dependency) diff --git a/lib/rubygems/resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb index cafcd5b472..21c9b8920c 100644 --- a/lib/rubygems/resolver/api_set.rb +++ b/lib/rubygems/resolver/api_set.rb @@ -4,6 +4,8 @@ # Returns instances of APISpecification. class Gem::Resolver::APISet < Gem::Resolver::Set + autoload :GemParser, File.expand_path("api_set/gem_parser", __dir__) + ## # The URI for the dependency API this APISet uses. @@ -24,13 +26,13 @@ class Gem::Resolver::APISet < Gem::Resolver::Set # API URL +dep_uri+ which is described at # https://guides.rubygems.org/rubygems-org-api - def initialize(dep_uri = 'https://rubygems.org/api/v1/dependencies') + def initialize(dep_uri = 'https://index.rubygems.org/info/') super() dep_uri = URI dep_uri unless URI === dep_uri @dep_uri = dep_uri - @uri = dep_uri + '../..' + @uri = dep_uri + '..' @data = Hash.new {|h,k| h[k] = [] } @source = Gem::Source.new @uri @@ -75,20 +77,8 @@ class Gem::Resolver::APISet < Gem::Resolver::Set def prefetch_now # :nodoc: needed, @to_fetch = @to_fetch, [] - uri = @dep_uri + "?gems=#{needed.sort.join ','}" - str = Gem::RemoteFetcher.fetcher.fetch_path uri - - loaded = [] - - Marshal.load(str).each do |ver| - name = ver[:name] - - @data[name] << ver - loaded << name - end - - (needed - loaded).each do |missing| - @data[missing] = [] + needed.sort.each do |name| + versions(name) end end @@ -111,13 +101,32 @@ class Gem::Resolver::APISet < Gem::Resolver::Set return @data[name] end - uri = @dep_uri + "?gems=#{name}" + uri = @dep_uri + name str = Gem::RemoteFetcher.fetcher.fetch_path uri - Marshal.load(str).each do |ver| - @data[ver[:name]] << ver + lines(str).each do |ver| + number, platform, dependencies, requirements = parse_gem(ver) + + platform ||= "ruby" + dependencies = dependencies.map {|dep_name, reqs| [dep_name, reqs.join(", ")] } + requirements = requirements.map {|req_name, reqs| [req_name.to_sym, reqs] }.to_h + + @data[name] << { name: name, number: number, platform: platform, dependencies: dependencies, requirements: requirements } end @data[name] end + + private + + def lines(str) + lines = str.split("\n") + header = lines.index("---") + header ? lines[header + 1..-1] : lines + end + + def parse_gem(string) + @gem_parser ||= GemParser.new + @gem_parser.parse(string) + end end diff --git a/lib/rubygems/resolver/api_set/gem_parser.rb b/lib/rubygems/resolver/api_set/gem_parser.rb new file mode 100644 index 0000000000..685c39558d --- /dev/null +++ b/lib/rubygems/resolver/api_set/gem_parser.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class Gem::Resolver::APISet::GemParser + def parse(line) + version_and_platform, rest = line.split(" ", 2) + version, platform = version_and_platform.split("-", 2) + dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest + dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : [] + requirements = requirements ? requirements.map {|d| parse_dependency(d) } : [] + [version, platform, dependencies, requirements] + end + + private + + def parse_dependency(string) + dependency = string.split(":") + dependency[-1] = dependency[-1].split("&") if dependency.size > 1 + dependency + end +end diff --git a/lib/rubygems/resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb index 589ea1ba61..b5aa0b71d4 100644 --- a/lib/rubygems/resolver/api_specification.rb +++ b/lib/rubygems/resolver/api_specification.rb @@ -35,6 +35,8 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification @dependencies = api_data[:dependencies].map do |name, ver| Gem::Dependency.new(name, ver.split(/\s*,\s*/)).freeze end.freeze + @required_ruby_version = Gem::Requirement.new(api_data.dig(:requirements, :ruby)).freeze + @required_rubygems_version = Gem::Requirement.new(api_data.dig(:requirements, :rubygems)).freeze end def ==(other) # :nodoc: @@ -42,12 +44,11 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification @set == other.set and @name == other.name and @version == other.version and - @platform == other.platform and - @dependencies == other.dependencies + @platform == other.platform end def hash - @set.hash ^ @name.hash ^ @version.hash ^ @platform.hash ^ @dependencies.hash + @set.hash ^ @name.hash ^ @version.hash ^ @platform.hash end def fetch_development_dependencies # :nodoc: diff --git a/lib/rubygems/resolver/best_set.rb b/lib/rubygems/resolver/best_set.rb index 7a708ee391..b6a46a9403 100644 --- a/lib/rubygems/resolver/best_set.rb +++ b/lib/rubygems/resolver/best_set.rb @@ -60,7 +60,7 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet def replace_failed_api_set(error) # :nodoc: uri = error.uri uri = URI uri unless URI === uri - uri.query = nil + uri = uri + "." raise error unless api_set = @sets.find do |set| Gem::Resolver::APISet === set and set.dep_uri == uri diff --git a/lib/rubygems/resolver/index_specification.rb b/lib/rubygems/resolver/index_specification.rb index 3b75b719b0..45ff130a14 100644 --- a/lib/rubygems/resolver/index_specification.rb +++ b/lib/rubygems/resolver/index_specification.rb @@ -33,6 +33,21 @@ class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification spec.dependencies end + ## + # The required_ruby_version constraint for this specification + + def required_ruby_version + spec.required_ruby_version + end + + ## + # The required_rubygems_version constraint for this specification + # + + def required_rubygems_version + spec.required_rubygems_version + end + def ==(other) self.class === other && @name == other.name && diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb index b0e4ce5a37..60181315b0 100644 --- a/lib/rubygems/resolver/installer_set.rb +++ b/lib/rubygems/resolver/installer_set.rb @@ -25,6 +25,12 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set attr_reader :remote_set # :nodoc: + ## + # Ignore ruby & rubygems specification constraints. + # + + attr_accessor :force # :nodoc: + ## # Creates a new InstallerSet that will look for gems in +domain+. @@ -41,6 +47,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set @local = {} @local_source = Gem::Source::Local.new @remote_set = Gem::Resolver::BestSet.new + @force = false @specs = {} end @@ -63,15 +70,30 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set Gem::Platform.local === s.platform end - if found.empty? - exc = Gem::UnsatisfiableDependencyError.new request - exc.errors = errors - - raise exc + found = found.sort_by do |s| + [s.version, s.platform == Gem::Platform::RUBY ? -1 : 1] end - newest = found.max_by do |s| - [s.version, s.platform == Gem::Platform::RUBY ? -1 : 1] + newest = found.last + + unless @force + found_matching_metadata = found.select do |spec| + metadata_satisfied?(spec) + end + + if found_matching_metadata.empty? + if newest + ensure_required_ruby_version_met(newest.spec) + ensure_required_rubygems_version_met(newest.spec) + else + exc = Gem::UnsatisfiableDependencyError.new request + exc.errors = errors + + raise exc + end + else + newest = found_matching_metadata.last + end end @always_install << newest.spec @@ -221,4 +243,32 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set @domain = :local unless remote end end + + private + + def metadata_satisfied?(spec) + spec.required_ruby_version.satisfied_by?(Gem.ruby_version) && + spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version) + end + + def ensure_required_ruby_version_met(spec) # :nodoc: + if rrv = spec.required_ruby_version + ruby_version = Gem.ruby_version + unless rrv.satisfied_by? ruby_version + raise Gem::RuntimeRequirementNotMetError, + "#{spec.full_name} requires Ruby version #{rrv}. The current ruby version is #{ruby_version}." + end + end + end + + def ensure_required_rubygems_version_met(spec) # :nodoc: + if rrgv = spec.required_rubygems_version + unless rrgv.satisfied_by? Gem.rubygems_version + rg_version = Gem::VERSION + raise Gem::RuntimeRequirementNotMetError, + "#{spec.full_name} requires RubyGems version #{rrgv}. The current RubyGems version is #{rg_version}. " + + "Try 'gem update --system' to update RubyGems itself." + end + end + end end diff --git a/lib/rubygems/resolver/spec_specification.rb b/lib/rubygems/resolver/spec_specification.rb index bde5d9cddc..7b665fe876 100644 --- a/lib/rubygems/resolver/spec_specification.rb +++ b/lib/rubygems/resolver/spec_specification.rb @@ -22,6 +22,20 @@ class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification spec.dependencies end + ## + # The required_ruby_version constraint for this specification + + def required_ruby_version + spec.required_ruby_version + end + + ## + # The required_rubygems_version constraint for this specification + + def required_rubygems_version + spec.required_rubygems_version + end + ## # The name and version of the specification. # diff --git a/lib/rubygems/resolver/specification.rb b/lib/rubygems/resolver/specification.rb index 5ae5f15813..8c6fc9afcf 100644 --- a/lib/rubygems/resolver/specification.rb +++ b/lib/rubygems/resolver/specification.rb @@ -43,6 +43,16 @@ class Gem::Resolver::Specification attr_reader :version + ## + # The required_ruby_version constraint for this specification. + + attr_reader :required_ruby_version + + ## + # The required_ruby_version constraint for this specification. + + attr_reader :required_rubygems_version + ## # Sets default instance variables for the specification. @@ -53,6 +63,8 @@ class Gem::Resolver::Specification @set = nil @source = nil @version = nil + @required_ruby_version = Gem::Requirement.default + @required_rubygems_version = Gem::Requirement.default end ## diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb index a2848112e7..4ae84cf532 100644 --- a/lib/rubygems/source.rb +++ b/lib/rubygems/source.rb @@ -79,7 +79,15 @@ class Gem::Source def dependency_resolver_set # :nodoc: return Gem::Resolver::IndexSet.new self if 'file' == uri.scheme - bundler_api_uri = enforce_trailing_slash(uri) + './api/v1/dependencies' + fetch_uri = if uri.host == "rubygems.org" + index_uri = uri.dup + index_uri.host = "index.rubygems.org" + index_uri + else + uri + end + + bundler_api_uri = enforce_trailing_slash(fetch_uri) begin fetcher = Gem::RemoteFetcher.fetcher @@ -87,11 +95,7 @@ class Gem::Source rescue Gem::RemoteFetcher::FetchError Gem::Resolver::IndexSet.new self else - if response.respond_to? :uri - Gem::Resolver::APISet.new response.uri - else - Gem::Resolver::APISet.new bundler_api_uri - end + Gem::Resolver::APISet.new response.uri + "./info/" end end diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb index 85f8ceef62..1449bb642e 100644 --- a/spec/bundler/commands/exec_spec.rb +++ b/spec/bundler/commands/exec_spec.rb @@ -67,6 +67,32 @@ RSpec.describe "bundle exec" do expect(out).to eq(Gem::VERSION) end + it "works when exec'ing back to bundler with a lockfile that doesn't include the current platform" do + install_gemfile <<-G + gem "rack", "0.9.1" + G + + # simulate lockfile generated with old version not including specific platform + lockfile <<-L + GEM + specs: + rack (0.9.1) + + PLATFORMS + RUBY + + DEPENDENCIES + rack (= 0.9.1) + + BUNDLED WITH + 2.1.4 + L + + bundle "exec bundle cache", :env => { "BUNDLER_VERSION" => Bundler::VERSION } + + expect(out).to include("Updating files in vendor/cache") + end + it "respects custom process title when loading through ruby" do skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index a8b174a547..043805df79 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -630,4 +630,47 @@ RSpec.describe "bundle install with gem sources" do "setting them for authentication.") end end + + context "in a frozen bundle" do + before do + build_repo4 do + build_gem "libv8", "8.4.255.0" do |s| + s.platform = "x86_64-darwin-19" + end + end + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + + gem "libv8" + G + + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + libv8 (8.4.255.0-x86_64-darwin-19) + + PLATFORMS + x86_64-darwin-19 + + DEPENDENCIES + libv8 + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "config set --local deployment true" + end + + it "should fail loudly if the lockfile platforms don't include the current platform" do + simulate_platform(Gem::Platform.new("x86_64-linux")) { bundle "install", :raise_on_error => false } + + expect(err).to eq( + "Your bundle only supports platforms [\"x86_64-darwin-19\"] but your local platform is x86_64-linux. " \ + "Add the current platform to the lockfile with `bundle lock --add-platform x86_64-linux` and try again." + ) + end + end end diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index 6400152039..afc3b03a53 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -383,6 +383,90 @@ RSpec.describe "bundle lock" do expect(out).to match(/Writing lockfile to.+Gemfile\.lock/) end + it "adds all more specific candidates when they all have the same dependencies" do + build_repo4 do + build_gem "libv8", "8.4.255.0" do |s| + s.platform = "x86_64-darwin-19" + end + + build_gem "libv8", "8.4.255.0" do |s| + s.platform = "x86_64-darwin-20" + end + end + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + + gem "libv8" + G + + simulate_platform(Gem::Platform.new("x86_64-darwin")) { bundle "lock" } + + lockfile_should_be <<-G + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + libv8 (8.4.255.0-x86_64-darwin-19) + libv8 (8.4.255.0-x86_64-darwin-20) + + PLATFORMS + x86_64-darwin + + DEPENDENCIES + libv8 + + BUNDLED WITH + #{Bundler::VERSION} + G + end + + it "respects the previous lockfile if it had a matching less specific platform already locked, and installs the best variant for each platform" do + build_repo4 do + build_gem "libv8", "8.4.255.0" do |s| + s.platform = "x86_64-darwin-19" + end + + build_gem "libv8", "8.4.255.0" do |s| + s.platform = "x86_64-darwin-20" + end + end + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + + gem "libv8" + G + + lockfile <<-G + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + libv8 (8.4.255.0-x86_64-darwin-19) + libv8 (8.4.255.0-x86_64-darwin-20) + + PLATFORMS + x86_64-darwin + + DEPENDENCIES + libv8 + + BUNDLED WITH + #{Bundler::VERSION} + G + + previous_lockfile = lockfile + + %w[x86_64-darwin-19 x86_64-darwin-20].each do |platform| + simulate_platform(Gem::Platform.new(platform)) do + bundle "lock" + expect(lockfile).to eq(previous_lockfile) + + bundle "install" + expect(the_bundle).to include_gem("libv8 8.4.255.0 #{platform}") + end + end + end + context "when an update is available" do let(:repo) { gem_repo2 } diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb index d85c670b64..b58726064f 100644 --- a/spec/bundler/install/gemfile/specific_platform_spec.rb +++ b/spec/bundler/install/gemfile/specific_platform_spec.rb @@ -54,6 +54,56 @@ RSpec.describe "bundle install with specific platforms" do expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin") end + it "understands that a non-plaform specific gem in a new lockfile locked only to RUBY doesn't necessarily mean installing the non-specific variant" do + setup_multiplatform_gem + + system_gems "bundler-2.1.4" + + # Consistent location to install and look for gems + bundle "config set --local path vendor/bundle", :env => { "BUNDLER_VERSION" => "2.1.4" } + + gemfile google_protobuf + + # simulate lockfile created with old bundler, which only locks for ruby platform + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo2)}/ + specs: + google-protobuf (3.0.0.alpha.4.0) + + PLATFORMS + ruby + + DEPENDENCIES + google-protobuf + + BUNDLED WITH + 2.1.4 + L + + bundle "update", :env => { "BUNDLER_VERSION" => Bundler::VERSION } + + # make sure the platform that the platform specific dependency is used, since we're only locked to ruby + expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin") + + # make sure we're still only locked to ruby + lockfile_should_be <<-L + GEM + remote: #{file_uri_for(gem_repo2)}/ + specs: + google-protobuf (3.0.0.alpha.5.0.5.1) + + PLATFORMS + ruby + + DEPENDENCIES + google-protobuf + + BUNDLED WITH + #{Bundler::VERSION} + L + end + it "caches the universal-darwin gem when --all-platforms is passed and properly picks it up on further bundler invocations" do setup_multiplatform_gem gemfile(google_protobuf) diff --git a/spec/bundler/support/artifice/vcr.rb b/spec/bundler/support/artifice/vcr.rb index bd13a338b4..88c33d93dc 100644 --- a/spec/bundler/support/artifice/vcr.rb +++ b/spec/bundler/support/artifice/vcr.rb @@ -39,7 +39,7 @@ class BundlerVCRHTTP < Net::HTTP response_io = ::Net::BufferedIO.new(response_file) ::Net::HTTPResponse.read_new(response_io).tap do |response| response.decode_content = request.decode_content if request.respond_to?(:decode_content) - response.uri = request.uri if request.respond_to?(:uri) + response.uri = request.uri response.reading_body(response_io, request.response_body_permitted?) do response_block.call(response) if response_block diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb index 08530bfeca..c8015f9985 100644 --- a/test/rubygems/test_gem_commands_install_command.rb +++ b/test/rubygems/test_gem_commands_install_command.rb @@ -504,6 +504,137 @@ ERROR: Possible alternatives: non_existent_with_hint assert_equal %w[a-2], @cmd.installed_specs.map {|spec| spec.full_name } end + def test_execute_required_ruby_version + next_ruby = Gem.ruby_version.segments.map.with_index{|n, i| i == 1 ? n + 1 : n }.join(".") + + local = Gem::Platform.local + spec_fetcher do |fetcher| + fetcher.download 'a', 2 + fetcher.download 'a', 2 do |s| + s.required_ruby_version = "< #{RUBY_VERSION}.a" + s.platform = local + end + fetcher.download 'a', 3 do |s| + s.required_ruby_version = ">= #{next_ruby}" + end + fetcher.download 'a', 3 do |s| + s.required_ruby_version = ">= #{next_ruby}" + s.platform = local + end + end + + @cmd.options[:args] = %w[a] + + use_ui @ui do + assert_raises Gem::MockGemUi::SystemExitException, @ui.error do + @cmd.execute + end + end + + assert_equal %w[a-2], @cmd.installed_specs.map {|spec| spec.full_name } + end + + def test_execute_required_ruby_version_upper_bound + local = Gem::Platform.local + spec_fetcher do |fetcher| + fetcher.gem 'a', 2.0 + fetcher.gem 'a', 2.0 do |s| + s.required_ruby_version = "< #{RUBY_VERSION}.a" + s.platform = local + end + end + + @cmd.options[:args] = %w[a] + + use_ui @ui do + assert_raises Gem::MockGemUi::SystemExitException, @ui.error do + @cmd.execute + end + end + + assert_equal %w[a-2.0], @cmd.installed_specs.map {|spec| spec.full_name } + end + + def test_execute_required_ruby_version_specific_not_met + spec_fetcher do |fetcher| + fetcher.gem 'a', '1.0' do |s| + s.required_ruby_version = '= 1.4.6' + end + end + + @cmd.options[:args] = %w[a] + + use_ui @ui do + assert_raises Gem::MockGemUi::TermError do + @cmd.execute + end + end + + errs = @ui.error.split("\n") + assert_equal "ERROR: Error installing a:", errs.shift + assert_equal "\ta-1.0 requires Ruby version = 1.4.6. The current ruby version is #{Gem.ruby_version}.", errs.shift + end + + def test_execute_required_ruby_version_specific_prerelease_met + spec_fetcher do |fetcher| + fetcher.gem 'a', '1.0' do |s| + s.required_ruby_version = '>= 1.4.6.preview2' + end + end + + @cmd.options[:args] = %w[a] + + use_ui @ui do + assert_raises Gem::MockGemUi::SystemExitException, @ui.error do + @cmd.execute + end + end + + assert_equal %w[a-1.0], @cmd.installed_specs.map {|spec| spec.full_name } + end + + def test_execute_required_ruby_version_specific_prerelease_not_met + next_ruby_pre = Gem.ruby_version.segments.map.with_index{|n, i| i == 1 ? n + 1 : n }.join(".") + ".a" + + spec_fetcher do |fetcher| + fetcher.gem 'a', '1.0' do |s| + s.required_ruby_version = "> #{next_ruby_pre}" + end + end + + @cmd.options[:args] = %w[a] + + use_ui @ui do + assert_raises Gem::MockGemUi::TermError do + @cmd.execute + end + end + + errs = @ui.error.split("\n") + assert_equal "ERROR: Error installing a:", errs.shift + assert_equal "\ta-1.0 requires Ruby version > #{next_ruby_pre}. The current ruby version is #{Gem.ruby_version}.", errs.shift + end + + def test_execute_required_rubygems_version_wrong + spec_fetcher do |fetcher| + fetcher.gem 'a', '1.0' do |s| + s.required_rubygems_version = '< 0' + end + end + + @cmd.options[:args] = %w[a] + + use_ui @ui do + assert_raises Gem::MockGemUi::TermError do + @cmd.execute + end + end + + errs = @ui.error.split("\n") + assert_equal "ERROR: Error installing a:", errs.shift + assert_equal "\ta-1.0 requires RubyGems version < 0. The current RubyGems version is #{Gem.rubygems_version}. Try 'gem update --system' to update RubyGems itself.", errs.shift + end + def test_execute_rdoc specs = spec_fetcher do |fetcher| fetcher.gem 'a', 2 diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb index 0a9a2e38dd..4ce7e92442 100644 --- a/test/rubygems/test_gem_installer.rb +++ b/test/rubygems/test_gem_installer.rb @@ -1172,12 +1172,16 @@ gem 'other', version end def test_install_force + _, missing_dep_gem = util_gem 'missing_dep', '1' do |s| + s.add_dependency 'doesnt_exist', '1' + end + use_ui @ui do - installer = Gem::Installer.at old_ruby_required('= 1.4.6'), :force => true + installer = Gem::Installer.at missing_dep_gem, :force => true installer.install end - gem_dir = File.join(@gemhome, 'gems', 'old_ruby_required-1') + gem_dir = File.join(@gemhome, 'gems', 'missing_dep-1') assert_path_exists gem_dir end @@ -1620,54 +1624,6 @@ gem 'other', version end end - def test_pre_install_checks_ruby_version - use_ui @ui do - installer = Gem::Installer.at old_ruby_required('= 1.4.6') - e = assert_raises Gem::RuntimeRequirementNotMetError do - installer.pre_install_checks - end - rv = Gem.ruby_version - assert_equal "old_ruby_required requires Ruby version = 1.4.6. The current ruby version is #{rv}.", - e.message - end - end - - def test_pre_install_checks_ruby_version_with_prereleases - util_set_RUBY_VERSION '2.6.0', -1, '63539', 'ruby 2.6.0preview2 (2018-05-31 trunk 63539) [x86_64-linux]' - - installer = Gem::Installer.at old_ruby_required('>= 2.6.0.preview2') - assert installer.pre_install_checks - - installer = Gem::Installer.at old_ruby_required('> 2.6.0.preview2') - e = assert_raises Gem::RuntimeRequirementNotMetError do - assert installer.pre_install_checks - end - assert_equal "old_ruby_required requires Ruby version > 2.6.0.preview2. The current ruby version is 2.6.0.preview2.", - e.message - ensure - util_restore_RUBY_VERSION - end - - def test_pre_install_checks_wrong_rubygems_version - spec = util_spec 'old_rubygems_required', '1' do |s| - s.required_rubygems_version = '< 0' - end - - util_build_gem spec - - gem = File.join(@gemhome, 'cache', spec.file_name) - - use_ui @ui do - installer = Gem::Installer.at gem - e = assert_raises Gem::RuntimeRequirementNotMetError do - installer.pre_install_checks - end - rgv = Gem::VERSION - assert_equal "old_rubygems_required requires RubyGems version < 0. The current RubyGems version is #{rgv}. " + - "Try 'gem update --system' to update RubyGems itself.", e.message - end - end - def test_pre_install_checks_malicious_name spec = util_spec '../malicious', '1' def spec.full_name # so the spec is buildable @@ -2252,16 +2208,6 @@ gem 'other', version assert_kind_of(String, installer.gem) end - def old_ruby_required(requirement) - spec = util_spec 'old_ruby_required', '1' do |s| - s.required_ruby_version = requirement - end - - util_build_gem spec - - spec.cache_file - end - def util_execless @spec = util_spec 'z' util_build_gem @spec diff --git a/test/rubygems/test_gem_resolver_api_set.rb b/test/rubygems/test_gem_resolver_api_set.rb index 8a838c761b..aa17ec6f3a 100644 --- a/test/rubygems/test_gem_resolver_api_set.rb +++ b/test/rubygems/test_gem_resolver_api_set.rb @@ -6,29 +6,29 @@ class TestGemResolverAPISet < Gem::TestCase super @DR = Gem::Resolver - @dep_uri = URI "#{@gem_repo}api/v1/dependencies" + @dep_uri = URI "#{@gem_repo}info/" end def test_initialize set = @DR::APISet.new - assert_equal URI('https://rubygems.org/api/v1/dependencies'), set.dep_uri - assert_equal URI('https://rubygems.org'), set.uri - assert_equal Gem::Source.new(URI('https://rubygems.org')), set.source + assert_equal URI('https://index.rubygems.org/info/'), set.dep_uri + assert_equal URI('https://index.rubygems.org/'), set.uri + assert_equal Gem::Source.new(URI('https://index.rubygems.org')), set.source end def test_initialize_deeper_uri - set = @DR::APISet.new 'https://rubygemsserver.com/mygems/api/v1/dependencies' + set = @DR::APISet.new 'https://rubygemsserver.com/mygems/info' - assert_equal URI('https://rubygemsserver.com/mygems/api/v1/dependencies'), set.dep_uri - assert_equal URI('https://rubygemsserver.com/mygems/'), set.uri - assert_equal Gem::Source.new(URI('https://rubygemsserver.com/mygems/')), set.source + assert_equal URI('https://rubygemsserver.com/mygems/info'), set.dep_uri + assert_equal URI('https://rubygemsserver.com/'), set.uri + assert_equal Gem::Source.new(URI('https://rubygemsserver.com/')), set.source end def test_initialize_uri set = @DR::APISet.new @dep_uri - assert_equal URI("#{@gem_repo}api/v1/dependencies"), set.dep_uri + assert_equal URI("#{@gem_repo}info/"), set.dep_uri assert_equal URI("#{@gem_repo}"), set.uri end @@ -42,7 +42,7 @@ class TestGemResolverAPISet < Gem::TestCase :dependencies => [] }, ] - @fetcher.data["#{@dep_uri}?gems=a"] = Marshal.dump data + @fetcher.data["#{@dep_uri}a"] = "---\n1 " set = @DR::APISet.new @dep_uri @@ -69,7 +69,7 @@ class TestGemResolverAPISet < Gem::TestCase :dependencies => [] }, ] - @fetcher.data["#{@dep_uri}?gems=a"] = Marshal.dump data + @fetcher.data["#{@dep_uri}a"] = "---\n1\n2.a" set = @DR::APISet.new @dep_uri set.prerelease = true @@ -94,7 +94,7 @@ class TestGemResolverAPISet < Gem::TestCase :dependencies => [] }, ] - @fetcher.data["#{@dep_uri}?gems=a"] = Marshal.dump data + @fetcher.data["#{@dep_uri}a"] = "---\n1 " set = @DR::APISet.new @dep_uri @@ -108,7 +108,7 @@ class TestGemResolverAPISet < Gem::TestCase assert_equal expected, set.find_all(a_dep) - @fetcher.data.delete "#{@dep_uri}?gems=a" + @fetcher.data.delete "#{@dep_uri}a" end def test_find_all_local @@ -123,7 +123,7 @@ class TestGemResolverAPISet < Gem::TestCase def test_find_all_missing spec_fetcher - @fetcher.data["#{@dep_uri}?gems=a"] = Marshal.dump [] + @fetcher.data["#{@dep_uri}a"] = "---" set = @DR::APISet.new @dep_uri @@ -131,7 +131,7 @@ class TestGemResolverAPISet < Gem::TestCase assert_empty set.find_all(a_dep) - @fetcher.data.delete "#{@dep_uri}?gems=a" + @fetcher.data.delete "#{@dep_uri}a" assert_empty set.find_all(a_dep) end @@ -139,15 +139,8 @@ class TestGemResolverAPISet < Gem::TestCase def test_prefetch spec_fetcher - data = [ - { :name => 'a', - :number => '1', - :platform => 'ruby', - :dependencies => [] }, - ] - - @fetcher.data["#{@dep_uri}?gems=a,b"] = Marshal.dump data - @fetcher.data["#{@dep_uri}?gems=b"] = Marshal.dump [] + @fetcher.data["#{@dep_uri}a"] = "---\n1 \n" + @fetcher.data["#{@dep_uri}b"] = "---" set = @DR::APISet.new @dep_uri @@ -163,14 +156,7 @@ class TestGemResolverAPISet < Gem::TestCase def test_prefetch_cache spec_fetcher - data = [ - { :name => 'a', - :number => '1', - :platform => 'ruby', - :dependencies => [] }, - ] - - @fetcher.data["#{@dep_uri}?gems=a"] = Marshal.dump data + @fetcher.data["#{@dep_uri}a"] = "---\n1 \n" set = @DR::APISet.new @dep_uri @@ -179,8 +165,8 @@ class TestGemResolverAPISet < Gem::TestCase set.prefetch [a_dep] - @fetcher.data.delete "#{@dep_uri}?gems=a" - @fetcher.data["#{@dep_uri}?gems=b"] = Marshal.dump [] + @fetcher.data.delete "#{@dep_uri}a" + @fetcher.data["#{@dep_uri}?b"] = "---" set.prefetch [a_dep, b_dep] end @@ -188,14 +174,8 @@ class TestGemResolverAPISet < Gem::TestCase def test_prefetch_cache_missing spec_fetcher - data = [ - { :name => 'a', - :number => '1', - :platform => 'ruby', - :dependencies => [] }, - ] - - @fetcher.data["#{@dep_uri}?gems=a,b"] = Marshal.dump data + @fetcher.data["#{@dep_uri}a"] = "---\n1 \n" + @fetcher.data["#{@dep_uri}b"] = "---" set = @DR::APISet.new @dep_uri @@ -204,7 +184,8 @@ class TestGemResolverAPISet < Gem::TestCase set.prefetch [a_dep, b_dep] - @fetcher.data.delete "#{@dep_uri}?gems=a,b" + @fetcher.data.delete "#{@dep_uri}a" + @fetcher.data.delete "#{@dep_uri}b" set.prefetch [a_dep, b_dep] end @@ -212,15 +193,8 @@ class TestGemResolverAPISet < Gem::TestCase def test_prefetch_local spec_fetcher - data = [ - { :name => 'a', - :number => '1', - :platform => 'ruby', - :dependencies => [] }, - ] - - @fetcher.data["#{@dep_uri}?gems=a,b"] = Marshal.dump data - @fetcher.data["#{@dep_uri}?gems=b"] = Marshal.dump [] + @fetcher.data["#{@dep_uri}a"] = "---\n1 \n" + @fetcher.data["#{@dep_uri}b"] = "---" set = @DR::APISet.new @dep_uri set.remote = false diff --git a/test/rubygems/test_gem_resolver_api_specification.rb b/test/rubygems/test_gem_resolver_api_specification.rb index 3db57677f3..e9ba4ae481 100644 --- a/test/rubygems/test_gem_resolver_api_specification.rb +++ b/test/rubygems/test_gem_resolver_api_specification.rb @@ -39,7 +39,7 @@ class TestGemResolverAPISpecification < Gem::TestCase rails = specs['rails-3.0.3'] - repo = @gem_repo + 'api/v1/dependencies' + repo = @gem_repo + 'info' set = Gem::Resolver::APISet.new repo @@ -123,7 +123,7 @@ class TestGemResolverAPISpecification < Gem::TestCase fetcher.spec 'a', 1 end - dep_uri = URI(@gem_repo) + 'api/v1/dependencies' + dep_uri = URI(@gem_repo) + 'info' set = Gem::Resolver::APISet.new dep_uri data = { :name => 'a', @@ -147,7 +147,7 @@ class TestGemResolverAPISpecification < Gem::TestCase end end - dep_uri = URI(@gem_repo) + 'api/v1/dependencies' + dep_uri = URI(@gem_repo) + 'info' set = Gem::Resolver::APISet.new dep_uri data = { :name => 'j', diff --git a/test/rubygems/test_gem_resolver_best_set.rb b/test/rubygems/test_gem_resolver_best_set.rb index 8218bf5d70..657ad33387 100644 --- a/test/rubygems/test_gem_resolver_best_set.rb +++ b/test/rubygems/test_gem_resolver_best_set.rb @@ -39,7 +39,7 @@ class TestGemResolverBestSet < Gem::TestCase set = @DR::BestSet.new - api_uri = URI(@gem_repo) + './api/v1/dependencies' + api_uri = URI(@gem_repo) set.sets << Gem::Resolver::APISet.new(api_uri) @@ -99,12 +99,12 @@ class TestGemResolverBestSet < Gem::TestCase def test_replace_failed_api_set set = @DR::BestSet.new - api_uri = URI(@gem_repo) + './api/v1/dependencies' + api_uri = URI(@gem_repo) + './info/' api_set = Gem::Resolver::APISet.new api_uri set.sets << api_set - error_uri = api_uri + '?gems=a' + error_uri = api_uri + 'a' error = Gem::RemoteFetcher::FetchError.new 'bogus', error_uri diff --git a/test/rubygems/test_gem_source.rb b/test/rubygems/test_gem_source.rb index 09c510f096..d6c2ffa051 100644 --- a/test/rubygems/test_gem_source.rb +++ b/test/rubygems/test_gem_source.rb @@ -44,9 +44,9 @@ class TestGemSource < Gem::TestCase def test_dependency_resolver_set_bundler_api response = Net::HTTPResponse.new '1.1', 200, 'OK' - response.uri = URI('http://example') if response.respond_to? :uri + response.uri = URI('http://example') - @fetcher.data["#{@gem_repo}api/v1/dependencies"] = response + @fetcher.data[@gem_repo] = response set = @source.dependency_resolver_set diff --git a/test/rubygems/test_gem_source_subpath_problem.rb b/test/rubygems/test_gem_source_subpath_problem.rb index eb92b7d404..b2289ea625 100644 --- a/test/rubygems/test_gem_source_subpath_problem.rb +++ b/test/rubygems/test_gem_source_subpath_problem.rb @@ -21,9 +21,9 @@ class TestGemSourceSubpathProblem < Gem::TestCase def test_dependency_resolver_set response = Net::HTTPResponse.new '1.1', 200, 'OK' - response.uri = URI('http://example') if response.respond_to? :uri + response.uri = URI('http://example') - @fetcher.data["#{@gem_repo}/api/v1/dependencies"] = response + @fetcher.data["#{@gem_repo}/"] = response set = @source.dependency_resolver_set