1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/lib/bundler/lazy_specification.rb
David Rodríguez 466a760e18 [rubygems/rubygems] Fix yanked gems being unintentionally update when other gems are unlocked
This is a regression from a change intended to raise errors when user
puts a gem under an incorrect source in the Gemfile by mistake. To fix
the issue, we revert the change that caused it and implement it in a
different way that restores the resolver independency from real
specifications. Now it deals only with names and versions and does not
try to materialize anything into real specifications before resolving.

https://github.com/rubygems/rubygems/commit/d2bf1b86eb
2022-08-06 15:41:46 +09:00

167 lines
5 KiB
Ruby

# frozen_string_literal: true
require_relative "match_platform"
module Bundler
class LazySpecification
include MatchPlatform
attr_reader :name, :version, :dependencies, :platform
attr_accessor :source, :remote, :force_ruby_platform
def initialize(name, version, platform, source = nil)
@name = name
@version = version
@dependencies = []
@platform = platform || Gem::Platform::RUBY
@source = source
@specification = nil
end
def full_name
if platform == Gem::Platform::RUBY
"#{@name}-#{@version}"
else
"#{@name}-#{@version}-#{platform}"
end
end
def ==(other)
identifier == other.identifier
end
def eql?(other)
identifier.eql?(other.identifier)
end
def hash
identifier.hash
end
##
# Does this locked specification satisfy +dependency+?
#
# NOTE: Rubygems default requirement is ">= 0", which doesn't match
# prereleases of 0 versions, like "0.0.0.dev" or "0.0.0.SNAPSHOT". However,
# bundler users expect those to work. We need to make sure that Gemfile
# dependencies without explicit requirements (which use ">= 0" under the
# hood by default) are still valid for locked specs using this kind of
# versions. The method implements an ad-hoc fix for that. A better solution
# might be to change default rubygems requirement of dependencies to be ">=
# 0.A" but that's a major refactoring likely to break things. Hopefully we
# can attempt it in the future.
#
def satisfies?(dependency)
effective_requirement = dependency.requirement == Gem::Requirement.default ? Gem::Requirement.new(">= 0.A") : dependency.requirement
@name == dependency.name && effective_requirement.satisfied_by?(Gem::Version.new(@version))
end
def to_lock
out = String.new
if platform == Gem::Platform::RUBY
out << " #{name} (#{version})\n"
else
out << " #{name} (#{version}-#{platform})\n"
end
dependencies.sort_by(&:to_s).uniq.each do |dep|
next if dep.type == :development
out << " #{dep.to_lock}\n"
end
out
end
def materialize_for_installation
source.local!
candidates = if source.is_a?(Source::Path) || !ruby_platform_materializes_to_ruby_platform?
target_platform = ruby_platform_materializes_to_ruby_platform? ? platform : Bundler.local_platform
source.specs.search(Dependency.new(name, version)).select do |spec|
MatchPlatform.platforms_match?(spec.platform, target_platform)
end
else
source.specs.search(self)
end
return self if candidates.empty?
__materialize__(candidates)
end
def __materialize__(candidates)
@specification = begin
search = candidates.reverse.find do |spec|
spec.is_a?(StubSpecification) ||
(spec.required_ruby_version.satisfied_by?(Gem.ruby_version) &&
spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version))
end
if search.nil? && Bundler.frozen_bundle?
search = candidates.last
else
search.dependencies = dependencies if search && search.full_name == full_name && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification))
end
search
end
end
def respond_to?(*args)
super || @specification ? @specification.respond_to?(*args) : nil
end
def to_s
@__to_s ||= if platform == Gem::Platform::RUBY
"#{name} (#{version})"
else
"#{name} (#{version}-#{platform})"
end
end
def identifier
@__identifier ||= [name, version, platform_string]
end
def git_version
return unless source.is_a?(Bundler::Source::Git)
" #{source.revision[0..6]}"
end
protected
def platform_string
platform_string = platform.to_s
platform_string == Index::RUBY ? Index::NULL : platform_string
end
private
def to_ary
nil
end
def method_missing(method, *args, &blk)
raise "LazySpecification has not been materialized yet (calling :#{method} #{args.inspect})" unless @specification
return super unless respond_to?(method)
@specification.send(method, *args, &blk)
end
#
# For backwards compatibility with existing lockfiles, if the most specific
# locked platform is RUBY, we keep the previous behaviour of resolving the
# best platform variant at materiliazation time. For previous bundler
# versions (before 2.2.0) this was always the case (except when the lockfile
# only included non-ruby platforms), but we're also keeping this behaviour
# on newer bundlers unless users generate the lockfile from scratch or
# explicitly add a more specific platform.
#
def ruby_platform_materializes_to_ruby_platform?
!Bundler.most_specific_locked_platform?(generic_local_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform]
end
end
end