Merge RubyGems-3.5.2 and Bundler-2.5.2

This commit is contained in:
Hiroshi SHIBATA 2023-12-22 07:01:12 +09:00
parent fc549b2b3a
commit 82496f2b38
28 changed files with 394 additions and 267 deletions

View File

@ -496,7 +496,15 @@ module Bundler
private :sources private :sources
def nothing_changed? def nothing_changed?
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@missing_lockfile_dep && !@unlocking_bundler && !@invalid_lockfile_dep !@source_changes &&
!@dependency_changes &&
!@new_platform &&
!@path_changes &&
!@local_changes &&
!@missing_lockfile_dep &&
!@unlocking_bundler &&
!@locked_spec_with_missing_deps &&
!@locked_spec_with_invalid_deps
end end
def no_resolve_needed? def no_resolve_needed?
@ -653,7 +661,8 @@ module Bundler
[@local_changes, "the gemspecs for git local 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 lock file is missing \"#{@missing_lockfile_dep}\""],
[@unlocking_bundler, "an update to the version of Bundler itself was requested"], [@unlocking_bundler, "an update to the version of Bundler itself was requested"],
[@invalid_lockfile_dep, "your lock file has an invalid dependency \"#{@invalid_lockfile_dep}\""], [@locked_spec_with_missing_deps, "your lock file 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(", ") ].select(&:first).map(&:last).join(", ")
end end
@ -708,26 +717,25 @@ module Bundler
end end
def check_lockfile def check_lockfile
@invalid_lockfile_dep = nil
@missing_lockfile_dep = nil @missing_lockfile_dep = nil
locked_names = @locked_specs.map(&:name) @locked_spec_with_invalid_deps = nil
@locked_spec_with_missing_deps = nil
missing = [] missing = []
invalid = [] invalid = []
@locked_specs.each do |s| @locked_specs.each do |s|
s.dependencies.each do |dep| validation = @locked_specs.validate_deps(s)
next if dep.name == "bundler"
missing << s unless locked_names.include?(dep.name) missing << s if validation == :missing
invalid << s if @locked_specs.none? {|spec| dep.matches_spec?(spec) } invalid << s if validation == :invalid
end
end end
if missing.any? if missing.any?
@locked_specs.delete(missing) @locked_specs.delete(missing)
@missing_lockfile_dep = missing.first.name @locked_spec_with_missing_deps = missing.first.name
elsif !@dependency_changes elsif !@dependency_changes
@missing_lockfile_dep = current_dependencies.find do |d| @missing_lockfile_dep = current_dependencies.find do |d|
@locked_specs[d.name].empty? && d.name != "bundler" @locked_specs[d.name].empty? && d.name != "bundler"
@ -737,7 +745,7 @@ module Bundler
if invalid.any? if invalid.any?
@locked_specs.delete(invalid) @locked_specs.delete(invalid)
@invalid_lockfile_dep = invalid.first.name @locked_spec_with_invalid_deps = invalid.first.name
end end
end end

View File

@ -10,6 +10,8 @@ module Bundler
attr_reader :name, :version, :platform attr_reader :name, :version, :platform
attr_accessor :source, :remote, :force_ruby_platform, :dependencies, :required_ruby_version, :required_rubygems_version attr_accessor :source, :remote, :force_ruby_platform, :dependencies, :required_ruby_version, :required_rubygems_version
alias_method :runtime_dependencies, :dependencies
def self.from_spec(s) def self.from_spec(s)
lazy_spec = new(s.name, s.version, s.platform, s.source) lazy_spec = new(s.name, s.version, s.platform, s.source)
lazy_spec.dependencies = s.dependencies lazy_spec.dependencies = s.dependencies

View File

@ -88,6 +88,10 @@ module Bundler
end end
end end
def runtime_dependencies
dependencies.select(&:runtime?)
end
def git_version def git_version
return unless loaded_from && source.is_a?(Bundler::Source::Git) return unless loaded_from && source.is_a?(Bundler::Source::Git)
" #{source.revision[0..6]}" " #{source.revision[0..6]}"

View File

@ -121,7 +121,7 @@ module Bundler
source = Bundler::Source::Rubygems.new("remotes" => "https://rubygems.org") source = Bundler::Source::Rubygems.new("remotes" => "https://rubygems.org")
source.remote! source.remote!
source.add_dependency_names("bundler") source.add_dependency_names("bundler")
source.specs source.specs.select(&:matches_current_metadata?)
end end
end end

View File

@ -37,7 +37,7 @@ module Bundler
specs_for_dep.first.dependencies.each do |d| specs_for_dep.first.dependencies.each do |d|
next if d.type == :development next if d.type == :development
incomplete = true if d.name != "bundler" && lookup[d.name].empty? incomplete = true if d.name != "bundler" && lookup[d.name].nil?
deps << [d, dep[1]] deps << [d, dep[1]]
end end
else else
@ -45,7 +45,7 @@ module Bundler
end end
if incomplete && check if incomplete && check
@incomplete_specs += lookup[name].any? ? lookup[name] : [LazySpecification.new(name, nil, nil)] @incomplete_specs += lookup[name] || [LazySpecification.new(name, nil, nil)]
end end
end end
@ -64,7 +64,9 @@ module Bundler
valid_platform = lookup.all? do |_, specs| valid_platform = lookup.all? do |_, specs|
spec = specs.first spec = specs.first
matching_specs = spec.source.specs.search([spec.name, spec.version]) matching_specs = spec.source.specs.search([spec.name, spec.version])
platform_spec = GemHelpers.select_best_platform_match(matching_specs, platform).find(&:matches_current_metadata?) platform_spec = GemHelpers.select_best_platform_match(matching_specs, platform).find do |s|
s.matches_current_metadata? && valid_dependencies?(s)
end
if platform_spec if platform_spec
new_specs << LazySpecification.from_spec(platform_spec) new_specs << LazySpecification.from_spec(platform_spec)
@ -90,9 +92,20 @@ module Bundler
platforms platforms
end end
def validate_deps(s)
s.runtime_dependencies.each do |dep|
next if dep.name == "bundler"
return :missing unless names.include?(dep.name)
return :invalid if none? {|spec| dep.matches_spec?(spec) }
end
:valid
end
def [](key) def [](key)
key = key.name if key.respond_to?(:name) key = key.name if key.respond_to?(:name)
lookup[key].reverse lookup[key]&.reverse || []
end end
def []=(key, value) def []=(key, value)
@ -167,7 +180,7 @@ module Bundler
end end
def what_required(spec) def what_required(spec)
unless req = find {|s| s.dependencies.any? {|d| d.type == :runtime && d.name == spec.name } } unless req = find {|s| s.runtime_dependencies.any? {|d| d.name == spec.name } }
return [spec] return [spec]
end end
what_required(req) << spec what_required(req) << spec
@ -193,8 +206,16 @@ module Bundler
sorted.each(&b) sorted.each(&b)
end end
def names
lookup.keys
end
private private
def valid_dependencies?(s)
validate_deps(s) == :valid
end
def sorted def sorted
rake = @specs.find {|s| s.name == "rake" } rake = @specs.find {|s| s.name == "rake" }
begin begin
@ -213,8 +234,9 @@ module Bundler
def lookup def lookup
@lookup ||= begin @lookup ||= begin
lookup = Hash.new {|h, k| h[k] = [] } lookup = {}
@specs.each do |s| @specs.each do |s|
lookup[s.name] ||= []
lookup[s.name] << s lookup[s.name] << s
end end
lookup lookup
@ -228,6 +250,8 @@ module Bundler
def specs_for_dependency(dep, platform) def specs_for_dependency(dep, platform)
specs_for_name = lookup[dep.name] specs_for_name = lookup[dep.name]
return [] unless specs_for_name
matching_specs = if dep.force_ruby_platform matching_specs = if dep.force_ruby_platform
GemHelpers.force_ruby_platform(specs_for_name) GemHelpers.force_ruby_platform(specs_for_name)
else else
@ -240,7 +264,11 @@ module Bundler
def tsort_each_child(s) def tsort_each_child(s)
s.dependencies.sort_by(&:name).each do |d| s.dependencies.sort_by(&:name).each do |d|
next if d.type == :development next if d.type == :development
lookup[d.name].each {|s2| yield s2 }
specs_for_name = lookup[d.name]
next unless specs_for_name
specs_for_name.each {|s2| yield s2 }
end end
end end
end end

View File

@ -1,5 +1,4 @@
require_relative "basic" require_relative "basic"
require_relative "lcs_diff"
class Bundler::Thor class Bundler::Thor
module Shell module Shell
@ -7,8 +6,6 @@ class Bundler::Thor
# Bundler::Thor::Shell::Basic to see all available methods. # Bundler::Thor::Shell::Basic to see all available methods.
# #
class Color < Basic class Color < Basic
include LCSDiff
# Embed in a String to clear all previous ANSI sequences. # Embed in a String to clear all previous ANSI sequences.
CLEAR = "\e[0m" CLEAR = "\e[0m"
# The start of an ANSI bold sequence. # The start of an ANSI bold sequence.

View File

@ -1,5 +1,4 @@
require_relative "basic" require_relative "basic"
require_relative "lcs_diff"
class Bundler::Thor class Bundler::Thor
module Shell module Shell
@ -7,8 +6,6 @@ class Bundler::Thor
# Bundler::Thor::Shell::Basic to see all available methods. # Bundler::Thor::Shell::Basic to see all available methods.
# #
class HTML < Basic class HTML < Basic
include LCSDiff
# The start of an HTML bold sequence. # The start of an HTML bold sequence.
BOLD = "font-weight: bold" BOLD = "font-weight: bold"

View File

@ -1,49 +0,0 @@
module LCSDiff
protected
# Overwrite show_diff to show diff with colors if Diff::LCS is
# available.
def show_diff(destination, content) #:nodoc:
if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
actual = File.binread(destination).to_s.split("\n")
content = content.to_s.split("\n")
Diff::LCS.sdiff(actual, content).each do |diff|
output_diff_line(diff)
end
else
super
end
end
private
def output_diff_line(diff) #:nodoc:
case diff.action
when "-"
say "- #{diff.old_element.chomp}", :red, true
when "+"
say "+ #{diff.new_element.chomp}", :green, true
when "!"
say "- #{diff.old_element.chomp}", :red, true
say "+ #{diff.new_element.chomp}", :green, true
else
say " #{diff.old_element.chomp}", nil, true
end
end
# Check if Diff::LCS is loaded. If it is, use it to create pretty output
# for diff.
def diff_lcs_loaded? #:nodoc:
return true if defined?(Diff::LCS)
return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
@diff_lcs_loaded = begin
require "diff/lcs"
true
rescue LoadError
false
end
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: false # frozen_string_literal: false
module Bundler module Bundler
VERSION = "2.5.1".freeze VERSION = "2.5.2".freeze
def self.bundler_major_version def self.bundler_major_version
@bundler_major_version ||= VERSION.split(".").first.to_i @bundler_major_version ||= VERSION.split(".").first.to_i

View File

@ -9,7 +9,7 @@
require "rbconfig" require "rbconfig"
module Gem module Gem
VERSION = "3.5.1" VERSION = "3.5.2"
end end
# Must be first since it unloads the prelude from 1.9.2 # Must be first since it unloads the prelude from 1.9.2
@ -942,6 +942,13 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
end].compact.uniq end].compact.uniq
end end
##
# Suffixes for dynamic library require-able paths.
def self.dynamic_library_suffixes
@dynamic_library_suffixes ||= suffixes - [".rb"]
end
## ##
# Prints the amount of time the supplied block takes to run using the debug # Prints the amount of time the supplied block takes to run using the debug
# UI output. # UI output.

