mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
Merge RubyGems-3.6.7 and Bundler-2.6.7
This commit is contained in:
parent
db2bf9f078
commit
e580145171
57 changed files with 721 additions and 289 deletions
|
@ -190,7 +190,7 @@ module Bundler
|
|||
def replace(spec, checksum)
|
||||
return unless checksum
|
||||
|
||||
lock_name = spec.name_tuple.lock_name
|
||||
lock_name = spec.lock_name
|
||||
@store_mutex.synchronize do
|
||||
existing = fetch_checksum(lock_name, checksum.algo)
|
||||
if !existing || existing.same_source?(checksum)
|
||||
|
@ -201,10 +201,12 @@ module Bundler
|
|||
end
|
||||
end
|
||||
|
||||
def register(spec, checksum)
|
||||
return unless checksum
|
||||
def missing?(spec)
|
||||
@store[spec.lock_name].nil?
|
||||
end
|
||||
|
||||
register_checksum(spec.name_tuple.lock_name, checksum)
|
||||
def register(spec, checksum)
|
||||
register_checksum(spec.lock_name, checksum)
|
||||
end
|
||||
|
||||
def merge!(other)
|
||||
|
@ -216,9 +218,9 @@ module Bundler
|
|||
end
|
||||
|
||||
def to_lock(spec)
|
||||
lock_name = spec.name_tuple.lock_name
|
||||
lock_name = spec.lock_name
|
||||
checksums = @store[lock_name]
|
||||
if checksums
|
||||
if checksums&.any?
|
||||
"#{lock_name} #{checksums.values.map(&:to_lock).sort.join(",")}"
|
||||
else
|
||||
lock_name
|
||||
|
@ -229,11 +231,15 @@ module Bundler
|
|||
|
||||
def register_checksum(lock_name, checksum)
|
||||
@store_mutex.synchronize do
|
||||
existing = fetch_checksum(lock_name, checksum.algo)
|
||||
if existing
|
||||
merge_checksum(lock_name, checksum, existing)
|
||||
if checksum
|
||||
existing = fetch_checksum(lock_name, checksum.algo)
|
||||
if existing
|
||||
merge_checksum(lock_name, checksum, existing)
|
||||
else
|
||||
store_checksum(lock_name, checksum)
|
||||
end
|
||||
else
|
||||
store_checksum(lock_name, checksum)
|
||||
init_checksum(lock_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -243,7 +249,11 @@ module Bundler
|
|||
end
|
||||
|
||||
def store_checksum(lock_name, checksum)
|
||||
(@store[lock_name] ||= {})[checksum.algo] = checksum
|
||||
init_checksum(lock_name)[checksum.algo] = checksum
|
||||
end
|
||||
|
||||
def init_checksum(lock_name)
|
||||
@store[lock_name] ||= {}
|
||||
end
|
||||
|
||||
def fetch_checksum(lock_name, algo)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative "gem_parser"
|
||||
require "rubygems/resolver/api_set/gem_parser"
|
||||
|
||||
module Bundler
|
||||
class CompactIndexClient
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Bundler
|
||||
class CompactIndexClient
|
||||
if defined?(Gem::Resolver::APISet::GemParser)
|
||||
GemParser = Gem::Resolver::APISet::GemParser
|
||||
else
|
||||
class GemParser
|
||||
EMPTY_ARRAY = [].freeze
|
||||
private_constant :EMPTY_ARRAY
|
||||
|
||||
def parse(line)
|
||||
version_and_platform, rest = line.split(" ", 2)
|
||||
version, platform = version_and_platform.split("-", 2)
|
||||
dependencies, requirements = rest.split("|", 2).map! {|s| s.split(",") } if rest
|
||||
dependencies = dependencies ? dependencies.map! {|d| parse_dependency(d) } : EMPTY_ARRAY
|
||||
requirements = requirements ? requirements.map! {|d| parse_dependency(d) } : EMPTY_ARRAY
|
||||
[version, platform, dependencies, requirements]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_dependency(string)
|
||||
dependency = string.split(":")
|
||||
dependency[-1] = dependency[-1].split("&") if dependency.size > 1
|
||||
dependency[0] = -dependency[0]
|
||||
dependency
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -64,7 +64,7 @@ module Bundler
|
|||
end
|
||||
|
||||
def gem_parser
|
||||
@gem_parser ||= GemParser.new
|
||||
@gem_parser ||= Gem::Resolver::APISet::GemParser.new
|
||||
end
|
||||
|
||||
# This is mostly the same as `split(" ", 3)` but it avoids allocating extra objects.
|
||||
|
|
|
@ -95,6 +95,7 @@ module Bundler
|
|||
@locked_ruby_version = nil
|
||||
@new_platforms = []
|
||||
@removed_platforms = []
|
||||
@originally_invalid_platforms = []
|
||||
|
||||
if lockfile_exists?
|
||||
@lockfile_contents = Bundler.read_file(lockfile)
|
||||
|
@ -147,9 +148,8 @@ module Bundler
|
|||
|
||||
@current_platform_missing = add_current_platform unless Bundler.frozen_bundle?
|
||||
|
||||
converge_path_sources_to_gemspec_sources
|
||||
@path_changes = converge_paths
|
||||
@source_changes = converge_sources
|
||||
@path_changes = converge_paths
|
||||
|
||||
if conservative
|
||||
@gems_to_unlock = @explicit_unlocks.any? ? @explicit_unlocks : @dependencies.map(&:name)
|
||||
|
@ -539,12 +539,13 @@ module Bundler
|
|||
|
||||
reason = resolve_needed? ? change_reason : "some dependencies were deleted from your gemfile"
|
||||
|
||||
msg = String.new
|
||||
msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because #{update_refused_reason}"
|
||||
msg = String.new("#{reason.capitalize.strip}, but ")
|
||||
msg << "the lockfile " unless msg.start_with?("Your lockfile")
|
||||
msg << "can't be updated because #{update_refused_reason}"
|
||||
msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
|
||||
msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
|
||||
msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
|
||||
msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_gemfile_path} to version control.\n" unless unlocking?
|
||||
msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_lockfile_path} to version control.\n" unless unlocking?
|
||||
msg
|
||||
end
|
||||
|
||||
|
@ -563,6 +564,7 @@ module Bundler
|
|||
@local_changes ||
|
||||
@missing_lockfile_dep ||
|
||||
@unlocking_bundler ||
|
||||
@locked_spec_with_missing_checksums ||
|
||||
@locked_spec_with_missing_deps ||
|
||||
@locked_spec_with_invalid_deps
|
||||
end
|
||||
|
@ -759,7 +761,11 @@ module Bundler
|
|||
end
|
||||
end
|
||||
|
||||
@platforms = result.add_extra_platforms!(platforms) if should_add_extra_platforms?
|
||||
if should_add_extra_platforms?
|
||||
result.add_extra_platforms!(platforms)
|
||||
elsif @originally_invalid_platforms.any?
|
||||
result.add_originally_invalid_platforms!(platforms, @originally_invalid_platforms)
|
||||
end
|
||||
|
||||
SpecSet.new(result.for(dependencies, @platforms | [Gem::Platform::RUBY]))
|
||||
end
|
||||
|
@ -809,13 +815,14 @@ module Bundler
|
|||
[
|
||||
[@source_changes, "the list of sources changed"],
|
||||
[@dependency_changes, "the dependencies in your gemfile changed"],
|
||||
[@current_platform_missing, "your lockfile does not include the current platform"],
|
||||
[@current_platform_missing, "your lockfile is missing the current platform"],
|
||||
[@new_platforms.any?, "you are adding a new platform to your lockfile"],
|
||||
[@path_changes, "the gemspecs for path gems changed"],
|
||||
[@local_changes, "the gemspecs for git local gems changed"],
|
||||
[@missing_lockfile_dep, "your lock file is missing \"#{@missing_lockfile_dep}\""],
|
||||
[@missing_lockfile_dep, "your lockfile is missing \"#{@missing_lockfile_dep}\""],
|
||||
[@unlocking_bundler, "an update to the version of Bundler itself was requested"],
|
||||
[@locked_spec_with_missing_deps, "your lock file includes \"#{@locked_spec_with_missing_deps}\" but not some of its dependencies"],
|
||||
[@locked_spec_with_missing_checksums, "your lockfile is missing a CHECKSUMS entry for \"#{@locked_spec_with_missing_checksums}\""],
|
||||
[@locked_spec_with_missing_deps, "your lockfile includes \"#{@locked_spec_with_missing_deps}\" but not some of its dependencies"],
|
||||
[@locked_spec_with_invalid_deps, "your lockfile does not satisfy dependencies of \"#{@locked_spec_with_invalid_deps}\""],
|
||||
].select(&:first).map(&:last).join(", ")
|
||||
end
|
||||
|
@ -832,8 +839,8 @@ module Bundler
|
|||
!locked || dependencies_for_source_changed?(source, locked) || specs_for_source_changed?(source)
|
||||
end
|
||||
|
||||
def dependencies_for_source_changed?(source, locked_source = source)
|
||||
deps_for_source = @dependencies.select {|s| s.source == source }
|
||||
def dependencies_for_source_changed?(source, locked_source)
|
||||
deps_for_source = @dependencies.select {|dep| dep.source == source }
|
||||
locked_deps_for_source = locked_dependencies.select {|dep| dep.source == locked_source }
|
||||
|
||||
deps_for_source.uniq.sort != locked_deps_for_source.sort
|
||||
|
@ -841,7 +848,7 @@ module Bundler
|
|||
|
||||
def specs_for_source_changed?(source)
|
||||
locked_index = Index.new
|
||||
locked_index.use(@locked_specs.select {|s| source.can_lock?(s) })
|
||||
locked_index.use(@locked_specs.select {|s| s.replace_source_with!(source) })
|
||||
|
||||
!locked_index.subset?(source.specs)
|
||||
rescue PathError, GitError => e
|
||||
|
@ -873,21 +880,27 @@ module Bundler
|
|||
def check_lockfile
|
||||
@locked_spec_with_invalid_deps = nil
|
||||
@locked_spec_with_missing_deps = nil
|
||||
@locked_spec_with_missing_checksums = nil
|
||||
|
||||
missing = []
|
||||
missing_deps = []
|
||||
missing_checksums = []
|
||||
invalid = []
|
||||
|
||||
@locked_specs.each do |s|
|
||||
missing_checksums << s if @locked_checksums && s.source.checksum_store.missing?(s)
|
||||
|
||||
validation = @locked_specs.validate_deps(s)
|
||||
|
||||
missing << s if validation == :missing
|
||||
missing_deps << s if validation == :missing
|
||||
invalid << s if validation == :invalid
|
||||
end
|
||||
|
||||
if missing.any?
|
||||
@locked_specs.delete(missing)
|
||||
@locked_spec_with_missing_checksums = missing_checksums.first.name if missing_checksums.any?
|
||||
|
||||
@locked_spec_with_missing_deps = missing.first.name
|
||||
if missing_deps.any?
|
||||
@locked_specs.delete(missing_deps)
|
||||
|
||||
@locked_spec_with_missing_deps = missing_deps.first.name
|
||||
end
|
||||
|
||||
if invalid.any?
|
||||
|
@ -903,24 +916,6 @@ module Bundler
|
|||
end
|
||||
end
|
||||
|
||||
def converge_path_source_to_gemspec_source(source)
|
||||
return source unless source.instance_of?(Source::Path)
|
||||
gemspec_source = sources.path_sources.find {|s| s.is_a?(Source::Gemspec) && s.as_path_source == source }
|
||||
gemspec_source || source
|
||||
end
|
||||
|
||||
def converge_path_sources_to_gemspec_sources
|
||||
@locked_sources.map! do |source|
|
||||
converge_path_source_to_gemspec_source(source)
|
||||
end
|
||||
@locked_specs.each do |spec|
|
||||
spec.source &&= converge_path_source_to_gemspec_source(spec.source)
|
||||
end
|
||||
@locked_deps.each do |_, dep|
|
||||
dep.source &&= converge_path_source_to_gemspec_source(dep.source)
|
||||
end
|
||||
end
|
||||
|
||||
def converge_sources
|
||||
# Replace the sources from the Gemfile with the sources from the Gemfile.lock,
|
||||
# if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
|
||||
|
@ -963,11 +958,17 @@ module Bundler
|
|||
unless name == "bundler"
|
||||
locked_specs = @originally_locked_specs[name]
|
||||
|
||||
if locked_specs.any? && !dep.matches_spec?(locked_specs.first)
|
||||
@gems_to_unlock << name
|
||||
dep_changed = true
|
||||
elsif locked_specs.empty? && dep_changed == false
|
||||
@missing_lockfile_dep = name
|
||||
if locked_specs.empty?
|
||||
@missing_lockfile_dep = name if dep_changed == false
|
||||
else
|
||||
if locked_specs.map(&:source).uniq.size > 1
|
||||
@locked_specs.delete(locked_specs.select {|s| s.source != dep.source })
|
||||
end
|
||||
|
||||
unless dep.matches_spec?(locked_specs.first)
|
||||
@gems_to_unlock << name
|
||||
dep_changed = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1141,16 +1142,21 @@ module Bundler
|
|||
def remove_invalid_platforms!
|
||||
return if Bundler.frozen_bundle?
|
||||
|
||||
platforms.reverse_each do |platform|
|
||||
@originally_invalid_platforms = platforms.select do |platform|
|
||||
next if local_platform == platform ||
|
||||
@new_platforms.include?(platform) ||
|
||||
@path_changes ||
|
||||
@dependency_changes ||
|
||||
@locked_spec_with_invalid_deps ||
|
||||
!spec_set_incomplete_for_platform?(@originally_locked_specs, platform)
|
||||
@new_platforms.include?(platform)
|
||||
|
||||
remove_platform(platform)
|
||||
# We should probably avoid removing non-ruby platforms, since that means
|
||||
# lockfile will no longer install on those platforms, so a error to give
|
||||
# heads up to the user may be better. However, we have tests expecting
|
||||
# non ruby platform autoremoval to work, so leaving that in place for
|
||||
# now.
|
||||
next if @dependency_changes && platform != Gem::Platform::RUBY
|
||||
|
||||
spec_set_incomplete_for_platform?(@originally_locked_specs, platform)
|
||||
end
|
||||
|
||||
@platforms -= @originally_invalid_platforms
|
||||
end
|
||||
|
||||
def spec_set_incomplete_for_platform?(spec_set, platform)
|
||||
|
|
|
@ -77,7 +77,7 @@ module Bundler
|
|||
|
||||
@gemspecs << spec
|
||||
|
||||
path path, "glob" => glob, "name" => spec.name do
|
||||
path path, "glob" => glob, "name" => spec.name, "gemspec" => spec do
|
||||
add_dependency spec.name
|
||||
end
|
||||
|
||||
|
@ -141,8 +141,7 @@ module Bundler
|
|||
def path(path, options = {}, &blk)
|
||||
source_options = normalize_hash(options).merge(
|
||||
"path" => Pathname.new(path),
|
||||
"root_path" => gemfile_root,
|
||||
"gemspec" => gemspecs.find {|g| g.name == options["name"] }
|
||||
"root_path" => gemfile_root
|
||||
)
|
||||
|
||||
source_options["global"] = true unless block_given?
|
||||
|
|
|
@ -175,6 +175,14 @@ module Bundler
|
|||
@force_ruby_platform = true
|
||||
end
|
||||
|
||||
def replace_source_with!(gemfile_source)
|
||||
return unless gemfile_source.can_lock?(self)
|
||||
|
||||
@source = gemfile_source
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def use_exact_resolved_specifications?
|
||||
|
@ -196,7 +204,7 @@ module Bundler
|
|||
|
||||
# If in frozen mode, we fallback to a non-installable candidate because by
|
||||
# doing this we avoid re-resolving and potentially end up changing the
|
||||
# lock file, which is not allowed. In that case, we will give a proper error
|
||||
# lockfile, which is not allowed. In that case, we will give a proper error
|
||||
# about the mismatch higher up the stack, right before trying to install the
|
||||
# bad gem.
|
||||
def choose_compatible(candidates, fallback_to_non_installable: Bundler.frozen_bundle?)
|
||||
|
|
|
@ -239,7 +239,6 @@ module Bundler
|
|||
spaces = $1
|
||||
return unless spaces.size == 2
|
||||
checksums = $6
|
||||
return unless checksums
|
||||
name = $2
|
||||
version = $3
|
||||
platform = $4
|
||||
|
@ -249,10 +248,14 @@ module Bundler
|
|||
full_name = Gem::NameTuple.new(name, version, platform).full_name
|
||||
return unless spec = @specs[full_name]
|
||||
|
||||
checksums.split(",") do |lock_checksum|
|
||||
column = line.index(lock_checksum) + 1
|
||||
checksum = Checksum.from_lock(lock_checksum, "#{@lockfile_path}:#{@pos.line}:#{column}")
|
||||
spec.source.checksum_store.register(spec, checksum)
|
||||
if checksums
|
||||
checksums.split(",") do |lock_checksum|
|
||||
column = line.index(lock_checksum) + 1
|
||||
checksum = Checksum.from_lock(lock_checksum, "#{@lockfile_path}:#{@pos.line}:#{column}")
|
||||
spec.source.checksum_store.register(spec, checksum)
|
||||
end
|
||||
else
|
||||
spec.source.checksum_store.register(spec, nil)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ module Bundler
|
|||
@sources[name]
|
||||
end
|
||||
|
||||
# @param [Hash] The options that are present in the lock file
|
||||
# @param [Hash] The options that are present in the lockfile
|
||||
# @return [API::Source] the instance of the class that handles the source
|
||||
# type passed in locked_opts
|
||||
def from_lock(locked_opts)
|
||||
|
|
|
@ -67,7 +67,7 @@ module Bundler
|
|||
# to check out same version of gem later.
|
||||
#
|
||||
# There options are passed when the source plugin is created from the
|
||||
# lock file.
|
||||
# lockfile.
|
||||
#
|
||||
# @return [Hash]
|
||||
def options_to_lock
|
||||
|
|
|
@ -8,6 +8,14 @@ module Bundler
|
|||
SharedHelpers.in_bundle? ? Bundler.root : Plugin.root
|
||||
end
|
||||
|
||||
def eql?(other)
|
||||
return unless other.class == self.class
|
||||
expanded_original_path == other.expanded_original_path &&
|
||||
version == other.version
|
||||
end
|
||||
|
||||
alias_method :==, :eql?
|
||||
|
||||
def generate_bin(spec, disable_extensions = false)
|
||||
# Need to find a way without code duplication
|
||||
# For now, we can ignore this
|
||||
|
|
|
@ -12,6 +12,7 @@ module Bundler
|
|||
require_relative "resolver/candidate"
|
||||
require_relative "resolver/incompatibility"
|
||||
require_relative "resolver/root"
|
||||
require_relative "resolver/strategy"
|
||||
|
||||
include GemHelpers
|
||||
|
||||
|
@ -78,7 +79,7 @@ module Bundler
|
|||
end
|
||||
|
||||
def solve_versions(root:, logger:)
|
||||
solver = PubGrub::VersionSolver.new(source: self, root: root, logger: logger)
|
||||
solver = PubGrub::VersionSolver.new(source: self, root: root, strategy: Strategy.new(self), logger: logger)
|
||||
result = solver.solve
|
||||
resolved_specs = result.flat_map {|package, version| version.to_specs(package, @most_specific_locked_platform) }
|
||||
SpecSet.new(resolved_specs).specs_with_additional_variants_from(@base.locked_specs)
|
||||
|
@ -167,15 +168,7 @@ module Bundler
|
|||
end
|
||||
|
||||
def versions_for(package, range=VersionRange.any)
|
||||
versions = select_sorted_versions(package, range)
|
||||
|
||||
# Conditional avoids (among other things) calling
|
||||
# sort_versions_by_preferred with the root package
|
||||
if versions.size > 1
|
||||
sort_versions_by_preferred(package, versions)
|
||||
else
|
||||
versions
|
||||
end
|
||||
range.select_versions(@sorted_versions[package])
|
||||
end
|
||||
|
||||
def no_versions_incompatibility_for(package, unsatisfied_term)
|
||||
|
@ -284,8 +277,8 @@ module Bundler
|
|||
ruby_group = Resolver::SpecGroup.new(ruby_specs)
|
||||
|
||||
unless ruby_group.empty?
|
||||
platform_specs.each do |specs|
|
||||
ruby_group.merge(Resolver::SpecGroup.new(specs))
|
||||
platform_specs.each do |s|
|
||||
ruby_group.merge(Resolver::SpecGroup.new(s))
|
||||
end
|
||||
|
||||
groups << Resolver::Candidate.new(version, group: ruby_group, priority: -1)
|
||||
|
@ -355,6 +348,10 @@ module Bundler
|
|||
raise GemNotFound, message
|
||||
end
|
||||
|
||||
def sort_versions_by_preferred(package, versions)
|
||||
@gem_version_promoter.sort_versions(package, versions)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filtered_versions_for(package)
|
||||
|
@ -414,10 +411,6 @@ module Bundler
|
|||
requirement.satisfied_by?(spec.version) || spec.source.is_a?(Source::Gemspec)
|
||||
end
|
||||
|
||||
def sort_versions_by_preferred(package, versions)
|
||||
@gem_version_promoter.sort_versions(package, versions)
|
||||
end
|
||||
|
||||
def repository_for(package)
|
||||
source_for(package.name)
|
||||
end
|
||||
|
@ -433,7 +426,7 @@ module Bundler
|
|||
next [dep_package, dep_constraint] if name == "bundler"
|
||||
|
||||
dep_range = dep_constraint.range
|
||||
versions = select_sorted_versions(dep_package, dep_range)
|
||||
versions = versions_for(dep_package, dep_range)
|
||||
if versions.empty?
|
||||
if dep_package.ignores_prereleases? || dep_package.prefer_local?
|
||||
@all_versions.delete(dep_package)
|
||||
|
@ -441,7 +434,7 @@ module Bundler
|
|||
end
|
||||
dep_package.consider_prereleases! if dep_package.ignores_prereleases?
|
||||
dep_package.consider_remote_versions! if dep_package.prefer_local?
|
||||
versions = select_sorted_versions(dep_package, dep_range)
|
||||
versions = versions_for(dep_package, dep_range)
|
||||
end
|
||||
|
||||
if versions.empty? && select_all_versions(dep_package, dep_range).any?
|
||||
|
@ -456,10 +449,6 @@ module Bundler
|
|||
end.to_h
|
||||
end
|
||||
|
||||
def select_sorted_versions(package, range)
|
||||
range.select_versions(@sorted_versions[package])
|
||||
end
|
||||
|
||||
def select_all_versions(package, range)
|
||||
range.select_versions(@all_versions[package])
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ module Bundler
|
|||
# Some candidates may also keep some information explicitly about the
|
||||
# package they refer to. These candidates are referred to as "canonical" and
|
||||
# are used when materializing resolution results back into RubyGems
|
||||
# specifications that can be installed, written to lock files, and so on.
|
||||
# specifications that can be installed, written to lockfiles, and so on.
|
||||
#
|
||||
class Candidate
|
||||
include Comparable
|
||||
|
|
40
lib/bundler/resolver/strategy.rb
Normal file
40
lib/bundler/resolver/strategy.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Bundler
|
||||
class Resolver
|
||||
class Strategy
|
||||
def initialize(source)
|
||||
@source = source
|
||||
end
|
||||
|
||||
def next_package_and_version(unsatisfied)
|
||||
package, range = next_term_to_try_from(unsatisfied)
|
||||
|
||||
[package, most_preferred_version_of(package, range).first]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def next_term_to_try_from(unsatisfied)
|
||||
unsatisfied.min_by do |package, range|
|
||||
matching_versions = @source.versions_for(package, range)
|
||||
higher_versions = @source.versions_for(package, range.upper_invert)
|
||||
|
||||
[matching_versions.count <= 1 ? 0 : 1, higher_versions.count]
|
||||
end
|
||||
end
|
||||
|
||||
def most_preferred_version_of(package, range)
|
||||
versions = @source.versions_for(package, range)
|
||||
|
||||
# Conditional avoids (among other things) calling
|
||||
# sort_versions_by_preferred with the root package
|
||||
if versions.size > 1
|
||||
@source.sort_versions_by_preferred(package, versions)
|
||||
else
|
||||
versions
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -262,6 +262,10 @@ module Gem
|
|||
!default_gem? && !File.directory?(full_gem_path)
|
||||
end
|
||||
|
||||
def lock_name
|
||||
@lock_name ||= name_tuple.lock_name
|
||||
end
|
||||
|
||||
unless VALIDATES_FOR_RESOLUTION
|
||||
def validate_for_resolution
|
||||
SpecificationPolicy.new(self).validate_for_resolution
|
||||
|
@ -443,6 +447,17 @@ module Gem
|
|||
end
|
||||
end
|
||||
|
||||
unless Gem.rubygems_version >= Gem::Version.new("3.6.7")
|
||||
module UnfreezeCompactIndexParsedResponse
|
||||
def parse(line)
|
||||
version, platform, dependencies, requirements = super
|
||||
[version, platform, dependencies.frozen? ? dependencies.dup : dependencies, requirements.frozen? ? requirements.dup : requirements]
|
||||
end
|
||||
end
|
||||
|
||||
Resolver::APISet::GemParser.prepend(UnfreezeCompactIndexParsedResponse)
|
||||
end
|
||||
|
||||
if Gem.rubygems_version < Gem::Version.new("3.6.0")
|
||||
class Package; end
|
||||
require "rubygems/package/tar_reader"
|
||||
|
|
|
@ -130,11 +130,14 @@ module Bundler
|
|||
|
||||
specs_to_cache.each do |spec|
|
||||
next if spec.name == "bundler"
|
||||
next if spec.source.is_a?(Source::Gemspec)
|
||||
if spec.source.respond_to?(:migrate_cache)
|
||||
spec.source.migrate_cache(custom_path, local: local)
|
||||
elsif spec.source.respond_to?(:cache)
|
||||
spec.source.cache(spec, custom_path)
|
||||
|
||||
source = spec.source
|
||||
next if source.is_a?(Source::Gemspec)
|
||||
|
||||
if source.respond_to?(:migrate_cache)
|
||||
source.migrate_cache(custom_path, local: local)
|
||||
elsif source.respond_to?(:cache)
|
||||
source.cache(spec, custom_path)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,15 +4,12 @@ module Bundler
|
|||
class Source
|
||||
class Gemspec < Path
|
||||
attr_reader :gemspec
|
||||
attr_writer :checksum_store
|
||||
|
||||
def initialize(options)
|
||||
super
|
||||
@gemspec = options["gemspec"]
|
||||
end
|
||||
|
||||
def as_path_source
|
||||
Path.new(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -185,7 +185,8 @@ module Bundler
|
|||
_, err, status = capture(command, nil)
|
||||
return extra_ref if status.success?
|
||||
|
||||
if err.include?("Could not find remote branch")
|
||||
if err.include?("Could not find remote branch") || # git up to 2.49
|
||||
err.include?("Remote branch #{branch_option} not found") # git 2.49 or higher
|
||||
raise MissingGitRevisionError.new(command_with_no_credentials, nil, explicit_ref, credential_filtered_uri)
|
||||
else
|
||||
idx = command.index("--depth")
|
||||
|
@ -262,7 +263,7 @@ module Bundler
|
|||
end
|
||||
|
||||
def not_pinned?
|
||||
branch || tag || ref.nil?
|
||||
branch_option || ref.nil?
|
||||
end
|
||||
|
||||
def pinned_to_full_sha?
|
||||
|
@ -426,7 +427,7 @@ module Bundler
|
|||
# anyways.
|
||||
return args if @revision
|
||||
|
||||
args += ["--branch", branch || tag] if branch || tag
|
||||
args += ["--branch", branch_option] if branch_option
|
||||
args
|
||||
end
|
||||
|
||||
|
@ -442,6 +443,10 @@ module Bundler
|
|||
extra_args
|
||||
end
|
||||
|
||||
def branch_option
|
||||
branch || tag
|
||||
end
|
||||
|
||||
def full_clone?
|
||||
depth.nil?
|
||||
end
|
||||
|
|
|
@ -60,8 +60,8 @@ module Bundler
|
|||
end
|
||||
|
||||
def eql?(other)
|
||||
return unless other.class == self.class
|
||||
expanded_original_path == other.expanded_original_path &&
|
||||
[Gemspec, Path].include?(other.class) &&
|
||||
expanded_original_path == other.expanded_original_path &&
|
||||
version == other.version
|
||||
end
|
||||
|
||||
|
|
|
@ -173,39 +173,57 @@ module Bundler
|
|||
|
||||
def map_sources(replacement_sources)
|
||||
rubygems = @rubygems_sources.map do |source|
|
||||
replace_rubygems_source(replacement_sources, source) || source
|
||||
replace_rubygems_source(replacement_sources, source)
|
||||
end
|
||||
|
||||
git, plugin = [@git_sources, @plugin_sources].map do |sources|
|
||||
sources.map do |source|
|
||||
replacement_sources.find {|s| s == source } || source
|
||||
replace_source(replacement_sources, source)
|
||||
end
|
||||
end
|
||||
|
||||
path = @path_sources.map do |source|
|
||||
replacement_sources.find {|s| s == (source.is_a?(Source::Gemspec) ? source.as_path_source : source) } || source
|
||||
replace_path_source(replacement_sources, source)
|
||||
end
|
||||
|
||||
[rubygems, path, git, plugin]
|
||||
end
|
||||
|
||||
def global_replacement_source(replacement_sources)
|
||||
replacement_source = replace_rubygems_source(replacement_sources, global_rubygems_source)
|
||||
return global_rubygems_source unless replacement_source
|
||||
|
||||
replacement_source.local!
|
||||
replacement_source
|
||||
replace_rubygems_source(replacement_sources, global_rubygems_source, &:local!)
|
||||
end
|
||||
|
||||
def replace_rubygems_source(replacement_sources, gemfile_source)
|
||||
replacement_source = replacement_sources.find {|s| s == gemfile_source }
|
||||
return unless replacement_source
|
||||
replace_source(replacement_sources, gemfile_source) do |replacement_source|
|
||||
# locked sources never include credentials so always prefer remotes from the gemfile
|
||||
replacement_source.remotes = gemfile_source.remotes
|
||||
|
||||
yield replacement_source if block_given?
|
||||
|
||||
replacement_source
|
||||
end
|
||||
end
|
||||
|
||||
def replace_source(replacement_sources, gemfile_source)
|
||||
replacement_source = replacement_sources.find {|s| s == gemfile_source }
|
||||
return gemfile_source unless replacement_source
|
||||
|
||||
replacement_source = yield(replacement_source) if block_given?
|
||||
|
||||
# locked sources never include credentials so always prefer remotes from the gemfile
|
||||
replacement_source.remotes = gemfile_source.remotes
|
||||
replacement_source
|
||||
end
|
||||
|
||||
def replace_path_source(replacement_sources, gemfile_source)
|
||||
replace_source(replacement_sources, gemfile_source) do |replacement_source|
|
||||
if gemfile_source.is_a?(Source::Gemspec)
|
||||
gemfile_source.checksum_store = replacement_source.checksum_store
|
||||
gemfile_source
|
||||
else
|
||||
replacement_source
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def different_sources?(lock_sources, replacement_sources)
|
||||
!equivalent_sources?(lock_sources, replacement_sources)
|
||||
end
|
||||
|
|
|
@ -47,8 +47,17 @@ module Bundler
|
|||
end.uniq
|
||||
end
|
||||
|
||||
def add_originally_invalid_platforms!(platforms, originally_invalid_platforms)
|
||||
originally_invalid_platforms.each do |originally_invalid_platform|
|
||||
platforms << originally_invalid_platform if complete_platform(originally_invalid_platform)
|
||||
end
|
||||
end
|
||||
|
||||
def add_extra_platforms!(platforms)
|
||||
return platforms.concat([Gem::Platform::RUBY]).uniq if @specs.empty?
|
||||
if @specs.empty?
|
||||
platforms.concat([Gem::Platform::RUBY]).uniq
|
||||
return
|
||||
end
|
||||
|
||||
new_platforms = all_platforms.select do |platform|
|
||||
next if platforms.include?(platform)
|
||||
|
@ -56,14 +65,12 @@ module Bundler
|
|||
|
||||
complete_platform(platform)
|
||||
end
|
||||
return platforms if new_platforms.empty?
|
||||
return if new_platforms.empty?
|
||||
|
||||
platforms.concat(new_platforms)
|
||||
|
||||
less_specific_platform = new_platforms.find {|platform| platform != Gem::Platform::RUBY && Bundler.local_platform === platform && platform === Bundler.local_platform }
|
||||
platforms.delete(Bundler.local_platform) if less_specific_platform
|
||||
|
||||
platforms
|
||||
end
|
||||
|
||||
def validate_deps(s)
|
||||
|
|
|
@ -160,6 +160,12 @@ class Bundler::ConnectionPool
|
|||
@available.shutdown(reload: true, &block)
|
||||
end
|
||||
|
||||
## Reaps idle connections that have been idle for over +idle_seconds+.
|
||||
# +idle_seconds+ defaults to 60.
|
||||
def reap(idle_seconds = 60, &block)
|
||||
@available.reap(idle_seconds, &block)
|
||||
end
|
||||
|
||||
# Size of this connection pool
|
||||
attr_reader :size
|
||||
# Automatically drop all connections after fork
|
||||
|
@ -169,6 +175,11 @@ class Bundler::ConnectionPool
|
|||
def available
|
||||
@available.length
|
||||
end
|
||||
|
||||
# Number of pool entries created and idle in the pool.
|
||||
def idle
|
||||
@available.idle
|
||||
end
|
||||
end
|
||||
|
||||
require_relative "connection_pool/timed_stack"
|
||||
|
|
|
@ -41,6 +41,7 @@ class Bundler::ConnectionPool::TimedStack
|
|||
def push(obj, options = {})
|
||||
@mutex.synchronize do
|
||||
if @shutdown_block
|
||||
@created -= 1 unless @created == 0
|
||||
@shutdown_block.call(obj)
|
||||
else
|
||||
store_connection obj, options
|
||||
|
@ -98,6 +99,26 @@ class Bundler::ConnectionPool::TimedStack
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Reaps connections that were checked in more than +idle_seconds+ ago.
|
||||
def reap(idle_seconds, &block)
|
||||
raise ArgumentError, "reap must receive a block" unless block
|
||||
raise ArgumentError, "idle_seconds must be a number" unless idle_seconds.is_a?(Numeric)
|
||||
raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block
|
||||
|
||||
idle.times do
|
||||
conn =
|
||||
@mutex.synchronize do
|
||||
raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block
|
||||
|
||||
reserve_idle_connection(idle_seconds)
|
||||
end
|
||||
break unless conn
|
||||
|
||||
block.call(conn)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Returns +true+ if there are no available connections.
|
||||
|
||||
|
@ -112,6 +133,12 @@ class Bundler::ConnectionPool::TimedStack
|
|||
@max - @created + @que.length
|
||||
end
|
||||
|
||||
##
|
||||
# The number of connections created and available on the stack.
|
||||
def idle
|
||||
@que.length
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def current_time
|
||||
|
@ -133,7 +160,7 @@ class Bundler::ConnectionPool::TimedStack
|
|||
# This method must return a connection from the stack.
|
||||
|
||||
def fetch_connection(options = nil)
|
||||
@que.pop
|
||||
@que.pop&.first
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -144,9 +171,32 @@ class Bundler::ConnectionPool::TimedStack
|
|||
def shutdown_connections(options = nil)
|
||||
while connection_stored?(options)
|
||||
conn = fetch_connection(options)
|
||||
@created -= 1 unless @created == 0
|
||||
@shutdown_block.call(conn)
|
||||
end
|
||||
@created = 0
|
||||
end
|
||||
|
||||
##
|
||||
# This is an extension point for TimedStack and is called with a mutex.
|
||||
#
|
||||
# This method returns the oldest idle connection if it has been idle for more than idle_seconds.
|
||||
# This requires that the stack is kept in order of checked in time (oldest first).
|
||||
|
||||
def reserve_idle_connection(idle_seconds)
|
||||
return unless idle_connections?(idle_seconds)
|
||||
|
||||
@created -= 1 unless @created == 0
|
||||
|
||||
@que.shift.first
|
||||
end
|
||||
|
||||
##
|
||||
# This is an extension point for TimedStack and is called with a mutex.
|
||||
#
|
||||
# Returns true if the first connection in the stack has been idle for more than idle_seconds
|
||||
|
||||
def idle_connections?(idle_seconds)
|
||||
connection_stored? && (current_time - @que.first.last > idle_seconds)
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -155,7 +205,7 @@ class Bundler::ConnectionPool::TimedStack
|
|||
# This method must return +obj+ to the stack.
|
||||
|
||||
def store_connection(obj, options = nil)
|
||||
@que.push obj
|
||||
@que.push [obj, current_time]
|
||||
end
|
||||
|
||||
##
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
class Bundler::ConnectionPool
|
||||
VERSION = "2.4.1"
|
||||
VERSION = "2.5.0"
|
||||
end
|
||||
|
|
|
@ -79,29 +79,17 @@ module Bundler::PubGrub
|
|||
dependencies_for(@root_package, @root_version)
|
||||
end
|
||||
|
||||
# Override me (maybe)
|
||||
#
|
||||
# If not overridden, the order returned by all_versions_for will be used
|
||||
#
|
||||
# Returns: Array of versions in preferred order
|
||||
def sort_versions_by_preferred(package, sorted_versions)
|
||||
indexes = @version_indexes[package]
|
||||
sorted_versions.sort_by { |version| indexes[version] }
|
||||
end
|
||||
|
||||
def initialize
|
||||
@root_package = Package.root
|
||||
@root_version = Package.root_version
|
||||
|
||||
@cached_versions = Hash.new do |h,k|
|
||||
@sorted_versions = Hash.new do |h,k|
|
||||
if k == @root_package
|
||||
h[k] = [@root_version]
|
||||
else
|
||||
h[k] = all_versions_for(k)
|
||||
h[k] = all_versions_for(k).sort
|
||||
end
|
||||
end
|
||||
@sorted_versions = Hash.new { |h,k| h[k] = @cached_versions[k].sort }
|
||||
@version_indexes = Hash.new { |h,k| h[k] = @cached_versions[k].each.with_index.to_h }
|
||||
|
||||
@cached_dependencies = Hash.new do |packages, package|
|
||||
if package == @root_package
|
||||
|
@ -117,15 +105,7 @@ module Bundler::PubGrub
|
|||
end
|
||||
|
||||
def versions_for(package, range=VersionRange.any)
|
||||
versions = range.select_versions(@sorted_versions[package])
|
||||
|
||||
# Conditional avoids (among other things) calling
|
||||
# sort_versions_by_preferred with the root package
|
||||
if versions.size > 1
|
||||
sort_versions_by_preferred(package, versions)
|
||||
else
|
||||
versions
|
||||
end
|
||||
range.select_versions(@sorted_versions[package])
|
||||
end
|
||||
|
||||
def no_versions_incompatibility_for(_package, unsatisfied_term)
|
||||
|
@ -164,7 +144,7 @@ module Bundler::PubGrub
|
|||
sorted_versions[high]
|
||||
end
|
||||
|
||||
range = VersionRange.new(min: low, max: high, include_min: true)
|
||||
range = VersionRange.new(min: low, max: high, include_min: !low.nil?)
|
||||
|
||||
self_constraint = VersionConstraint.new(package, range: range)
|
||||
|
||||
|
|
42
lib/bundler/vendor/pub_grub/lib/pub_grub/strategy.rb
vendored
Normal file
42
lib/bundler/vendor/pub_grub/lib/pub_grub/strategy.rb
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
module Bundler::PubGrub
|
||||
class Strategy
|
||||
def initialize(source)
|
||||
@source = source
|
||||
|
||||
@root_package = Package.root
|
||||
@root_version = Package.root_version
|
||||
|
||||
@version_indexes = Hash.new do |h,k|
|
||||
if k == @root_package
|
||||
h[k] = { @root_version => 0 }
|
||||
else
|
||||
h[k] = @source.all_versions_for(k).each.with_index.to_h
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def next_package_and_version(unsatisfied)
|
||||
package, range = next_term_to_try_from(unsatisfied)
|
||||
|
||||
[package, most_preferred_version_of(package, range)]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def most_preferred_version_of(package, range)
|
||||
versions = @source.versions_for(package, range)
|
||||
|
||||
indexes = @version_indexes[package]
|
||||
versions.min_by { |version| indexes[version] }
|
||||
end
|
||||
|
||||
def next_term_to_try_from(unsatisfied)
|
||||
unsatisfied.min_by do |package, range|
|
||||
matching_versions = @source.versions_for(package, range)
|
||||
higher_versions = @source.versions_for(package, range.upper_invert)
|
||||
|
||||
[matching_versions.count <= 1 ? 0 : 1, higher_versions.count]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -76,6 +76,9 @@ module Bundler::PubGrub
|
|||
end
|
||||
|
||||
def initialize(min: nil, max: nil, include_min: false, include_max: false, name: nil)
|
||||
raise ArgumentError, "Ranges without a lower bound cannot have include_min == true" if !min && include_min == true
|
||||
raise ArgumentError, "Ranges without an upper bound cannot have include_max == true" if !max && include_max == true
|
||||
|
||||
@min = min
|
||||
@max = max
|
||||
@include_min = include_min
|
||||
|
@ -311,10 +314,19 @@ module Bundler::PubGrub
|
|||
|
||||
def contiguous_to?(other)
|
||||
return false if other.empty?
|
||||
return true if any?
|
||||
|
||||
intersects?(other) ||
|
||||
(min == other.max && (include_min || other.include_max)) ||
|
||||
(max == other.min && (include_max || other.include_min))
|
||||
intersects?(other) || contiguous_below?(other) || contiguous_above?(other)
|
||||
end
|
||||
|
||||
def contiguous_below?(other)
|
||||
return false if !max || !other.min
|
||||
|
||||
max == other.min && (include_max || other.include_min)
|
||||
end
|
||||
|
||||
def contiguous_above?(other)
|
||||
other.contiguous_below?(self)
|
||||
end
|
||||
|
||||
def allows_all?(other)
|
||||
|
@ -375,15 +387,15 @@ module Bundler::PubGrub
|
|||
def invert
|
||||
return self.class.empty if any?
|
||||
|
||||
low = VersionRange.new(max: min, include_max: !include_min)
|
||||
high = VersionRange.new(min: max, include_min: !include_max)
|
||||
low = -> { VersionRange.new(max: min, include_max: !include_min) }
|
||||
high = -> { VersionRange.new(min: max, include_min: !include_max) }
|
||||
|
||||
if !min
|
||||
high
|
||||
high.call
|
||||
elsif !max
|
||||
low
|
||||
low.call
|
||||
else
|
||||
low.union(high)
|
||||
low.call.union(high.call)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,17 +2,20 @@ require_relative 'partial_solution'
|
|||
require_relative 'term'
|
||||
require_relative 'incompatibility'
|
||||
require_relative 'solve_failure'
|
||||
require_relative 'strategy'
|
||||
|
||||
module Bundler::PubGrub
|
||||
class VersionSolver
|
||||
attr_reader :logger
|
||||
attr_reader :source
|
||||
attr_reader :solution
|
||||
attr_reader :strategy
|
||||
|
||||
def initialize(source:, root: Package.root, logger: Bundler::PubGrub.logger)
|
||||
def initialize(source:, root: Package.root, strategy: Strategy.new(source), logger: Bundler::PubGrub.logger)
|
||||
@logger = logger
|
||||
|
||||
@source = source
|
||||
@strategy = strategy
|
||||
|
||||
# { package => [incompatibility, ...]}
|
||||
@incompatibilities = Hash.new do |h, k|
|
||||
|
@ -36,26 +39,25 @@ module Bundler::PubGrub
|
|||
|
||||
# Returns true if there is more work to be done, false otherwise
|
||||
def work
|
||||
return false if solved?
|
||||
|
||||
next_package = choose_package_version
|
||||
propagate(next_package)
|
||||
|
||||
if solved?
|
||||
unsatisfied_terms = solution.unsatisfied
|
||||
if unsatisfied_terms.empty?
|
||||
logger.info { "Solution found after #{solution.attempted_solutions} attempts:" }
|
||||
solution.decisions.each do |package, version|
|
||||
next if Package.root?(package)
|
||||
logger.info { "* #{package} #{version}" }
|
||||
end
|
||||
|
||||
false
|
||||
else
|
||||
true
|
||||
return false
|
||||
end
|
||||
|
||||
next_package = choose_package_version_from(unsatisfied_terms)
|
||||
propagate(next_package)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def solve
|
||||
work until solved?
|
||||
while work; end
|
||||
|
||||
solution.decisions
|
||||
end
|
||||
|
@ -105,29 +107,15 @@ module Bundler::PubGrub
|
|||
unsatisfied.package
|
||||
end
|
||||
|
||||
def next_package_to_try
|
||||
solution.unsatisfied.min_by do |term|
|
||||
package = term.package
|
||||
range = term.constraint.range
|
||||
matching_versions = source.versions_for(package, range)
|
||||
higher_versions = source.versions_for(package, range.upper_invert)
|
||||
def choose_package_version_from(unsatisfied_terms)
|
||||
remaining = unsatisfied_terms.map { |t| [t.package, t.constraint.range] }.to_h
|
||||
|
||||
[matching_versions.count <= 1 ? 0 : 1, higher_versions.count]
|
||||
end.package
|
||||
end
|
||||
package, version = strategy.next_package_and_version(remaining)
|
||||
|
||||
def choose_package_version
|
||||
if solution.unsatisfied.empty?
|
||||
logger.info "No packages unsatisfied. Solving complete!"
|
||||
return nil
|
||||
end
|
||||
|
||||
package = next_package_to_try
|
||||
unsatisfied_term = solution.unsatisfied.find { |t| t.package == package }
|
||||
version = source.versions_for(package, unsatisfied_term.constraint.range).first
|
||||
logger.debug { "attempting #{package} #{version}" }
|
||||
|
||||
if version.nil?
|
||||
unsatisfied_term = unsatisfied_terms.find { |t| t.package == package }
|
||||
add_incompatibility source.no_versions_incompatibility_for(package, unsatisfied_term)
|
||||
return package
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: false
|
||||
|
||||
module Bundler
|
||||
VERSION = "2.6.6".freeze
|
||||
VERSION = "2.6.7".freeze
|
||||
|
||||
def self.bundler_major_version
|
||||
@bundler_major_version ||= VERSION.split(".").first.to_i
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
require "rbconfig"
|
||||
|
||||
module Gem
|
||||
VERSION = "3.6.6"
|
||||
VERSION = "3.6.7"
|
||||
end
|
||||
|
||||
# Must be first since it unloads the prelude from 1.9.2
|
||||
|
@ -156,6 +156,13 @@ module Gem
|
|||
specifications/default
|
||||
].freeze
|
||||
|
||||
##
|
||||
# The default value for SOURCE_DATE_EPOCH if not specified.
|
||||
# We want a date after 1980-01-01, to prevent issues with Zip files.
|
||||
# This particular timestamp is for 1980-01-02 00:00:00 GMT.
|
||||
|
||||
DEFAULT_SOURCE_DATE_EPOCH = 315_619_200
|
||||
|
||||
@@win_platform = nil
|
||||
|
||||
@configuration = nil
|
||||
|
@ -1155,8 +1162,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
|
|||
|
||||
##
|
||||
# If the SOURCE_DATE_EPOCH environment variable is set, returns it's value.
|
||||
# Otherwise, returns the time that +Gem.source_date_epoch_string+ was
|
||||
# first called in the same format as SOURCE_DATE_EPOCH.
|
||||
# Otherwise, returns DEFAULT_SOURCE_DATE_EPOCH as a string.
|
||||
#
|
||||
# NOTE(@duckinator): The implementation is a tad weird because we want to:
|
||||
# 1. Make builds reproducible by default, by having this function always
|
||||
|
@ -1171,15 +1177,12 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
|
|||
# https://reproducible-builds.org/specs/source-date-epoch/
|
||||
|
||||
def self.source_date_epoch_string
|
||||
# The value used if $SOURCE_DATE_EPOCH is not set.
|
||||
@default_source_date_epoch ||= Time.now.to_i.to_s
|
||||
|
||||
specified_epoch = ENV["SOURCE_DATE_EPOCH"]
|
||||
|
||||
# If it's empty or just whitespace, treat it like it wasn't set at all.
|
||||
specified_epoch = nil if !specified_epoch.nil? && specified_epoch.strip.empty?
|
||||
|
||||
epoch = specified_epoch || @default_source_date_epoch
|
||||
epoch = specified_epoch || DEFAULT_SOURCE_DATE_EPOCH.to_s
|
||||
|
||||
epoch.strip
|
||||
end
|
||||
|
|
|
@ -195,7 +195,7 @@ to the same gem path as user-installed gems.
|
|||
argv = ARGV.clone
|
||||
ARGV.replace options[:args]
|
||||
|
||||
exe = executable = options[:executable]
|
||||
executable = options[:executable]
|
||||
|
||||
contains_executable = Gem.loaded_specs.values.select do |spec|
|
||||
spec.executables.include?(executable)
|
||||
|
@ -206,13 +206,22 @@ to the same gem path as user-installed gems.
|
|||
end
|
||||
|
||||
if contains_executable.empty?
|
||||
if (spec = Gem.loaded_specs[executable]) && (exe = spec.executable)
|
||||
contains_executable << spec
|
||||
else
|
||||
spec = Gem.loaded_specs[executable]
|
||||
|
||||
if spec.nil? || spec.executables.empty?
|
||||
alert_error "Failed to load executable `#{executable}`," \
|
||||
" are you sure the gem `#{options[:gem_name]}` contains it?"
|
||||
terminate_interaction 1
|
||||
end
|
||||
|
||||
if spec.executables.size > 1
|
||||
alert_error "Ambiguous which executable from gem `#{executable}` should be run: " \
|
||||
"the options are #{spec.executables.sort}, specify one via COMMAND, and use `-g` and `-v` to specify gem and version"
|
||||
terminate_interaction 1
|
||||
end
|
||||
|
||||
contains_executable << spec
|
||||
executable = spec.executable
|
||||
end
|
||||
|
||||
if contains_executable.size > 1
|
||||
|
@ -223,8 +232,8 @@ to the same gem path as user-installed gems.
|
|||
end
|
||||
|
||||
old_exe = $0
|
||||
$0 = exe
|
||||
load Gem.activate_bin_path(contains_executable.first.name, exe, ">= 0.a")
|
||||
$0 = executable
|
||||
load Gem.activate_bin_path(contains_executable.first.name, executable, ">= 0.a")
|
||||
ensure
|
||||
$0 = old_exe if old_exe
|
||||
ARGV.replace argv
|
||||
|
|
|
@ -239,7 +239,7 @@ module Gem
|
|||
# Enables automatic installation into user directory
|
||||
|
||||
def self.default_user_install # :nodoc:
|
||||
if !ENV.key?("GEM_HOME") && (File.exist?(Gem.dir) && !File.writable?(Gem.dir))
|
||||
if !ENV.key?("GEM_HOME") && File.exist?(Gem.dir) && !File.writable?(Gem.dir)
|
||||
Gem.ui.say "Defaulting to user installation because default installation directory (#{Gem.dir}) is not writable."
|
||||
return true
|
||||
end
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Gem::Resolver::APISet::GemParser
|
||||
EMPTY_ARRAY = [].freeze
|
||||
private_constant :EMPTY_ARRAY
|
||||
|
||||
def parse(line)
|
||||
version_and_platform, rest = line.split(" ", 2)
|
||||
version, platform = version_and_platform.split("-", 2)
|
||||
dependencies, requirements = rest.split("|", 2).map! {|s| s.split(",") } if rest
|
||||
dependencies = dependencies ? dependencies.map! {|d| parse_dependency(d) } : EMPTY_ARRAY
|
||||
requirements = requirements ? requirements.map! {|d| parse_dependency(d) } : EMPTY_ARRAY
|
||||
dependencies = dependencies ? dependencies.map! {|d| parse_dependency(d) } : []
|
||||
requirements = requirements ? requirements.map! {|d| parse_dependency(d) } : []
|
||||
[version, platform, dependencies, requirements]
|
||||
end
|
||||
|
||||
|
|
|
@ -2144,11 +2144,11 @@ class Gem::Specification < Gem::BasicSpecification
|
|||
@files.concat(@extra_rdoc_files)
|
||||
end
|
||||
|
||||
@files = @files.uniq if @files
|
||||
@extensions = @extensions.uniq if @extensions
|
||||
@test_files = @test_files.uniq if @test_files
|
||||
@executables = @executables.uniq if @executables
|
||||
@extra_rdoc_files = @extra_rdoc_files.uniq if @extra_rdoc_files
|
||||
@files = @files.uniq.sort if @files
|
||||
@extensions = @extensions.uniq.sort if @extensions
|
||||
@test_files = @test_files.uniq.sort if @test_files
|
||||
@executables = @executables.uniq.sort if @executables
|
||||
@extra_rdoc_files = @extra_rdoc_files.uniq.sort if @extra_rdoc_files
|
||||
end
|
||||
|
||||
##
|
||||
|
|
|
@ -368,13 +368,13 @@ class Gem::Version
|
|||
|
||||
lhsize = lhsegments.size
|
||||
rhsize = rhsegments.size
|
||||
limit = (lhsize > rhsize ? lhsize : rhsize) - 1
|
||||
limit = (lhsize > rhsize ? rhsize : lhsize)
|
||||
|
||||
i = 0
|
||||
|
||||
while i <= limit
|
||||
lhs = lhsegments[i] || 0
|
||||
rhs = rhsegments[i] || 0
|
||||
while i < limit
|
||||
lhs = lhsegments[i]
|
||||
rhs = rhsegments[i]
|
||||
i += 1
|
||||
|
||||
next if lhs == rhs
|
||||
|
@ -384,6 +384,24 @@ class Gem::Version
|
|||
return lhs <=> rhs
|
||||
end
|
||||
|
||||
lhs = lhsegments[i]
|
||||
|
||||
if lhs.nil?
|
||||
rhs = rhsegments[i]
|
||||
|
||||
while i < rhsize
|
||||
return 1 if String === rhs
|
||||
return -1 unless rhs.zero?
|
||||
rhs = rhsegments[i += 1]
|
||||
end
|
||||
else
|
||||
while i < lhsize
|
||||
return -1 if String === lhs
|
||||
return 1 unless lhs.zero?
|
||||
lhs = lhsegments[i += 1]
|
||||
end
|
||||
end
|
||||
|
||||
0
|
||||
end
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ RSpec.describe Bundler::Definition do
|
|||
before { Bundler::Definition.no_lock = true }
|
||||
after { Bundler::Definition.no_lock = false }
|
||||
|
||||
it "does not create a lock file" do
|
||||
it "does not create a lockfile" do
|
||||
subject.lock
|
||||
expect(bundled_app_lock).not_to be_file
|
||||
end
|
||||
|
|
|
@ -138,7 +138,7 @@ RSpec.describe Bundler::LockfileParser do
|
|||
expect(subject.ruby_version).to eq ruby_version
|
||||
rake_spec = specs.last
|
||||
checksums = subject.sources.last.checksum_store.to_lock(specs.last)
|
||||
expect(checksums).to eq("#{rake_spec.name_tuple.lock_name} #{rake_checksums.map(&:to_lock).sort.join(",")}")
|
||||
expect(checksums).to eq("#{rake_spec.lock_name} #{rake_checksums.map(&:to_lock).sort.join(",")}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ RSpec.describe Bundler::SharedHelpers do
|
|||
|
||||
before { allow(subject).to receive(:default_gemfile).and_return(gemfile_path) }
|
||||
|
||||
it "returns the lock file path" do
|
||||
it "returns the lockfile path" do
|
||||
expect(subject.default_lockfile).to eq(expected_lockfile_path)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -258,7 +258,7 @@ RSpec.describe "bundle check" do
|
|||
expect(err).not_to include("Unfortunately, a fatal error has occurred. ")
|
||||
end
|
||||
|
||||
it "fails when there's no lock file and frozen is set" do
|
||||
it "fails when there's no lockfile and frozen is set" do
|
||||
install_gemfile <<-G
|
||||
source "https://gem.repo1"
|
||||
gem "foo"
|
||||
|
|
|
@ -49,7 +49,7 @@ RSpec.describe "bundle install with gem sources" do
|
|||
expect(bundled_app(".bundle")).not_to exist
|
||||
end
|
||||
|
||||
it "creates lock files based on the Gemfile name" do
|
||||
it "creates lockfiles based on the Gemfile name" do
|
||||
gemfile bundled_app("OmgFile"), <<-G
|
||||
source "https://gem.repo1"
|
||||
gem "myrack", "1.0"
|
||||
|
@ -1368,7 +1368,7 @@ RSpec.describe "bundle install with gem sources" do
|
|||
it "adds the current platform to the lockfile" do
|
||||
bundle "install --verbose"
|
||||
|
||||
expect(out).to include("re-resolving dependencies because your lockfile does not include the current platform")
|
||||
expect(out).to include("re-resolving dependencies because your lockfile is missing the current platform")
|
||||
expect(out).not_to include("you are adding a new platform to your lockfile")
|
||||
|
||||
expect(lockfile).to eq <<~L
|
||||
|
|
|
@ -80,7 +80,7 @@ RSpec.describe "bundle update" do
|
|||
end
|
||||
|
||||
describe "with --gemfile" do
|
||||
it "creates lock files based on the Gemfile name" do
|
||||
it "creates lockfiles based on the Gemfile name" do
|
||||
gemfile bundled_app("OmgFile"), <<-G
|
||||
source "https://gem.repo1"
|
||||
gem "myrack", "1.0"
|
||||
|
@ -640,7 +640,7 @@ RSpec.describe "bundle update" do
|
|||
myrack
|
||||
|
||||
PLATFORMS
|
||||
#{local_platform}
|
||||
x86-darwin-100
|
||||
|
||||
DEPENDENCIES
|
||||
activesupport
|
||||
|
|
|
@ -321,7 +321,7 @@ RSpec.describe "install in deployment or frozen mode" do
|
|||
L
|
||||
|
||||
bundle :install, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false, artifice: "compact_index"
|
||||
expect(err).to include("Your lock file is missing \"bar\", but the lockfile can't be updated because frozen mode is set")
|
||||
expect(err).to include("Your lockfile is missing \"bar\", but can't be updated because frozen mode is set")
|
||||
end
|
||||
|
||||
it "explodes if a path gem is missing" do
|
||||
|
@ -550,7 +550,7 @@ RSpec.describe "install in deployment or frozen mode" do
|
|||
pristine_system_gems :bundler
|
||||
bundle "config set --local deployment true"
|
||||
bundle "install --verbose"
|
||||
expect(out).not_to include("but the lockfile can't be updated because frozen mode is set")
|
||||
expect(out).not_to include("can't be updated because frozen mode is set")
|
||||
expect(out).not_to include("You have added to the Gemfile")
|
||||
expect(out).not_to include("You have deleted from the Gemfile")
|
||||
expect(out).to include("vendor/cache/foo")
|
||||
|
|
|
@ -273,7 +273,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
|
|||
expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0", source: "remote3")
|
||||
|
||||
# In https://github.com/bundler/bundler/issues/3585 this failed
|
||||
# when there is already a lock file, and the gems are missing, so try again
|
||||
# when there is already a lockfile, and the gems are missing, so try again
|
||||
system_gems []
|
||||
bundle :install, artifice: "compact_index"
|
||||
|
||||
|
@ -482,7 +482,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
|
|||
expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0")
|
||||
|
||||
# In https://github.com/rubygems/bundler/issues/3585 this failed
|
||||
# when there is already a lock file, and the gems are missing, so try again
|
||||
# when there is already a lockfile, and the gems are missing, so try again
|
||||
system_gems []
|
||||
bundle :install, artifice: "compact_index"
|
||||
|
||||
|
@ -1221,7 +1221,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
|
|||
|
||||
DEPENDENCIES
|
||||
myrack!
|
||||
#{checksums_section}
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
|
|
|
@ -91,7 +91,7 @@ RSpec.describe "bundle install with specific platforms" do
|
|||
#{Bundler::VERSION}
|
||||
L
|
||||
|
||||
# force strict usage of the lock file by setting frozen mode
|
||||
# force strict usage of the lockfile by setting frozen mode
|
||||
bundle "config set --local frozen true"
|
||||
|
||||
# make sure the platform that got actually installed with the old bundler is used
|
||||
|
@ -752,6 +752,80 @@ RSpec.describe "bundle install with specific platforms" do
|
|||
L
|
||||
end
|
||||
|
||||
it "automatically fixes the lockfile when adding a gem that introduces dependencies with no ruby platform variants transitively" do
|
||||
simulate_platform "x86_64-linux" do
|
||||
build_repo4 do
|
||||
build_gem "nokogiri", "1.18.2"
|
||||
|
||||
build_gem "nokogiri", "1.18.2" do |s|
|
||||
s.platform = "x86_64-linux"
|
||||
end
|
||||
|
||||
build_gem("sorbet", "0.5.11835") do |s|
|
||||
s.add_dependency "sorbet-static", "= 0.5.11835"
|
||||
end
|
||||
|
||||
build_gem "sorbet-static", "0.5.11835" do |s|
|
||||
s.platform = "x86_64-linux"
|
||||
end
|
||||
end
|
||||
|
||||
gemfile <<~G
|
||||
source "https://gem.repo4"
|
||||
|
||||
gem "nokogiri"
|
||||
gem "sorbet"
|
||||
G
|
||||
|
||||
lockfile <<~L
|
||||
GEM
|
||||
remote: https://gem.repo4/
|
||||
specs:
|
||||
nokogiri (1.18.2)
|
||||
nokogiri (1.18.2-x86_64-linux)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
nokogiri
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
|
||||
bundle "lock"
|
||||
|
||||
checksums = checksums_section_when_enabled do |c|
|
||||
c.checksum gem_repo4, "nokogiri", "1.18.2", "x86_64-linux"
|
||||
c.checksum gem_repo4, "sorbet", "0.5.11835"
|
||||
c.checksum gem_repo4, "sorbet-static", "0.5.11835", "x86_64-linux"
|
||||
end
|
||||
|
||||
expect(lockfile).to eq <<~L
|
||||
GEM
|
||||
remote: https://gem.repo4/
|
||||
specs:
|
||||
nokogiri (1.18.2)
|
||||
nokogiri (1.18.2-x86_64-linux)
|
||||
sorbet (0.5.11835)
|
||||
sorbet-static (= 0.5.11835)
|
||||
sorbet-static (0.5.11835-x86_64-linux)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
nokogiri
|
||||
sorbet
|
||||
#{checksums}
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
end
|
||||
end
|
||||
|
||||
it "automatically fixes the lockfile if multiple platforms locked, but no valid versions of direct dependencies for all of them" do
|
||||
simulate_platform "x86_64-linux" do
|
||||
build_repo4 do
|
||||
|
|
|
@ -96,7 +96,6 @@ RSpec.describe "compact index api" do
|
|||
bundle :install, artifice: "compact_index"
|
||||
|
||||
bundle "config set --local deployment true"
|
||||
bundle "config set --local path vendor/bundle"
|
||||
bundle :install, artifice: "compact_index"
|
||||
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
||||
expect(the_bundle).to include_gems "myrack 1.0.0"
|
||||
|
@ -1090,4 +1089,11 @@ Running `bundle update rails` should fix the problem.
|
|||
count = lockfile.match?("CHECKSUMS") ? 2 : 1 # Once in the specs, and once in CHECKSUMS
|
||||
expect(lockfile.scan(/activemerchant \(/).size).to eq(count)
|
||||
end
|
||||
|
||||
it "handles an API that does not provide checksums info (undocumented, support may get removed)" do
|
||||
install_gemfile <<-G, artifice: "compact_index_no_checksums"
|
||||
source "https://gem.repo1"
|
||||
gem "rake"
|
||||
G
|
||||
end
|
||||
end
|
||||
|
|
|
@ -61,7 +61,6 @@ RSpec.describe "gemcutter's dependency API" do
|
|||
bundle :install, artifice: "endpoint"
|
||||
|
||||
bundle "config set --local deployment true"
|
||||
bundle "config set --local path vendor/bundle"
|
||||
bundle :install, artifice: "endpoint"
|
||||
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
||||
expect(the_bundle).to include_gems "myrack 1.0.0"
|
||||
|
|
|
@ -394,7 +394,7 @@ RSpec.describe "bundle install with install-time dependencies" do
|
|||
end
|
||||
end
|
||||
|
||||
context "with a Gemfile and lock file that don't resolve under the current platform" do
|
||||
context "with a Gemfile and lockfile that don't resolve under the current platform" do
|
||||
before do
|
||||
build_repo4 do
|
||||
build_gem "sorbet", "0.5.10554" do |s|
|
||||
|
|
|
@ -248,6 +248,7 @@ RSpec.describe "global gem caching" do
|
|||
describe "extension caching" do
|
||||
it "works" do
|
||||
skip "gets incorrect ref in path" if Gem.win_platform?
|
||||
skip "fails for unknown reason when run by ruby-core" if ruby_core?
|
||||
|
||||
build_git "very_simple_git_binary", &:add_c_extension
|
||||
build_lib "very_simple_path_binary", &:add_c_extension
|
||||
|
|
|
@ -24,7 +24,7 @@ RSpec.describe "process lock spec" do
|
|||
context "when creating a lock raises Errno::ENOTSUP" do
|
||||
before { allow(File).to receive(:open).and_raise(Errno::ENOTSUP) }
|
||||
|
||||
it "skips creating the lock file and yields" do
|
||||
it "skips creating the lockfile and yields" do
|
||||
processed = false
|
||||
Bundler::ProcessLock.lock(default_bundle_path) { processed = true }
|
||||
|
||||
|
@ -35,7 +35,7 @@ RSpec.describe "process lock spec" do
|
|||
context "when creating a lock raises Errno::EPERM" do
|
||||
before { allow(File).to receive(:open).and_raise(Errno::EPERM) }
|
||||
|
||||
it "skips creating the lock file and yields" do
|
||||
it "skips creating the lockfile and yields" do
|
||||
processed = false
|
||||
Bundler::ProcessLock.lock(default_bundle_path) { processed = true }
|
||||
|
||||
|
@ -46,7 +46,7 @@ RSpec.describe "process lock spec" do
|
|||
context "when creating a lock raises Errno::EROFS" do
|
||||
before { allow(File).to receive(:open).and_raise(Errno::EROFS) }
|
||||
|
||||
it "skips creating the lock file and yields" do
|
||||
it "skips creating the lockfile and yields" do
|
||||
processed = false
|
||||
Bundler::ProcessLock.lock(default_bundle_path) { processed = true }
|
||||
|
||||
|
|
|
@ -1613,6 +1613,39 @@ RSpec.describe "the lockfile format" do
|
|||
expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
|
||||
end
|
||||
|
||||
it "raises a clear error when frozen mode is set and lockfile is missing entries in CHECKSUMS section, and does not install any gems" do
|
||||
lockfile <<-L
|
||||
GEM
|
||||
remote: https://gem.repo2/
|
||||
specs:
|
||||
myrack_middleware (1.0)
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
myrack_middleware
|
||||
|
||||
CHECKSUMS
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
|
||||
install_gemfile <<-G, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
|
||||
source "https://gem.repo2"
|
||||
gem "myrack_middleware"
|
||||
G
|
||||
|
||||
expect(err).to eq <<~L.strip
|
||||
Your lockfile is missing a checksums entry for \"myrack_middleware\", but can't be updated because frozen mode is set
|
||||
|
||||
Run `bundle install` elsewhere and add the updated Gemfile.lock to version control.
|
||||
L
|
||||
|
||||
expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
|
||||
end
|
||||
|
||||
it "automatically fixes the lockfile when it's missing deps, they conflict with other locked deps, but conflicts are fixable" do
|
||||
build_repo4 do
|
||||
build_gem "other_dep", "0.9"
|
||||
|
@ -1875,6 +1908,120 @@ RSpec.describe "the lockfile format" do
|
|||
L
|
||||
end
|
||||
|
||||
it "automatically fixes the lockfile when it includes a gem under the correct GIT section, but also under an incorrect GEM section, with a higher version, and with no explicit Gemfile requirement" do
|
||||
git = build_git "foo"
|
||||
|
||||
gemfile <<~G
|
||||
source "https://gem.repo1/"
|
||||
gem "foo", git: "#{lib_path("foo-1.0")}"
|
||||
G
|
||||
|
||||
# If the lockfile erroneously lists platform versions of the gem
|
||||
# that don't match the locked version of the git repo we should remove them.
|
||||
|
||||
lockfile <<~L
|
||||
GIT
|
||||
remote: #{lib_path("foo-1.0")}
|
||||
revision: #{git.ref_for("main")}
|
||||
specs:
|
||||
foo (1.0)
|
||||
|
||||
GEM
|
||||
remote: https://gem.repo1/
|
||||
specs:
|
||||
foo (1.1-x86_64-linux-gnu)
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
foo!
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
|
||||
bundle "install"
|
||||
|
||||
expect(lockfile).to eq <<~L
|
||||
GIT
|
||||
remote: #{lib_path("foo-1.0")}
|
||||
revision: #{git.ref_for("main")}
|
||||
specs:
|
||||
foo (1.0)
|
||||
|
||||
GEM
|
||||
remote: https://gem.repo1/
|
||||
specs:
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
foo!
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
end
|
||||
|
||||
it "automatically fixes the lockfile when it includes a gem under the correct GIT section, but also under an incorrect GEM section, with a higher version" do
|
||||
git = build_git "foo"
|
||||
|
||||
gemfile <<~G
|
||||
source "https://gem.repo1/"
|
||||
gem "foo", "= 1.0", git: "#{lib_path("foo-1.0")}"
|
||||
G
|
||||
|
||||
# If the lockfile erroneously lists platform versions of the gem
|
||||
# that don't match the locked version of the git repo we should remove them.
|
||||
|
||||
lockfile <<~L
|
||||
GIT
|
||||
remote: #{lib_path("foo-1.0")}
|
||||
revision: #{git.ref_for("main")}
|
||||
specs:
|
||||
foo (1.0)
|
||||
|
||||
GEM
|
||||
remote: https://gem.repo1/
|
||||
specs:
|
||||
foo (1.1-x86_64-linux-gnu)
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
foo (= 1.0)!
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
|
||||
bundle "install"
|
||||
|
||||
expect(lockfile).to eq <<~L
|
||||
GIT
|
||||
remote: #{lib_path("foo-1.0")}
|
||||
revision: #{git.ref_for("main")}
|
||||
specs:
|
||||
foo (1.0)
|
||||
|
||||
GEM
|
||||
remote: https://gem.repo1/
|
||||
specs:
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
foo (= 1.0)!
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
end
|
||||
|
||||
it "automatically fixes the lockfile when it has incorrect deps, keeping the locked version" do
|
||||
build_repo4 do
|
||||
build_gem "net-smtp", "0.5.0" do |s|
|
||||
|
@ -2030,7 +2177,7 @@ RSpec.describe "the lockfile format" do
|
|||
L
|
||||
|
||||
bundle "install --verbose"
|
||||
expect(out).to include("re-resolving dependencies because your lock file includes \"minitest-bisect\" but not some of its dependencies")
|
||||
expect(out).to include("re-resolving dependencies because your lockfile includes \"minitest-bisect\" but not some of its dependencies")
|
||||
|
||||
expect(lockfile).to eq <<~L
|
||||
GEM
|
||||
|
|
|
@ -67,7 +67,7 @@ RSpec.describe "real source plugins" do
|
|||
expect(the_bundle).to include_gems("a-path-gem 1.0")
|
||||
end
|
||||
|
||||
it "writes to lock file" do
|
||||
it "writes to lockfile" do
|
||||
bundle "install"
|
||||
|
||||
checksums = checksums_section_when_enabled do |c|
|
||||
|
@ -336,7 +336,7 @@ RSpec.describe "real source plugins" do
|
|||
expect(the_bundle).to include_gems("ma-gitp-gem 1.0")
|
||||
end
|
||||
|
||||
it "writes to lock file" do
|
||||
it "writes to lockfile" do
|
||||
revision = revision_for(lib_path("ma-gitp-gem-1.0"))
|
||||
bundle "install"
|
||||
|
||||
|
|
|
@ -1683,7 +1683,7 @@ end
|
|||
RUBY
|
||||
end
|
||||
|
||||
expect(err).to include("Your lockfile does not include the current platform, but the lockfile can't be updated because file system is read-only")
|
||||
expect(err).to include("Your lockfile is missing the current platform, but can't be updated because file system is read-only")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
16
spec/bundler/support/artifice/compact_index_no_checksums.rb
Normal file
16
spec/bundler/support/artifice/compact_index_no_checksums.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative "helpers/compact_index"
|
||||
|
||||
class CompactIndexNoChecksums < CompactIndexAPI
|
||||
get "/info/:name" do
|
||||
etag_response do
|
||||
gem = gems.find {|g| g.name == params[:name] }
|
||||
gem.versions.map(&:number).join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require_relative "helpers/artifice"
|
||||
|
||||
Artifice.activate_with(CompactIndexNoChecksums)
|
|
@ -370,8 +370,11 @@ class TestGemCommandsExecCommand < Gem::TestCase
|
|||
util_clear_gems
|
||||
|
||||
use_ui @ui do
|
||||
@cmd.invoke "a:2"
|
||||
assert_equal "a-2 foo\n", @ui.output
|
||||
e = assert_raise Gem::MockGemUi::TermError do
|
||||
@cmd.invoke "a:2"
|
||||
end
|
||||
assert_equal 1, e.exit_code
|
||||
assert_equal "ERROR: Ambiguous which executable from gem `a` should be run: the options are [\"bar\", \"foo\"], specify one via COMMAND, and use `-g` and `-v` to specify gem and version\n", @ui.error
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ class TestGemExtCmakeBuilder < Gem::TestCase
|
|||
def test_self_build
|
||||
File.open File.join(@ext, "CMakeLists.txt"), "w" do |cmakelists|
|
||||
cmakelists.write <<-EO_CMAKE
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(self_build NONE)
|
||||
install (FILES test.txt DESTINATION bin)
|
||||
EO_CMAKE
|
||||
|
|
|
@ -33,7 +33,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
|||
f.write "a" * 10
|
||||
end
|
||||
|
||||
assert_headers_equal(tar_file_header("x", "", 0o644, 10, Time.now),
|
||||
assert_headers_equal(tar_file_header("x", "", 0o644, 10, Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc),
|
||||
@io.string[0, 512])
|
||||
end
|
||||
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
||||
|
@ -54,7 +54,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
|||
Time.stub :now, Time.at(1_458_518_157) do
|
||||
@tar_writer.add_symlink "x", "y", 0o644
|
||||
|
||||
assert_headers_equal(tar_symlink_header("x", "", 0o644, Time.now, "y"),
|
||||
assert_headers_equal(tar_symlink_header("x", "", 0o644, Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc, "y"),
|
||||
@io.string[0, 512])
|
||||
end
|
||||
assert_equal 512, @io.pos
|
||||
|
@ -86,7 +86,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
|||
"e1cf14b0",
|
||||
digests["SHA512"].hexdigest
|
||||
|
||||
assert_headers_equal(tar_file_header("x", "", 0o644, 10, Time.now),
|
||||
assert_headers_equal(tar_file_header("x", "", 0o644, 10, Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc),
|
||||
@io.string[0, 512])
|
||||
end
|
||||
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
||||
|
@ -109,7 +109,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
|||
"e1cf14b0",
|
||||
digests["SHA512"].hexdigest
|
||||
|
||||
assert_headers_equal(tar_file_header("x", "", 0o644, 10, Time.now),
|
||||
assert_headers_equal(tar_file_header("x", "", 0o644, 10, Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc),
|
||||
@io.string[0, 512])
|
||||
end
|
||||
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
||||
|
@ -126,7 +126,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
|||
io.write "a" * 10
|
||||
end
|
||||
|
||||
assert_headers_equal(tar_file_header("x", "", 0o644, 10, Time.now),
|
||||
assert_headers_equal(tar_file_header("x", "", 0o644, 10, Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc),
|
||||
@io.string[0, 512])
|
||||
|
||||
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
||||
|
@ -137,7 +137,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
|||
signature = signer.sign digest.digest
|
||||
|
||||
assert_headers_equal(tar_file_header("x.sig", "", 0o444, signature.length,
|
||||
Time.now),
|
||||
Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc),
|
||||
@io.string[1024, 512])
|
||||
assert_equal "#{signature}#{"\0" * (512 - signature.length)}",
|
||||
@io.string[1536, 512]
|
||||
|
@ -154,7 +154,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
|||
io.write "a" * 10
|
||||
end
|
||||
|
||||
assert_headers_equal(tar_file_header("x", "", 0o644, 10, Time.now),
|
||||
assert_headers_equal(tar_file_header("x", "", 0o644, 10, Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc),
|
||||
@io.string[0, 512])
|
||||
end
|
||||
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
||||
|
@ -168,7 +168,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
|||
io.write "a" * 10
|
||||
end
|
||||
|
||||
assert_headers_equal(tar_file_header("x", "", 0o644, 10, Time.now),
|
||||
assert_headers_equal(tar_file_header("x", "", 0o644, 10, Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc),
|
||||
@io.string[0, 512])
|
||||
|
||||
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
||||
|
@ -192,7 +192,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
|||
Time.stub :now, Time.at(1_458_518_157) do
|
||||
@tar_writer.add_file_simple "x", 0, 100
|
||||
|
||||
assert_headers_equal tar_file_header("x", "", 0, 100, Time.now),
|
||||
assert_headers_equal tar_file_header("x", "", 0, 100, Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc),
|
||||
@io.string[0, 512]
|
||||
end
|
||||
|
||||
|
@ -250,7 +250,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
|||
Time.stub :now, Time.at(1_458_518_157) do
|
||||
@tar_writer.mkdir "foo", 0o644
|
||||
|
||||
assert_headers_equal tar_dir_header("foo", "", 0o644, Time.now),
|
||||
assert_headers_equal tar_dir_header("foo", "", 0o644, Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc),
|
||||
@io.string[0, 512]
|
||||
|
||||
assert_equal 512, @io.pos
|
||||
|
|
|
@ -16,7 +16,7 @@ rubygems_version: "1.0"
|
|||
name: keyedlist
|
||||
version: !ruby/object:Gem::Version
|
||||
version: 0.4.0
|
||||
date: 2004-03-28 15:37:49.828000 +02:00
|
||||
date: 1980-01-02 00:00:00 UTC
|
||||
platform:
|
||||
summary: A Hash which automatically computes keys.
|
||||
require_paths:
|
||||
|
@ -75,7 +75,7 @@ end
|
|||
def assert_date(date)
|
||||
assert_kind_of Time, date
|
||||
assert_equal [0, 0, 0], [date.hour, date.min, date.sec]
|
||||
assert_operator (Gem::Specification::TODAY..Time.now), :cover?, date
|
||||
assert_equal Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc, date
|
||||
end
|
||||
|
||||
def setup
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue