From e373d99f356779c1823c81603eaee6ed71cf68f5 Mon Sep 17 00:00:00 2001 From: hsbt Date: Fri, 4 Mar 2016 00:29:40 +0000 Subject: * lib/rubygems.rb, lib/rubygems/*, test/rubygems/*: Update rubygems-2.6.1. Please see entries of 2.6.0 and 2.6.1 on https://github.com/rubygems/rubygems/blob/master/History.txt [fix GH-1270] Patch by @segiddins git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@53992 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 7 ++ lib/rubygems.rb | 57 +++++++++++-- lib/rubygems/basic_specification.rb | 3 +- lib/rubygems/command.rb | 9 +- lib/rubygems/commands/cleanup_command.rb | 11 ++- lib/rubygems/commands/install_command.rb | 1 + lib/rubygems/commands/open_command.rb | 4 + lib/rubygems/commands/push_command.rb | 10 ++- lib/rubygems/commands/update_command.rb | 1 + lib/rubygems/config_file.rb | 13 ++- lib/rubygems/dependency.rb | 3 +- lib/rubygems/gemcutter_utilities.rb | 10 ++- lib/rubygems/install_update_options.rb | 1 + lib/rubygems/path_support.rb | 36 +++----- lib/rubygems/resolver/installer_set.rb | 12 ++- .../molinillo/lib/molinillo/dependency_graph.rb | 4 + .../molinillo/lib/molinillo/gem_metadata.rb | 2 +- .../resolver/molinillo/lib/molinillo/modules/ui.rb | 3 +- .../resolver/molinillo/lib/molinillo/resolution.rb | 51 +++++++---- lib/rubygems/source_list.rb | 2 +- lib/rubygems/specification.rb | 3 + lib/rubygems/test_case.rb | 2 +- lib/rubygems/version.rb | 28 ++++--- lib/ubygems.rb | 2 +- test/rubygems/test_gem.rb | 98 ++++++++++++++++++++++ test/rubygems/test_gem_command.rb | 4 + test/rubygems/test_gem_commands_cleanup_command.rb | 30 ++++++- test/rubygems/test_gem_commands_install_command.rb | 20 +++++ test/rubygems/test_gem_commands_open_command.rb | 19 ++++- test/rubygems/test_gem_commands_push_command.rb | 58 +++++++++++-- test/rubygems/test_gem_ext_cmake_builder.rb | 4 +- test/rubygems/test_gem_gemcutter_utilities.rb | 4 +- test/rubygems/test_gem_path_support.rb | 48 +++++++++-- test/rubygems/test_gem_source_list.rb | 6 ++ test/rubygems/test_gem_version.rb | 8 ++ 35 files changed, 474 insertions(+), 100 deletions(-) diff --git a/ChangeLog b/ChangeLog index aee4ee1f16..e6916ac31b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Fri Mar 4 09:28:18 2016 SHIBATA Hiroshi + + * lib/rubygems.rb, lib/rubygems/*, test/rubygems/*: Update rubygems-2.6.1. + Please see entries of 2.6.0 and 2.6.1 on + https://github.com/rubygems/rubygems/blob/master/History.txt + [fix GH-1270] Patch by @segiddins + Thu Mar 3 14:09:00 2016 Nobuyoshi Nakada * lib/ostruct.rb (modifiable?, new_ostruct_member!, table!): diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 04031c765c..9be2398f3d 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -10,7 +10,7 @@ require 'rbconfig' require 'thread' module Gem - VERSION = '2.5.2' + VERSION = '2.6.1' end # Must be first since it unloads the prelude from 1.9.2 @@ -174,6 +174,14 @@ module Gem @pre_reset_hooks ||= [] @post_reset_hooks ||= [] + def self.env_requirement(gem_name) + @env_requirements_by_name ||= {} + @env_requirements_by_name[gem_name] ||= begin + req = ENV["GEM_REQUIREMENT_#{gem_name.upcase}"] || '>= 0'.freeze + Gem::Requirement.create(req) + end + end + ## # Try to activate a gem containing +path+. Returns true if # activation succeeded or wasn't needed because it was already @@ -192,8 +200,13 @@ module Gem begin spec.activate - rescue Gem::LoadError # this could fail due to gem dep collisions, go lax - Gem::Specification.find_by_name(spec.name).activate + rescue Gem::LoadError => e # this could fail due to gem dep collisions, go lax + spec_by_name = Gem::Specification.find_by_name(spec.name) + if spec_by_name.nil? + raise e + else + spec_by_name.activate + end end return true @@ -326,16 +339,38 @@ module Gem # lookup files. def self.paths - @paths ||= Gem::PathSupport.new + @paths ||= Gem::PathSupport.new(ENV) end # Initialize the filesystem paths to use from +env+. # +env+ is a hash-like object (typically ENV) that # is queried for 'GEM_HOME', 'GEM_PATH', and 'GEM_SPEC_CACHE' + # Keys for the +env+ hash should be Strings, and values of the hash should + # be Strings or +nil+. def self.paths=(env) clear_paths - @paths = Gem::PathSupport.new env + target = {} + env.each_pair do |k,v| + case k + when 'GEM_HOME', 'GEM_PATH', 'GEM_SPEC_CACHE' + case v + when nil, String + target[k] = v + when Array + unless Gem::Deprecate.skip + warn <<-eowarn +Array values in the parameter are deprecated. Please use a String or nil. +An Array was passed in from #{caller[3]} + eowarn + end + target[k] = v.join File::PATH_SEPARATOR + end + else + target[k] = v + end + end + @paths = Gem::PathSupport.new ENV.to_hash.merge(target) Gem::Specification.dirs = @paths.path end @@ -430,7 +465,9 @@ module Gem files = find_files_from_load_path glob if check_load_path - files.concat Gem::Specification.stubs.map { |spec| + gem_specifications = @gemdeps ? Gem.loaded_specs.values : Gem::Specification.stubs + + files.concat gem_specifications.map { |spec| spec.matches_for_glob("#{glob}#{Gem.suffix_pattern}") }.flatten @@ -939,9 +976,11 @@ module Gem # by the unit tests to provide environment isolation. def self.use_paths(home, *paths) - paths = nil if paths == [nil] - paths = paths.first if Array === Array(paths).first - self.paths = { "GEM_HOME" => home, "GEM_PATH" => paths } + paths.flatten! + paths.compact! + hash = { "GEM_HOME" => home, "GEM_PATH" => paths.empty? ? home : paths.join(File::PATH_SEPARATOR) } + hash.delete_if { |_, v| v.nil? } + self.paths = hash end ## diff --git a/lib/rubygems/basic_specification.rb b/lib/rubygems/basic_specification.rb index 5c57076f91..86f2248b52 100644 --- a/lib/rubygems/basic_specification.rb +++ b/lib/rubygems/basic_specification.rb @@ -95,7 +95,7 @@ class Gem::BasicSpecification # Returns path to the extensions directory. def extensions_dir - @extensions_dir ||= Gem.default_ext_dir_for(base_dir) || + Gem.default_ext_dir_for(base_dir) || File.join(base_dir, 'extensions', Gem::Platform.local.to_s, Gem.extension_api_version) end @@ -196,7 +196,6 @@ class Gem::BasicSpecification def internal_init # :nodoc: @extension_dir = nil - @extensions_dir = nil @full_gem_path = nil @gem_dir = nil @ignored = nil diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb index 5b41fc288e..14b70b75ec 100644 --- a/lib/rubygems/command.rb +++ b/lib/rubygems/command.rb @@ -300,6 +300,8 @@ class Gem::Command options[:build_args] = build_args + self.ui = Gem::SilentUI.new if options[:silent] + if options[:help] then show_help elsif @when_invoked then @@ -520,10 +522,15 @@ class Gem::Command end end - add_common_option('-q', '--quiet', 'Silence commands') do |value, options| + add_common_option('-q', '--quiet', 'Silence command progress meter') do |value, options| Gem.configuration.verbose = false end + add_common_option("--silent", + "Silence rubygems output") do |value, options| + options[:silent] = true + end + # Backtrace and config-file are added so they show up in the help # commands. Both options are actually handled before the other # options get parsed. diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb index 46f89f1bb8..83ee6b5c7c 100644 --- a/lib/rubygems/commands/cleanup_command.rb +++ b/lib/rubygems/commands/cleanup_command.rb @@ -76,6 +76,9 @@ If no gems are named all gems in GEM_HOME are cleaned. end def clean_gems + @original_home = Gem.dir + @original_path = Gem.path + get_primary_gems get_candidate_gems get_gems_to_cleanup @@ -87,9 +90,6 @@ If no gems are named all gems in GEM_HOME are cleaned. deps = deplist.strongly_connected_components.flatten - @original_home = Gem.dir - @original_path = Gem.path - deps.reverse_each do |spec| uninstall_dep spec end @@ -108,6 +108,7 @@ If no gems are named all gems in GEM_HOME are cleaned. end def get_gems_to_cleanup + gems_to_cleanup = @candidate_gems.select { |spec| @primary_gems[spec.name].version != spec.version } @@ -116,6 +117,10 @@ If no gems are named all gems in GEM_HOME are cleaned. spec.default_gem? } + gems_to_cleanup = gems_to_cleanup.select { |spec| + spec.base_dir == @original_home + } + @default_gems += default_gems @default_gems.uniq! @gems_to_cleanup = gems_to_cleanup.uniq diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb index 5f0934aa17..4dd2f943c1 100644 --- a/lib/rubygems/commands/install_command.rb +++ b/lib/rubygems/commands/install_command.rb @@ -186,6 +186,7 @@ to write the specification by hand. For example: end def execute + if options.include? :gemdeps then install_from_gemdeps return # not reached diff --git a/lib/rubygems/commands/open_command.rb b/lib/rubygems/commands/open_command.rb index 957d369b26..a89b7421e3 100644 --- a/lib/rubygems/commands/open_command.rb +++ b/lib/rubygems/commands/open_command.rb @@ -15,6 +15,10 @@ class Gem::Commands::OpenCommand < Gem::Command "Opens gem sources in EDITOR") do |editor, options| options[:editor] = editor || get_env_editor end + add_option('-v', '--version VERSION', String, + "Opens specific gem version") do |version| + options[:version] = version + end end def arguments # :nodoc: diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb index 035a03e5e7..6adeff6b30 100644 --- a/lib/rubygems/commands/push_command.rb +++ b/lib/rubygems/commands/push_command.rb @@ -76,13 +76,17 @@ You can upgrade or downgrade to the latest release version with: @host = gem_data.spec.metadata['default_gem_server'] end - # Always include this, even if it's nil - args << @host + push_host = nil if gem_data.spec.metadata.has_key?('allowed_push_host') - args << gem_data.spec.metadata['allowed_push_host'] + push_host = gem_data.spec.metadata['allowed_push_host'] end + @host ||= push_host + + # Always include @host, even if it's nil + args += [ @host, push_host ] + say "Pushing gem to #{@host || Gem.host}..." response = rubygems_api_request(*args) do |request| diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb index d654a5fa4e..688e9b0e6c 100644 --- a/lib/rubygems/commands/update_command.rb +++ b/lib/rubygems/commands/update_command.rb @@ -85,6 +85,7 @@ command to remove old versions. end def execute + if options[:system] then update_rubygems return diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb index de90cbfd65..c8014814b4 100644 --- a/lib/rubygems/config_file.rb +++ b/lib/rubygems/config_file.rb @@ -306,9 +306,18 @@ if you believe they were disclosed to a third party. # Sets the RubyGems.org API key to +api_key+ def rubygems_api_key= api_key + set_api_key :rubygems_api_key, api_key + + @rubygems_api_key = api_key + end + + ## + # Set a specific host's API key to +api_key+ + + def set_api_key host, api_key check_credentials_permissions - config = load_file(credentials_path).merge(:rubygems_api_key => api_key) + config = load_file(credentials_path).merge(host => api_key) dirname = File.dirname credentials_path Dir.mkdir(dirname) unless File.exist? dirname @@ -320,7 +329,7 @@ if you believe they were disclosed to a third party. f.write config.to_yaml end - @rubygems_api_key = api_key + load_api_keys # reload end def load_file(filename) diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb index c7b2451c6a..bf24b6e724 100644 --- a/lib/rubygems/dependency.rb +++ b/lib/rubygems/dependency.rb @@ -275,8 +275,9 @@ class Gem::Dependency end def matching_specs platform_only = false + env_req = Gem.env_requirement(name) matches = Gem::Specification.stubs_for(name).find_all { |spec| - requirement.satisfied_by? spec.version + requirement.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version) }.map(&:to_spec) if platform_only diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb index 464993b11f..7c6d6bb364 100644 --- a/lib/rubygems/gemcutter_utilities.rb +++ b/lib/rubygems/gemcutter_utilities.rb @@ -115,7 +115,7 @@ module Gem::GemcutterUtilities with_response response do |resp| say "Signed in." - Gem.configuration.rubygems_api_key = resp.body + set_api_key host, resp.body end end @@ -156,5 +156,13 @@ module Gem::GemcutterUtilities end end + def set_api_key host, key + if host == Gem::DEFAULT_HOST + Gem.configuration.rubygems_api_key = key + else + Gem.configuration.set_api_key host, key + end + end + end diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb index 26c5e84d1f..b2ab419800 100644 --- a/lib/rubygems/install_update_options.rb +++ b/lib/rubygems/install_update_options.rb @@ -179,6 +179,7 @@ module Gem::InstallUpdateOptions "Print post install message") do |value, options| options[:post_install_message] = value end + end ## diff --git a/lib/rubygems/path_support.rb b/lib/rubygems/path_support.rb index afb559d472..618bc793c4 100644 --- a/lib/rubygems/path_support.rb +++ b/lib/rubygems/path_support.rb @@ -22,21 +22,16 @@ class Gem::PathSupport # Constructor. Takes a single argument which is to be treated like a # hashtable, or defaults to ENV, the system environment. # - def initialize(env=ENV) - @env = env - - # note 'env' vs 'ENV'... - @home = env["GEM_HOME"] || ENV["GEM_HOME"] || Gem.default_dir + def initialize(env) + @home = env["GEM_HOME"] || Gem.default_dir if File::ALT_SEPARATOR then @home = @home.gsub(File::ALT_SEPARATOR, File::SEPARATOR) end - self.path = env["GEM_PATH"] || ENV["GEM_PATH"] + @path = split_gem_path env["GEM_PATH"], @home - @spec_cache_dir = - env["GEM_SPEC_CACHE"] || ENV["GEM_SPEC_CACHE"] || - Gem.default_spec_cache_dir + @spec_cache_dir = env["GEM_SPEC_CACHE"] || Gem.default_spec_cache_dir @spec_cache_dir = @spec_cache_dir.dup.untaint end @@ -44,24 +39,19 @@ class Gem::PathSupport private ## - # Set the Gem search path (as reported by Gem.path). + # Split the Gem search path (as reported by Gem.path). - def path=(gpaths) + def split_gem_path gpaths, home # FIX: it should be [home, *path], not [*path, home] gem_path = [] - # FIX: I can't tell wtf this is doing. - gpaths ||= (ENV['GEM_PATH'] || "").empty? ? nil : ENV["GEM_PATH"] - if gpaths - if gpaths.kind_of?(Array) - gem_path = gpaths.dup - else - gem_path = gpaths.split(Gem.path_separator) - if gpaths.end_with?(Gem.path_separator) - gem_path += default_path - end + gem_path = gpaths.split(Gem.path_separator) + # Handle the path_separator being set to a regexp, which will cause + # end_with? to error + if gpaths =~ /#{Gem.path_separator}\z/ + gem_path += default_path end if File::ALT_SEPARATOR then @@ -70,12 +60,12 @@ class Gem::PathSupport end end - gem_path << @home + gem_path << home else gem_path = default_path end - @path = gem_path.uniq + gem_path.uniq end # Return the default Gem path diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb index 1ed02e6f9f..07fffeb150 100644 --- a/lib/rubygems/resolver/installer_set.rb +++ b/lib/rubygems/resolver/installer_set.rb @@ -138,10 +138,14 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set local_source = Gem::Source::Local.new - if local_spec = local_source.find_gem(name, dep.requirement) then - res << Gem::Resolver::IndexSpecification.new( - self, local_spec.name, local_spec.version, - local_source, local_spec.platform) + begin + if local_spec = local_source.find_gem(name, dep.requirement) then + res << Gem::Resolver::IndexSpecification.new( + self, local_spec.name, local_spec.version, + local_source, local_spec.platform) + end + rescue Gem::Package::FormatError + # ignore end end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb index deb4659448..42563664d6 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb @@ -127,6 +127,10 @@ module Gem::Resolver::Molinillo v.incoming_edges.delete(e) detach_vertex_named(v.name) unless v.root? || v.predecessors.any? end + vertex.incoming_edges.each do |e| + v = e.origin + v.outgoing_edges.delete(e) + end end # @param [String] name diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb index 79cae2c697..1b66500f0f 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Gem::Resolver::Molinillo # The version of Gem::Resolver::Molinillo. - VERSION = '0.4.1'.freeze + VERSION = '0.4.3'.freeze end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb index 348ace286a..540b5b809c 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb @@ -58,7 +58,8 @@ module Gem::Resolver::Molinillo # # @return [Boolean] def debug? - @debug_mode ||= ENV['MOLINILLO_DEBUG'] + return @debug_mode if defined?(@debug_mode) + @debug_mode = ENV['MOLINILLO_DEBUG'] end end end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb index 0f822f0b82..2fc18843fe 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb @@ -342,26 +342,37 @@ module Gem::Resolver::Molinillo # @return [Boolean] Whether the possibility was swapped into {#activated} def attempt_to_swap_possibility swapped = activated.dup - swapped.vertex_named(name).payload = possibility - return unless swapped.vertex_named(name).requirements. + vertex = swapped.vertex_named(name) + vertex.payload = possibility + return unless vertex.requirements. all? { |r| requirement_satisfied_by?(r, swapped, possibility) } - attempt_to_activate_new_spec + return unless new_spec_satisfied? + actual_vertex = activated.vertex_named(name) + actual_vertex.payload = possibility + fixup_swapped_children(actual_vertex) + activate_spec + end + + # Ensures there are no orphaned successors to the given {vertex}. + # @param [DependencyGraph::Vertex] vertex the vertex to fix up. + # @return [void] + def fixup_swapped_children(vertex) + payload = vertex.payload + dep_names = dependencies_for(payload).map(&method(:name_for)) + vertex.successors.each do |succ| + if !dep_names.include?(succ.name) && !succ.root? && succ.predecessors.to_a == [vertex] + debug(depth) { "Removing orphaned spec #{succ.name} after swapping #{name}" } + activated.detach_vertex_named(succ.name) + requirements.delete_if { |r| name_for(r) == succ.name } + end + end end # Attempts to activate the current {#possibility} (given that it hasn't # already been activated) # @return [void] def attempt_to_activate_new_spec - satisfied = begin - locked_requirement = locked_requirement_named(name) - requested_spec_satisfied = requirement_satisfied_by?(requirement, activated, possibility) - locked_spec_satisfied = !locked_requirement || - requirement_satisfied_by?(locked_requirement, activated, possibility) - debug(depth) { 'Unsatisfied by requested spec' } unless requested_spec_satisfied - debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied - requested_spec_satisfied && locked_spec_satisfied - end - if satisfied + if new_spec_satisfied? activate_spec else create_conflict @@ -369,6 +380,18 @@ module Gem::Resolver::Molinillo end end + # @return [Boolean] whether the current spec is satisfied as a new + # possibility. + def new_spec_satisfied? + locked_requirement = locked_requirement_named(name) + requested_spec_satisfied = requirement_satisfied_by?(requirement, activated, possibility) + locked_spec_satisfied = !locked_requirement || + requirement_satisfied_by?(locked_requirement, activated, possibility) + debug(depth) { 'Unsatisfied by requested spec' } unless requested_spec_satisfied + debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied + requested_spec_satisfied && locked_spec_satisfied + end + # @param [String] requirement_name the spec name to search for # @return [Object] the locked spec named `requirement_name`, if one # is found on {#base} @@ -394,7 +417,7 @@ module Gem::Resolver::Molinillo # @return [void] def require_nested_dependencies_for(activated_spec) nested_dependencies = dependencies_for(activated_spec) - debug(depth) { "Requiring nested dependencies (#{nested_dependencies.map(&:to_s).join(', ')})" } + debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" } nested_dependencies.each { |d| activated.add_child_vertex(name_for(d), nil, [name_for(activated_spec)], d) } push_state_for_requirements(requirements + nested_dependencies, nested_dependencies.size > 0) diff --git a/lib/rubygems/source_list.rb b/lib/rubygems/source_list.rb index 942d657963..66ce4d57ed 100644 --- a/lib/rubygems/source_list.rb +++ b/lib/rubygems/source_list.rb @@ -59,7 +59,7 @@ class Gem::SourceList Gem::Source.new(URI.parse(obj)) end - @sources << src + @sources << src unless @sources.include?(src) src end diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 8e2557cdb2..8ff6299a41 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -1684,6 +1684,8 @@ class Gem::Specification < Gem::BasicSpecification (conflicts[spec] ||= []) << dep end } + env_req = Gem.env_requirement(name) + (conflicts[self] ||= []) << env_req unless env_req.satisfied_by? version conflicts end @@ -1701,6 +1703,7 @@ class Gem::Specification < Gem::BasicSpecification # Return true if there are possible conflicts against the currently loaded specs. def has_conflicts? + return true unless Gem.env_requirement(name).satisfied_by?(version) self.dependencies.any? { |dep| if dep.runtime? then spec = Gem.loaded_specs[dep.name] diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb index aebc36bc6c..0864f33478 100644 --- a/lib/rubygems/test_case.rb +++ b/lib/rubygems/test_case.rb @@ -570,7 +570,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase def write_file(path) path = File.join @gemhome, path unless Pathname.new(path).absolute? dir = File.dirname path - FileUtils.mkdir_p dir + FileUtils.mkdir_p dir unless File.directory? dir open path, 'wb' do |io| yield io if block_given? diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb index 54da49e8e8..2f6cfae6ed 100644 --- a/lib/rubygems/version.rb +++ b/lib/rubygems/version.rb @@ -219,7 +219,7 @@ class Gem::Version def bump @bump ||= begin - segments = self.segments.dup + segments = self.segments segments.pop while segments.any? { |s| String === s } segments.pop if segments.size > 1 @@ -298,7 +298,7 @@ class Gem::Version def release @release ||= if prerelease? - segments = self.segments.dup + segments = self.segments segments.pop while segments.any? { |s| String === s } self.class.new segments.join('.') else @@ -307,20 +307,14 @@ class Gem::Version end def segments # :nodoc: - - # segments is lazy so it can pick up version values that come from - # old marshaled versions, which don't go through marshal_load. - - @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s| - /^\d+$/ =~ s ? s.to_i : s - end + _segments.dup end ## # A recommended version for use with a ~> Requirement. def approximate_recommendation - segments = self.segments.dup + segments = self.segments segments.pop while segments.any? { |s| String === s } segments.pop while segments.size > 2 @@ -339,8 +333,8 @@ class Gem::Version return unless Gem::Version === other return 0 if @version == other._version - lhsegments = segments - rhsegments = other.segments + lhsegments = _segments + rhsegments = other._segments lhsize = lhsegments.size rhsize = rhsegments.size @@ -367,4 +361,14 @@ class Gem::Version def _version @version end + + def _segments + # segments is lazy so it can pick up version values that come from + # old marshaled versions, which don't go through marshal_load. + # since this version object is cached in @@all, its @segments should be frozen + + @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s| + /^\d+$/ =~ s ? s.to_i : s + end.freeze + end end diff --git a/lib/ubygems.rb b/lib/ubygems.rb index 3d1798fe98..51ee23e880 100644 --- a/lib/ubygems.rb +++ b/lib/ubygems.rb @@ -1,4 +1,4 @@ -# frozen_string_literal: false +# frozen_string_literal: true # This file allows for the running of rubygems with a nice # command line look-and-feel: ruby -rubygems foo.rb #-- diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb index aec9d98c9a..aa57261d5e 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -473,6 +473,45 @@ class TestGem < Gem::TestCase assert_equal cwd, $LOAD_PATH.shift end + def test_self_find_files_with_gemfile + # write_file(File.join Dir.pwd, 'Gemfile') fails on travis 1.8.7 with $SAFE=1 + skip if RUBY_VERSION <= "1.8.7" + + cwd = File.expand_path("test/rubygems", @@project_dir) + $LOAD_PATH.unshift cwd + + discover_path = File.join 'lib', 'sff', 'discover.rb' + + foo1, _ = %w(1 2).map { |version| + spec = quick_gem 'sff', version do |s| + s.files << discover_path + end + + write_file(File.join 'gems', spec.full_name, discover_path) do |fp| + fp.puts "# #{spec.full_name}" + end + + spec + } + Gem.refresh + + write_file(File.join Dir.pwd, 'Gemfile') do |fp| + fp.puts "source 'https://rubygems.org'" + fp.puts "gem '#{foo1.name}', '#{foo1.version}'" + end + Gem.use_gemdeps(File.join Dir.pwd, 'Gemfile') + + expected = [ + File.expand_path('test/rubygems/sff/discover.rb', @@project_dir), + File.join(foo1.full_gem_path, discover_path) + ] + + assert_equal expected, Gem.find_files('sff/discover') + assert_equal expected, Gem.find_files('sff/**.rb'), '[ruby-core:31730]' + ensure + assert_equal cwd, $LOAD_PATH.shift unless RUBY_VERSION <= "1.8.7" + end + def test_self_find_latest_files cwd = File.expand_path("test/rubygems", @@project_dir) $LOAD_PATH.unshift cwd @@ -929,6 +968,26 @@ class TestGem < Gem::TestCase assert_match %r%Could not find 'b' %, e.message end + def test_self_try_activate_missing_prerelease + b = util_spec 'b', '1.0rc1' + a = util_spec 'a', '1.0rc1', 'b' => '1.0rc1' + + install_specs b, a + uninstall_gem b + + a_file = File.join a.gem_dir, 'lib', 'a_file.rb' + + write_file a_file do |io| + io.puts '# a_file.rb' + end + + e = assert_raises Gem::LoadError do + Gem.try_activate 'a_file' + end + + assert_match %r%Could not find 'b' \(= 1.0rc1\)%, e.message + end + def test_self_try_activate_missing_extensions spec = util_spec 'ext', '1' do |s| s.extensions = %w[ext/extconf.rb] @@ -951,6 +1010,45 @@ class TestGem < Gem::TestCase assert_equal expected, err end + def test_self_use_paths_with_nils + orig_home = ENV.delete 'GEM_HOME' + orig_path = ENV.delete 'GEM_PATH' + Gem.use_paths nil, nil + assert_equal Gem.default_dir, Gem.paths.home + assert_equal (Gem.default_path + [Gem.paths.home]).uniq, Gem.paths.path + ensure + ENV['GEM_HOME'] = orig_home + ENV['GEM_PATH'] = orig_path + end + + def test_setting_paths_does_not_warn_about_unknown_keys + stdout, stderr = capture_io do + Gem.paths = { 'foo' => [], + 'bar' => Object.new, + 'GEM_HOME' => Gem.paths.home, + 'GEM_PATH' => 'foo' } + end + assert_equal ['foo', Gem.paths.home], Gem.paths.path + assert_equal '', stderr + assert_equal '', stdout + end + + def test_setting_paths_does_not_mutate_parameter_object + Gem.paths = { 'GEM_HOME' => Gem.paths.home, + 'GEM_PATH' => 'foo' }.freeze + assert_equal ['foo', Gem.paths.home], Gem.paths.path + end + + def test_deprecated_paths= + stdout, stderr = capture_io do + Gem.paths = { 'GEM_HOME' => Gem.paths.home, + 'GEM_PATH' => [Gem.paths.home, 'foo'] } + end + assert_equal [Gem.paths.home, 'foo'], Gem.paths.path + assert_match(/Array values in the parameter are deprecated. Please use a String or nil/, stderr) + assert_equal '', stdout + end + def test_self_use_paths util_ensure_gem_dirs diff --git a/test/rubygems/test_gem_command.rb b/test/rubygems/test_gem_command.rb index 61b73874fd..8100a34c25 100644 --- a/test/rubygems/test_gem_command.rb +++ b/test/rubygems/test_gem_command.rb @@ -170,12 +170,16 @@ class TestGemCommand < Gem::TestCase @cmd.add_option('-f', '--file FILE', 'File option') do |value, options| options[:help] = true end + @cmd.add_option('--silent', 'Silence rubygems output') do |value, options| + options[:silent] = true + end assert @cmd.handles?(['-x']) assert @cmd.handles?(['-h']) assert @cmd.handles?(['-h', 'command']) assert @cmd.handles?(['--help', 'command']) assert @cmd.handles?(['-f', 'filename']) assert @cmd.handles?(['--file=filename']) + assert @cmd.handles?(['--silent']) refute @cmd.handles?(['-z']) refute @cmd.handles?(['-f']) refute @cmd.handles?(['--toothpaste']) diff --git a/test/rubygems/test_gem_commands_cleanup_command.rb b/test/rubygems/test_gem_commands_cleanup_command.rb index f988f81e55..8354160dbf 100644 --- a/test/rubygems/test_gem_commands_cleanup_command.rb +++ b/test/rubygems/test_gem_commands_cleanup_command.rb @@ -112,7 +112,7 @@ class TestGemCommandsCleanupCommand < Gem::TestCase @cmd.execute assert_path_exists @a_1.gem_dir - refute_path_exists @a_1_1.gem_dir + assert_path_exists @a_1_1.gem_dir ensure FileUtils.chmod 0755, @gemhome end unless win_platform? @@ -165,5 +165,33 @@ class TestGemCommandsCleanupCommand < Gem::TestCase assert_match %r%^Skipped default gems: b-2%, @ui.output assert_empty @ui.error end + + def test_execute_remove_gem_home_only + c_1, = util_gem 'c', '1' + c_2, = util_gem 'c', '2' + d_1, = util_gem 'd', '1' + d_2, = util_gem 'd', '2' + e_1, = util_gem 'e', '1' + e_2, = util_gem 'e', '2' + + c_1 = install_gem c_1, :user_install => true # pick up user install path + c_2 = install_gem c_2 + + d_1 = install_gem d_1 + d_2 = install_gem d_2, :user_install => true # pick up user install path + + e_1 = install_gem e_1 + e_2 = install_gem e_2 + + Gem::Specification.dirs = [Gem.dir, Gem.user_dir] + + @cmd.options[:args] = [] + + @cmd.execute + + assert_path_exists c_1.gem_dir + refute_path_exists d_1.gem_dir + refute_path_exists e_1.gem_dir + end end diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb index 2525f26789..13b9a7bbad 100644 --- a/test/rubygems/test_gem_commands_install_command.rb +++ b/test/rubygems/test_gem_commands_install_command.rb @@ -438,6 +438,26 @@ ERROR: Possible alternatives: non_existent_with_hint assert_match "1 gem installed", @ui.output end + def test_execute_with_invalid_gem_file + FileUtils.touch("a.gem") + + spec_fetcher do |fetcher| + fetcher.gem 'a', 2 + 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 } + + assert_match "1 gem installed", @ui.output + end + def test_execute_remote_ignores_files specs = spec_fetcher do |fetcher| fetcher.gem 'a', 1 diff --git a/test/rubygems/test_gem_commands_open_command.rb b/test/rubygems/test_gem_commands_open_command.rb index e6cbb43355..3ec38972e6 100644 --- a/test/rubygems/test_gem_commands_open_command.rb +++ b/test/rubygems/test_gem_commands_open_command.rb @@ -10,9 +10,10 @@ class TestGemCommandsOpenCommand < Gem::TestCase @cmd = Gem::Commands::OpenCommand.new end - def gem name + def gem(name, version = "1.0") spec = quick_gem name do |gem| gem.files = %W[lib/#{name}.rb Rakefile] + gem.version = version end write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb]) write_file File.join(*%W[gems #{spec.full_name} Rakefile]) @@ -37,6 +38,22 @@ class TestGemCommandsOpenCommand < Gem::TestCase assert_equal "", @ui.error end + def test_wrong_version + @cmd.options[:version] = "4.0" + @cmd.options[:args] = %w[foo] + + gem "foo", "5.0" + + assert_raises Gem::MockGemUi::TermError do + use_ui @ui do + @cmd.execute + end + end + + assert_match %r|Unable to find gem 'foo'|, @ui.output + assert_equal "", @ui.error + end + def test_execute_bad_gem @cmd.options[:args] = %w[foo] diff --git a/test/rubygems/test_gem_commands_push_command.rb b/test/rubygems/test_gem_commands_push_command.rb index 729de46f9d..b888a741f0 100644 --- a/test/rubygems/test_gem_commands_push_command.rb +++ b/test/rubygems/test_gem_commands_push_command.rb @@ -118,7 +118,7 @@ class TestGemCommandsPushCommand < Gem::TestCase end def test_sending_gem_to_metadata_host - @host = "http://rubygems.engineyard.com" + @host = "http://privategemserver.example" @spec, @path = util_gem "freebird", "1.0.1" do |spec| spec.metadata['default_gem_server'] = @host @@ -152,7 +152,7 @@ class TestGemCommandsPushCommand < Gem::TestCase end def test_sending_gem_to_allowed_push_host - @host = "http://privategemserver.com" + @host = "http://privategemserver.example" @spec, @path = util_gem "freebird", "1.0.1" do |spec| spec.metadata['allowed_push_host'] = @host @@ -179,8 +179,8 @@ class TestGemCommandsPushCommand < Gem::TestCase end def test_sending_gem_to_allowed_push_host_with_basic_credentials - @sanitized_host = "http://privategemserver.com" - @host = "http://user:password@privategemserver.com" + @sanitized_host = "http://privategemserver.example" + @host = "http://user:password@privategemserver.example" @spec, @path = util_gem "freebird", "1.0.1" do |spec| spec.metadata['allowed_push_host'] = @sanitized_host @@ -207,10 +207,10 @@ class TestGemCommandsPushCommand < Gem::TestCase def test_sending_gem_to_disallowed_default_host @spec, @path = util_gem "freebird", "1.0.1" do |spec| - spec.metadata['allowed_push_host'] = "https://privategemserver.com" + spec.metadata['allowed_push_host'] = "https://privategemserver.example" end - response = %{ERROR: "#{@host}" is not allowed by the gemspec, which only allows "https://privategemserver.com"} + response = %{ERROR: "#{@host}" is not allowed by the gemspec, which only allows "https://privategemserver.example"} assert_raises Gem::MockGemUi::TermError do send_battery @@ -220,10 +220,11 @@ class TestGemCommandsPushCommand < Gem::TestCase end def test_sending_gem_to_disallowed_push_host - @host = "https://somebodyelse.com" + @host = "https://anotherprivategemserver.example" + push_host = "https://privategemserver.example" @spec, @path = util_gem "freebird", "1.0.1" do |spec| - spec.metadata['allowed_push_host'] = "https://privategemserver.com" + spec.metadata['allowed_push_host'] = push_host end @api_key = "PRIVKEY" @@ -241,7 +242,7 @@ class TestGemCommandsPushCommand < Gem::TestCase FileUtils.rm Gem.configuration.credentials_path - response = 'ERROR: "https://somebodyelse.com" is not allowed by the gemspec, which only allows "https://privategemserver.com"' + response = "ERROR: \"#{@host}\" is not allowed by the gemspec, which only allows \"#{push_host}\"" assert_raises Gem::MockGemUi::TermError do send_battery @@ -250,6 +251,45 @@ class TestGemCommandsPushCommand < Gem::TestCase assert_match response, @ui.error end + def test_sending_gem_defaulting_to_allowed_push_host + host = "http://privategemserver.example" + + @spec, @path = util_gem "freebird", "1.0.1" do |spec| + spec.metadata.delete('default_gem_server') + spec.metadata['allowed_push_host'] = host + end + + api_key = "PRIVKEY" + + keys = { + host => api_key + } + + FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path + open Gem.configuration.credentials_path, 'w' do |f| + f.write keys.to_yaml + end + Gem.configuration.load_api_keys + + FileUtils.rm Gem.configuration.credentials_path + + @response = "Successfully registered gem: freebird (1.0.1)" + @fetcher.data["#{host}/api/v1/gems"] = [@response, 200, 'OK'] + + # do not set @host + use_ui(@ui) { @cmd.send_gem(@path) } + + assert_match %r{Pushing gem to #{host}...}, @ui.output + + assert_equal Net::HTTP::Post, @fetcher.last_request.class + assert_equal Gem.read_binary(@path), @fetcher.last_request.body + assert_equal File.size(@path), @fetcher.last_request["Content-Length"].to_i + assert_equal "application/octet-stream", @fetcher.last_request["Content-Type"] + assert_equal api_key, @fetcher.last_request["Authorization"] + + assert_match @response, @ui.output + end + def test_raises_error_with_no_arguments def @cmd.sign_in(*); end assert_raises Gem::CommandLineError do diff --git a/test/rubygems/test_gem_ext_cmake_builder.rb b/test/rubygems/test_gem_ext_cmake_builder.rb index b54399ad3d..2093c7da55 100644 --- a/test/rubygems/test_gem_ext_cmake_builder.rb +++ b/test/rubygems/test_gem_ext_cmake_builder.rb @@ -7,6 +7,9 @@ class TestGemExtCmakeBuilder < Gem::TestCase def setup super + # Details: https://github.com/rubygems/rubygems/issues/1270#issuecomment-177368340 + skip "CmakeBuilder doesn't work on Windows." if Gem.win_platform? + `cmake #{Gem::Ext::Builder.redirector}` skip 'cmake not present' unless $?.success? @@ -82,4 +85,3 @@ install (FILES test.txt DESTINATION bin) end end - diff --git a/test/rubygems/test_gem_gemcutter_utilities.rb b/test/rubygems/test_gem_gemcutter_utilities.rb index bd84d4b25a..c3f9a7ea18 100644 --- a/test/rubygems/test_gem_gemcutter_utilities.rb +++ b/test/rubygems/test_gem_gemcutter_utilities.rb @@ -110,7 +110,7 @@ class TestGemGemcutterUtilities < Gem::TestCase assert_match %r{Signed in.}, @sign_in_ui.output credentials = YAML.load_file Gem.configuration.credentials_path - assert_equal api_key, credentials[:rubygems_api_key] + assert_equal api_key, credentials['http://example.com'] end def test_sign_in_with_host_nil @@ -137,7 +137,7 @@ class TestGemGemcutterUtilities < Gem::TestCase assert_match %r{Signed in.}, @sign_in_ui.output credentials = YAML.load_file Gem.configuration.credentials_path - assert_equal api_key, credentials[:rubygems_api_key] + assert_equal api_key, credentials['http://example.com'] end def test_sign_in_skips_with_existing_credentials diff --git a/test/rubygems/test_gem_path_support.rb b/test/rubygems/test_gem_path_support.rb index a4be66fe61..754c43e893 100644 --- a/test/rubygems/test_gem_path_support.rb +++ b/test/rubygems/test_gem_path_support.rb @@ -12,7 +12,7 @@ class TestGemPathSupport < Gem::TestCase end def test_initialize - ps = Gem::PathSupport.new + ps = Gem::PathSupport.new ENV assert_equal ENV["GEM_HOME"], ps.home @@ -21,7 +21,7 @@ class TestGemPathSupport < Gem::TestCase end def test_initialize_home - ps = Gem::PathSupport.new "GEM_HOME" => "#{@tempdir}/foo" + ps = Gem::PathSupport.new ENV.to_hash.merge("GEM_HOME" => "#{@tempdir}/foo") assert_equal File.join(@tempdir, "foo"), ps.home @@ -39,7 +39,7 @@ class TestGemPathSupport < Gem::TestCase end def test_initialize_path - ps = Gem::PathSupport.new "GEM_PATH" => %W[#{@tempdir}/foo #{@tempdir}/bar] + ps = Gem::PathSupport.new ENV.to_hash.merge("GEM_PATH" => %W[#{@tempdir}/foo #{@tempdir}/bar].join(Gem.path_separator)) assert_equal ENV["GEM_HOME"], ps.home @@ -52,9 +52,45 @@ class TestGemPathSupport < Gem::TestCase assert_equal expected, ps.path end + def test_initialize_regexp_path_separator + Gem.stub(:path_separator, /#{File::PATH_SEPARATOR}/) do + path = %W[#{@tempdir}/foo + #{File::PATH_SEPARATOR} + #{@tempdir}/bar + #{File::PATH_SEPARATOR}].join + ps = Gem::PathSupport.new "GEM_PATH" => path, "GEM_HOME" => ENV["GEM_HOME"] + + assert_equal ENV["GEM_HOME"], ps.home + + expected = [ + File.join(@tempdir, 'foo'), + File.join(@tempdir, 'bar'), + ] + Gem.default_path << ENV["GEM_HOME"] + + assert_equal expected, ps.path + end + end + + def test_initialize_path_with_defaults + path = %W[#{@tempdir}/foo + #{File::PATH_SEPARATOR} + #{@tempdir}/bar + #{File::PATH_SEPARATOR}].join + ps = Gem::PathSupport.new "GEM_PATH" => path, "GEM_HOME" => ENV["GEM_HOME"] + + assert_equal ENV["GEM_HOME"], ps.home + + expected = [ + File.join(@tempdir, 'foo'), + File.join(@tempdir, 'bar'), + ] + Gem.default_path << ENV["GEM_HOME"] + + assert_equal expected, ps.path + end + def test_initialize_home_path ps = Gem::PathSupport.new("GEM_HOME" => "#{@tempdir}/foo", - "GEM_PATH" => %W[#{@tempdir}/foo #{@tempdir}/bar]) + "GEM_PATH" => %W[#{@tempdir}/foo #{@tempdir}/bar].join(Gem.path_separator)) assert_equal File.join(@tempdir, "foo"), ps.home @@ -69,12 +105,12 @@ class TestGemPathSupport < Gem::TestCase def test_initialize_spec ENV["GEM_SPEC_CACHE"] = nil - ps = Gem::PathSupport.new + ps = Gem::PathSupport.new ENV assert_equal Gem.default_spec_cache_dir, ps.spec_cache_dir ENV["GEM_SPEC_CACHE"] = 'bar' - ps = Gem::PathSupport.new + ps = Gem::PathSupport.new ENV assert_equal ENV["GEM_SPEC_CACHE"], ps.spec_cache_dir ENV["GEM_SPEC_CACHE"] = File.join @tempdir, 'spec_cache' diff --git a/test/rubygems/test_gem_source_list.rb b/test/rubygems/test_gem_source_list.rb index 15aff9c9b4..c93e0a8697 100644 --- a/test/rubygems/test_gem_source_list.rb +++ b/test/rubygems/test_gem_source_list.rb @@ -25,6 +25,12 @@ class TestGemSourceList < Gem::TestCase def test_append sl = Gem::SourceList.new + sl << @uri + sl << @uri + + assert_equal sl.to_a.size, 1 + + sl.clear source = (sl << @uri) assert_kind_of Gem::Source, source diff --git a/test/rubygems/test_gem_version.rb b/test/rubygems/test_gem_version.rb index 53e2020aba..9898669ce6 100644 --- a/test/rubygems/test_gem_version.rb +++ b/test/rubygems/test_gem_version.rb @@ -146,6 +146,14 @@ class TestGemVersion < Gem::TestCase assert_less_than "1.0.0-1", "1" end + # modifying the segments of a version should not affect the segments of the cached version object + def test_segments + v('9.8.7').segments[2] += 1 + + refute_version_equal "9.8.8", "9.8.7" + assert_equal [9,8,7], v("9.8.7").segments + end + # Asserts that +version+ is a prerelease. def assert_prerelease version -- cgit v1.2.3