diff options
32 files changed, 984 insertions, 260 deletions
@@ -1,3 +1,9 @@ +Sat Jun 18 13:59:54 2016 SHIBATA Hiroshi <hsbt@ruby-lang.org> + + * lib/rubygems.rb, lib/rubygems/*, test/rubygems/*: Update rubygems + HEAD(2c6d256). It contains to update vendored Molinillo to 0.5.0. + https://github.com/rubygems/rubygems/pull/1638 + Sat Jun 18 10:13:37 2016 Nobuyoshi Nakada <nobu@ruby-lang.org> * common.mk (build-ext), ext/extmk.rb: use variable EXTENCS diff --git a/lib/rubygems.rb b/lib/rubygems.rb index e6cc859cbe..b041dd1042 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -154,6 +154,26 @@ module Gem specifications/default ] + ## + # Exception classes used in a Gem.read_binary +rescue+ statement. Not all of + # these are defined in Ruby 1.8.7, hence the need for this convoluted setup. + + READ_BINARY_ERRORS = begin + read_binary_errors = [Errno::EACCES] + read_binary_errors << Errno::ENOTSUP if Errno.const_defined?(:ENOTSUP) + read_binary_errors + end.freeze + + ## + # Exception classes used in Gem.write_binary +rescue+ statement. Not all of + # these are defined in Ruby 1.8.7. + + WRITE_BINARY_ERRORS = begin + write_binary_errors = [] + write_binary_errors << Errno::ENOTSUP if Errno.const_defined?(:ENOTSUP) + write_binary_errors + end.freeze + @@win_platform = nil @configuration = nil @@ -829,7 +849,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} f.flock(File::LOCK_EX) f.read end - rescue Errno::EACCES + rescue *READ_BINARY_ERRORS open path, 'rb' do |f| f.read end @@ -844,6 +864,26 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} end ## + # Safely write a file in binary mode on all platforms. + def self.write_binary(path, data) + open(path, 'wb') do |io| + begin + io.flock(File::LOCK_EX) + rescue *WRITE_BINARY_ERRORS + end + io.write data + end + rescue Errno::ENOLCK # NFS + if Thread.main != Thread.current + raise + else + open(path, 'wb') do |io| + io.write data + end + end + end + + ## # The path to the running Ruby interpreter. def self.ruby diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 3269179200..6e77185547 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -284,6 +284,7 @@ class Gem::Installer # Completely remove any previous gem files FileUtils.rm_rf gem_dir + FileUtils.rm_rf spec.extension_dir FileUtils.mkdir_p gem_dir diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb index 0d9adba26e..c36e71d800 100644 --- a/lib/rubygems/package.rb +++ b/lib/rubygems/package.rb @@ -211,7 +211,9 @@ class Gem::Package stat = File.lstat file if stat.symlink? - tar.add_symlink file, File.readlink(file), stat.mode + relative_dir = File.dirname(file).sub("#{Dir.pwd}/", '') + target_path = File.join(relative_dir, File.readlink(file)) + tar.add_symlink file, target_path, stat.mode end next unless stat.file? diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb index ab0313c9f8..f68b8d4c5e 100644 --- a/lib/rubygems/package/tar_writer.rb +++ b/lib/rubygems/package/tar_writer.rb @@ -310,27 +310,21 @@ class Gem::Package::TarWriter # Splits +name+ into a name and prefix that can fit in the TarHeader def split_name(name) # :nodoc: - if name.bytesize > 256 + if name.bytesize > 256 then raise Gem::Package::TooLongFileName.new("File \"#{name}\" has a too long path (should be 256 or less)") end - if name.bytesize <= 100 then - prefix = "" - else - parts = name.split(/\//) - newname = parts.pop - nxt = "" - - loop do - nxt = parts.pop - break if newname.bytesize + 1 + nxt.bytesize > 100 - newname = nxt + "/" + newname + prefix = '' + if name.bytesize > 100 then + parts = name.split('/', -1) # parts are never empty here + name = parts.pop # initially empty for names with a trailing slash ("foo/.../bar/") + prefix = parts.join('/') # if empty, then it's impossible to split (parts is empty too) + while !parts.empty? && (prefix.bytesize > 155 || name.empty?) + name = parts.pop + '/' + name + prefix = parts.join('/') end - prefix = (parts + [nxt]).join "/" - name = newname - - if name.bytesize > 100 + if name.bytesize > 100 or prefix.empty? then raise Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long name (should be 100 or less)") end diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index a2d8ba45e3..da4db724a1 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -328,20 +328,7 @@ class Gem::RemoteFetcher end if update and path - begin - open(path, 'wb') do |io| - io.flock(File::LOCK_EX) - io.write data - end - rescue Errno::ENOLCK # NFS - if Thread.main != Thread.current - raise - else - open(path, 'wb') do |io| - io.write data - end - end - end + Gem.write_binary(path, data) end data @@ -427,4 +414,3 @@ class Gem::RemoteFetcher end end end - diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb new file mode 100644 index 0000000000..1bbc72c1f6 --- /dev/null +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true +module Gem::Resolver::Molinillo + # @!visibility private + module Delegates + # Delegates all {Gem::Resolver::Molinillo::ResolutionState} methods to a `#state` property. + module ResolutionState + # (see Gem::Resolver::Molinillo::ResolutionState#name) + def name + current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty + current_state.name + end + + # (see Gem::Resolver::Molinillo::ResolutionState#requirements) + def requirements + current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty + current_state.requirements + end + + # (see Gem::Resolver::Molinillo::ResolutionState#activated) + def activated + current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty + current_state.activated + end + + # (see Gem::Resolver::Molinillo::ResolutionState#requirement) + def requirement + current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty + current_state.requirement + end + + # (see Gem::Resolver::Molinillo::ResolutionState#possibilities) + def possibilities + current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty + current_state.possibilities + end + + # (see Gem::Resolver::Molinillo::ResolutionState#depth) + def depth + current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty + current_state.depth + end + + # (see Gem::Resolver::Molinillo::ResolutionState#conflicts) + def conflicts + current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty + current_state.conflicts + end + end + end +end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb new file mode 100644 index 0000000000..71903c7e86 --- /dev/null +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true +module Gem::Resolver::Molinillo + module Delegates + # Delegates all {Gem::Resolver::Molinillo::SpecificationProvider} methods to a + # `#specification_provider` property. + module SpecificationProvider + # (see Gem::Resolver::Molinillo::SpecificationProvider#search_for) + def search_for(dependency) + with_no_such_dependency_error_handling do + specification_provider.search_for(dependency) + end + end + + # (see Gem::Resolver::Molinillo::SpecificationProvider#dependencies_for) + def dependencies_for(specification) + with_no_such_dependency_error_handling do + specification_provider.dependencies_for(specification) + end + end + + # (see Gem::Resolver::Molinillo::SpecificationProvider#requirement_satisfied_by?) + def requirement_satisfied_by?(requirement, activated, spec) + with_no_such_dependency_error_handling do + specification_provider.requirement_satisfied_by?(requirement, activated, spec) + end + end + + # (see Gem::Resolver::Molinillo::SpecificationProvider#name_for) + def name_for(dependency) + with_no_such_dependency_error_handling do + specification_provider.name_for(dependency) + end + end + + # (see Gem::Resolver::Molinillo::SpecificationProvider#name_for_explicit_dependency_source) + def name_for_explicit_dependency_source + with_no_such_dependency_error_handling do + specification_provider.name_for_explicit_dependency_source + end + end + + # (see Gem::Resolver::Molinillo::SpecificationProvider#name_for_locking_dependency_source) + def name_for_locking_dependency_source + with_no_such_dependency_error_handling do + specification_provider.name_for_locking_dependency_source + end + end + + # (see Gem::Resolver::Molinillo::SpecificationProvider#sort_dependencies) + def sort_dependencies(dependencies, activated, conflicts) + with_no_such_dependency_error_handling do + specification_provider.sort_dependencies(dependencies, activated, conflicts) + end + end + + # (see Gem::Resolver::Molinillo::SpecificationProvider#allow_missing?) + def allow_missing?(dependency) + with_no_such_dependency_error_handling do + specification_provider.allow_missing?(dependency) + end + end + + private + + # Ensures any raised {NoSuchDependencyError} has its + # {NoSuchDependencyError#required_by} set. + # @yield + def with_no_such_dependency_error_handling + yield + rescue NoSuchDependencyError => error + if state + vertex = activated.vertex_named(name_for(error.dependency)) + error.required_by += vertex.incoming_edges.map { |e| e.origin.name } + error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty? + end + raise + end + end + 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 42563664d6..6189a717cd 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb @@ -2,6 +2,9 @@ require 'set' require 'tsort' +require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log' +require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex' + module Gem::Resolver::Molinillo # A directed acyclic graph that is tuned to hold named dependencies class DependencyGraph @@ -10,15 +13,16 @@ module Gem::Resolver::Molinillo # Enumerates through the vertices of the graph. # @return [Array<Vertex>] The graph's vertices. def each + return vertices.values.each unless block_given? vertices.values.each { |v| yield v } end include TSort - # @visibility private - alias_method :tsort_each_node, :each + # @!visibility private + alias tsort_each_node each - # @visibility private + # @!visibility private def tsort_each_child(vertex, &block) vertex.successors.each(&block) end @@ -44,9 +48,27 @@ module Gem::Resolver::Molinillo # by {Vertex#name} attr_reader :vertices + # @return [Log] the op log for this graph + attr_reader :log + # Initializes an empty dependency graph def initialize @vertices = {} + @log = Log.new + end + + # Tags the current state of the dependency as the given tag + # @param [Object] tag an opaque tag for the current state of the graph + # @return [Void] + def tag(tag) + log.tag(self, tag) + end + + # Rewinds the graph to the state tagged as `tag` + # @param [Object] tag the tag to rewind to + # @return [Void] + def rewind_to(tag) + log.rewind_to(self, tag) end # Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices} @@ -55,6 +77,7 @@ module Gem::Resolver::Molinillo def initialize_copy(other) super @vertices = {} + @log = other.log.dup traverse = lambda do |new_v, old_v| return if new_v.outgoing_edges.size == old_v.outgoing_edges.size old_v.outgoing_edges.each do |edge| @@ -75,6 +98,22 @@ module Gem::Resolver::Molinillo "#{self.class}:#{vertices.values.inspect}" end + # @return [String] Returns a dot format representation of the graph + def to_dot + dot_vertices = [] + dot_edges = [] + vertices.each do |n, v| + dot_vertices << " #{n} [label=\"{#{n}|#{v.payload}}\"]" + v.outgoing_edges.each do |e| + dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=\"#{e.requirement}\"]" + end + end + dot_vertices.sort! + dot_edges.sort! + dot = dot_vertices.unshift('digraph G {').push('') + dot_edges.push('}') + dot.join("\n") + end + # @return [Boolean] whether the two dependency graphs are equal, determined # by a recursive traversal of each {#root_vertices} and its # {Vertex#successors} @@ -93,12 +132,9 @@ module Gem::Resolver::Molinillo # @param [Object] requirement the requirement that is requiring the child # @return [void] def add_child_vertex(name, payload, parent_names, requirement) - vertex = add_vertex(name, payload) + root = !parent_names.delete(nil) { true } + vertex = add_vertex(name, payload, root) parent_names.each do |parent_name| - unless parent_name - vertex.root = true - next - end parent_node = vertex_named(parent_name) add_edge(parent_node, vertex, requirement) end @@ -110,10 +146,7 @@ module Gem::Resolver::Molinillo # @param [Object] payload # @return [Vertex] the vertex that was added to `self` def add_vertex(name, payload, root = false) - vertex = vertices[name] ||= Vertex.new(name, payload) - vertex.payload ||= payload - vertex.root ||= root - vertex + log.add_vertex(self, name, payload, root) end # Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively @@ -121,16 +154,7 @@ module Gem::Resolver::Molinillo # @param [String] name # @return [void] def detach_vertex_named(name) - return unless vertex = vertices.delete(name) - vertex.outgoing_edges.each do |e| - v = e.destination - 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 + log.detach_vertex_named(self, name) end # @param [String] name @@ -158,134 +182,22 @@ module Gem::Resolver::Molinillo add_edge_no_circular(origin, destination, requirement) end + # Sets the payload of the vertex with the given name + # @param [String] name the name of the vertex + # @param [Object] payload the payload + # @return [Void] + def set_payload(name, payload) + log.set_payload(self, name, payload) + end + private # Adds a new {Edge} to the dependency graph without checking for # circularity. + # @param (see #add_edge) + # @return (see #add_edge) def add_edge_no_circular(origin, destination, requirement) - edge = Edge.new(origin, destination, requirement) - origin.outgoing_edges << edge - destination.incoming_edges << edge - edge - end - - # A vertex in a {DependencyGraph} that encapsulates a {#name} and a - # {#payload} - class Vertex - # @return [String] the name of the vertex - attr_accessor :name - - # @return [Object] the payload the vertex holds - attr_accessor :payload - - # @return [Arrary<Object>] the explicit requirements that required - # this vertex - attr_reader :explicit_requirements - - # @return [Boolean] whether the vertex is considered a root vertex - attr_accessor :root - alias_method :root?, :root - - # Initializes a vertex with the given name and payload. - # @param [String] name see {#name} - # @param [Object] payload see {#payload} - def initialize(name, payload) - @name = name - @payload = payload - @explicit_requirements = [] - @outgoing_edges = [] - @incoming_edges = [] - end - - # @return [Array<Object>] all of the requirements that required - # this vertex - def requirements - incoming_edges.map(&:requirement) + explicit_requirements - end - - # @return [Array<Edge>] the edges of {#graph} that have `self` as their - # {Edge#origin} - attr_accessor :outgoing_edges - - # @return [Array<Edge>] the edges of {#graph} that have `self` as their - # {Edge#destination} - attr_accessor :incoming_edges - - # @return [Array<Vertex>] the vertices of {#graph} that have an edge with - # `self` as their {Edge#destination} - def predecessors - incoming_edges.map(&:origin) - end - - # @return [Array<Vertex>] the vertices of {#graph} where `self` is a - # {#descendent?} - def recursive_predecessors - vertices = predecessors - vertices += vertices.map(&:recursive_predecessors).flatten(1) - vertices.uniq! - vertices - end - - # @return [Array<Vertex>] the vertices of {#graph} that have an edge with - # `self` as their {Edge#origin} - def successors - outgoing_edges.map(&:destination) - end - - # @return [Array<Vertex>] the vertices of {#graph} where `self` is an - # {#ancestor?} - def recursive_successors - vertices = successors - vertices += vertices.map(&:recursive_successors).flatten(1) - vertices.uniq! - vertices - end - - # @return [String] a string suitable for debugging - def inspect - "#{self.class}:#{name}(#{payload.inspect})" - end - - # @return [Boolean] whether the two vertices are equal, determined - # by a recursive traversal of each {Vertex#successors} - def ==(other) - shallow_eql?(other) && - successors.to_set == other.successors.to_set - end - - # @param [Vertex] other the other vertex to compare to - # @return [Boolean] whether the two vertices are equal, determined - # solely by {#name} and {#payload} equality - def shallow_eql?(other) - other && - name == other.name && - payload == other.payload - end - - alias_method :eql?, :== - - # @return [Fixnum] a hash for the vertex based upon its {#name} - def hash - name.hash - end - - # Is there a path from `self` to `other` following edges in the - # dependency graph? - # @return true iff there is a path following edges within this {#graph} - def path_to?(other) - equal?(other) || successors.any? { |v| v.path_to?(other) } - end - - alias_method :descendent?, :path_to? - - # Is there a path from `other` to `self` following edges in the - # dependency graph? - # @return true iff there is a path following edges within this {#graph} - def ancestor?(other) - other.path_to?(self) - end - - alias_method :is_reachable_from?, :ancestor? + log.add_edge_no_circular(self, origin.name, destination.name, requirement) end end end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb new file mode 100644 index 0000000000..dbf4b0b803 --- /dev/null +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true +module Gem::Resolver::Molinillo + class DependencyGraph + # An action that modifies a {DependencyGraph} that is reversible. + # @abstract + class Action + # rubocop:disable Lint/UnusedMethodArgument + + # @return [Symbol] The name of the action. + def self.name + raise 'Abstract' + end + + # Performs the action on the given graph. + # @param [DependencyGraph] graph the graph to perform the action on. + # @return [Void] + def up(graph) + raise 'Abstract' + end + + # Reverses the action on the given graph. + # @param [DependencyGraph] graph the graph to reverse the action on. + # @return [Void] + def down(graph) + raise 'Abstract' + end + + # @return [Action,Nil] The previous action + attr_accessor :previous + + # @return [Action,Nil] The next action + attr_accessor :next + end + end +end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb new file mode 100644 index 0000000000..b2d569ddc9 --- /dev/null +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true +require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action' +module Gem::Resolver::Molinillo + class DependencyGraph + # @!visibility private + # (see DependencyGraph#add_edge_no_circular) + class AddEdgeNoCircular < Action + # @!group Action + + # (see Action.name) + def self.name + :add_vertex + end + + # (see Action#up) + def up(graph) + edge = make_edge(graph) + edge.origin.outgoing_edges << edge + edge.destination.incoming_edges << edge + edge + end + + # (see Action#down) + def down(graph) + edge = make_edge(graph) + edge.origin.outgoing_edges.delete(edge) + edge.destination.incoming_edges.delete(edge) + end + + # @!group AddEdgeNoCircular + + # @return [String] the name of the origin of the edge + attr_reader :origin + + # @return [String] the name of the destination of the edge + attr_reader :destination + + # @return [Object] the requirement that the edge represents + attr_reader :requirement + + # @param [DependencyGraph] graph the graph to find vertices from + # @return [Edge] The edge this action adds + def make_edge(graph) + Edge.new(graph.vertex_named(origin), graph.vertex_named(destination), requirement) + end + + # Initialize an action to add an edge to a dependency graph + # @param [String] origin the name of the origin of the edge + # @param [String] destination the name of the destination of the edge + # @param [Object] requirement the requirement that the edge represents + def initialize(origin, destination, requirement) + @origin = origin + @destination = destination + @requirement = requirement + end + end + end +end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb new file mode 100644 index 0000000000..e8b4278ba4 --- /dev/null +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true +require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action' +module Gem::Resolver::Molinillo + class DependencyGraph + # @!visibility private + # (see DependencyGraph#add_vertex) + class AddVertex < Action # :nodoc: + # @!group Action + + # (see Action.name) + def self.name + :add_vertex + end + + # (see Action#up) + def up(graph) + if existing = graph.vertices[name] + @existing_payload = existing.payload + @existing_root = existing.root + end + vertex = existing || Vertex.new(name, payload) + graph.vertices[vertex.name] = vertex + vertex.payload ||= payload + vertex.root ||= root + vertex + end + + # (see Action#down) + def down(graph) + if defined?(@existing_payload) + vertex = graph.vertices[name] + vertex.payload = @existing_payload + vertex.root = @existing_root + else + graph.vertices.delete(name) + end + end + + # @!group AddVertex + + # @return [String] the name of the vertex + attr_reader :name + + # @return [Object] the payload for the vertex + attr_reader :payload + + # @return [Boolean] whether the vertex is root or not + attr_reader :root + + # Initialize an action to add a vertex to a dependency graph + # @param [String] name the name of the vertex + # @param [Object] payload the payload for the vertex + # @param [Boolean] root whether the vertex is root or not + def initialize(name, payload, root) + @name = name + @payload = payload + @root = root + end + end + end +end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb new file mode 100644 index 0000000000..59ef7d8c09 --- /dev/null +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true +require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action' +module Gem::Resolver::Molinillo + class DependencyGraph + # @!visibility private + # @see DependencyGraph#detach_vertex_named + class DetachVertexNamed < Action + # @!group Action + + # (see Action#name) + def self.name + :add_vertex + end + + # (see Action#up) + def up(graph) + return unless @vertex = graph.vertices.delete(name) + @vertex.outgoing_edges.each do |e| + v = e.destination + v.incoming_edges.delete(e) + graph.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 + + # (see Action#down) + def down(graph) + return unless @vertex + graph.vertices[@vertex.name] = @vertex + @vertex.outgoing_edges.each do |e| + e.destination.incoming_edges << e + end + @vertex.incoming_edges.each do |e| + e.origin.outgoing_edges << e + end + end + + # @!group DetachVertexNamed + + # @return [String] the name of the vertex to detach + attr_reader :name + + # Initialize an action to detach a vertex from a dependency graph + # @param [String] name the name of the vertex to detach + def initialize(name) + @name = name + end + end + end +end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb new file mode 100644 index 0000000000..874c4480e3 --- /dev/null +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true +require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular' +require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex' +require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named' +require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload' +require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag' + +module Gem::Resolver::Molinillo + class DependencyGraph + # A log for dependency graph actions + class Log + # Initializes an empty log + def initialize + @current_action = @first_action = nil + end + + # @!macro [new] action + # {include:DependencyGraph#$0} + # @param [Graph] graph the graph to perform the action on + # @param (see DependencyGraph#$0) + # @return (see DependencyGraph#$0) + + # @macro action + def tag(graph, tag) + push_action(graph, Tag.new(tag)) + end + + # @macro action + def add_vertex(graph, name, payload, root) + push_action(graph, AddVertex.new(name, payload, root)) + end + + # @macro action + def detach_vertex_named(graph, name) + push_action(graph, DetachVertexNamed.new(name)) + end + + # @macro action + def add_edge_no_circular(graph, origin, destination, requirement) + push_action(graph, AddEdgeNoCircular.new(origin, destination, requirement)) + end + + # @macro action + def set_payload(graph, name, payload) + push_action(graph, SetPayload.new(name, payload)) + end + + # Pops the most recent action from the log and undoes the action + # @param [DependencyGraph] graph + # @return [Action] the action that was popped off the log + def pop!(graph) + return unless action = @current_action + unless @current_action = action.previous + @first_action = nil + end + action.down(graph) + action + end + + extend Enumerable + + # @!visibility private + # Enumerates each action in the log + # @yield [Action] + def each + return enum_for unless block_given? + action = @first_action + loop do + break unless action + yield action + action = action.next + end + self + end + + # @!visibility private + # Enumerates each action in the log in reverse order + # @yield [Action] + def reverse_each + return enum_for(:reverse_each) unless block_given? + action = @current_action + loop do + break unless action + yield action + action = action.previous + end + self + end + + # @macro action + def rewind_to(graph, tag) + loop do + action = pop!(graph) + raise "No tag #{tag.inspect} found" unless action + break if action.class.name == :tag && action.tag == tag + end + end + + private + + # Adds the given action to the log, running the action + # @param [DependencyGraph] graph + # @param [Action] action + # @return The value returned by `action.up` + def push_action(graph, action) + action.previous = @current_action + @current_action.next = action if @current_action + @current_action = action + @first_action ||= action + action.up(graph) + end + end + end +end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb new file mode 100644 index 0000000000..633bc64601 --- /dev/null +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true +require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action' +module Gem::Resolver::Molinillo + class DependencyGraph + # @!visibility private + # @see DependencyGraph#set_payload + class SetPayload < Action # :nodoc: + # @!group Action + + # (see Action.name) + def self.name + :set_payload + end + + # (see Action#up) + def up(graph) + vertex = graph.vertex_named(name) + @old_payload = vertex.payload + vertex.payload = payload + end + + # (see Action#down) + def down(graph) + graph.vertex_named(name).payload = @old_payload + end + + # @!group SetPayload + + # @return [String] the name of the vertex + attr_reader :name + + # @return [Object] the payload for the vertex + attr_reader :payload + + # Initialize an action to add set the payload for a vertex in a dependency + # graph + # @param [String] name the name of the vertex + # @param [Object] payload the payload for the vertex + def initialize(name, payload) + @name = name + @payload = payload + end + end + end +end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb new file mode 100644 index 0000000000..808cd6a3d8 --- /dev/null +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true +require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action' +module Gem::Resolver::Molinillo + class DependencyGraph + # @!visibility private + # @see DependencyGraph#tag + class Tag < Action + # @!group Action + + # (see Action.name) + def self.name + :tag + end + + # (see Action#up) + def up(_graph) + end + + # (see Action#down) + def down(_graph) + end + + # @!group Tag + + # @return [Object] An opaque tag + attr_reader :tag + + # Initialize an action to tag a state of a dependency graph + # @param [Object] tag an opaque tag + def initialize(tag) + @tag = tag + end + end + end +end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb new file mode 100644 index 0000000000..88d109c94f --- /dev/null +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true +module Gem::Resolver::Molinillo + class DependencyGraph + # A vertex in a {DependencyGraph} that encapsulates a {#name} and a + # {#payload} + class Vertex + # @return [String] the name of the vertex + attr_accessor :name + + # @return [Object] the payload the vertex holds + attr_accessor :payload + + # @return [Arrary<Object>] the explicit requirements that required + # this vertex + attr_reader :explicit_requirements + + # @return [Boolean] whether the vertex is considered a root vertex + attr_accessor :root + alias root? root + + # Initializes a vertex with the given name and payload. + # @param [String] name see {#name} + # @param [Object] payload see {#payload} + def initialize(name, payload) + @name = name.frozen? ? name : name.dup.freeze + @payload = payload + @explicit_requirements = [] + @outgoing_edges = [] + @incoming_edges = [] + end + + # @return [Array<Object>] all of the requirements that required + # this vertex + def requirements + incoming_edges.map(&:requirement) + explicit_requirements + end + + # @return [Array<Edge>] the edges of {#graph} that have `self` as their + # {Edge#origin} + attr_accessor :outgoing_edges + + # @return [Array<Edge>] the edges of {#graph} that have `self` as their + # {Edge#destination} + attr_accessor :incoming_edges + + # @return [Array<Vertex>] the vertices of {#graph} that have an edge with + # `self` as their {Edge#destination} + def predecessors + incoming_edges.map(&:origin) + end + + # @return [Array<Vertex>] the vertices of {#graph} where `self` is a + # {#descendent?} + def recursive_predecessors + vertices = predecessors + vertices += vertices.map(&:recursive_predecessors).flatten(1) + vertices.uniq! + vertices + end + + # @return [Array<Vertex>] the vertices of {#graph} that have an edge with + # `self` as their {Edge#origin} + def successors + outgoing_edges.map(&:destination) + end + + # @return [Array<Vertex>] the vertices of {#graph} where `self` is an + # {#ancestor?} + def recursive_successors + vertices = successors + vertices += vertices.map(&:recursive_successors).flatten(1) + vertices.uniq! + vertices + end + + # @return [String] a string suitable for debugging + def inspect + "#{self.class}:#{name}(#{payload.inspect})" + end + + # @return [Boolean] whether the two vertices are equal, determined + # by a recursive traversal of each {Vertex#successors} + def ==(other) + shallow_eql?(other) && + successors.to_set == other.successors.to_set + end + + # @param [Vertex] other the other vertex to compare to + # @return [Boolean] whether the two vertices are equal, determined + # solely by {#name} and {#payload} equality + def shallow_eql?(other) + other && + name == other.name && + payload == other.payload + end + + alias eql? == + + # @return [Fixnum] a hash for the vertex based upon its {#name} + def hash + name.hash + end + + # Is there a path from `self` to `other` following edges in the + # dependency graph? + # @return true iff there is a path following edges within this {#graph} + def path_to?(other) + equal?(other) || successors.any? { |v| v.path_to?(other) } + end + + alias descendent? path_to? + + # Is there a path from `other` to `self` following edges in the + # dependency graph? + # @return true iff there is a path following edges within this {#graph} + def ancestor?(other) + other.path_to?(self) + end + + alias is_reachable_from? ancestor? + end + end +end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb index 3fad948392..129246bf4a 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb @@ -26,7 +26,7 @@ module Gem::Resolver::Molinillo def message sources = required_by.map { |r| "`#{r}`" }.join(' and ') message = "Unable to find a specification for `#{dependency}`" - message << " depended upon by #{sources}" unless sources.empty? + message += " depended upon by #{sources}" unless sources.empty? message end end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb index 1b66500f0f..1a82da0e7a 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.3'.freeze + VERSION = '0.5.0'.freeze end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb index 2fc18843fe..1fb7a1e921 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb @@ -52,6 +52,7 @@ module Gem::Resolver::Molinillo @base = base @states = [] @iteration_counter = 0 + @parent_of = {} end # Resolves the {#original_requested} dependencies into a full dependency @@ -67,7 +68,12 @@ module Gem::Resolver::Molinillo indicate_progress if state.respond_to?(:pop_possibility_state) # DependencyState debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" } - state.pop_possibility_state.tap { |s| states.push(s) if s } + state.pop_possibility_state.tap do |s| + if s + states.push(s) + activated.tag(s) + end + end end process_topmost_state end @@ -118,27 +124,11 @@ module Gem::Resolver::Molinillo require 'rubygems/resolver/molinillo/lib/molinillo/state' require 'rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider' - ResolutionState.new.members.each do |member| - define_method member do |*args, &block| - current_state = state || ResolutionState.empty - current_state.send(member, *args, &block) - end - end + require 'rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state' + require 'rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider' - SpecificationProvider.instance_methods(false).each do |instance_method| - define_method instance_method do |*args, &block| - begin - specification_provider.send(instance_method, *args, &block) - rescue NoSuchDependencyError => error - if state - vertex = activated.vertex_named(name_for error.dependency) - error.required_by += vertex.incoming_edges.map { |e| e.origin.name } - error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty? - end - raise - end - end - end + include Gem::Resolver::Molinillo::Delegates::ResolutionState + include Gem::Resolver::Molinillo::Delegates::SpecificationProvider # Processes the topmost available {RequirementState} on the stack # @return [void] @@ -169,6 +159,7 @@ module Gem::Resolver::Molinillo def initial_state graph = DependencyGraph.new.tap do |dg| original_requested.each { |r| dg.add_vertex(name_for(r), nil, true).tap { |v| v.explicit_requirements << r } } + dg.tag(:initial_state) end requirements = sort_dependencies(original_requested, graph, {}) @@ -189,8 +180,9 @@ module Gem::Resolver::Molinillo def unwind_for_conflict debug(depth) { "Unwinding for conflict: #{requirement}" } conflicts.tap do |c| - states.slice!((state_index_for_unwind + 1)..-1) + sliced_states = states.slice!((state_index_for_unwind + 1)..-1) raise VersionConflict.new(c) unless state + activated.rewind_to(sliced_states.first || :initial_state) if sliced_states state.conflicts = c end end @@ -217,20 +209,14 @@ module Gem::Resolver::Molinillo # @return [Object] the requirement that led to `requirement` being added # to the list of requirements. def parent_of(requirement) - return nil unless requirement - seen = false - state = states.reverse_each.find do |s| - seen ||= s.requirement == requirement || s.requirements.include?(requirement) - seen && s.requirement != requirement && !s.requirements.include?(requirement) - end - state && state.requirement + @parent_of[requirement] end # @return [Object] the requirement that led to a version of a possibility # with the given name being activated. def requirement_for_existing_name(name) return nil unless activated.vertex_named(name).payload - states.reverse_each.find { |s| !s.activated.vertex_named(name).payload }.requirement + states.find { |s| s.name == name }.requirement end # @return [ResolutionState] the state whose `requirement` is the given @@ -250,19 +236,25 @@ module Gem::Resolver::Molinillo # the {#possibility} in conjunction with the current {#state} def create_conflict vertex = activated.vertex_named(name) - requirements = { - name_for_explicit_dependency_source => vertex.explicit_requirements, - name_for_locking_dependency_source => Array(locked_requirement_named(name)), - } + locked_requirement = locked_requirement_named(name) + + requirements = {} + unless vertex.explicit_requirements.empty? + requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements + end + requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(edge.requirement) } + + activated_by_name = {} + activated.each { |v| activated_by_name[v.name] = v.payload if v.payload } conflicts[name] = Conflict.new( requirement, - Hash[requirements.select { |_, r| !r.empty? }], + requirements, vertex.payload, possibility, - locked_requirement_named(name), + locked_requirement, requirement_trees, - Hash[activated.map { |v| [v.name, v.payload] }.select(&:last)] + activated_by_name ) end @@ -341,15 +333,16 @@ module Gem::Resolver::Molinillo # spec with the given name # @return [Boolean] Whether the possibility was swapped into {#activated} def attempt_to_swap_possibility - swapped = activated.dup - vertex = swapped.vertex_named(name) - vertex.payload = possibility - return unless vertex.requirements. - all? { |r| requirement_satisfied_by?(r, swapped, possibility) } - return unless new_spec_satisfied? - actual_vertex = activated.vertex_named(name) - actual_vertex.payload = possibility - fixup_swapped_children(actual_vertex) + activated.tag(:swap) + vertex = activated.vertex_named(name) + activated.set_payload(name, possibility) + if !vertex.requirements. + all? { |r| requirement_satisfied_by?(r, activated, possibility) } || + !new_spec_satisfied? + activated.rewind_to(:swap) + return + end + fixup_swapped_children(vertex) activate_spec end @@ -363,7 +356,13 @@ module Gem::Resolver::Molinillo 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 } + + all_successor_names = succ.recursive_successors.map(&:name) + + requirements.delete_if do |requirement| + requirement_name = name_for(requirement) + (requirement_name == succ.name) || all_successor_names.include?(requirement_name) + end end end end @@ -406,8 +405,7 @@ module Gem::Resolver::Molinillo def activate_spec conflicts.delete(name) debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s } - vertex = activated.vertex_named(name) - vertex.payload = possibility + activated.set_payload(name, possibility) require_nested_dependencies_for(possibility) end @@ -418,19 +416,22 @@ module Gem::Resolver::Molinillo def require_nested_dependencies_for(activated_spec) nested_dependencies = dependencies_for(activated_spec) 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) } + nested_dependencies.each do |d| + activated.add_child_vertex(name_for(d), nil, [name_for(activated_spec)], d) + @parent_of[d] = requirement + end - push_state_for_requirements(requirements + nested_dependencies, nested_dependencies.size > 0) + push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?) end # Pushes a new {DependencyState} that encapsulates both existing and new # requirements # @param [Array] new_requirements # @return [void] - def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated.dup) + def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated) new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort new_requirement = new_requirements.shift - new_name = new_requirement ? name_for(new_requirement) : '' + new_name = new_requirement ? name_for(new_requirement) : ''.freeze possibilities = new_requirement ? search_for(new_requirement) : [] handle_missing_or_push_dependency_state DependencyState.new( new_name, new_requirements, new_activated, @@ -451,7 +452,7 @@ module Gem::Resolver::Molinillo state.activated.detach_vertex_named(state.name) push_state_for_requirements(state.requirements.dup, false, state.activated) else - states.push state + states.push(state).tap { activated.tag(state) } end end end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb index ac25538a5a..c20de98854 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb @@ -36,12 +36,14 @@ module Gem::Resolver::Molinillo PossibilityState.new( name, requirements.dup, - activated.dup, + activated, requirement, [possibilities.pop], depth + 1, conflicts.dup - ) + ).tap do |state| + state.activated.tag(state) + end end end diff --git a/lib/rubygems/security/signer.rb b/lib/rubygems/security/signer.rb index 1c9d9b7d3b..0c6ef60a9a 100644 --- a/lib/rubygems/security/signer.rb +++ b/lib/rubygems/security/signer.rb @@ -102,6 +102,8 @@ class Gem::Security::Signer def sign data return unless @key + raise Gem::Security::Exception, 'no certs provided' if @cert_chain.empty? + if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now then re_sign_key end diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index c50ff31fdb..bb912ce24c 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -209,9 +209,9 @@ class Gem::Specification < Gem::BasicSpecification ## # Paths in the gem to add to <code>$LOAD_PATH</code> when this gem is # activated. - # + #-- # See also #require_paths - # + #++ # If you have an extension you do not need to add <code>"ext"</code> to the # require path, the extension build process will copy the extension files # into "lib" for you. diff --git a/lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRoot.pem b/lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRoot.pem deleted file mode 100644 index 471f3c553b..0000000000 --- a/lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRoot.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx -GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds -b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV -BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD -VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa -DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc -THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb -Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP -c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX -gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF -AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj -Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG -j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH -hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC -X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== ------END CERTIFICATE-----
\ No newline at end of file diff --git a/lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem b/lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem new file mode 100644 index 0000000000..f4ce4ca43d --- /dev/null +++ b/lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- diff --git a/lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot-2048.pem b/lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem index 20585f1c01..20585f1c01 100644 --- a/lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot-2048.pem +++ b/lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem diff --git a/test/rubygems/test_bundled_ca.rb b/test/rubygems/test_bundled_ca.rb index 5ce2850518..97a64af323 100644 --- a/test/rubygems/test_bundled_ca.rb +++ b/test/rubygems/test_bundled_ca.rb @@ -8,7 +8,7 @@ require 'rubygems/request' # The tested hosts are explained in detail here: https://github.com/rubygems/rubygems/commit/5e16a5428f973667cabfa07e94ff939e7a83ebd9 # -if ENV["TRAVIS"] || ENV["TEST_SSL"] +if ENV["CI"] || ENV["TEST_SSL"] class TestBundledCA < Gem::TestCase THIS_FILE = File.expand_path __FILE__ diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb index dedb4c99ca..b8bf24e183 100644 --- a/test/rubygems/test_gem_installer.rb +++ b/test/rubygems/test_gem_installer.rb @@ -1106,6 +1106,77 @@ gem 'other', version assert_path_exists expected_makefile end + def test_install_extension_dir_is_removed_on_reinstall + @spec.extensions << "extconf.rb" + write_file File.join(@tempdir, "extconf.rb") do |io| + io.write <<-RUBY + require "mkmf" + create_makefile("#{@spec.name}") + RUBY + end + + @spec.files += %w[extconf.rb] + + path = Gem::Package.build @spec + + # Install a gem with an extension + use_ui @ui do + installer = Gem::Installer.at path + installer.install + end + + # pretend that a binary file was created as part of the build + should_be_removed = File.join(@spec.extension_dir, "#{@spec.name}.so") + write_file should_be_removed do |io| + io.write "DELETE ME ON REINSTALL" + end + assert_path_exists should_be_removed + + # reinstall the gem, this is also the same as pristine + use_ui @ui do + installer = Gem::Installer.at path + installer.install + end + + refute_path_exists should_be_removed + end + + def test_find_lib_file_after_install + @spec.extensions << "extconf.rb" + write_file File.join(@tempdir, "extconf.rb") do |io| + io.write <<-RUBY + require "mkmf" + create_makefile("#{@spec.name}") + RUBY + end + + write_file File.join(@tempdir, "a.c") do |io| + io.write <<-C + #include <ruby.h> + void Init_a() { } + C + end + + Dir.mkdir File.join(@tempdir, "lib") + write_file File.join(@tempdir, 'lib', "b.rb") do |io| + io.write "# b.rb" + end + + @spec.files += %w[extconf.rb lib/b.rb a.c] + + use_ui @ui do + path = Gem::Package.build @spec + + installer = Gem::Installer.at path + installer.install + end + + expected = File.join @spec.full_require_paths.find { |path| + File.exist? File.join path, 'b.rb' + }, 'b.rb' + assert_equal expected, @spec.matches_for_glob('b.rb').first + end + def test_install_extension_and_script @spec.extensions << "extconf.rb" write_file File.join(@tempdir, "extconf.rb") do |io| diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb index 2f8747bc97..9d47f0dea4 100644 --- a/test/rubygems/test_gem_package.rb +++ b/test/rubygems/test_gem_package.rb @@ -141,7 +141,9 @@ class TestGemPackage < Gem::Package::TarTestCase FileUtils.mkdir_p 'lib' open 'lib/code.rb', 'w' do |io| io.write '# lib/code.rb' end - File.symlink('lib/code.rb', 'lib/code_sym.rb') + + # NOTE: 'code.rb' is correct, because it's relative to lib/code_sym.rb + File.symlink('code.rb', 'lib/code_sym.rb') package = Gem::Package.new 'bogus.gem' package.spec = spec @@ -156,12 +158,16 @@ class TestGemPackage < Gem::Package::TarTestCase Gem::Package::TarReader.new tar do |tar_io| tar_io.each_entry do |entry| - (entry.symlink? ? symlinks : files) << entry.full_name + if entry.symlink? + symlinks << { entry.full_name => entry.header.linkname } + else + files << entry.full_name + end end end assert_equal %w[lib/code.rb], files - assert_equal %w[lib/code_sym.rb], symlinks + assert_equal [{'lib/code_sym.rb' => 'lib/code.rb'}], symlinks end def test_build diff --git a/test/rubygems/test_gem_package_tar_writer.rb b/test/rubygems/test_gem_package_tar_writer.rb index 48d480ec53..bed1e3b221 100644 --- a/test/rubygems/test_gem_package_tar_writer.rb +++ b/test/rubygems/test_gem_package_tar_writer.rb @@ -229,6 +229,22 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase assert_equal ["#{'qwer/' * 19}bla", 'a' * 151], @tar_writer.split_name("#{'a' * 151}/#{'qwer/' * 19}bla") + names = [ + ([''] + ['123456789'] * 9 + ['1234567890']).join('/'), # 101 bytes (several pieces) + (['123456789'] * 9 + ['1234567890'] + ['']).join('/'), # 101 bytes (several pieces) + '/' * 99, + '/' * 100, + '/' * 101, + '/' * 102, + ] + names.each do |name| + newname, prefix = @tar_writer.split_name(name) + assert(!(newname.empty?), "split_name() returned empty name") + assert(newname.bytesize <= 100, "split_name() returned name longer than 100 bytes: '#{newname}' for '#{name}'") + assert(prefix.bytesize <= 155, "split_name() returned prefix longer than 155 bytes: '#{prefix}' for '#{name}'") + newname = [prefix, newname].join('/') unless prefix.empty? + assert_equal name, newname + end end def test_split_name_too_long_name @@ -240,6 +256,14 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase @tar_writer.split_name name end assert_includes exception.message, name + + # note, GNU tar 1.28 is unable to handle this case too, + # tested with "tar --format=ustar -cPf /tmp/foo.tartar -- /aaaaaa....a" + name = '/' + 'a' * 100 + exception = assert_raises Gem::Package::TooLongFileName do + @tar_writer.split_name name + end + assert_includes exception.message, name end def test_split_name_too_long_prefix diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb index cf1b27d547..49b6b6656c 100644 --- a/test/rubygems/test_gem_remote_fetcher.rb +++ b/test/rubygems/test_gem_remote_fetcher.rb @@ -81,6 +81,7 @@ gems: # Generated via: # x = OpenSSL::PKey::DH.new(2048) # wait a while... # x.to_s => pem + # x.priv_key.to_s => hex for OpenSSL::BN.new TEST_KEY_DH2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_ -----BEGIN DH PARAMETERS----- MIIBCAKCAQEA3Ze2EHSfYkZLUn557torAmjBgPsqzbodaRaGZtgK1gEU+9nNJaFV @@ -92,6 +93,17 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== -----END DH PARAMETERS----- _end_of_pem_ + TEST_KEY_DH2048.priv_key = OpenSSL::BN.new("108911488509734781344423639" \ + "5585749502236089033416160524030987005037540379474123441273555416835" \ + "4725688238369352738266590757370603937618499698665047757588998555345" \ + "3446251978586372525530219375408331096098220027413238477359960428372" \ + "0195464393332338164504352015535549496585792320286513563739305843396" \ + "9294344974028713065472959376197728193162272314514335882399554394661" \ + "5306385003430991221886779612878793446851681835397455333989268503748" \ + "7862488679178398716189205737442996155432191656080664090596502674943" \ + "7902481557157485795980326766117882761941455140582265347052939604724" \ + "964857770053363840471912215799994973597613931991572884", 16) + def setup @proxies = %w[https_proxy http_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY] @old_proxies = @proxies.map {|k| ENV[k] } diff --git a/test/rubygems/test_gem_security_signer.rb b/test/rubygems/test_gem_security_signer.rb index fe161762e5..685a13949e 100644 --- a/test/rubygems/test_gem_security_signer.rb +++ b/test/rubygems/test_gem_security_signer.rb @@ -205,5 +205,13 @@ c7NM7KZZjj7G++SXjYTEI1PHSA7aFQ/i/+qSUvx+Pg== end end + def test_sign_no_certs + signer = Gem::Security::Signer.new ALTERNATE_KEY, [] + + assert_raises Gem::Security::Exception do + signer.sign 'hello' + end + end + end if defined?(OpenSSL::SSL) |