diff options
32 files changed, 705 insertions, 360 deletions
diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb index 7959e5c089..9e0180fac7 100644 --- a/lib/bundler/compact_index_client/updater.rb +++ b/lib/bundler/compact_index_client/updater.rb @@ -54,7 +54,7 @@ module Bundler if response.is_a?(Net::HTTPPartialContent) && local_temp_path.size.nonzero? local_temp_path.open("a") {|f| f << slice_body(content, 1..-1) } else - local_temp_path.open("w") {|f| f << content } + local_temp_path.open("wb") {|f| f << content } end end diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index a09d661a07..686721cd32 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -106,6 +106,17 @@ module Bundler @locked_platforms = [] end + @locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) } + @disable_multisource = @locked_gem_sources.all?(&:disable_multisource?) + + unless @disable_multisource + msg = "Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. You should run `bundle update` or generate your lockfile from scratch." + + Bundler::SharedHelpers.major_deprecation 2, msg + + @sources.merged_gem_lockfile_sections! + end + @unlock[:gems] ||= [] @unlock[:sources] ||= [] @unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object @@ -145,6 +156,10 @@ module Bundler end end + def disable_multisource? + @disable_multisource + end + def resolve_with_cache! raise "Specs already loaded" if @specs sources.cached! @@ -530,6 +545,9 @@ module Bundler attr_reader :sources private :sources + attr_reader :locked_gem_sources + private :locked_gem_sources + def nothing_changed? !@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@locked_specs_incomplete_for_platform end @@ -654,10 +672,8 @@ module Bundler end def converge_rubygems_sources - return false if Bundler.feature_flag.disable_multisource? + return false if disable_multisource? - # Get the RubyGems sources from the Gemfile.lock - locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) } return false if locked_gem_sources.empty? # Get the RubyGems remotes from the Gemfile diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index 23fba99ffa..313d1a9a41 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -460,19 +460,16 @@ repo_name ||= user_name @sources.add_rubygems_remote(source) end - if Bundler.feature_flag.disable_multisource? + if Bundler.feature_flag.bundler_3_mode? msg = "This Gemfile contains multiple primary sources. " \ "Each source after the first must include a block to indicate which gems " \ - "should come from that source. To downgrade this error to a warning, run " \ - "`bundle config unset disable_multisource`" + "should come from that source" raise GemfileEvalError, msg else Bundler::SharedHelpers.major_deprecation 2, "Your Gemfile contains multiple primary sources. " \ "Using `source` more than once without a block is a security risk, and " \ "may result in installing unexpected gems. To resolve this warning, use " \ - "a block to indicate which gems should come from the secondary source. " \ - "To upgrade this warning to an error, run `bundle config set --local " \ - "disable_multisource true`." + "a block to indicate which gems should come from the secondary source." end end diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb index a6d1de29e6..c3bf5843b7 100644 --- a/lib/bundler/installer/parallel_installer.rb +++ b/lib/bundler/installer/parallel_installer.rb @@ -27,13 +27,8 @@ module Bundler state == :failed end - def installation_attempted? - installed? || failed? - end - - # Only true when spec in neither installed nor already enqueued def ready_to_enqueue? - !enqueued? && !installation_attempted? + state == :none end def has_post_install_message? @@ -93,6 +88,11 @@ module Bundler def call check_for_corrupt_lockfile + if @rake + do_install(@rake, 0) + Gem::Specification.reset + end + if @size > 1 install_with_worker else @@ -217,8 +217,6 @@ module Bundler # are installed. def enqueue_specs @specs.select(&:ready_to_enqueue?).each do |spec| - next if @rake && !@rake.installed? && spec.name != @rake.name - if spec.dependencies_installed? @specs spec.state = :enqueued worker_pool.enq spec diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb index 058d353bbe..f87faff70c 100644 --- a/lib/bundler/lockfile_parser.rb +++ b/lib/bundler/lockfile_parser.rb @@ -131,18 +131,8 @@ module Bundler @sources << @current_source end when GEM - source_remotes = Array(@opts["remote"]) - - if source_remotes.size == 1 - @opts["remotes"] = @opts.delete("remote") - @current_source = TYPES[@type].from_lock(@opts) - else - source_remotes.each do |url| - rubygems_aggregate.add_remote(url) - end - @current_source = rubygems_aggregate - end - + @opts["remotes"] = Array(@opts.delete("remote")).reverse + @current_source = TYPES[@type].from_lock(@opts) @sources << @current_source when PLUGIN @current_source = Plugin.source_from_lock(@opts) @@ -245,9 +235,5 @@ module Bundler def parse_ruby(line) @ruby_version = line.strip end - - def rubygems_aggregate - @rubygems_aggregate ||= Source::Rubygems.new - end end end diff --git a/lib/bundler/plugin/api/source.rb b/lib/bundler/plugin/api/source.rb index e1f0826874..d70a16f4bc 100644 --- a/lib/bundler/plugin/api/source.rb +++ b/lib/bundler/plugin/api/source.rb @@ -140,6 +140,13 @@ module Bundler end end + # Set internal representation to fetch the gems/specs locally. + # + # When this is called, the source should try to fetch the specs and + # install from the local system. + def local! + end + # Set internal representation to fetch the gems/specs from remote. # # When this is called, the source should try to fetch the specs and diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb index be00143f5a..a3f4b09cce 100644 --- a/lib/bundler/source.rb +++ b/lib/bundler/source.rb @@ -33,6 +33,12 @@ module Bundler spec.source == self end + def local!; end + + def cached!; end + + def remote!; end + # it's possible that gems from one source depend on gems from some # other source, so now we download gemspecs and iterate over those # dependencies, looking for gems we don't have info on yet. diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb index 0867879861..50b65ce0ea 100644 --- a/lib/bundler/source/metadata.rb +++ b/lib/bundler/source/metadata.rb @@ -33,10 +33,6 @@ module Bundler end end - def cached!; end - - def remote!; end - def options {} end diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index 5b89b1645d..cede580b0a 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -20,17 +20,29 @@ module Bundler @dependency_names = [] @allow_remote = false @allow_cached = false + @allow_local = options["allow_local"] || false @caches = [cache_path, *Bundler.rubygems.gem_cache] - Array(options["remotes"] || []).reverse_each {|r| add_remote(r) } + Array(options["remotes"]).reverse_each {|r| add_remote(r) } + end + + def local! + return if @allow_local + + @specs = nil + @allow_local = true end def remote! + return if @allow_remote + @specs = nil @allow_remote = true end def cached! + return if @allow_cached + @specs = nil @allow_cached = true end @@ -49,8 +61,12 @@ module Bundler o.is_a?(Rubygems) && (o.credless_remotes - credless_remotes).empty? end + def disable_multisource? + @remotes.size <= 1 + end + def can_lock?(spec) - return super if Bundler.feature_flag.disable_multisource? + return super if disable_multisource? spec.source.is_a?(Rubygems) end @@ -87,7 +103,7 @@ module Bundler # small_idx.use large_idx. idx = @allow_remote ? remote_specs.dup : Index.new idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote - idx.use(installed_specs, :override_dupes) + idx.use(installed_specs, :override_dupes) if @allow_local idx end end @@ -365,7 +381,7 @@ module Bundler def cached_specs @cached_specs ||= begin - idx = installed_specs.dup + idx = @allow_local ? installed_specs.dup : Index.new Dir["#{cache_path}/*.gem"].each do |gemfile| next if gemfile =~ /^bundler\-[\d\.]+?\.gem/ diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb index 44b167ca3e..51698af46e 100644 --- a/lib/bundler/source_list.rb +++ b/lib/bundler/source_list.rb @@ -9,7 +9,7 @@ module Bundler :metadata_source def global_rubygems_source - @global_rubygems_source ||= rubygems_aggregate_class.new + @global_rubygems_source ||= rubygems_aggregate_class.new("allow_local" => true) end def initialize @@ -20,6 +20,16 @@ module Bundler @global_path_source = nil @rubygems_sources = [] @metadata_source = Source::Metadata.new + + @disable_multisource = true + end + + def disable_multisource? + @disable_multisource + end + + def merged_gem_lockfile_sections! + @disable_multisource = false end def add_path_source(options = {}) @@ -47,7 +57,7 @@ module Bundler end def global_rubygems_source=(uri) - @global_rubygems_source ||= rubygems_aggregate_class.new("remotes" => uri) + @global_rubygems_source ||= rubygems_aggregate_class.new("remotes" => uri, "allow_local" => true) end def add_rubygems_remote(uri) @@ -77,7 +87,7 @@ module Bundler def lock_sources lock_sources = (path_sources + git_sources + plugin_sources).sort_by(&:to_s) - if Bundler.feature_flag.disable_multisource? + if disable_multisource? lock_sources + rubygems_sources.sort_by(&:to_s) else lock_sources << combine_rubygems_sources @@ -94,7 +104,7 @@ module Bundler end end - replacement_rubygems = !Bundler.feature_flag.disable_multisource? && + replacement_rubygems = !disable_multisource? && replacement_sources.detect {|s| s.is_a?(Source::Rubygems) } @global_rubygems_source = replacement_rubygems if replacement_rubygems diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index 399c91fea5..dfc3114c41 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -82,6 +82,7 @@ module Bundler materialized.map! do |s| next s unless s.is_a?(LazySpecification) s.source.dependency_names = deps if s.source.respond_to?(:dependency_names=) + s.source.local! spec = s.__materialize__ unless spec unless missing_specs @@ -102,6 +103,7 @@ module Bundler @specs.map do |s| next s unless s.is_a?(LazySpecification) s.source.dependency_names = names if s.source.respond_to?(:dependency_names=) + s.source.local! s.source.remote! spec = s.__materialize__ raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index 46c5ecb2f3..5e71e22df8 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "2.2.13".freeze + VERSION = "2.2.14".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 609517e1e7..682360264a 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -8,7 +8,7 @@ require 'rbconfig' module Gem - VERSION = "3.2.13".freeze + VERSION = "3.2.14".freeze end # Must be first since it unloads the prelude from 1.9.2 diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb index e01d0494d6..fd1c0a62ac 100644 --- a/lib/rubygems/platform.rb +++ b/lib/rubygems/platform.rb @@ -66,7 +66,7 @@ class Gem::Platform when String then arch = arch.split '-' - if arch.length > 2 and arch.last !~ /\d+(\.\d+)?$/ # reassemble x86-linux-{libc} + if arch.length > 2 and arch.last !~ /\d/ # reassemble x86-linux-gnu extra = arch.pop arch.last << "-#{extra}" end @@ -146,8 +146,7 @@ class Gem::Platform ## # Does +other+ match this platform? Two platforms match if they have the # same CPU, or either has a CPU of 'universal', they have the same OS, and - # they have the same version, or either has no version (except for 'linux' - # where the version is the libc name, with no version standing for 'gnu') + # they have the same version, or either has no version. # # Additionally, the platform will match if the local CPU is 'arm' and the # other CPU starts with "arm" (for generic ARM family support). @@ -163,10 +162,7 @@ class Gem::Platform @os == other.os and # version - ( - (@os != 'linux' and (@version.nil? or other.version.nil?)) or - @version == other.version - ) + (@version.nil? or other.version.nil? or @version == other.version) end ## diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 7ac334d30d..da7b46e06e 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -51,6 +51,7 @@ class Gem::RemoteFetcher class UnknownHostError < FetchError end + deprecate_constant(:UnknownHostError) @fetcher = nil @@ -262,15 +263,9 @@ class Gem::RemoteFetcher end data - rescue Timeout::Error - raise UnknownHostError.new('timed out', uri) - rescue IOError, SocketError, SystemCallError, + rescue Timeout::Error, IOError, SocketError, SystemCallError, *(OpenSSL::SSL::SSLError if Gem::HAVE_OPENSSL) => e - if e.message =~ /getaddrinfo/ - raise UnknownHostError.new('no such name', uri) - else - raise FetchError.new("#{e.class}: #{e}", uri) - end + raise FetchError.new("#{e.class}: #{e}", uri) end def fetch_s3(uri, mtime = nil, head = false) diff --git a/spec/bundler/bundler/compact_index_client/updater_spec.rb b/spec/bundler/bundler/compact_index_client/updater_spec.rb index d8bb15df7e..cecaddfba4 100644 --- a/spec/bundler/bundler/compact_index_client/updater_spec.rb +++ b/spec/bundler/bundler/compact_index_client/updater_spec.rb @@ -46,4 +46,25 @@ RSpec.describe Bundler::CompactIndexClient::Updater do end.to raise_error(Bundler::PermissionError) end end + + context "when receiving non UTF-8 data and default internal encoding set to ASCII" do + let(:response) { double(:response, :body => "\x8B".b) } + + it "works just fine" do + old_verbose = $VERBOSE + previous_internal_encoding = Encoding.default_internal + + begin + $VERBOSE = false + Encoding.default_internal = "ASCII" + expect(response).to receive(:[]).with("ETag") { nil } + expect(fetcher).to receive(:call) { response } + + updater.update(local_path, remote_path) + ensure + Encoding.default_internal = previous_internal_encoding + $VERBOSE = old_verbose + end + end + end end diff --git a/spec/bundler/bundler/source_list_spec.rb b/spec/bundler/bundler/source_list_spec.rb index 3a0691b959..0c40ba8a77 100644 --- a/spec/bundler/bundler/source_list_spec.rb +++ b/spec/bundler/bundler/source_list_spec.rb @@ -372,26 +372,7 @@ RSpec.describe Bundler::SourceList do source_list.add_git_source("uri" => "git://first-git.org/path.git") end - it "combines the rubygems sources into a single instance, removing duplicate remotes from the end", :bundler => "< 3" do - expect(source_list.lock_sources).to eq [ - Bundler::Source::Git.new("uri" => "git://first-git.org/path.git"), - Bundler::Source::Git.new("uri" => "git://second-git.org/path.git"), - Bundler::Source::Git.new("uri" => "git://third-git.org/path.git"), - ASourcePlugin.new("uri" => "https://second-plugin.org/random"), - ASourcePlugin.new("uri" => "https://third-bar.org/foo"), - Bundler::Source::Path.new("path" => "/first/path/to/gem"), - Bundler::Source::Path.new("path" => "/second/path/to/gem"), - Bundler::Source::Path.new("path" => "/third/path/to/gem"), - Bundler::Source::Rubygems.new("remotes" => [ - "https://duplicate-rubygems.org", - "https://first-rubygems.org", - "https://second-rubygems.org", - "https://third-rubygems.org", - ]), - ] - end - - it "returns all sources, without combining rubygems sources", :bundler => "3" do + it "returns all sources, without combining rubygems sources" do expect(source_list.lock_sources).to eq [ Bundler::Source::Git.new("uri" => "git://first-git.org/path.git"), Bundler::Source::Git.new("uri" => "git://second-git.org/path.git"), diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index 7724170a63..264af30f6c 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -86,7 +86,7 @@ RSpec.describe "bundle lock" do it "does not fetch remote specs when using the --local option" do bundle "lock --update --local", :raise_on_error => false - expect(err).to match(/sources listed in your Gemfile|installed locally/) + expect(err).to match(/installed locally/) end it "works with --gemfile flag" do diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb index adc835eb95..3e3bbe816f 100644 --- a/spec/bundler/install/deploy_spec.rb +++ b/spec/bundler/install/deploy_spec.rb @@ -126,21 +126,21 @@ RSpec.describe "install in deployment or frozen mode" do bundle "config --local path vendor/bundle" bundle "install" gemfile <<-G - source "http://user_name:password@localgemserver.test/" - gem "rack" + source "http://user_name:password@localgemserver.test/" + gem "rack" G lockfile <<-G - GEM - remote: http://localgemserver.test/ - specs: - rack (1.0.0) + GEM + remote: http://localgemserver.test/ + specs: + rack (1.0.0) - PLATFORMS - #{local} + PLATFORMS + #{local} - DEPENDENCIES - rack + DEPENDENCIES + rack G bundle "config set --local deployment true" diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb index 7916b83977..0f95bfac8f 100644 --- a/spec/bundler/install/gemfile/sources_spec.rb +++ b/spec/bundler/install/gemfile/sources_spec.rb @@ -141,156 +141,159 @@ RSpec.describe "bundle install with gems on multiple sources" do end end - context "when a pinned gem has an indirect dependency" do + context "when a pinned gem has an indirect dependency in the pinned source" do before do build_repo gem_repo3 do build_gem "depends_on_rack", "1.0.1" do |s| s.add_dependency "rack" end end - end - context "when the indirect dependency is in the pinned source" do - before do - # we need a working rack gem in repo3 - update_repo gem_repo3 do - build_gem "rack", "1.0.0" - end - - gemfile <<-G - source "#{file_uri_for(gem_repo2)}" - source "#{file_uri_for(gem_repo3)}" do - gem "depends_on_rack" - end - G + # we need a working rack gem in repo3 + update_repo gem_repo3 do + build_gem "rack", "1.0.0" end - context "and not in any other sources" do - before do - build_repo(gem_repo2) {} + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + source "#{file_uri_for(gem_repo3)}" do + gem "depends_on_rack" end + G + end - it "installs from the same source without any warning" do - bundle :install - expect(err).not_to include("Warning") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") - end + context "and not in any other sources" do + before do + build_repo(gem_repo2) {} end - context "and in another source" do - before do - # need this to be broken to check for correct source ordering - build_repo gem_repo2 do - build_gem "rack", "1.0.0" do |s| - s.write "lib/rack.rb", "RACK = 'FAIL'" - end + it "installs from the same source without any warning" do + bundle :install + expect(err).not_to include("Warning") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") + end + end + + context "and in another source" do + before do + # need this to be broken to check for correct source ordering + build_repo gem_repo2 do + build_gem "rack", "1.0.0" do |s| + s.write "lib/rack.rb", "RACK = 'FAIL'" end end + end - it "installs from the same source without any warning" do - bundle :install + it "installs from the same source without any warning" do + bundle :install - expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") + expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") - # In https://github.com/bundler/bundler/issues/3585 this failed - # when there is already a lock file, and the gems are missing, so try again - system_gems [] - bundle :install + # In https://github.com/bundler/bundler/issues/3585 this failed + # when there is already a lock file, and the gems are missing, so try again + system_gems [] + bundle :install - expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") - end + expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") end end + end - context "when the indirect dependency is in a different source" do - before do - # In these tests, we need a working rack gem in repo2 and not repo3 - build_repo gem_repo2 do - build_gem "rack", "1.0.0" + context "when a pinned gem has an indirect dependency in a different source" do + before do + # In these tests, we need a working rack gem in repo2 and not repo3 + + build_repo gem_repo3 do + build_gem "depends_on_rack", "1.0.1" do |s| + s.add_dependency "rack" end end - context "and not in any other sources" do - before do - install_gemfile <<-G - source "#{file_uri_for(gem_repo2)}" - source "#{file_uri_for(gem_repo3)}" do - gem "depends_on_rack" - end - G - end + build_repo gem_repo2 do + build_gem "rack", "1.0.0" + end + end - it "installs from the other source without any warning" do - expect(err).not_to include("Warning") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") - end + context "and not in any other sources" do + before do + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + source "#{file_uri_for(gem_repo3)}" do + gem "depends_on_rack" + end + G end - context "and in yet another source" do - before do - gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - source "#{file_uri_for(gem_repo2)}" - source "#{file_uri_for(gem_repo3)}" do - gem "depends_on_rack" - end - G - end + it "installs from the other source without any warning" do + expect(err).not_to include("Warning") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") + end + end - it "installs from the other source and warns about ambiguous gems", :bundler => "< 3" do - bundle :install - expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") - expect(err).to include("Installed from: #{file_uri_for(gem_repo2)}") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") - end + context "and in yet another source" do + before do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + source "#{file_uri_for(gem_repo2)}" + source "#{file_uri_for(gem_repo3)}" do + gem "depends_on_rack" + end + G + end - it "fails", :bundler => "3" do - bundle :install, :raise_on_error => false - expect(err).to include("Each source after the first must include a block") - expect(exitstatus).to eq(4) - end + it "installs from the other source and warns about ambiguous gems", :bundler => "< 3" do + bundle :install + expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") + expect(err).to include("Installed from: #{file_uri_for(gem_repo2)}") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") end - context "and only the dependency is pinned" do - before do - # need this to be broken to check for correct source ordering - build_repo gem_repo2 do - build_gem "rack", "1.0.0" do |s| - s.write "lib/rack.rb", "RACK = 'FAIL'" - end + it "fails", :bundler => "3" do + bundle :install, :raise_on_error => false + expect(err).to include("Each source after the first must include a block") + expect(exitstatus).to eq(4) + end + end + + context "and only the dependency is pinned" do + before do + # need this to be broken to check for correct source ordering + build_repo gem_repo2 do + build_gem "rack", "1.0.0" do |s| + s.write "lib/rack.rb", "RACK = 'FAIL'" end + end - gemfile <<-G - source "#{file_uri_for(gem_repo3)}" # contains depends_on_rack - source "#{file_uri_for(gem_repo2)}" # contains broken rack + gemfile <<-G + source "#{file_uri_for(gem_repo3)}" # contains depends_on_rack + source "#{file_uri_for(gem_repo2)}" # contains broken rack - gem "depends_on_rack" # installed from gem_repo3 - gem "rack", :source => "#{file_uri_for(gem_repo1)}" - G - end + gem "depends_on_rack" # installed from gem_repo3 + gem "rack", :source => "#{file_uri_for(gem_repo1)}" + G + end - it "installs the dependency from the pinned source without warning", :bundler => "< 3" do - bundle :install + it "installs the dependency from the pinned source without warning", :bundler => "< 3" do + bundle :install - expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") + expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") - # In https://github.com/rubygems/bundler/issues/3585 this failed - # when there is already a lock file, and the gems are missing, so try again - system_gems [] - bundle :install + # In https://github.com/rubygems/bundler/issues/3585 this failed + # when there is already a lock file, and the gems are missing, so try again + system_gems [] + bundle :install - expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") - end + expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") + end - it "fails", :bundler => "3" do - bundle :install, :raise_on_error => false - expect(err).to include("Each source after the first must include a block") - expect(exitstatus).to eq(4) - end + it "fails", :bundler => "3" do + bundle :install, :raise_on_error => false + expect(err).to include("Each source after the first must include a block") + expect(exitstatus).to eq(4) end end end @@ -511,9 +514,149 @@ RSpec.describe "bundle install with gems on multiple sources" do L end - it "upgrades gems when running bundle update, without printing any warnings or errors" do + it "does not install newer versions or generate lockfile changes when running bundle install, and warns", :bundler => "< 3" do + initial_lockfile = lockfile + + bundle :install + + expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.") + + expect(the_bundle).to include_gems("activesupport 6.0.3.4") + expect(the_bundle).not_to include_gems("activesupport 6.1.2.1") + expect(the_bundle).to include_gems("tzinfo 1.2.9") + expect(the_bundle).not_to include_gems("tzinfo 2.0.4") + expect(the_bundle).to include_gems("concurrent-ruby 1.1.8") + expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.9") + + expect(lockfile).to eq(initial_lockfile) + end + + it "fails when running bundle install", :bundler => "3" do + initial_lockfile = lockfile + + bundle :install, :raise_on_error => false + + expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.") + + expect(lockfile).to eq(initial_lockfile) + end + + it "splits sections and upgrades gems when running bundle update, and doesn't warn" do bundle "update --all" expect(err).to be_empty + + expect(the_bundle).not_to include_gems("activesupport 6.0.3.4") + expect(the_bundle).to include_gems("activesupport 6.1.2.1") + expect(the_bundle).not_to include_gems("tzinfo 1.2.9") + expect(the_bundle).to include_gems("tzinfo 2.0.4") + expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.8") + expect(the_bundle).to include_gems("concurrent-ruby 1.1.9") + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo2)}/ + specs: + activesupport (6.1.2.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + concurrent-ruby (1.1.9) + connection_pool (2.2.3) + i18n (1.8.9) + concurrent-ruby (~> 1.0) + minitest (5.14.3) + rack (2.2.3) + redis (4.2.5) + sidekiq (6.1.3) + connection_pool (>= 2.2.2) + rack (~> 2.0) + redis (>= 4.2.0) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) + zeitwerk (2.4.2) + + GEM + remote: #{file_uri_for(gem_repo3)}/ + specs: + sidekiq-pro (5.2.1) + connection_pool (>= 2.2.3) + sidekiq (>= 6.1.0) + + PLATFORMS + #{specific_local_platform} + + DEPENDENCIES + activesupport + sidekiq-pro! + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "it keeps the currrent lockfile format and upgrades the requested gem when running bundle update with an argument, and warns", :bundler => "< 3" do + bundle "update concurrent-ruby" + expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.") + + expect(the_bundle).to include_gems("activesupport 6.0.3.4") + expect(the_bundle).not_to include_gems("activesupport 6.1.2.1") + expect(the_bundle).to include_gems("tzinfo 1.2.9") + expect(the_bundle).not_to include_gems("tzinfo 2.0.4") + expect(the_bundle).to include_gems("concurrent-ruby 1.1.9") + expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.8") + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo2)}/ + remote: #{file_uri_for(gem_repo3)}/ + specs: + activesupport (6.0.3.4) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + zeitwerk (~> 2.2, >= 2.2.2) + concurrent-ruby (1.1.9) + connection_pool (2.2.3) + i18n (1.8.9) + concurrent-ruby (~> 1.0) + minitest (5.14.3) + rack (2.2.3) + redis (4.2.5) + sidekiq (6.1.3) + connection_pool (>= 2.2.2) + rack (~> 2.0) + redis (>= 4.2.0) + sidekiq-pro (5.2.1) + connection_pool (>= 2.2.3) + sidekiq (>= 6.1.0) + thread_safe (0.3.6) + tzinfo (1.2.9) + thread_safe (~> 0.1) + zeitwerk (2.4.2) + + PLATFORMS + #{specific_local_platform} + + DEPENDENCIES + activesupport + sidekiq-pro! + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "fails when running bundle update with an argument", :bundler => "3" do + initial_lockfile = lockfile + + bundle "update concurrent-ruby", :raise_on_error => false + + expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.") + + expect(lockfile).to eq(initial_lockfile) end end end @@ -551,7 +694,7 @@ RSpec.describe "bundle install with gems on multiple sources" do end end - context "when a pinned gem has an indirect dependency with more than one level of indirection in the default source ", :bundler => "< 3" do + context "when a pinned gem has an indirect dependency with more than one level of indirection in the default source " do before do build_repo gem_repo3 do build_gem "handsoap", "0.2.5.5" do |s| @@ -578,12 +721,38 @@ RSpec.describe "bundle install with gems on multiple sources" do G end - it "installs from the proper sources without any warnings or errors" do + it "installs from the default source without any warnings or errors and generates a proper lockfile" do + expected_lockfile = <<~L + GEM + remote: #{file_uri_for(gem_repo2)}/ + specs: + nokogiri (1.11.1) + racca (~> 1.4) + racca (1.5.2) + + GEM + remote: #{file_uri_for(gem_repo3)}/ + specs: + handsoap (0.2.5.5) + nokogiri (>= 1.2.3) + + PLATFORMS + #{specific_local_platform} + + DEPENDENCIES + handsoap! + nokogiri + + BUNDLED WITH + #{Bundler::VERSION} + L + bundle "install --verbose" expect(err).not_to include("Warning") expect(the_bundle).to include_gems("handsoap 0.2.5.5", "nokogiri 1.11.1", "racca 1.5.2") expect(the_bundle).to include_gems("handsoap 0.2.5.5", :source => "remote3") expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", :source => "remote2") + expect(lockfile).to eq(expected_lockfile) # Even if the gems are already installed FileUtils.rm bundled_app_lock @@ -592,6 +761,7 @@ RSpec.describe "bundle install with gems on multiple sources" do expect(the_bundle).to include_gems("handsoap 0.2.5.5", "nokogiri 1.11.1", "racca 1.5.2") expect(the_bundle).to include_gems("handsoap 0.2.5.5", :source => "remote3") expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", :source => "remote2") + expect(lockfile).to eq(expected_lockfile) end end @@ -619,6 +789,9 @@ RSpec.describe "bundle install with gems on multiple sources" do lockfile <<-L GEM remote: #{file_uri_for(gem_repo1)} + specs: + + GEM remote: #{file_uri_for(gem_repo3)} specs: rack (0.9.1) @@ -644,6 +817,84 @@ RSpec.describe "bundle install with gems on multiple sources" do end end + context "with a lockfile with aggregated rubygems sources" do + let(:aggregate_gem_section_lockfile) do + <<~L + GEM + remote: #{file_uri_for(gem_repo1)}/ + remote: #{file_uri_for(gem_repo3)}/ + specs: + rack (0.9.1) + + PLATFORMS + #{specific_local_platform} + + DEPENDENCIES + rack! + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + let(:split_gem_section_lockfile) do + <<~L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + + GEM + remote: #{file_uri_for(gem_repo3)}/ + specs: + rack (0.9.1) + + PLATFORMS + #{specific_local_platform} + + DEPENDENCIES + rack! + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + before do + build_repo gem_repo3 do + build_gem "rack", "0.9.1" + end + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + source "#{file_uri_for(gem_repo3)}" do + gem 'rack' + end + G + + lockfile aggregate_gem_section_lockfile + end + + it "installs the existing lockfile but prints a warning", :bundler => "< 3" do + bundle "config set --local deployment true" + + bundle "install" + + expect(lockfile).to eq(aggregate_gem_section_lockfile) + expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.") + expect(the_bundle).to include_gems("rack 0.9.1", :source => "remote3") + end + + it "refuses to install the existing lockfile and prints an error", :bundler => "3" do + bundle "config set --local deployment true" + + bundle "install", :raise_on_error =>false + + expect(lockfile).to eq(aggregate_gem_section_lockfile) + expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.") + expect(out).to be_empty + end + end + context "with a path gem in the same Gemfile" do before do build_lib "foo" @@ -825,13 +1076,34 @@ RSpec.describe "bundle install with gems on multiple sources" do G end - it "keeps the old version", :bundler => "< 3" do - expect(the_bundle).to include_gems("rack 1.0.0") + it "installs the higher version in the new repo" do + expect(the_bundle).to include_gems("rack 1.2") + end + end + + it "doesn't update version when a gem uses a source block but a higher version from another source is already installed locally" do + build_repo2 do + build_gem "example", "0.1.0" end - it "installs the higher version in the new repo", :bundler => "3" do - expect(the_bundle).to include_gems("rack 1.2") + build_repo4 do + build_gem "example", "1.0.2" end + + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + + gem "example", :source => "#{file_uri_for(gem_repo2)}" + G + + bundle "info example" + expect(out).to include("example (0.1.0)") + + system_gems "example-1.0.2", :path => default_bundle_path, :gem_repo => gem_repo4 + + bundle "update example --verbose" + expect(out).not_to include("Using example 1.0.2") + expect(out).to include("Using example 0.1.0") end context "when a gem is available from multiple ambiguous sources", :bundler => "3" do diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb index 7ab0ded26d..326ec51214 100644 --- a/spec/bundler/install/gems/flex_spec.rb +++ b/spec/bundler/install/gems/flex_spec.rb @@ -245,37 +245,7 @@ RSpec.describe "bundle flex_install" do end describe "when adding a new source" do - it "updates the lockfile", :bundler => "< 3" do - build_repo2 - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" - G - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - source "#{file_uri_for(gem_repo2)}" - gem "rack" - G - - lockfile_should_be <<-L - GEM - remote: #{file_uri_for(gem_repo1)}/ - remote: #{file_uri_for(gem_repo2)}/ - specs: - rack (1.0.0) - - PLATFORMS - #{lockfile_platforms} - - DEPENDENCIES - rack - - BUNDLED WITH - #{Bundler::VERSION} - L - end - - it "updates the lockfile", :bundler => "3" do + it "updates the lockfile" do build_repo2 install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb index 035ed9a7f3..94fac0052c 100644 --- a/spec/bundler/install/gems/resolving_spec.rb +++ b/spec/bundler/install/gems/resolving_spec.rb @@ -3,6 +3,32 @@ RSpec.describe "bundle install with install-time dependencies" do before do build_repo2 do + build_gem "with_implicit_rake_dep" do |s| + s.extensions << "Rakefile" + s.write "Rakefile", <<-RUBY + task :default do + path = File.expand_path("../lib", __FILE__) + FileUtils.mkdir_p(path) + File.open("\#{path}/implicit_rake_dep.rb", "w") do |f| + f.puts "IMPLICIT_RAKE_DEP = 'YES'" + end + end + RUBY + end + + build_gem "another_implicit_rake_dep" do |s| + s.extensions << "Rakefile" + s.write "Rakefile", <<-RUBY + task :default do + path = File.expand_path("../lib", __FILE__) + FileUtils.mkdir_p(path) + File.open("\#{path}/another_implicit_rake_dep.rb", "w") do |f| + f.puts "ANOTHER_IMPLICIT_RAKE_DEP = 'YES'" + end + end + RUBY + end + # Test complicated gem dependencies for install build_gem "net_a" do |s| s.add_dependency "net_b" @@ -55,6 +81,25 @@ RSpec.describe "bundle install with install-time dependencies" do expect(out).to eq("YES\nYES") end + it "installs gems with implicit rake dependencies without rake previously installed" do + with_path_as("") do + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "with_implicit_rake_dep" + gem "another_implicit_rake_dep" + gem "rake" + G + end + + run <<-R + require 'implicit_rake_dep' + require 'another_implicit_rake_dep' + puts IMPLICIT_RAKE_DEP + puts ANOTHER_IMPLICIT_RAKE_DEP + R + expect(out).to eq("YES\nYES") + end + it "installs gems with a dependency with no type" do skip "incorrect data check error" if Gem.win_platform? diff --git a/spec/bundler/install/gems/sudo_spec.rb b/spec/bundler/install/gems/sudo_spec.rb index ff73b4a1fa..3e5d38ea4c 100644 --- a/spec/bundler/install/gems/sudo_spec.rb +++ b/spec/bundler/install/gems/sudo_spec.rb @@ -49,8 +49,23 @@ RSpec.describe "when using sudo", :sudo => true do end it "installs rake and a gem dependent on rake in the same session" do + build_repo2 do + build_gem "another_implicit_rake_dep" do |s| + s.extensions << "Rakefile" + s.write "Rakefile", <<-RUBY + task :default do + path = File.expand_path("../lib", __FILE__) + FileUtils.mkdir_p(path) + File.open("\#{path}/another_implicit_rake_dep.rb", "w") do |f| + f.puts "ANOTHER_IMPLICIT_RAKE_DEP = 'YES'" + end + end + RUBY + end + end + gemfile <<-G - source "#{file_uri_for(gem_repo1)}" + source "#{file_uri_for(gem_repo2)}" gem "rake" gem "another_implicit_rake_dep" G diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index d3591cd62a..639ccb87ea 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -318,40 +318,7 @@ RSpec.describe "the lockfile format" do G end - it "generates a lockfile without credentials for a configured source", :bundler => "< 3" do - bundle "config set http://localgemserver.test/ user:pass" - - install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true) - source "http://localgemserver.test/" do - - end - - source "http://user:pass@othergemserver.test/" do - gem "rack-obama", ">= 1.0" - end - G - - lockfile_should_be <<-G - GEM - remote: http://localgemserver.test/ - remote: http://user:pass@othergemserver.test/ - specs: - rack (1.0.0) - rack-obama (1.0) - rack - - PLATFORMS - #{lockfile_platforms} - - DEPENDENCIES - rack-obama (>= 1.0)! - - BUNDLED WITH - #{Bundler::VERSION} - G - end - - it "generates a lockfile without credentials for a configured source", :bundler => "3" do + it "generates a lockfile without credentials for a configured source" do bundle "config set http://localgemserver.test/ user:pass" install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true) diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb index 0758d29746..7dbb80af80 100644 --- a/spec/bundler/other/major_deprecation_spec.rb +++ b/spec/bundler/other/major_deprecation_spec.rb @@ -383,15 +383,53 @@ RSpec.describe "major deprecations" do "Your Gemfile contains multiple primary sources. " \ "Using `source` more than once without a block is a security risk, and " \ "may result in installing unexpected gems. To resolve this warning, use " \ - "a block to indicate which gems should come from the secondary source. " \ - "To upgrade this warning to an error, run `bundle config set --local " \ - "disable_multisource true`." + "a block to indicate which gems should come from the secondary source." ) end pending "fails with a helpful error", :bundler => "3" end + context "bundle install with a lockfile with a single rubygems section with multiple remotes" do + before do + build_repo gem_repo3 do + build_gem "rack", "0.9.1" + end + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + source "#{file_uri_for(gem_repo3)}" do + gem 'rack' + end + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo1)}/ + remote: #{file_uri_for(gem_repo3)}/ + specs: + rack (0.9.1) + + PLATFORMS + ruby + + DEPENDENCIES + rack! + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "shows a deprecation", :bundler => "< 3" do + bundle "install" + + expect(deprecations).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. You should run `bundle update` or generate your lockfile from scratch.") + end + + pending "fails with a helpful error", :bundler => "3" + end + context "when Bundler.setup is run in a ruby script" do before do create_file "gems.rb" diff --git a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock index 6945be3ed2..05bcb877db 100644 --- a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock +++ b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock @@ -6,7 +6,7 @@ PATH GEM remote: https://rubygems.org/ specs: - jruby-jars (9.2.14.0) + jruby-jars (9.2.16.0) jruby-rack (1.1.21) rake (13.0.1) rubyzip (1.3.0) @@ -19,6 +19,7 @@ GEM PLATFORMS java ruby + universal-java-11 DEPENDENCIES demo! @@ -26,4 +27,4 @@ DEPENDENCIES warbler (~> 2.0) BUNDLED WITH - 2.2.0.rc.2 + 2.3.0.dev diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb index 68d8537715..6587ce5d55 100644 --- a/spec/bundler/spec_helper.rb +++ b/spec/bundler/spec_helper.rb @@ -89,6 +89,8 @@ RSpec.configure do |config| end config.before :all do + check_test_gems! + build_repo1 reset_paths! diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb index c76c3f505e..d593ced27e 100644 --- a/spec/bundler/support/builders.rb +++ b/spec/bundler/support/builders.rb @@ -30,7 +30,11 @@ module Spec end def build_repo1 + rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first + build_repo gem_repo1 do + FileUtils.cp rake_path, "#{gem_repo1}/gems/" + build_gem "rack", %w[0.9.1 1.0.0] do |s| s.executables = "rackup" s.post_install_message = "Rack's post install message" @@ -150,32 +154,6 @@ module Spec build_gem "duradura", "7.0" - build_gem "with_implicit_rake_dep" do |s| - s.extensions << "Rakefile" - s.write "Rakefile", <<-RUBY - task :default do - path = File.expand_path("../lib", __FILE__) - FileUtils.mkdir_p(path) - File.open("\#{path}/implicit_rake_dep.rb", "w") do |f| - f.puts "IMPLICIT_RAKE_DEP = 'YES'" - end - end - RUBY - end - - build_gem "another_implicit_rake_dep" do |s| - s.extensions << "Rakefile" - s.write "Rakefile", <<-RUBY - task :default do - path = File.expand_path("../lib", __FILE__) - FileUtils.mkdir_p(path) - File.open("\#{path}/another_implicit_rake_dep.rb", "w") do |f| - f.puts "ANOTHER_IMPLICIT_RAKE_DEP = 'YES'" - end - end - RUBY - end - build_gem "very_simple_binary", &:add_c_extension build_gem "simple_binary", &:add_c_extension @@ -255,6 +233,13 @@ module Spec def build_repo(path, &blk) return if File.directory?(path) + + FileUtils.mkdir_p("#{path}/gems") + + update_repo(path, &blk) + end + + def check_test_gems! rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first if rake_path.nil? @@ -263,14 +248,9 @@ module Spec rake_path = Dir["#{Path.base_system_gems}/**/rake*.gem"].first end - if rake_path - FileUtils.mkdir_p("#{path}/gems") - FileUtils.cp rake_path, "#{path}/gems/" - else + if rake_path.nil? abort "Your test gems are missing! Run `rm -rf #{tmp}` and try again." end - - update_repo(path, &blk) end def update_repo(path) diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb index a7cc1ce810..7e2e92a1ec 100644 --- a/spec/bundler/support/helpers.rb +++ b/spec/bundler/support/helpers.rb @@ -130,7 +130,7 @@ module Spec def ruby(ruby, options = {}) ruby_cmd = build_ruby_cmd - escaped_ruby = RUBY_PLATFORM == "java" ? ruby.shellescape.dump : ruby.shellescape + escaped_ruby = ruby.shellescape sys_exec(%(#{ruby_cmd} -w -e #{escaped_ruby}), options) end diff --git a/spec/bundler/support/matchers.rb b/spec/bundler/support/matchers.rb index 5d129ed849..1613662981 100644 --- a/spec/bundler/support/matchers.rb +++ b/spec/bundler/support/matchers.rb @@ -114,30 +114,49 @@ module Spec match do opts = names.last.is_a?(Hash) ? names.pop : {} source = opts.delete(:source) - groups = Array(opts[:groups]) + groups = Array(opts.delete(:groups)).map(&:inspect).join(", ") opts[:raise_on_error] = false - groups << opts - @errors = names.map do |name| - name, version, platform = name.split(/\s+/) + @errors = names.map do |full_name| + name, version, platform = full_name.split(/\s+/) require_path = name == "bundler" ? "#{lib_dir}/bundler" : name.tr("-", "/") version_const = name == "bundler" ? "Bundler::VERSION" : Spec::Builders.constantize(name) - code = [] - code << "require '#{require_path}.rb'" - code << "puts #{version_const}" - run code.join("; "), *groups - actual_version, actual_platform = out.strip.split(/\s+/, 2) - unless Gem::Version.new(actual_version) == Gem::Version.new(version) + source_const = "#{Spec::Builders.constantize(name)}_SOURCE" + ruby <<~R, opts + require '#{lib_dir}/bundler' + Bundler.setup(#{groups}) + + require '#{require_path}.rb' + actual_version, actual_platform = #{version_const}.split(/\s+/, 2) + unless Gem::Version.new(actual_version) == Gem::Version.new('#{version}') + puts actual_version + exit 64 + end + unless actual_platform.to_s == '#{platform}' + puts actual_platform + exit 65 + end + require '#{require_path}/source' + exit 0 if #{source.nil?} + actual_source = #{source_const} + unless actual_source == '#{source}' + puts actual_source + exit 66 + end + R + next if exitstatus == 0 + if exitstatus == 64 + actual_version = out.split("\n").last next "#{name} was expected to be at version #{version} but was #{actual_version}" end - unless actual_platform == platform + if exitstatus == 65 + actual_platform = out.split("\n").last next "#{name} was expected to be of platform #{platform} but was #{actual_platform}" end - next unless source - source_const = "#{Spec::Builders.constantize(name)}_SOURCE" - run "require '#{require_path}/source'; puts #{source_const}", *groups - unless out.strip == source - next "Expected #{name} (#{version}) to be installed from `#{source}`, was actually from `#{out}`" + if exitstatus == 66 + actual_source = out.split("\n").last + next "Expected #{name} (#{version}) to be installed from `#{source}`, was actually from `#{actual_source}`" end + next "Command to check forgem inclusion of gem #{full_name} failed" end.compact @errors.empty? diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb index ad7285c082..84754402ad 100644 --- a/test/rubygems/test_gem_platform.rb +++ b/test/rubygems/test_gem_platform.rb @@ -134,9 +134,7 @@ class TestGemPlatform < Gem::TestCase 'i386-solaris2.8' => ['x86', 'solaris', '2.8'], 'mswin32' => ['x86', 'mswin32', nil], 'x86_64-linux' => ['x86_64', 'linux', nil], - 'x86_64-linux-gnu' => ['x86_64', 'linux', nil], 'x86_64-linux-musl' => ['x86_64', 'linux', 'musl'], - 'x86_64-linux-uclibc' => ['x86_64', 'linux', 'uclibc'], 'x86_64-openbsd3.9' => ['x86_64', 'openbsd', '3.9'], 'x86_64-openbsd4.0' => ['x86_64', 'openbsd', '4.0'], 'x86_64-openbsd' => ['x86_64', 'openbsd', nil], @@ -145,7 +143,6 @@ class TestGemPlatform < Gem::TestCase test_cases.each do |arch, expected| platform = Gem::Platform.new arch assert_equal expected, platform.to_a, arch.inspect - assert_equal expected, Gem::Platform.new(platform.to_s).to_a, arch.inspect end end @@ -264,32 +261,6 @@ class TestGemPlatform < Gem::TestCase assert((with_x86_arch === with_nil_arch), 'x86 =~ nil') end - def test_nil_version_is_treated_as_any_version - x86_darwin_8 = Gem::Platform.new 'i686-darwin8.0' - x86_darwin_nil = Gem::Platform.new 'i686-darwin' - - assert((x86_darwin_8 === x86_darwin_nil), '8.0 =~ nil') - assert((x86_darwin_nil === x86_darwin_8), 'nil =~ 8.0') - end - - def test_nil_version_is_stricter_for_linux_os - x86_linux = Gem::Platform.new 'i686-linux' - x86_linux_gnu = Gem::Platform.new 'i686-linux-gnu' - x86_linux_musl = Gem::Platform.new 'i686-linux-musl' - x86_linux_uclibc = Gem::Platform.new 'i686-linux-uclibc' - - assert((x86_linux === x86_linux_gnu), 'linux =~ linux-gnu') - assert((x86_linux_gnu === x86_linux), 'linux-gnu =~ linux') - assert(!(x86_linux_gnu === x86_linux_musl), 'linux-gnu =~ linux-musl') - assert(!(x86_linux_musl === x86_linux_gnu), 'linux-musl =~ linux-gnu') - assert(!(x86_linux_uclibc === x86_linux_musl), 'linux-uclibc =~ linux-musl') - assert(!(x86_linux_musl === x86_linux_uclibc), 'linux-musl =~ linux-uclibc') - assert(!(x86_linux === x86_linux_musl), 'linux =~ linux-musl') - assert(!(x86_linux_musl === x86_linux), 'linux-musl =~ linux') - assert(!(x86_linux === x86_linux_uclibc), 'linux =~ linux-uclibc') - assert(!(x86_linux_uclibc === x86_linux), 'linux-uclibc =~ linux') - end - def test_equals3_cpu_arm arm = Gem::Platform.new 'arm-linux' armv5 = Gem::Platform.new 'armv5-linux' diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb index 32600deee2..f08579c0cc 100644 --- a/test/rubygems/test_gem_remote_fetcher.rb +++ b/test/rubygems/test_gem_remote_fetcher.rb @@ -496,6 +496,44 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== assert_equal url, e.uri end + def test_fetch_path_timeout_error + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + + def fetcher.fetch_http(uri, mtime = nil, head = nil) + raise Timeout::Error, 'timed out' + end + + url = 'http://example.com/uri' + + e = assert_raises Gem::RemoteFetcher::FetchError do + fetcher.fetch_path url + end + + assert_match %r{Timeout::Error: timed out \(#{Regexp.escape url}\)\z}, + e.message + assert_equal url, e.uri + end + + def test_fetch_path_getaddrinfo_error + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + + def fetcher.fetch_http(uri, mtime = nil, head = nil) + raise SocketError, 'getaddrinfo: nodename nor servname provided' + end + + url = 'http://example.com/uri' + + e = assert_raises Gem::RemoteFetcher::FetchError do + fetcher.fetch_path url + end + + assert_match %r{SocketError: getaddrinfo: nodename nor servname provided \(#{Regexp.escape url}\)\z}, + e.message + assert_equal url, e.uri + end + def test_fetch_path_openssl_ssl_sslerror fetcher = Gem::RemoteFetcher.new nil @fetcher = fetcher |