View File

@ -84,8 +84,14 @@ class Gem::BasicSpecification
return false return false
end end
is_soext = file.end_with?(".so", ".o")
if is_soext
have_file? file.delete_suffix(File.extname(file)), Gem.dynamic_library_suffixes
else
have_file? file, Gem.suffixes have_file? file, Gem.suffixes
end end
end
def default_gem? def default_gem?
loaded_from && loaded_from &&

View File

@ -268,7 +268,7 @@ class Gem::Package
tar.add_file_simple file, stat.mode, stat.size do |dst_io| tar.add_file_simple file, stat.mode, stat.size do |dst_io|
File.open file, "rb" do |src_io| File.open file, "rb" do |src_io|
dst_io.write src_io.read 16_384 until src_io.eof? copy_stream(src_io, dst_io)
end end
end end
end end
@ -453,7 +453,7 @@ EOM
end end
if entry.file? if entry.file?
File.open(destination, "wb") {|out| out.write entry.read } File.open(destination, "wb") {|out| copy_stream(entry, out) }
FileUtils.chmod file_mode(entry.header.mode), destination FileUtils.chmod file_mode(entry.header.mode), destination
end end
@ -714,6 +714,16 @@ EOM
rescue Zlib::GzipFile::Error => e rescue Zlib::GzipFile::Error => e
raise Gem::Package::FormatError.new(e.message, entry.full_name) raise Gem::Package::FormatError.new(e.message, entry.full_name)
end end
if RUBY_ENGINE == "truffleruby"
def copy_stream(src, dst) # :nodoc:
dst.write src.read
end
else
def copy_stream(src, dst) # :nodoc:
IO.copy_stream(src, dst)
end
end
end end
require_relative "package/digest_io" require_relative "package/digest_io"

