From ac526abcd6d82545b8dc3586efb55d75f45f7417 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 5 Feb 2024 23:51:04 +0900 Subject: [PATCH] Merge RubyGems 3.5.5 and Bundler 2.5.5 (#9676) * Merge RubyGems-3.5.4 and Bundler-2.5.4 * Merge RubyGems-3.5.5 and Bundler-2.5.5 * Make tests play with upstream Ruby tests CI broke in https://github.com/ruby/ruby/pull/9604 because if any Ruby tests run `require 'net/http'`, they will pollute the `$LOADED_FEATURES` for the RubyGems tests. We can fix this by renaming the test default gem from `net-http` to `my-http`. See https://github.com/rubygems/rubygems/pull/7379#issuecomment-1901241299 for more details. --------- Co-authored-by: Stan Hu --- lib/bundler/compact_index_client/updater.rb | 8 +- lib/bundler/definition.rb | 12 +-- lib/bundler/dsl.rb | 14 ++-- lib/bundler/man/bundle-config.1 | 4 +- lib/bundler/man/bundle-config.1.ronn | 4 +- lib/bundler/spec_set.rb | 81 ++++++++++++------- lib/bundler/version.rb | 2 +- lib/rubygems.rb | 9 ++- lib/rubygems/commands/update_command.rb | 8 +- lib/rubygems/core_ext/kernel_require.rb | 13 ++- lib/rubygems/defaults.rb | 2 +- lib/rubygems/specification.rb | 2 +- .../compact_index_client/updater_spec.rb | 24 +++--- spec/bundler/commands/install_spec.rb | 29 +++++++ spec/bundler/commands/update_spec.rb | 2 +- .../install/gems/compact_index_spec.rb | 14 ++-- spec/bundler/lock/lockfile_spec.rb | 40 +++++++++ spec/bundler/support/artifice/endpoint_500.rb | 2 +- .../support/artifice/helpers/compact_index.rb | 3 +- .../support/artifice/helpers/endpoint.rb | 2 +- spec/bundler/support/artifice/windows.rb | 2 +- spec/bundler/support/rubygems_ext.rb | 2 +- test/rubygems/helper.rb | 45 +++++------ .../test_gem_commands_update_command.rb | 34 ++++++-- .../ext/custom_name_lib/Cargo.lock | 8 +- .../ext/custom_name_lib/Cargo.toml | 2 +- .../rust_ruby_example/Cargo.lock | 8 +- .../rust_ruby_example/Cargo.toml | 2 +- test/rubygems/test_require.rb | 70 ++++++++++++++++ tool/bundler/dev_gems.rb | 2 +- tool/bundler/test_gems.rb | 1 + 31 files changed, 323 insertions(+), 128 deletions(-) diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb index 84a606dc34..36f6b81db8 100644 --- a/lib/bundler/compact_index_client/updater.rb +++ b/lib/bundler/compact_index_client/updater.rb @@ -42,7 +42,7 @@ module Bundler else file.write(response.body) end - CacheFile.write(etag_path, etag(response)) + CacheFile.write(etag_path, etag_from_response(response)) true end end @@ -53,13 +53,13 @@ module Bundler response = @fetcher.call(remote_path, request_headers(etag)) return true if response.is_a?(Gem::Net::HTTPNotModified) CacheFile.write(local_path, response.body, parse_digests(response)) - CacheFile.write(etag_path, etag(response)) + CacheFile.write(etag_path, etag_from_response(response)) end def request_headers(etag, range_start = nil) headers = {} headers["Range"] = "bytes=#{range_start}-" if range_start - headers["If-None-Match"] = etag if etag + headers["If-None-Match"] = %("#{etag}") if etag headers end @@ -77,7 +77,7 @@ module Bundler etag end - def etag(response) + def etag_from_response(response) return unless response["ETag"] etag = response["ETag"].delete_prefix("W/") return if etag.delete_prefix!('"') && !etag.delete_suffix!('"') diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 9b905db1f9..0b0e63f77e 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -312,10 +312,6 @@ module Bundler end end - def should_complete_platforms? - !lockfile_exists? && generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform] - end - def spec_git_paths sources.git_sources.map {|s| File.realpath(s.path) if File.exist?(s.path) }.compact end @@ -517,6 +513,10 @@ module Bundler private + def should_add_extra_platforms? + !lockfile_exists? && generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform] + end + def lockfile_exists? lockfile && File.exist?(lockfile) end @@ -600,7 +600,9 @@ module Bundler result = SpecSet.new(resolver.start) @resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version - @platforms = result.complete_platforms!(platforms) if should_complete_platforms? + @platforms = result.add_extra_platforms!(platforms) if should_add_extra_platforms? + + result.complete_platforms!(platforms) SpecSet.new(result.for(dependencies, false, @platforms)) end diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index 1eca749617..1460b9f52f 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -102,9 +102,6 @@ module Bundler # if there's already a dependency with this name we try to prefer one if current = @dependencies.find {|d| d.name == dep.name } - # Always prefer the dependency from the Gemfile - @dependencies.delete(current) if current.gemspec_dev_dep? - if current.requirement != dep.requirement current_requirement_open = current.requirements_list.include?(">= 0") @@ -116,8 +113,6 @@ module Bundler Bundler.ui.warn "A gemspec development dependency (#{gemspec_dep.name}, #{gemspec_dep.requirement}) is being overridden by a Gemfile dependency (#{gemfile_dep.name}, #{gemfile_dep.requirement}).\n" \ "This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement\n" end - - return if dep.gemspec_dev_dep? else update_prompt = "" @@ -135,8 +130,13 @@ module Bundler "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \ "#{update_prompt}" end - elsif current.gemspec_dev_dep? || dep.gemspec_dev_dep? - return if dep.gemspec_dev_dep? + end + + # Always prefer the dependency from the Gemfile + if current.gemspec_dev_dep? + @dependencies.delete(current) + elsif dep.gemspec_dev_dep? + return elsif current.source != dep.source raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \ "You specified that #{dep.name} (#{dep.requirement}) should come from " \ diff --git a/lib/bundler/man/bundle-config.1 b/lib/bundler/man/bundle-config.1 index 8090e03f7d..c5a976da46 100644 --- a/lib/bundler/man/bundle-config.1 +++ b/lib/bundler/man/bundle-config.1 @@ -302,9 +302,9 @@ Note that any configured credentials will be redacted by informative commands su .P Also note that to guarantee a sane mapping between valid environment variable names and valid host names, bundler makes the following transformations: .IP "\(bu" 4 -Any \fB\-\fR characters in a host name are mapped to a triple dash (\fB___\fR) in the corresponding environment variable\. +Any \fB\-\fR characters in a host name are mapped to a triple underscore (\fB___\fR) in the corresponding environment variable\. .IP "\(bu" 4 -Any \fB\.\fR characters in a host name are mapped to a double dash (\fB__\fR) in the corresponding environment variable\. +Any \fB\.\fR characters in a host name are mapped to a double underscore (\fB__\fR) in the corresponding environment variable\. .IP "" 0 .P This means that if you have a gem server named \fBmy\.gem\-host\.com\fR, you'll need to use the \fBBUNDLE_MY__GEM___HOST__COM\fR variable to configure credentials for it through ENV\. diff --git a/lib/bundler/man/bundle-config.1.ronn b/lib/bundler/man/bundle-config.1.ronn index b935329b4e..587d31dbad 100644 --- a/lib/bundler/man/bundle-config.1.ronn +++ b/lib/bundler/man/bundle-config.1.ronn @@ -388,10 +388,10 @@ copy-pasting bundler output. Also note that to guarantee a sane mapping between valid environment variable names and valid host names, bundler makes the following transformations: -* Any `-` characters in a host name are mapped to a triple dash (`___`) in the +* Any `-` characters in a host name are mapped to a triple underscore (`___`) in the corresponding environment variable. -* Any `.` characters in a host name are mapped to a double dash (`__`) in the +* Any `.` characters in a host name are mapped to a double underscore (`__`) in the corresponding environment variable. This means that if you have a gem server named `my.gem-host.com`, you'll need to diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index ceaac2cec5..cc649abaf8 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -52,32 +52,14 @@ module Bundler specs.uniq end - def complete_platforms!(platforms) + def add_extra_platforms!(platforms) return platforms.concat([Gem::Platform::RUBY]).uniq if @specs.empty? - new_platforms = @specs.flat_map {|spec| spec.source.specs.search([spec.name, spec.version]).map(&:platform) }.uniq.select do |platform| + new_platforms = all_platforms.select do |platform| next if platforms.include?(platform) next unless GemHelpers.generic(platform) == Gem::Platform::RUBY - new_specs = [] - - valid_platform = lookup.all? do |_, specs| - spec = specs.first - matching_specs = spec.source.specs.search([spec.name, spec.version]) - platform_spec = GemHelpers.select_best_platform_match(matching_specs, platform).find do |s| - s.matches_current_metadata? && valid_dependencies?(s) - end - - if platform_spec - new_specs << LazySpecification.from_spec(platform_spec) - true - else - false - end - end - next unless valid_platform - - @specs.concat(new_specs.uniq) + complete_platform(platform) end return platforms if new_platforms.empty? @@ -86,12 +68,15 @@ module Bundler less_specific_platform = new_platforms.find {|platform| platform != Gem::Platform::RUBY && platform === Bundler.local_platform } platforms.delete(Bundler.local_platform) if less_specific_platform - @sorted = nil - @lookup = nil - platforms end + def complete_platforms!(platforms) + platforms.each do |platform| + complete_platform(platform) + end + end + def validate_deps(s) s.runtime_dependencies.each do |dep| next if dep.name == "bundler" @@ -110,14 +95,14 @@ module Bundler def []=(key, value) @specs << value - @lookup = nil - @sorted = nil + + reset! end def delete(specs) specs.each {|spec| @specs.delete(spec) } - @lookup = nil - @sorted = nil + + reset! end def sort! @@ -175,8 +160,8 @@ module Bundler def delete_by_name(name) @specs.reject! {|spec| spec.name == name } - @lookup = nil - @sorted = nil + + reset! end def what_required(spec) @@ -212,6 +197,42 @@ module Bundler private + def reset! + @sorted = nil + @lookup = nil + end + + def complete_platform(platform) + new_specs = [] + + valid_platform = lookup.all? do |_, specs| + spec = specs.first + matching_specs = spec.source.specs.search([spec.name, spec.version]) + platform_spec = GemHelpers.select_best_platform_match(matching_specs, platform).find do |s| + s.matches_current_metadata? && valid_dependencies?(s) + end + + if platform_spec + new_specs << LazySpecification.from_spec(platform_spec) unless specs.include?(platform_spec) + true + else + false + end + end + + if valid_platform && new_specs.any? + @specs.concat(new_specs) + + reset! + end + + valid_platform + end + + def all_platforms + @specs.flat_map {|spec| spec.source.specs.search([spec.name, spec.version]).map(&:platform) }.uniq + end + def valid_dependencies?(s) validate_deps(s) == :valid end diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index a0ef931d07..05b6c66ce4 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "2.5.3".freeze + VERSION = "2.5.5".freeze def self.bundler_major_version @bundler_major_version ||= VERSION.split(".").first.to_i diff --git a/lib/rubygems.rb b/lib/rubygems.rb index fa3da5cc1a..d4138b2d8f 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -9,7 +9,7 @@ require "rbconfig" module Gem - VERSION = "3.5.3" + VERSION = "3.5.5" end # Must be first since it unloads the prelude from 1.9.2 @@ -1216,6 +1216,13 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} ## # Find a Gem::Specification of default gem from +path+ + def find_default_spec(path) + @path_to_default_spec_map[path] + end + + ## + # Find an unresolved Gem::Specification of default gem from +path+ + def find_unresolved_default_spec(path) default_spec = @path_to_default_spec_map[path] return default_spec if default_spec && loaded_specs[default_spec.name] != default_spec diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb index 10ae6d9a93..3d6fecaa40 100644 --- a/lib/rubygems/commands/update_command.rb +++ b/lib/rubygems/commands/update_command.rb @@ -244,7 +244,7 @@ command to remove old versions. @installer = Gem::DependencyInstaller.new update_options - say "Updating #{name}" unless options[:system] && options[:silent] + say "Updating #{name}" unless options[:system] begin @installer.install name, Gem::Requirement.new(version) rescue Gem::InstallError, Gem::DependencyError => e @@ -282,7 +282,7 @@ command to remove old versions. check_oldest_rubygems version installed_gems = Gem::Specification.find_all_by_name "rubygems-update", requirement - installed_gems = update_gem("rubygems-update", version) if installed_gems.empty? || installed_gems.first.version != version + installed_gems = update_gem("rubygems-update", requirement) if installed_gems.empty? || installed_gems.first.version != version return if installed_gems.empty? install_rubygems installed_gems.first @@ -294,9 +294,7 @@ command to remove old versions. args << "--prefix" << Gem.prefix if Gem.prefix args << "--no-document" unless options[:document].include?("rdoc") || options[:document].include?("ri") args << "--no-format-executable" if options[:no_format_executable] - args << "--previous-version" << Gem::VERSION if - options[:system] == true || - Gem::Version.new(options[:system]) >= Gem::Version.new(2) + args << "--previous-version" << Gem::VERSION args end diff --git a/lib/rubygems/core_ext/kernel_require.rb b/lib/rubygems/core_ext/kernel_require.rb index bbd7852e92..073966b696 100644 --- a/lib/rubygems/core_ext/kernel_require.rb +++ b/lib/rubygems/core_ext/kernel_require.rb @@ -39,7 +39,14 @@ module Kernel RUBYGEMS_ACTIVATION_MONITOR.synchronize do path = File.path(path) - if spec = Gem.find_unresolved_default_spec(path) + # If +path+ belongs to a default gem, we activate it and then go straight + # to normal require + + if spec = Gem.find_default_spec(path) + name = spec.name + + next if Gem.loaded_specs[name] + # Ensure -I beats a default gem resolved_path = begin rp = nil @@ -57,8 +64,10 @@ module Kernel rp end - Kernel.send(:gem, spec.name, Gem::Requirement.default_prerelease) unless + Kernel.send(:gem, name, Gem::Requirement.default_prerelease) unless resolved_path + + next end # If there are no unresolved deps, then we can use just try diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb index 00dc5707c3..19cf306f88 100644 --- a/lib/rubygems/defaults.rb +++ b/lib/rubygems/defaults.rb @@ -24,7 +24,7 @@ module Gem default_spec_cache_dir = File.join Gem.user_home, ".gem", "specs" unless File.exist?(default_spec_cache_dir) - default_spec_cache_dir = File.join Gem.data_home, "gem", "specs" + default_spec_cache_dir = File.join Gem.cache_home, "gem", "specs" end default_spec_cache_dir diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index a0c7faa133..169002d7c7 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -301,7 +301,7 @@ class Gem::Specification < Gem::BasicSpecification # # Usage: # - # spec.description = <<-EOF + # spec.description = <<~EOF # Rake is a Make-like program implemented in Ruby. Tasks and # dependencies are specified in standard Ruby syntax. # EOF diff --git a/spec/bundler/bundler/compact_index_client/updater_spec.rb b/spec/bundler/bundler/compact_index_client/updater_spec.rb index 51b838d2d2..6eed88ca9e 100644 --- a/spec/bundler/bundler/compact_index_client/updater_spec.rb +++ b/spec/bundler/bundler/compact_index_client/updater_spec.rb @@ -21,7 +21,7 @@ RSpec.describe Bundler::CompactIndexClient::Updater do before do allow(response).to receive(:[]).with("Repr-Digest") { nil } allow(response).to receive(:[]).with("Digest") { nil } - allow(response).to receive(:[]).with("ETag") { "thisisanetag" } + allow(response).to receive(:[]).with("ETag") { '"thisisanetag"' } end it "downloads the file without attempting append" do @@ -57,7 +57,7 @@ RSpec.describe Bundler::CompactIndexClient::Updater do let(:headers) do { - "If-None-Match" => "LocalEtag", + "If-None-Match" => '"LocalEtag"', "Range" => "bytes=2-", } end @@ -76,7 +76,7 @@ RSpec.describe Bundler::CompactIndexClient::Updater do it "appends the file if etags do not match" do expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response) allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" } - allow(response).to receive(:[]).with("ETag") { "NewEtag" } + allow(response).to receive(:[]).with("ETag") { '"NewEtag"' } allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { true } allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { false } allow(response).to receive(:body) { "c123" } @@ -90,7 +90,7 @@ RSpec.describe Bundler::CompactIndexClient::Updater do it "replaces the file if response ignores range" do expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response) allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" } - allow(response).to receive(:[]).with("ETag") { "NewEtag" } + allow(response).to receive(:[]).with("ETag") { '"NewEtag"' } allow(response).to receive(:body) { full_body } updater.update(remote_path, local_path, etag_path) @@ -107,8 +107,8 @@ RSpec.describe Bundler::CompactIndexClient::Updater do full_response = double(:full_response, body: full_body, is_a?: false) allow(full_response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" } - allow(full_response).to receive(:[]).with("ETag") { "NewEtag" } - expect(fetcher).to receive(:call).once.with(remote_path, { "If-None-Match" => "LocalEtag" }).and_return(full_response) + allow(full_response).to receive(:[]).with("ETag") { '"NewEtag"' } + expect(fetcher).to receive(:call).once.with(remote_path, { "If-None-Match" => '"LocalEtag"' }).and_return(full_response) updater.update(remote_path, local_path, etag_path) @@ -123,7 +123,7 @@ RSpec.describe Bundler::CompactIndexClient::Updater do "Range" => "bytes=2-", # This MD5 feature should be deleted after sufficient time has passed since release. # From then on, requests that still don't have a saved etag will be made without this header. - "If-None-Match" => Digest::MD5.hexdigest(local_body), + "If-None-Match" => %("#{Digest::MD5.hexdigest(local_body)}"), } end @@ -135,13 +135,13 @@ RSpec.describe Bundler::CompactIndexClient::Updater do updater.update(remote_path, local_path, etag_path) expect(local_path.read).to eq("abc") - expect(etag_path.read).to eq(headers["If-None-Match"]) + expect(%("#{etag_path.read}")).to eq(headers["If-None-Match"]) end it "appends the file" do expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response) allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" } - allow(response).to receive(:[]).with("ETag") { "OpaqueEtag" } + allow(response).to receive(:[]).with("ETag") { '"OpaqueEtag"' } allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { true } allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { false } allow(response).to receive(:body) { "c123" } @@ -156,7 +156,7 @@ RSpec.describe Bundler::CompactIndexClient::Updater do expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response) allow(response).to receive(:[]).with("Repr-Digest") { nil } allow(response).to receive(:[]).with("Digest") { nil } - allow(response).to receive(:[]).with("ETag") { "OpaqueEtag" } + allow(response).to receive(:[]).with("ETag") { '"OpaqueEtag"' } allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { false } allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { false } allow(response).to receive(:body) { full_body } @@ -180,8 +180,8 @@ RSpec.describe Bundler::CompactIndexClient::Updater do full_response = double(:full_response, body: full_body, is_a?: false) allow(full_response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" } - allow(full_response).to receive(:[]).with("ETag") { "NewEtag" } - expect(fetcher).to receive(:call).once.with(remote_path, { "If-None-Match" => "LocalEtag" }).and_return(full_response) + allow(full_response).to receive(:[]).with("ETag") { '"NewEtag"' } + expect(fetcher).to receive(:call).once.with(remote_path, { "If-None-Match" => '"LocalEtag"' }).and_return(full_response) updater.update(remote_path, local_path, etag_path) diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index 7c016ff3d8..f0c9aaea8e 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -460,6 +460,35 @@ RSpec.describe "bundle install with gem sources" do expect(the_bundle).to include_gems("rubocop 1.37.1") end + it "includes the gem without warning if two gemspecs add it with the same requirement" do + gem1 = tmp.join("my-gem-1") + gem2 = tmp.join("my-gem-2") + + build_lib "my-gem", path: gem1 do |s| + s.add_development_dependency "rubocop", "~> 1.36.0" + end + + build_lib "my-gem-2", path: gem2 do |s| + s.add_development_dependency "rubocop", "~> 1.36.0" + end + + build_repo4 do + build_gem "rubocop", "1.36.0" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gemspec path: "#{gem1}" + gemspec path: "#{gem2}" + G + + bundle :install + + expect(err).to be_empty + expect(the_bundle).to include_gems("rubocop 1.36.0") + end + it "warns when a Gemfile dependency is overriding a gemspec development dependency, with different requirements" do build_lib "my-gem", path: bundled_app do |s| s.add_development_dependency "rails", ">= 5" diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb index 59e7c3867c..2bde5a1586 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -1380,7 +1380,7 @@ RSpec.describe "bundle update --bundler" do build_bundler "999.0.0" end - install_gemfile <<-G + install_gemfile <<-G, artifice: nil, env: { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" } source "#{file_uri_for(gem_repo4)}" gem "rack" G diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb index e3c891e4c1..1375d8cab1 100644 --- a/spec/bundler/install/gems/compact_index_spec.rb +++ b/spec/bundler/install/gems/compact_index_spec.rb @@ -924,15 +924,19 @@ RSpec.describe "compact index api" do gem 'rack', '0.9.1' G - rake_info_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index", - "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "info", "rack") - bundle :install, artifice: "compact_index" + # We must remove the etag so that we don't ignore the range and get a 304 Not Modified. + rake_info_etag_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index", + "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "info-etags", "rack-11690b09f16021ff06a6857d784a1870") + File.unlink(rake_info_etag_path) if File.exist?(rake_info_etag_path) + + rake_info_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index", + "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "info", "rack") expected_rack_info_content = File.read(rake_info_path) - # Modify the cache files. We expect them to be reset to the normal ones when we re-run :install - File.open(rake_info_path, "a") {|f| f << "this is different" } + # Modify the cache files to make the range not satisfiable + File.open(rake_info_path, "a") {|f| f << "0.9.2 |checksum:c55b525b421fd833a93171ad3d7f04528ca8e87d99ac273f8933038942a5888c" } # Update the Gemfile so the next install does its normal things gemfile <<-G diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index 5e996f5aac..7f664abc4d 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -1195,6 +1195,46 @@ RSpec.describe "the lockfile format" do G end + it "adds compatible platform specific variants to the lockfile, even if resolution fallback to RUBY due to some other incompatible platform specific variant" do + simulate_platform "arm64-darwin-23" do + build_repo4 do + build_gem "google-protobuf", "3.25.1" + build_gem "google-protobuf", "3.25.1" do |s| + s.platform = "arm64-darwin-23" + end + build_gem "google-protobuf", "3.25.1" do |s| + s.platform = "x64-mingw-ucrt" + s.required_ruby_version = "> #{Gem.ruby_version}" + end + end + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem "google-protobuf" + G + bundle "lock --add-platform x64-mingw-ucrt" + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + google-protobuf (3.25.1) + google-protobuf (3.25.1-arm64-darwin-23) + + PLATFORMS + arm64-darwin-23 + ruby + x64-mingw-ucrt + + DEPENDENCIES + google-protobuf + + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + it "persists the spec's specific platform to the lockfile" do build_repo2 do build_gem "platform_specific", "1.0" do |s| diff --git a/spec/bundler/support/artifice/endpoint_500.rb b/spec/bundler/support/artifice/endpoint_500.rb index d8ab6b65bc..b1ed1964c8 100644 --- a/spec/bundler/support/artifice/endpoint_500.rb +++ b/spec/bundler/support/artifice/endpoint_500.rb @@ -2,7 +2,7 @@ require_relative "../path" -$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s)) +$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords,base64}-*/lib")].map(&:to_s)) require "sinatra/base" diff --git a/spec/bundler/support/artifice/helpers/compact_index.rb b/spec/bundler/support/artifice/helpers/compact_index.rb index cf8bb34c5a..a803a2d30a 100644 --- a/spec/bundler/support/artifice/helpers/compact_index.rb +++ b/spec/bundler/support/artifice/helpers/compact_index.rb @@ -19,8 +19,8 @@ class CompactIndexAPI < Endpoint def etag_response response_body = yield etag = Digest::MD5.hexdigest(response_body) - return if not_modified?(etag) headers "ETag" => quote(etag) + return if not_modified?(etag) headers "Repr-Digest" => "sha-256=:#{Digest::SHA256.base64digest(response_body)}:" headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60" content_type "text/plain" @@ -35,7 +35,6 @@ class CompactIndexAPI < Endpoint etags = parse_etags(request.env["HTTP_IF_NONE_MATCH"]) return unless etags.include?(etag) - headers "ETag" => quote(etag) status 304 body "" end diff --git a/spec/bundler/support/artifice/helpers/endpoint.rb b/spec/bundler/support/artifice/helpers/endpoint.rb index be52df6936..83ba1be0fc 100644 --- a/spec/bundler/support/artifice/helpers/endpoint.rb +++ b/spec/bundler/support/artifice/helpers/endpoint.rb @@ -2,7 +2,7 @@ require_relative "../../path" -$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s)) +$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords,base64}-*/lib")].map(&:to_s)) require "sinatra/base" diff --git a/spec/bundler/support/artifice/windows.rb b/spec/bundler/support/artifice/windows.rb index 4d90e0a426..fea991c071 100644 --- a/spec/bundler/support/artifice/windows.rb +++ b/spec/bundler/support/artifice/windows.rb @@ -2,7 +2,7 @@ require_relative "../path" -$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s)) +$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords,base64}-*/lib")].map(&:to_s)) require "sinatra/base" diff --git a/spec/bundler/support/rubygems_ext.rb b/spec/bundler/support/rubygems_ext.rb index c242e3a528..0d1ad2d528 100644 --- a/spec/bundler/support/rubygems_ext.rb +++ b/spec/bundler/support/rubygems_ext.rb @@ -86,7 +86,7 @@ module Spec puts success_message puts else - system("git status --porcelain") + system("git diff") puts puts error_message diff --git a/test/rubygems/helper.rb b/test/rubygems/helper.rb index d259fbde1b..e6774ded38 100644 --- a/test/rubygems/helper.rb +++ b/test/rubygems/helper.rb @@ -105,39 +105,32 @@ class Gem::TestCase < Test::Unit::TestCase refute File.directory?(path), msg end - # https://github.com/seattlerb/minitest/blob/21d9e804b63c619f602f3f4ece6c71b48974707a/lib/minitest/assertions.rb#L188 - def _synchronize - yield - end - - # https://github.com/seattlerb/minitest/blob/21d9e804b63c619f602f3f4ece6c71b48974707a/lib/minitest/assertions.rb#L546 + # Originally copied from minitest/assertions.rb def capture_subprocess_io - _synchronize do - require "tempfile" + require "tempfile" - captured_stdout = Tempfile.new("out") - captured_stderr = Tempfile.new("err") + captured_stdout = Tempfile.new("out") + captured_stderr = Tempfile.new("err") - orig_stdout = $stdout.dup - orig_stderr = $stderr.dup - $stdout.reopen captured_stdout - $stderr.reopen captured_stderr + orig_stdout = $stdout.dup + orig_stderr = $stderr.dup + $stdout.reopen captured_stdout + $stderr.reopen captured_stderr - yield + yield - $stdout.rewind - $stderr.rewind + $stdout.rewind + $stderr.rewind - return captured_stdout.read, captured_stderr.read - ensure - $stdout.reopen orig_stdout - $stderr.reopen orig_stderr + [captured_stdout.read, captured_stderr.read] + ensure + $stdout.reopen orig_stdout + $stderr.reopen orig_stderr - orig_stdout.close - orig_stderr.close - captured_stdout.close! - captured_stderr.close! - end + orig_stdout.close + orig_stderr.close + captured_stdout.close! + captured_stderr.close! end ## diff --git a/test/rubygems/test_gem_commands_update_command.rb b/test/rubygems/test_gem_commands_update_command.rb index 324bd9c747..2683840f2e 100644 --- a/test/rubygems/test_gem_commands_update_command.rb +++ b/test/rubygems/test_gem_commands_update_command.rb @@ -79,7 +79,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase end out = @ui.output.split "\n" - assert_equal "Updating rubygems-update", out.shift assert_equal "Installing RubyGems 9", out.shift assert_equal "RubyGems system software updated", out.shift @@ -123,7 +122,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase end out = @ui.output.split "\n" - assert_equal "Updating rubygems-update", out.shift assert_empty out err = @ui.error.split "\n" @@ -132,6 +130,34 @@ class TestGemCommandsUpdateCommand < Gem::TestCase assert_empty err end + def test_execute_system_when_latest_does_not_support_your_ruby_but_previous_one_does + spec_fetcher do |fetcher| + fetcher.download "rubygems-update", 9 do |s| + s.files = %w[setup.rb] + s.required_ruby_version = "> 9" + end + + fetcher.download "rubygems-update", 8 do |s| + s.files = %w[setup.rb] + end + end + + @cmd.options[:args] = [] + @cmd.options[:system] = true + + use_ui @ui do + @cmd.execute + end + + err = @ui.error.split "\n" + assert_empty err + + out = @ui.output.split "\n" + assert_equal "Installing RubyGems 8", out.shift + assert_equal "RubyGems system software updated", out.shift + assert_empty out + end + def test_execute_system_multiple spec_fetcher do |fetcher| fetcher.download "rubygems-update", 8 do |s| @@ -151,7 +177,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase end out = @ui.output.split "\n" - assert_equal "Updating rubygems-update", out.shift assert_equal "Installing RubyGems 9", out.shift assert_equal "RubyGems system software updated", out.shift @@ -185,7 +210,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase end out = @ui.output.split "\n" - assert_equal "Updating rubygems-update", out.shift assert_equal "Installing RubyGems 9", out.shift assert_equal "RubyGems system software updated", out.shift @@ -242,7 +266,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase end out = @ui.output.split "\n" - assert_equal "Updating rubygems-update", out.shift assert_equal "Installing RubyGems 8", out.shift assert_equal "RubyGems system software updated", out.shift @@ -353,7 +376,6 @@ class TestGemCommandsUpdateCommand < Gem::TestCase end out = @ui.output.split "\n" - assert_equal "Updating rubygems-update", out.shift assert_equal "Installing RubyGems 9", out.shift assert_equal "RubyGems system software updated", out.shift diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock index 0937f71973..aa9587b231 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock @@ -152,18 +152,18 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.9.84" +version = "0.9.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3def04a96a36ef8681a2b2e26c01683b93a8630175c845fa06cab76c5a8c7ce0" +checksum = "7285f2a7b92f58ab198e3fd59a71d2861478f9c4642f41e83582385818941697" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.84" +version = "0.9.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c017d134afd764dd43c2faa91aa50b698a3bb4ff30e83113da483c789e74be8c" +checksum = "71583945f94dabb6c0dfa63f1b71e929c1901e1e288ef3739ab8bed3b7069550" dependencies = [ "bindgen", "lazy_static", diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml index 6883495282..56a4188f15 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -rb-sys = "0.9.84" +rb-sys = "0.9.86" diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock index e65b28607d..8c394aec20 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock @@ -145,18 +145,18 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.9.83" +version = "0.9.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5b8d560b60790a3e60e56e73a8c7be88ac14e6af39fc82b5eca72c71753840" +checksum = "7285f2a7b92f58ab198e3fd59a71d2861478f9c4642f41e83582385818941697" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.83" +version = "0.9.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d2bfd00002007d7e9ad93d0397437933040caf452d260c26dbef5fd95ae1a6" +checksum = "71583945f94dabb6c0dfa63f1b71e929c1901e1e288ef3739ab8bed3b7069550" dependencies = [ "bindgen", "lazy_static", diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml index 26185e0e3c..39b2ae3919 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -rb-sys = "0.9.83" +rb-sys = "0.9.86" diff --git a/test/rubygems/test_require.rb b/test/rubygems/test_require.rb index fc5f219490..30a4a477f9 100644 --- a/test/rubygems/test_require.rb +++ b/test/rubygems/test_require.rb @@ -489,6 +489,17 @@ class TestGemRequire < Gem::TestCase assert_equal %w[default-3.0], loaded_spec_names end + def test_normal_gem_does_not_shadow_default_gem + default_gem_spec = new_default_spec("foo", "2.0", nil, "foo.rb") + install_default_gems(default_gem_spec) + + normal_gem_spec = util_spec("fake-foo", "3.0", nil, "lib/foo.rb") + install_specs(normal_gem_spec) + + assert_require "foo" + assert_equal %w[foo-2.0], loaded_spec_names + end + def test_normal_gems_with_overridden_load_error_message normal_gem_spec = util_spec("normal", "3.0", nil, "lib/normal/gem.rb") @@ -529,6 +540,65 @@ class TestGemRequire < Gem::TestCase assert_equal %w[default-3.0.0.rc2], loaded_spec_names end + def test_default_gem_with_unresolved_gems_depending_on_it + my_http_old = util_spec "my-http", "0.1.1", nil, "lib/my/http.rb" + install_gem my_http_old + + my_http_default = new_default_spec "my-http", "0.3.0", nil, "my/http.rb" + install_default_gems my_http_default + + faraday_1 = util_spec "faraday", "1", { "my-http" => ">= 0" } + install_gem faraday_1 + + faraday_2 = util_spec "faraday", "2", { "my-http" => ">= 0" } + install_gem faraday_2 + + chef = util_spec "chef", "1", { "faraday" => [">= 1", "< 3"] }, "lib/chef.rb" + install_gem chef + + assert_require "chef" + assert_require "my/http" + end + + def test_default_gem_required_circulary_with_unresolved_gems_depending_on_it + my_http_old = util_spec "my-http", "0.1.1", nil, "lib/my/http.rb" + install_gem my_http_old + + my_http_default = new_default_spec "my-http", "0.3.0", nil, "my/http.rb" + my_http_default_path = File.join(@tempdir, "default_gems", "lib", "my/http.rb") + install_default_gems my_http_default + File.write(my_http_default_path, 'require "my/http"') + + faraday_1 = util_spec "faraday", "1", { "my-http" => ">= 0" } + install_gem faraday_1 + + faraday_2 = util_spec "faraday", "2", { "my-http" => ">= 0" } + install_gem faraday_2 + + chef = util_spec "chef", "1", { "faraday" => [">= 1", "< 3"] }, "lib/chef.rb" + install_gem chef + + assert_require "chef" + + out, err = capture_output do + assert_require "my/http" + end + + assert_empty out + + circular_require_warning = false + + err_lines = err.split("\n").reject do |line| + if line.include?("circular require") + circular_require_warning = true + elsif circular_require_warning # ignore backtrace lines for circular require warning + circular_require_warning = line.start_with?(/[\s]/) + end + end + + assert_empty err_lines + end + def loaded_spec_names Gem.loaded_specs.values.map(&:full_name).sort end diff --git a/tool/bundler/dev_gems.rb b/tool/bundler/dev_gems.rb index 5e5ba94ec2..cb4b556d2f 100644 --- a/tool/bundler/dev_gems.rb +++ b/tool/bundler/dev_gems.rb @@ -13,7 +13,7 @@ gem "parallel", "~> 1.19" gem "rspec-core", "~> 3.12" gem "rspec-expectations", "~> 3.12" gem "rspec-mocks", "~> 3.12" -gem "uri", "~> 0.12.0" +gem "uri", "~> 0.13.0" group :doc do gem "nronn", "~> 0.11.1", platform: :ruby diff --git a/tool/bundler/test_gems.rb b/tool/bundler/test_gems.rb index aa1cfd09a5..32cb6b34ee 100644 --- a/tool/bundler/test_gems.rb +++ b/tool/bundler/test_gems.rb @@ -3,6 +3,7 @@ source "https://rubygems.org" gem "rack", "~> 2.0" +gem "base64" gem "webrick", "1.7.0" gem "rack-test", "~> 1.1" gem "compact_index", "~> 0.15.0"