mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
6a69807576
This parameter was coupling the concept of lockfile with the index. I don't think it's necessary. Also I believe it's causing some flaky test failures, which might leak into realworld issues. They are like this: ```` Invoking `/opt/hostedtoolcache/Ruby/3.0.4/x64/bin/ruby -I/home/runner/work/rubygems/rubygems/bundler/spec -r/home/runner/work/rubygems/rubygems/bundler/spec/support/artifice/fail.rb -r/home/runner/work/rubygems/rubygems/bundler/spec/support/hax.rb /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/bin/bundle lock` failed with output: ---------------------------------------------------------------------- --- ERROR REPORT TEMPLATE ------------------------------------------------------- ``` NoMethodError: undefined method `identifier' for #<Gem::Specification:0x00005639ad0db0a0 @extension_dir=nil, @full_gem_path=nil, @gem_dir=nil, @ignored=nil, @bin_dir=nil, @cache_dir=nil, @cache_file=nil, @doc_dir=nil, @ri_dir=nil, @spec_dir=nil, @spec_file=nil, @gems_dir=nil, @base_dir=nil, @loaded=false, @activated=false, @loaded_from=nil, @original_platform="ruby", @installed_by_version=nil, @autorequire=nil, @date=2022-07-25 00:00:00 UTC, @description="This is a completely fake gem, for testing purposes.", @email="foo@bar.baz", @homepage="http://example.com", @name="win32-process", @post_install_message=nil, @signing_key=nil, @summary="This is just a fake gem for testing", @version=#<Gem::Version "0.8.3">, @authors=["no one"], @bindir="bin", @cert_chain=[], @dependencies=[<Gem::Dependency type=:runtime name="ffi" requirements=">= 1.0.0">], @executables=[], @extensions=[], @extra_rdoc_files=[], @files=[], @licenses=[], @metadata={}, @platform="ruby", @rdoc_options=[], @require_paths=["lib"], @required_ruby_version=#<Gem::Requirement:0x00005639ad0dbc80 @requirements=[[">=", #<Gem::Version "0">]]>, @required_rubygems_version=#<Gem::Requirement:0x00005639ad0dba50 @requirements=[[">=", #<Gem::Version "0">]]>, @requirements=[], @rubygems_version="3.2.33", @specification_version=4, @test_files=[], @new_platform="ruby", @full_name="win32-process-0.8.3", @has_rdoc=true, @license=["MIT"] win32-process-0.8.3> /home/runner/work/rubygems/rubygems/bundler/tmp/rubygems/lib/rubygems/specification.rb:2116:in `method_missing' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/remote_specification.rb:115:in `method_missing' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/lazy_specification.rb:34:in `eql?' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/index.rb:189:in `eql?' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/index.rb:189:in `search_by_dependency' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/index.rb:96:in `local_search' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/index.rb:64:in `unsorted_search' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/index.rb:60:in `search' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/resolver.rb:179:in `results_for' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/resolver.rb:113:in `search_for' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/resolver.rb:216:in `block in sort_dependencies' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/resolver.rb:207:in `each' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/resolver.rb:207:in `sort_by' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/resolver.rb:207:in `sort_dependencies' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb:60:in `block in sort_dependencies' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb:77:in `with_no_such_dependency_error_handling' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb:59:in `sort_dependencies' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb:754:in `push_state_for_requirements' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb:744:in `require_nested_dependencies_for' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb:727:in `activate_new_spec' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb:684:in `attempt_to_activate' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb:254:in `process_topmost_state' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb:182:in `resolve' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb:43:in `resolve' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/resolver.rb:50:in `start' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/resolver.rb:24:in `resolve' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/definition.rb:480:in `reresolve' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/definition.rb:283:in `resolve' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/definition.rb:181:in `resolve_remotely!' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/cli/lock.rb:53:in `run' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/cli.rb:674:in `lock' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/thor/lib/thor.rb:392:in `dispatch' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/cli.rb:31:in `dispatch' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/vendor/thor/lib/thor/base.rb:485:in `start' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/cli.rb:25:in `start' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/exe/bundle:48:in `block in <top (required)>' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/lib/bundler/friendly_errors.rb:120:in `with_friendly_errors' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/gems/bundler-2.4.0.dev/exe/bundle:36:in `<top (required)>' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/bin/bundle:23:in `load' /home/runner/work/rubygems/rubygems/bundler/tmp/1/gems/system/bin/bundle:23:in `<main>' ``` ```` I think the issue is that now we eagerly materialize some base specifications before resolving in order to give better errors if user specified an incorrect source in the Gemfile. This means that the key for the index hash will have heterogeneous specification objects (some LazySpecification, some real Specification), and `LazySpecification#eql?` is incompatible with that. By dropping the base parameter from the index, we should no longer have these heterogenous objects as hash keys. https://github.com/rubygems/rubygems/commit/dc179d41c3
207 lines
4.9 KiB
Ruby
207 lines
4.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Bundler
|
|
class Index
|
|
include Enumerable
|
|
|
|
def self.build
|
|
i = new
|
|
yield i
|
|
i
|
|
end
|
|
|
|
attr_reader :specs, :all_specs, :sources
|
|
protected :specs, :all_specs
|
|
|
|
RUBY = "ruby".freeze
|
|
NULL = "\0".freeze
|
|
|
|
def initialize
|
|
@sources = []
|
|
@cache = {}
|
|
@specs = Hash.new {|h, k| h[k] = {} }
|
|
@all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
|
|
end
|
|
|
|
def initialize_copy(o)
|
|
@sources = o.sources.dup
|
|
@cache = {}
|
|
@specs = Hash.new {|h, k| h[k] = {} }
|
|
@all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
|
|
|
|
o.specs.each do |name, hash|
|
|
@specs[name] = hash.dup
|
|
end
|
|
o.all_specs.each do |name, array|
|
|
@all_specs[name] = array.dup
|
|
end
|
|
end
|
|
|
|
def inspect
|
|
"#<#{self.class}:0x#{object_id} sources=#{sources.map(&:inspect)} specs.size=#{specs.size}>"
|
|
end
|
|
|
|
def empty?
|
|
each { return false }
|
|
true
|
|
end
|
|
|
|
def search_all(name)
|
|
all_matches = local_search(name) + @all_specs[name]
|
|
@sources.each do |source|
|
|
all_matches.concat(source.search_all(name))
|
|
end
|
|
all_matches
|
|
end
|
|
|
|
# Search this index's specs, and any source indexes that this index knows
|
|
# about, returning all of the results.
|
|
def search(query)
|
|
sort_specs(unsorted_search(query))
|
|
end
|
|
|
|
def unsorted_search(query)
|
|
results = local_search(query)
|
|
|
|
seen = results.map(&:full_name).uniq unless @sources.empty?
|
|
|
|
@sources.each do |source|
|
|
source.unsorted_search(query).each do |spec|
|
|
next if seen.include?(spec.full_name)
|
|
|
|
seen << spec.full_name
|
|
results << spec
|
|
end
|
|
end
|
|
|
|
results
|
|
end
|
|
protected :unsorted_search
|
|
|
|
def self.sort_specs(specs)
|
|
specs.sort_by do |s|
|
|
platform_string = s.platform.to_s
|
|
[s.version, platform_string == RUBY ? NULL : platform_string]
|
|
end
|
|
end
|
|
|
|
def sort_specs(specs)
|
|
self.class.sort_specs(specs)
|
|
end
|
|
|
|
def local_search(query)
|
|
case query
|
|
when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query)
|
|
when String then specs_by_name(query)
|
|
when Gem::Dependency then search_by_dependency(query)
|
|
when DepProxy then search_by_dependency(query.dep)
|
|
else
|
|
raise "You can't search for a #{query.inspect}."
|
|
end
|
|
end
|
|
|
|
alias_method :[], :search
|
|
|
|
def <<(spec)
|
|
@specs[spec.name][spec.full_name] = spec
|
|
spec
|
|
end
|
|
|
|
def each(&blk)
|
|
return enum_for(:each) unless blk
|
|
specs.values.each do |spec_sets|
|
|
spec_sets.values.each(&blk)
|
|
end
|
|
sources.each {|s| s.each(&blk) }
|
|
self
|
|
end
|
|
|
|
def spec_names
|
|
names = specs.keys + sources.map(&:spec_names)
|
|
names.uniq!
|
|
names
|
|
end
|
|
|
|
def unmet_dependency_names
|
|
dependency_names.select do |name|
|
|
search(name).empty?
|
|
end
|
|
end
|
|
|
|
def dependency_names
|
|
names = []
|
|
each do |spec|
|
|
spec.dependencies.each do |dep|
|
|
next if dep.type == :development
|
|
names << dep.name
|
|
end
|
|
end
|
|
names.uniq
|
|
end
|
|
|
|
def use(other, override_dupes = false)
|
|
return unless other
|
|
other.each do |s|
|
|
if (dupes = search_by_spec(s)) && !dupes.empty?
|
|
# safe to << since it's a new array when it has contents
|
|
@all_specs[s.name] = dupes << s
|
|
next unless override_dupes
|
|
end
|
|
self << s
|
|
end
|
|
self
|
|
end
|
|
|
|
def size
|
|
@sources.inject(@specs.size) do |size, source|
|
|
size += source.size
|
|
end
|
|
end
|
|
|
|
# Whether all the specs in self are in other
|
|
# TODO: rename to #include?
|
|
def ==(other)
|
|
all? do |spec|
|
|
other_spec = other[spec].first
|
|
other_spec && dependencies_eql?(spec, other_spec) && spec.source == other_spec.source
|
|
end
|
|
end
|
|
|
|
def dependencies_eql?(spec, other_spec)
|
|
deps = spec.dependencies.select {|d| d.type != :development }
|
|
other_deps = other_spec.dependencies.select {|d| d.type != :development }
|
|
deps.sort == other_deps.sort
|
|
end
|
|
|
|
def add_source(index)
|
|
raise ArgumentError, "Source must be an index, not #{index.class}" unless index.is_a?(Index)
|
|
@sources << index
|
|
@sources.uniq! # need to use uniq! here instead of checking for the item before adding
|
|
end
|
|
|
|
private
|
|
|
|
def specs_by_name(name)
|
|
@specs[name].values
|
|
end
|
|
|
|
def search_by_dependency(dependency)
|
|
@cache[dependency] ||= begin
|
|
specs = specs_by_name(dependency.name)
|
|
found = specs.select do |spec|
|
|
next true if spec.source.is_a?(Source::Gemspec)
|
|
dependency.matches_spec?(spec)
|
|
end
|
|
|
|
found
|
|
end
|
|
end
|
|
|
|
EMPTY_SEARCH = [].freeze
|
|
|
|
def search_by_spec(spec)
|
|
spec = @specs[spec.name][spec.full_name]
|
|
spec ? [spec] : EMPTY_SEARCH
|
|
end
|
|
end
|
|
end
|