View File

@ -30,7 +30,7 @@ class Gem::Request
@uri = uri @uri = uri
@request_class = request_class @request_class = request_class
@last_modified = last_modified @last_modified = last_modified
@requests = Hash.new 0 @requests = Hash.new(0).compare_by_identity
@user_agent = user_agent @user_agent = user_agent
@connection_pool = pool @connection_pool = pool
@ -196,7 +196,7 @@ class Gem::Request
bad_response = false bad_response = false
begin begin
@requests[connection.object_id] += 1 @requests[connection] += 1
verbose "#{request.method} #{Gem::Uri.redact(@uri)}" verbose "#{request.method} #{Gem::Uri.redact(@uri)}"
@ -247,7 +247,7 @@ class Gem::Request
rescue EOFError, Gem::Timeout::Error, rescue EOFError, Gem::Timeout::Error,
Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
requests = @requests[connection.object_id] requests = @requests[connection]
verbose "connection reset after #{requests} requests, retrying" verbose "connection reset after #{requests} requests, retrying"
raise Gem::RemoteFetcher::FetchError.new("too many connection resets", @uri) if retried raise Gem::RemoteFetcher::FetchError.new("too many connection resets", @uri) if retried
@ -267,7 +267,7 @@ class Gem::Request
# Resets HTTP connection +connection+. # Resets HTTP connection +connection+.
def reset(connection) def reset(connection)
@requests.delete connection.object_id @requests.delete connection
connection.finish connection.finish
connection.start connection.start

