Merge RubyGems-3.6.7 and Bundler-2.6.7

This commit is contained in:
Hiroshi SHIBATA 2025-04-09 09:19:31 +09:00 committed by Takashi Kokubun
parent db2bf9f078
commit e580145171
57 changed files with 721 additions and 289 deletions

View file

@ -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,12 +231,16 @@ module Bundler
def register_checksum(lock_name, checksum)
@store_mutex.synchronize do
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
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)

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true
require_relative "gem_parser"
require "rubygems/resolver/api_set/gem_parser"
module Bundler
class CompactIndexClient

View file

@ -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

View file

@ -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.

View file

@ -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,12 +815,13 @@ 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 lockfile is missing \"#{@missing_lockfile_dep}\""],
[@unlocking_bundler, "an update to the version of Bundler itself was requested"],
[@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(", ")
@ -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)
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
elsif locked_specs.empty? && dep_changed == false
@missing_lockfile_dep = name
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)

View file

@ -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?

View file

@ -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?

View file

@ -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,11 +248,15 @@ module Bundler
full_name = Gem::NameTuple.new(name, version, platform).full_name
return unless spec = @specs[full_name]
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
def parse_spec(line)

View file

@ -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

View file

@ -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

View 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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -60,7 +60,7 @@ module Bundler
end
def eql?(other)
return unless other.class == self.class
[Gemspec, Path].include?(other.class) &&
expanded_original_path == other.expanded_original_path &&
version == other.version
end

View file

@ -173,38 +173,56 @@ 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?
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)

View file

@ -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)

View file

@ -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"

View file

@ -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
##

View file

@ -1,3 +1,3 @@
class Bundler::ConnectionPool
VERSION = "2.4.1"
VERSION = "2.5.0"
end

View file

@ -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)

View 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
##

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -640,7 +640,7 @@ RSpec.describe "bundle update" do
myrack
PLATFORMS
#{local_platform}
x86-darwin-100
DEPENDENCIES
activesupport

View file

@ -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")

View file

@ -1221,7 +1221,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
DEPENDENCIES
myrack!
#{checksums_section}
BUNDLED WITH
#{Bundler::VERSION}
L

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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|

View file

@ -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

View 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)

View file

@ -370,8 +370,11 @@ class TestGemCommandsExecCommand < Gem::TestCase
util_clear_gems
use_ui @ui do
e = assert_raise Gem::MockGemUi::TermError do
@cmd.invoke "a:2"
assert_equal "a-2 foo\n", @ui.output
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

View file

@ -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

View file

@ -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

View file

@ -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