View File

@ -121,10 +121,7 @@ RSpec.describe Bundler::Definition do
gem "foo", :path => "#{lib_path("foo")}" gem "foo", :path => "#{lib_path("foo")}"
G G
bundle :check, env: { "DEBUG" => "1" } expected_lockfile = <<~G
expect(out).to match(/using resolution from the lockfile/)
expect(lockfile).to eq <<~G
PATH PATH
remote: #{lib_path("foo")} remote: #{lib_path("foo")}
specs: specs:
@ -145,6 +142,13 @@ RSpec.describe Bundler::Definition do
BUNDLED WITH BUNDLED WITH
#{Bundler::VERSION} #{Bundler::VERSION}
G G
expect(lockfile).to eq(expected_lockfile)
bundle :check, env: { "DEBUG" => "1" }
expect(out).to match(/using resolution from the lockfile/)
expect(lockfile).to eq(expected_lockfile)
end end
it "for a locked gem for another platform" do it "for a locked gem for another platform" do

View File

@ -1371,26 +1371,28 @@ RSpec.describe "bundle update --bundler" do
expect(the_bundle).to include_gem "rack 1.0" expect(the_bundle).to include_gem "rack 1.0"
end end
it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo, :realworld do it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo do
pristine_system_gems "bundler-2.3.9" pristine_system_gems "bundler-2.3.9"
build_repo4 do build_repo4 do
build_gem "rack", "1.0" build_gem "rack", "1.0"
build_bundler "999.0.0"
end end
install_gemfile <<-G, env: { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" } install_gemfile <<-G
source "#{file_uri_for(gem_repo4)}" source "#{file_uri_for(gem_repo4)}"
gem "rack" gem "rack"
G G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9") lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9")
bundle :update, bundler: true, artifice: "vcr", verbose: true, env: { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" } bundle :update, bundler: true, artifice: "compact_index", verbose: true, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
# Only updates properly on modern RubyGems. # Only updates properly on modern RubyGems.
if Gem.rubygems_version >= Gem::Version.new("3.3.0.dev") if Gem.rubygems_version >= Gem::Version.new("3.3.0.dev")
expect(out).to include("Updating bundler to 2.3.10") expect(out).to include("Updating bundler to 999.0.0")
expect(out).to include("Using bundler 2.3.10") expect(out).to include("Using bundler 999.0.0")
expect(out).not_to include("Installing Bundler 2.3.9 and restarting using that version.") expect(out).not_to include("Installing Bundler 2.3.9 and restarting using that version.")
expect(lockfile).to eq <<~L expect(lockfile).to eq <<~L
@ -1406,16 +1408,63 @@ RSpec.describe "bundle update --bundler" do
rack rack
BUNDLED WITH BUNDLED WITH
2.3.10 999.0.0
L L
expect(the_bundle).to include_gems "bundler 2.3.10" expect(the_bundle).to include_gems "bundler 999.0.0"
expect(the_bundle).to include_gems "rack 1.0"
else
# Old RubyGems versions do not trampoline but they still change BUNDLED
# WITH to the latest bundler version. This means the below check fails
# because it tries to use bundler 999.0.0 which did not get installed.
# Workaround the bug by forcing the version we know is installed.
expect(the_bundle).to include_gems "rack 1.0", env: { "BUNDLER_VERSION" => "2.3.9" }
end
end end
it "does not update the bundler version in the lockfile if the latest version is not compatible with current ruby", :ruby_repo do
pristine_system_gems "bundler-2.3.9"
build_repo4 do
build_gem "rack", "1.0"
build_bundler "2.3.9"
build_bundler "999.0.0" do |s|
s.required_ruby_version = "> #{Gem.ruby_version}"
end
end
install_gemfile <<-G, env: { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
source "#{file_uri_for(gem_repo4)}"
gem "rack"
G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9")
bundle :update, bundler: true, artifice: "compact_index", verbose: true, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
expect(out).to include("Using bundler 2.3.9")
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
rack (1.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
rack
BUNDLED WITH
2.3.9
L
expect(the_bundle).to include_gems "bundler 2.3.9"
expect(the_bundle).to include_gems "rack 1.0" expect(the_bundle).to include_gems "rack 1.0"
end end
it "errors if the explicit target version does not exist", :realworld do it "errors if the explicit target version does not exist" do
pristine_system_gems "bundler-2.3.9" pristine_system_gems "bundler-2.3.9"
build_repo4 do build_repo4 do
@ -1428,7 +1477,7 @@ RSpec.describe "bundle update --bundler" do
G G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9") lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9")
bundle :update, bundler: "999.999.999", artifice: "vcr", raise_on_error: false bundle :update, bundler: "999.999.999", artifice: "compact_index", raise_on_error: false
# Only gives a meaningful error message on modern RubyGems. # Only gives a meaningful error message on modern RubyGems.

View File

@ -1220,6 +1220,48 @@ RSpec.describe "bundle install with specific platforms" do
end end
end end
it "does not add ruby platform gem if it brings extra dependencies not resolved originally" do
build_repo4 do
build_gem "nokogiri", "1.15.5" do |s|
s.add_dependency "mini_portile2", "~> 2.8.2"
end
build_gem "nokogiri", "1.15.5" do |s|
s.platform = "x86_64-linux"
end
end
gemfile <<~G
source "#{file_uri_for(gem_repo4)}"
gem "nokogiri"
G
checksums = checksums_section_when_existing do |c|
c.checksum gem_repo4, "nokogiri", "1.15.5", "x86_64-linux"
end
simulate_platform "x86_64-linux" do
bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
expect(lockfile).to eq(<<~L)
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
nokogiri (1.15.5-x86_64-linux)
PLATFORMS
x86_64-linux
DEPENDENCIES
nokogiri
#{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
end
end
private private
def setup_multiplatform_gem def setup_multiplatform_gem

View File

@ -1603,7 +1603,7 @@ RSpec.describe "the lockfile format" do
L L
bundle "install --verbose" bundle "install --verbose"
expect(out).to include("re-resolving dependencies because your lock file is missing \"minitest-bisect\"") expect(out).to include("re-resolving dependencies because your lock file includes \"minitest-bisect\" but not some of its dependencies")
expect(lockfile).to eq <<~L expect(lockfile).to eq <<~L
GEM GEM

View File

@ -219,140 +219,6 @@ RSpec.describe "real world edgecases", realworld: true do
expect(err).to include("resque-scheduler 2.2.0 includes a gemspec with `require_paths` set to an array of arrays. Newer versions of this gem might've already fixed this").once expect(err).to include("resque-scheduler 2.2.0 includes a gemspec with `require_paths` set to an array of arrays. Newer versions of this gem might've already fixed this").once
end end
it "doesn't hang on big gemfile" do
skip "Only for ruby 2.7" unless RUBY_VERSION.start_with?("2.7")
gemfile <<~G
# frozen_string_literal: true
source "https://rubygems.org"
ruby "~> 2.7.7"
gem "rails"
gem "pg", ">= 0.18", "< 2.0"
gem "goldiloader"
gem "awesome_nested_set"
gem "circuitbox"
gem "passenger"
gem "globalid"
gem "rack-cors"
gem "rails-pg-extras"
gem "linear_regression_trend"
gem "rack-protection"
gem "pundit"
gem "remote_ip_proxy_scrubber"
gem "bcrypt"
gem "searchkick"
gem "excon"
gem "faraday_middleware-aws-sigv4"
gem "typhoeus"
gem "sidekiq"
gem "sidekiq-undertaker"
gem "sidekiq-cron"
gem "storext"
gem "appsignal"
gem "fcm"
gem "business_time"
gem "tzinfo"
gem "holidays"
gem "bigdecimal"
gem "progress_bar"
gem "redis"
gem "hiredis"
gem "state_machines"
gem "state_machines-audit_trail"
gem "state_machines-activerecord"
gem "interactor"
gem "ar_transaction_changes"
gem "redis-rails"
gem "seed_migration"
gem "lograge"
gem "graphiql-rails", group: :development
gem "graphql"
gem "pusher"
gem "rbnacl"
gem "jwt"
gem "json-schema"
gem "discard"
gem "money"
gem "strip_attributes"
gem "validates_email_format_of"
gem "audited"
gem "concurrent-ruby"
gem "with_advisory_lock"
group :test do
gem "rspec-sidekiq"
gem "simplecov", require: false
end
group :development, :test do
gem "byebug", platform: :mri
gem "guard"
gem "guard-bundler"
gem "guard-rspec"
gem "rb-fsevent"
gem "rspec_junit_formatter"
gem "rspec-collection_matchers"
gem "rspec-rails"
gem "rspec-retry"
gem "state_machines-rspec"
gem "dotenv-rails"
gem "database_cleaner-active_record"
gem "database_cleaner-redis"
gem "timecop"
end
gem "factory_bot_rails"
gem "faker"
group :development do
gem "listen"
gem "sql_queries_count"
gem "rubocop"
gem "rubocop-performance"
gem "rubocop-rspec"
gem "rubocop-rails"
gem "brakeman"
gem "bundler-audit"
gem "solargraph"
gem "annotate"
end
G
if Bundler.feature_flag.bundler_3_mode?
# Conflicts on bundler version, so we count attempts differently
bundle :lock, env: { "DEBUG_RESOLVER" => "1" }, raise_on_error: false
expect(out.split("\n").grep(/backtracking to/).count).to eq(8)
else
bundle :lock, env: { "DEBUG_RESOLVER" => "1" }
expect(out).to include("Solution found after 7 attempts")
end
end
it "doesn't hang on tricky gemfile" do
skip "Only for ruby 2.7" unless RUBY_VERSION.start_with?("2.7")
gemfile <<~G
source 'https://rubygems.org'
group :development do
gem "puppet-module-posix-default-r2.7", '~> 0.3'
gem "puppet-module-posix-dev-r2.7", '~> 0.3'
gem "beaker-rspec"
gem "beaker-puppet"
gem "beaker-docker"
gem "beaker-puppet_install_helper"
gem "beaker-module_install_helper"
end
G
bundle :lock, env: { "DEBUG_RESOLVER" => "1" }
expect(out).to include("Solution found after 6 attempts")
end
it "doesn't hang on nix gemfile" do it "doesn't hang on nix gemfile" do
skip "Only for ruby 3.0" unless RUBY_VERSION.start_with?("3.0") skip "Only for ruby 3.0" unless RUBY_VERSION.start_with?("3.0")

View File

@ -30,4 +30,107 @@ RSpec.describe "bundle install with complex dependencies", realworld: true do
expect { bundle "lock" }.to take_less_than(30) # seconds expect { bundle "lock" }.to take_less_than(30) # seconds
end end
it "resolves big gemfile quickly" do
gemfile <<~G
# frozen_string_literal: true
source "https://rubygems.org"
gem "rails"
gem "pg", ">= 0.18", "< 2.0"
gem "goldiloader"
gem "awesome_nested_set"
gem "circuitbox"
gem "passenger"
gem "globalid"
gem "rack-cors"
gem "rails-pg-extras"
gem "linear_regression_trend"
gem "rack-protection"
gem "pundit"
gem "remote_ip_proxy_scrubber"
gem "bcrypt"
gem "searchkick"
gem "excon"
gem "faraday_middleware-aws-sigv4"
gem "typhoeus"
gem "sidekiq"
gem "sidekiq-undertaker"
gem "sidekiq-cron"
gem "storext"
gem "appsignal"
gem "fcm"
gem "business_time"
gem "tzinfo"
gem "holidays"
gem "bigdecimal"
gem "progress_bar"
gem "redis"
gem "hiredis"
gem "state_machines"
gem "state_machines-audit_trail"
gem "state_machines-activerecord"
gem "interactor"
gem "ar_transaction_changes"
gem "redis-rails"
gem "seed_migration"
gem "lograge"
gem "graphiql-rails", group: :development
gem "graphql"
gem "pusher"
gem "rbnacl"
gem "jwt"
gem "json-schema"
gem "discard"
gem "money"
gem "strip_attributes"
gem "validates_email_format_of"
gem "audited"
gem "concurrent-ruby"
gem "with_advisory_lock"
group :test do
gem "rspec-sidekiq"
gem "simplecov", require: false
end
group :development, :test do
gem "byebug", platform: :mri
gem "guard"
gem "guard-bundler"
gem "guard-rspec"
gem "rb-fsevent"
gem "rspec_junit_formatter"
gem "rspec-collection_matchers"
gem "rspec-rails"
gem "rspec-retry"
gem "state_machines-rspec"
gem "dotenv-rails"
gem "database_cleaner-active_record"
gem "database_cleaner-redis"
gem "timecop"
end
gem "factory_bot_rails"
gem "faker"
group :development do
gem "listen"
gem "sql_queries_count"
gem "rubocop"
gem "rubocop-performance"
gem "rubocop-rspec"
gem "rubocop-rails"
gem "brakeman"
gem "bundler-audit"
gem "solargraph"
gem "annotate"
end
G
expect do
bundle "lock", env: { "DEBUG_RESOLVER" => "1" }, raise_on_error: !Bundler.feature_flag.bundler_3_mode?
end.to take_less_than(30) # seconds
end
end end

View File

@ -77,7 +77,7 @@ class CompactIndexAPI < Endpoint
specs.group_by(&:name).map do |name, versions| specs.group_by(&:name).map do |name, versions|
gem_versions = versions.map do |spec| gem_versions = versions.map do |spec|
deps = spec.dependencies.select {|d| d.type == :runtime }.map do |d| deps = spec.runtime_dependencies.map do |d|
reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ") reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ")
CompactIndex::Dependency.new(d.name, reqs) CompactIndex::Dependency.new(d.name, reqs)
end end

View File

@ -72,7 +72,7 @@ class Endpoint < Sinatra::Base
name: spec.name, name: spec.name,
number: spec.version.version, number: spec.version.version,
platform: spec.platform.to_s, platform: spec.platform.to_s,
dependencies: spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep| dependencies: spec.runtime_dependencies.map do |dep|
[dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")] [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")]
end, end,
} }

View File

@ -297,6 +297,10 @@ module Spec
build_with(LibBuilder, name, args, &blk) build_with(LibBuilder, name, args, &blk)
end end
def build_bundler(*args, &blk)
build_with(BundlerBuilder, "bundler", args, &blk)
end
def build_gem(name, *args, &blk) def build_gem(name, *args, &blk)
build_with(GemBuilder, name, args, &blk) build_with(GemBuilder, name, args, &blk)
end end
@ -402,6 +406,49 @@ module Spec
alias_method :dep, :runtime alias_method :dep, :runtime
end end
class BundlerBuilder
attr_writer :required_ruby_version
def initialize(context, name, version)
raise "can only build bundler" unless name == "bundler"
@context = context
@version = version || Bundler::VERSION
end
def _build(options = {})
full_name = "bundler-#{@version}"
build_path = @context.tmp + full_name
bundler_path = build_path + "#{full_name}.gem"
Dir.mkdir build_path
@context.shipped_files.each do |shipped_file|
target_shipped_file = shipped_file
target_shipped_file = shipped_file.sub(/\Alibexec/, "exe") if @context.ruby_core?
target_shipped_file = build_path + target_shipped_file
target_shipped_dir = File.dirname(target_shipped_file)
FileUtils.mkdir_p target_shipped_dir unless File.directory?(target_shipped_dir)
FileUtils.cp shipped_file, target_shipped_file, preserve: true
end
@context.replace_version_file(@version, dir: build_path)
@context.replace_required_ruby_version(@required_ruby_version, dir: build_path) if @required_ruby_version
Spec::BuildMetadata.write_build_metadata(dir: build_path)
@context.gem_command "build #{@context.relative_gemspec}", dir: build_path
if block_given?
yield(bundler_path)
else
FileUtils.mv bundler_path, options[:path]
end
ensure
build_path.rmtree
end
end
class LibBuilder class LibBuilder
def initialize(context, name, version) def initialize(context, name, version)
@context = context @context = context

View File

@ -326,34 +326,8 @@ module Spec
gem_command "install #{args} '#{path}'" gem_command "install #{args} '#{path}'"
end end
def with_built_bundler(version = nil) def with_built_bundler(version = nil, &block)
version ||= Bundler::VERSION Builders::BundlerBuilder.new(self, "bundler", version)._build(&block)
full_name = "bundler-#{version}"
build_path = tmp + full_name
bundler_path = build_path + "#{full_name}.gem"
Dir.mkdir build_path
begin
shipped_files.each do |shipped_file|
target_shipped_file = shipped_file
target_shipped_file = shipped_file.sub(/\Alibexec/, "exe") if ruby_core?
target_shipped_file = build_path + target_shipped_file
target_shipped_dir = File.dirname(target_shipped_file)
FileUtils.mkdir_p target_shipped_dir unless File.directory?(target_shipped_dir)
FileUtils.cp shipped_file, target_shipped_file, preserve: true
end
replace_version_file(version, dir: build_path)
Spec::BuildMetadata.write_build_metadata(dir: build_path)
gem_command "build #{relative_gemspec}", dir: build_path
yield(bundler_path)
ensure
build_path.rmtree
end
end end
def with_gem_path_as(path) def with_gem_path_as(path)

View File

@ -103,7 +103,21 @@ module Spec
actual.call actual.call
(Time.now - start_time).to_f < seconds actual_time = (Time.now - start_time).to_f
acceptable = actual_time < seconds
@errors = ["took #{actual_time} seconds"] unless acceptable
acceptable
end
failure_message do
super() + " but:\n" + @errors.map {|e| indent(e) }.join("\n")
end
failure_message_when_negated do
super() + " but:\n" + @errors.map {|e| indent(e) }.join("\n")
end end
supports_block_expectations supports_block_expectations

View File

@ -250,6 +250,13 @@ module Spec
File.open(version_file, "w") {|f| f << contents } File.open(version_file, "w") {|f| f << contents }
end end
def replace_required_ruby_version(version, dir:)
gemspec_file = File.expand_path("bundler.gemspec", dir)
contents = File.read(gemspec_file)
contents.sub!(/(^\s+s\.required_ruby_version\s*=\s*)"[^"]+"/, %(\\1"#{version}"))
File.open(gemspec_file, "w") {|f| f << contents }
end
def ruby_core? def ruby_core?
# avoid to warnings # avoid to warnings
@ruby_core ||= nil @ruby_core ||= nil

View File

@ -152,18 +152,18 @@ dependencies = [
[[package]] [[package]]
name = "rb-sys" name = "rb-sys"
version = "0.9.83" version = "0.9.84"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e5b8d560b60790a3e60e56e73a8c7be88ac14e6af39fc82b5eca72c71753840" checksum = "3def04a96a36ef8681a2b2e26c01683b93a8630175c845fa06cab76c5a8c7ce0"
dependencies = [ dependencies = [
"rb-sys-build", "rb-sys-build",
] ]
[[package]] [[package]]
name = "rb-sys-build" name = "rb-sys-build"
version = "0.9.83" version = "0.9.84"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2d2bfd00002007d7e9ad93d0397437933040caf452d260c26dbef5fd95ae1a6" checksum = "c017d134afd764dd43c2faa91aa50b698a3bb4ff30e83113da483c789e74be8c"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"lazy_static", "lazy_static",

View File

@ -7,4 +7,4 @@ edition = "2021"
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
rb-sys = "0.9.83" rb-sys = "0.9.84"

View File

@ -1561,6 +1561,17 @@ dependencies: []
assert_empty err assert_empty err
end end
def test_contains_requirable_file_extension_soext
ext_spec
dlext = RbConfig::CONFIG["DLEXT"]
@ext.files += ["lib/ext.#{dlext}"]
FileUtils.mkdir_p @ext.extension_dir
FileUtils.touch File.join(@ext.extension_dir, "ext.#{dlext}")
FileUtils.touch File.join(@ext.extension_dir, "gem.build_complete")
assert @ext.contains_requirable_file? "ext.so"
end
def test_date def test_date
assert_date @a1.date assert_date @a1.date
end end