From a0025b6e5d440139ebc16a555cddb4dc9dd45449 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 21 Feb 2025 09:27:47 +0900 Subject: [PATCH] Merge RubyGems-3.6.4 and Bundler-2.6.4 --- lib/bundler/cli/console.rb | 14 +- lib/bundler/cli/doctor.rb | 14 +- lib/bundler/cli/info.rb | 8 +- lib/bundler/cli/issue.rb | 2 +- lib/bundler/cli/show.rb | 2 +- lib/bundler/current_ruby.rb | 56 ++--- lib/bundler/definition.rb | 118 ++++++----- lib/bundler/dependency.rb | 139 +++++++----- lib/bundler/dsl.rb | 161 +++++++------- lib/bundler/endpoint_specification.rb | 13 +- lib/bundler/errors.rb | 4 + lib/bundler/gem_helpers.rb | 14 +- lib/bundler/gem_version_promoter.rb | 2 - lib/bundler/installer.rb | 2 +- lib/bundler/lazy_specification.rb | 97 +++++---- lib/bundler/match_metadata.rb | 13 ++ lib/bundler/resolver.rb | 13 +- lib/bundler/resolver/package.rb | 10 +- lib/bundler/resolver/spec_group.rb | 26 +-- lib/bundler/rubygems_ext.rb | 165 +++++++-------- lib/bundler/rubygems_integration.rb | 5 +- lib/bundler/runtime.rb | 43 ++-- lib/bundler/source.rb | 2 + lib/bundler/source/rubygems.rb | 23 +- lib/bundler/source_list.rb | 4 + lib/bundler/spec_set.rb | 77 ++++--- lib/bundler/templates/newgem/Gemfile.tt | 1 + lib/bundler/version.rb | 2 +- lib/rubygems.rb | 2 +- lib/rubygems/requirement.rb | 8 +- lib/rubygems/specification.rb | 10 +- spec/bundler/bundler/current_ruby_spec.rb | 140 ++++++++++++ spec/bundler/bundler/dependency_spec.rb | 141 +------------ spec/bundler/bundler/dsl_spec.rb | 8 +- .../bundler/fetcher/downloader_spec.rb | 2 +- .../bundler/gem_version_promoter_spec.rb | 10 +- spec/bundler/bundler/lockfile_parser_spec.rb | 6 +- spec/bundler/bundler/source_spec.rb | 6 +- spec/bundler/cache/gems_spec.rb | 7 +- spec/bundler/commands/cache_spec.rb | 51 ++++- spec/bundler/commands/console_spec.rb | 199 +++++++++++------- spec/bundler/commands/doctor_spec.rb | 24 ++- spec/bundler/commands/info_spec.rb | 16 +- spec/bundler/commands/install_spec.rb | 50 ++++- spec/bundler/commands/lock_spec.rb | 33 ++- spec/bundler/commands/show_spec.rb | 4 +- spec/bundler/commands/update_spec.rb | 94 ++++++++- spec/bundler/install/gemfile/gemspec_spec.rb | 57 ++--- spec/bundler/install/gemfile/git_spec.rb | 2 +- spec/bundler/install/gemfile/platform_spec.rb | 6 +- .../install/gemfile/specific_platform_spec.rb | 14 +- spec/bundler/install/gemfile_spec.rb | 38 +++- .../install/gems/dependency_api_spec.rb | 2 +- spec/bundler/install/gems/flex_spec.rb | 24 +++ spec/bundler/install/gems/resolving_spec.rb | 4 +- spec/bundler/lock/lockfile_spec.rb | 84 +++++++- spec/bundler/other/ext_spec.rb | 20 +- .../realworld/fixtures/warbler/Gemfile.lock | 2 +- spec/bundler/resolver/basic_spec.rb | 20 +- spec/bundler/runtime/platform_spec.rb | 16 +- spec/bundler/runtime/require_spec.rb | 53 ++--- spec/bundler/support/builders.rb | 4 + spec/bundler/support/filters.rb | 13 +- spec/bundler/support/hax.rb | 17 +- spec/bundler/support/helpers.rb | 10 - spec/bundler/support/indexes.rb | 1 - spec/bundler/support/path.rb | 2 +- spec/bundler/support/platforms.rb | 66 ++---- spec/bundler/support/the_bundle.rb | 8 + .../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_gem_specification.rb | 12 +- 74 files changed, 1355 insertions(+), 981 deletions(-) create mode 100644 spec/bundler/bundler/current_ruby_spec.rb diff --git a/lib/bundler/cli/console.rb b/lib/bundler/cli/console.rb index 2ba5e69bde..f6389e8ea0 100644 --- a/lib/bundler/cli/console.rb +++ b/lib/bundler/cli/console.rb @@ -20,9 +20,14 @@ module Bundler require name get_constant(name) rescue LoadError - Bundler.ui.error "Couldn't load console #{name}, falling back to irb" - require "irb" - get_constant("irb") + if name == "irb" + Bundler.ui.error "#{name} is not available" + exit 1 + else + Bundler.ui.error "Couldn't load console #{name}, falling back to irb" + name = "irb" + retry + end end def get_constant(name) @@ -32,9 +37,6 @@ module Bundler "irb" => :IRB, }[name] Object.const_get(const_name) - rescue NameError - Bundler.ui.error "Could not find constant #{const_name}" - exit 1 end end end diff --git a/lib/bundler/cli/doctor.rb b/lib/bundler/cli/doctor.rb index ce016e3ad2..5365338823 100644 --- a/lib/bundler/cli/doctor.rb +++ b/lib/bundler/cli/doctor.rb @@ -2,7 +2,6 @@ require "rbconfig" require "shellwords" -require "fiddle" module Bundler class CLI::Doctor @@ -57,6 +56,14 @@ module Bundler Dir.glob("#{spec.full_gem_path}/**/*.bundle") end + def lookup_with_fiddle(path) + require "fiddle" + Fiddle.dlopen(path) + false + rescue Fiddle::DLError + true + end + def check! require_relative "check" Bundler::CLI::Check.new({}).run @@ -73,10 +80,7 @@ module Bundler definition.specs.each do |spec| bundles_for_gem(spec).each do |bundle| bad_paths = dylibs(bundle).select do |f| - Fiddle.dlopen(f) - false - rescue Fiddle::DLError - true + lookup_with_fiddle(f) end if bad_paths.any? broken_links[spec] ||= [] diff --git a/lib/bundler/cli/info.rb b/lib/bundler/cli/info.rb index d7a8530fba..cd01d4949b 100644 --- a/lib/bundler/cli/info.rb +++ b/lib/bundler/cli/info.rb @@ -39,8 +39,8 @@ module Bundler path = File.expand_path("../../..", __dir__) else path = spec.full_gem_path - if spec.deleted_gem? - return Bundler.ui.warn "The gem #{name} has been deleted. It was installed at: #{path}" + if spec.installation_missing? + return Bundler.ui.warn "The gem #{name} is missing. It should be installed at #{path}, but was not found" end end @@ -65,8 +65,8 @@ module Bundler gem_info << "\tDefault Gem: yes\n" if spec.respond_to?(:default_gem?) && spec.default_gem? gem_info << "\tReverse Dependencies: \n\t\t#{gem_dependencies.join("\n\t\t")}" if gem_dependencies.any? - if name != "bundler" && spec.deleted_gem? - return Bundler.ui.warn "The gem #{name} has been deleted. Gemspec information is still available though:\n#{gem_info}" + if name != "bundler" && spec.installation_missing? + return Bundler.ui.warn "The gem #{name} is missing. Gemspec information is still available though:\n#{gem_info}" end Bundler.ui.info gem_info diff --git a/lib/bundler/cli/issue.rb b/lib/bundler/cli/issue.rb index 5f2924c4bd..e16c9b0e39 100644 --- a/lib/bundler/cli/issue.rb +++ b/lib/bundler/cli/issue.rb @@ -10,7 +10,7 @@ module Bundler be sure to check out these resources: 1. Check out our troubleshooting guide for quick fixes to common issues: - https://github.com/rubygems/rubygems/blob/master/bundler/doc/TROUBLESHOOTING.md + https://github.com/rubygems/rubygems/blob/master/doc/bundler/TROUBLESHOOTING.md 2. Instructions for common Bundler uses can be found on the documentation site: https://bundler.io/ diff --git a/lib/bundler/cli/show.rb b/lib/bundler/cli/show.rb index 1896a0b11c..13bb8b774b 100644 --- a/lib/bundler/cli/show.rb +++ b/lib/bundler/cli/show.rb @@ -24,7 +24,7 @@ module Bundler return unless spec path = spec.full_gem_path unless File.directory?(path) - return Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at: #{path}" + return Bundler.ui.warn "The gem #{gem_name} is missing. It should be installed at #{path}, but was not found" end end return Bundler.ui.info(path) diff --git a/lib/bundler/current_ruby.rb b/lib/bundler/current_ruby.rb index 93e0c401c0..a8ccfed0f2 100644 --- a/lib/bundler/current_ruby.rb +++ b/lib/bundler/current_ruby.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative "rubygems_ext" + module Bundler # Returns current version of Ruby # @@ -9,38 +11,25 @@ module Bundler end class CurrentRuby - KNOWN_MINOR_VERSIONS = %w[ - 1.8 - 1.9 - 2.0 - 2.1 - 2.2 - 2.3 - 2.4 - 2.5 - 2.6 - 2.7 - 3.0 - 3.1 - 3.2 - 3.3 - ].freeze - - KNOWN_MAJOR_VERSIONS = KNOWN_MINOR_VERSIONS.map {|v| v.split(".", 2).first }.uniq.freeze - - KNOWN_PLATFORMS = %w[ - jruby - maglev - mingw - mri - mswin - mswin64 - rbx - ruby - truffleruby - windows - x64_mingw - ].freeze + ALL_RUBY_VERSIONS = (18..27).to_a.concat((30..35).to_a).freeze + KNOWN_MINOR_VERSIONS = ALL_RUBY_VERSIONS.map {|v| v.digits.reverse.join(".") }.freeze + KNOWN_MAJOR_VERSIONS = ALL_RUBY_VERSIONS.map {|v| v.digits.last.to_s }.uniq.freeze + PLATFORM_MAP = { + ruby: [Gem::Platform::RUBY, CurrentRuby::ALL_RUBY_VERSIONS], + mri: [Gem::Platform::RUBY, CurrentRuby::ALL_RUBY_VERSIONS], + rbx: [Gem::Platform::RUBY], + truffleruby: [Gem::Platform::RUBY], + jruby: [Gem::Platform::JAVA, [18, 19]], + windows: [Gem::Platform::WINDOWS, CurrentRuby::ALL_RUBY_VERSIONS], + # deprecated + mswin: [Gem::Platform::MSWIN, CurrentRuby::ALL_RUBY_VERSIONS], + mswin64: [Gem::Platform::MSWIN64, CurrentRuby::ALL_RUBY_VERSIONS - [18]], + mingw: [Gem::Platform::UNIVERSAL_MINGW, CurrentRuby::ALL_RUBY_VERSIONS], + x64_mingw: [Gem::Platform::UNIVERSAL_MINGW, CurrentRuby::ALL_RUBY_VERSIONS - [18, 19]], + }.each_with_object({}) do |(platform, spec), hash| + hash[platform] = spec[0] + spec[1]&.each {|version| hash[:"#{platform}_#{version}"] = spec[0] } + end.freeze def ruby? return true if Bundler::GemHelpers.generic_local_platform_is_ruby? @@ -82,7 +71,8 @@ module Bundler RUBY_VERSION.start_with?("#{version}.") end - KNOWN_PLATFORMS.each do |platform| + all_platforms = PLATFORM_MAP.keys << "maglev" + all_platforms.each do |platform| define_method(:"#{platform}_#{trimmed_version}?") do send(:"#{platform}?") && send(:"on_#{trimmed_version}?") end diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index c31682ce4e..24dae86493 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -58,17 +58,28 @@ module Bundler # @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version # @param optional_groups [Array(String)] A list of optional groups def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = []) - if [true, false].include?(unlock) + unlock ||= {} + + if unlock == true + @unlocking_all = true @unlocking_bundler = false @unlocking = unlock + @sources_to_unlock = [] + @unlocking_ruby = false + @explicit_unlocks = [] + conservative = false else + @unlocking_all = false @unlocking_bundler = unlock.delete(:bundler) @unlocking = unlock.any? {|_k, v| !Array(v).empty? } + @sources_to_unlock = unlock.delete(:sources) || [] + @unlocking_ruby = unlock.delete(:ruby) + @explicit_unlocks = unlock.delete(:gems) || [] + conservative = unlock.delete(:conservative) end @dependencies = dependencies @sources = sources - @unlock = unlock @optional_groups = optional_groups @prefer_local = false @specs = nil @@ -93,29 +104,24 @@ module Bundler @platforms = @locked_platforms.dup @locked_bundler_version = @locked_gems.bundler_version @locked_ruby_version = @locked_gems.ruby_version - @originally_locked_deps = @locked_gems.dependencies + @locked_deps = @locked_gems.dependencies @originally_locked_specs = SpecSet.new(@locked_gems.specs) @locked_checksums = @locked_gems.checksums - if unlock != true - @locked_deps = @originally_locked_deps - @locked_specs = @originally_locked_specs - @locked_sources = @locked_gems.sources - else - @unlock = {} - @locked_deps = {} + if @unlocking_all @locked_specs = SpecSet.new([]) @locked_sources = [] + else + @locked_specs = @originally_locked_specs + @locked_sources = @locked_gems.sources end else - @unlock = {} - @locked_gems = nil + @locked_gems = nil @locked_platforms = [] @most_specific_locked_platform = nil @platforms = [] @locked_deps = {} @locked_specs = SpecSet.new([]) - @originally_locked_deps = {} @originally_locked_specs = @locked_specs @locked_sources = [] @locked_checksums = Bundler.feature_flag.lockfile_checksums? @@ -134,11 +140,10 @@ module Bundler @sources.merged_gem_lockfile_sections!(locked_gem_sources.first) end - @sources_to_unlock = @unlock.delete(:sources) || [] - @unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object + @unlocking_ruby ||= if @ruby_version && locked_ruby_version_object @ruby_version.diff(locked_ruby_version_object) end - @unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version) + @unlocking ||= @unlocking_ruby ||= (!@locked_ruby_version ^ !@ruby_version) @current_platform_missing = add_current_platform unless Bundler.frozen_bundle? @@ -146,9 +151,7 @@ module Bundler @path_changes = converge_paths @source_changes = converge_sources - @explicit_unlocks = @unlock.delete(:gems) || [] - - if @unlock[:conservative] + if conservative @gems_to_unlock = @explicit_unlocks.any? ? @explicit_unlocks : @dependencies.map(&:name) else eager_unlock = @explicit_unlocks.map {|name| Dependency.new(name, ">= 0") } @@ -220,6 +223,8 @@ module Bundler def prefer_local! @prefer_local = true + + sources.prefer_local! end # For given dependency list returns a SpecSet with Gemspec of all the required @@ -374,7 +379,7 @@ module Bundler def locked_ruby_version return unless ruby_version - if @unlock[:ruby] || !@locked_ruby_version + if @unlocking_ruby || !@locked_ruby_version Bundler::RubyVersion.system else @locked_ruby_version @@ -435,23 +440,23 @@ module Bundler changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`" end - reason = nothing_changed? ? "some dependencies were deleted from your gemfile" : change_reason + reason = resolve_needed? ? change_reason : "some dependencies were deleted from your gemfile" msg = String.new msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because frozen mode is set" msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any? msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any? msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any? - msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_gemfile_path} to version control.\n" + msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_gemfile_path} to version control.\n" unless unlocking? unless explicit_flag suggested_command = unless Bundler.settings.locations("frozen").keys.include?(:env) "bundle config set frozen false" end - msg << "If this is a development machine, remove the #{SharedHelpers.relative_lockfile_path} " \ + msg << "\n\nIf this is a development machine, remove the #{SharedHelpers.relative_lockfile_path} " \ "freeze by running `#{suggested_command}`." if suggested_command end - raise ProductionError, msg if added.any? || deleted.any? || changed.any? || !nothing_changed? + raise ProductionError, msg if added.any? || deleted.any? || changed.any? || resolve_needed? end def validate_runtime! @@ -620,8 +625,8 @@ module Bundler @resolution_packages ||= begin last_resolve = converge_locked_specs remove_invalid_platforms! - packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local) - packages = additional_base_requirements_to_prevent_downgrades(packages, last_resolve) + packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @unlocking_all || @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: @new_platforms) + packages = additional_base_requirements_to_prevent_downgrades(packages) packages = additional_base_requirements_to_force_updates(packages) packages end @@ -639,6 +644,8 @@ module Bundler specs = begin resolve.materialize(dependencies) rescue IncorrectLockfileDependencies => e + raise if Bundler.frozen_bundle? + spec = e.spec raise "Infinite loop while fixing lockfile dependencies" if incorrect_spec == spec @@ -761,6 +768,7 @@ module Bundler @most_specific_non_local_locked_ruby_platform = find_most_specific_locked_ruby_platform return if @most_specific_non_local_locked_ruby_platform + @new_platforms << local_platform @platforms << local_platform true end @@ -782,7 +790,7 @@ module Bundler unlock_reason = if unlock_targets "#{unlock_targets.first}: (#{unlock_targets.last.join(", ")})" else - @unlock[:ruby] ? "ruby" : "" + @unlocking_ruby ? "ruby" : "" end return "bundler is unlocking #{unlock_reason}" @@ -852,8 +860,6 @@ module Bundler end def check_lockfile - @missing_lockfile_dep = nil - @locked_spec_with_invalid_deps = nil @locked_spec_with_missing_deps = nil @@ -871,10 +877,6 @@ module Bundler @locked_specs.delete(missing) @locked_spec_with_missing_deps = missing.first.name - elsif !@dependency_changes - @missing_lockfile_dep = current_dependencies.find do |d| - @locked_specs[d.name].empty? && d.name != "bundler" - end&.name end if invalid.any? @@ -935,32 +937,33 @@ module Bundler end def converge_dependencies - changes = false + @missing_lockfile_dep = nil + @changed_dependencies = [] - @dependencies.each do |dep| + current_dependencies.each do |dep| if dep.source dep.source = sources.get(dep.source) end - unless locked_dep = @originally_locked_deps[dep.name] - changes = true - next + name = dep.name + + dep_changed = @locked_deps[name].nil? + + unless name == "bundler" + locked_specs = @originally_locked_specs[name] + + if locked_specs.any? && !dep.matches_spec?(locked_specs.first) + @gems_to_unlock << name + dep_changed = true + elsif locked_specs.empty? && dep_changed == false + @missing_lockfile_dep = name + end end - # Gem::Dependency#== matches Gem::Dependency#type. As the lockfile - # doesn't carry a notion of the dependency type, if you use - # add_development_dependency in a gemspec that's loaded with the gemspec - # directive, the lockfile dependencies and resolved dependencies end up - # with a mismatch on #type. Work around that by setting the type on the - # dep from the lockfile. - locked_dep.instance_variable_set(:@type, dep.type) - - # We already know the name matches from the hash lookup - # so we only need to check the requirement now - changes ||= dep.requirement != locked_dep.requirement + @changed_dependencies << name if dep_changed end - changes + @changed_dependencies.any? end # Remove elements from the locked specs that are expired. This will most @@ -1022,11 +1025,6 @@ module Bundler end end - if dep.nil? && requested_dep = requested_dependencies.find {|d| name == d.name } - @gems_to_unlock << name - deps << requested_dep - end - converged << s end @@ -1095,11 +1093,15 @@ module Bundler current == proposed end - def additional_base_requirements_to_prevent_downgrades(resolution_packages, last_resolve) + def additional_base_requirements_to_prevent_downgrades(resolution_packages) return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources) - converge_specs(@originally_locked_specs - last_resolve).each do |locked_spec| + @originally_locked_specs.each do |locked_spec| next if locked_spec.source.is_a?(Source::Path) - resolution_packages.base_requirements[locked_spec.name] = Gem::Requirement.new(">= #{locked_spec.version}") + + name = locked_spec.name + next if @changed_dependencies.include?(name) + + resolution_packages.base_requirements[name] = Gem::Requirement.new(">= #{locked_spec.version}") end resolution_packages end diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb index 09a145b8c8..e81696ff42 100644 --- a/lib/bundler/dependency.rb +++ b/lib/bundler/dependency.rb @@ -2,51 +2,92 @@ require "rubygems/dependency" require_relative "shared_helpers" -require_relative "rubygems_ext" module Bundler class Dependency < Gem::Dependency - attr_reader :autorequire - attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref, :glob - - ALL_RUBY_VERSIONS = (18..27).to_a.concat((30..35).to_a).freeze - PLATFORM_MAP = { - ruby: [Gem::Platform::RUBY, ALL_RUBY_VERSIONS], - mri: [Gem::Platform::RUBY, ALL_RUBY_VERSIONS], - rbx: [Gem::Platform::RUBY], - truffleruby: [Gem::Platform::RUBY], - jruby: [Gem::Platform::JAVA, [18, 19]], - windows: [Gem::Platform::WINDOWS, ALL_RUBY_VERSIONS], - # deprecated - mswin: [Gem::Platform::MSWIN, ALL_RUBY_VERSIONS], - mswin64: [Gem::Platform::MSWIN64, ALL_RUBY_VERSIONS - [18]], - mingw: [Gem::Platform::MINGW, ALL_RUBY_VERSIONS], - x64_mingw: [Gem::Platform::X64_MINGW, ALL_RUBY_VERSIONS - [18, 19]], - }.each_with_object({}) do |(platform, spec), hash| - hash[platform] = spec[0] - spec[1]&.each {|version| hash[:"#{platform}_#{version}"] = spec[0] } - end.freeze - def initialize(name, version, options = {}, &blk) type = options["type"] || :runtime super(name, version, type) - @autorequire = nil - @groups = Array(options["group"] || :default).map(&:to_sym) - @source = options["source"] - @path = options["path"] - @git = options["git"] - @github = options["github"] - @branch = options["branch"] - @ref = options["ref"] - @glob = options["glob"] - @platforms = Array(options["platforms"]) - @env = options["env"] - @should_include = options.fetch("should_include", true) - @gemfile = options["gemfile"] - @force_ruby_platform = options["force_ruby_platform"] if options.key?("force_ruby_platform") + @options = options + end - @autorequire = Array(options["require"] || []) if options.key?("require") + def groups + @groups ||= Array(@options["group"] || :default).map(&:to_sym) + end + + def source + return @source if defined?(@source) + + @source = @options["source"] + end + + def path + return @path if defined?(@path) + + @path = @options["path"] + end + + def git + return @git if defined?(@git) + + @git = @options["git"] + end + + def github + return @github if defined?(@github) + + @github = @options["github"] + end + + def branch + return @branch if defined?(@branch) + + @branch = @options["branch"] + end + + def ref + return @ref if defined?(@ref) + + @ref = @options["ref"] + end + + def glob + return @glob if defined?(@glob) + + @glob = @options["glob"] + end + + def platforms + @platforms ||= Array(@options["platforms"]) + end + + def env + return @env if defined?(@env) + + @env = @options["env"] + end + + def should_include + @should_include ||= @options.fetch("should_include", true) + end + + def gemfile + return @gemfile if defined?(@gemfile) + + @gemfile = @options["gemfile"] + end + + def force_ruby_platform + return @force_ruby_platform if defined?(@force_ruby_platform) + + @force_ruby_platform = @options["force_ruby_platform"] + end + + def autorequire + return @autorequire if defined?(@autorequire) + + @autorequire = Array(@options["require"] || []) if @options.key?("require") end RUBY_PLATFORM_ARRAY = [Gem::Platform::RUBY].freeze @@ -56,37 +97,41 @@ module Bundler # passed in the `valid_platforms` parameter def gem_platforms(valid_platforms) return RUBY_PLATFORM_ARRAY if force_ruby_platform - return valid_platforms if @platforms.empty? + return valid_platforms if platforms.empty? valid_platforms.select {|p| expanded_platforms.include?(GemHelpers.generic(p)) } end def expanded_platforms - @expanded_platforms ||= @platforms.filter_map {|pl| PLATFORM_MAP[pl] }.flatten.uniq + @expanded_platforms ||= platforms.filter_map {|pl| CurrentRuby::PLATFORM_MAP[pl] }.flatten.uniq end def should_include? - @should_include && current_env? && current_platform? + should_include && current_env? && current_platform? end def gemspec_dev_dep? - type == :development + @gemspec_dev_dep ||= @options.fetch("gemspec_dev_dep", false) + end + + def gemfile_dep? + !gemspec_dev_dep? end def current_env? - return true unless @env - if @env.is_a?(Hash) - @env.all? do |key, val| + return true unless env + if env.is_a?(Hash) + env.all? do |key, val| ENV[key.to_s] && (val.is_a?(String) ? ENV[key.to_s] == val : ENV[key.to_s] =~ val) end else - ENV[@env.to_s] + ENV[env.to_s] end end def current_platform? - return true if @platforms.empty? - @platforms.any? do |p| + return true if platforms.empty? + platforms.any? do |p| Bundler.current_ruby.send("#{p}?") end end diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index 05c60f2f1a..c99520d8db 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -13,10 +13,10 @@ module Bundler builder.to_definition(lockfile, unlock) end - VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze + VALID_PLATFORMS = Bundler::CurrentRuby::PLATFORM_MAP.keys.freeze VALID_KEYS = %w[group groups git path glob name branch ref tag require submodules - platform platforms type source install_if gemfile force_ruby_platform].freeze + platform platforms source install_if force_ruby_platform].freeze GITHUB_PULL_REQUEST_URL = %r{\Ahttps://github\.com/([A-Za-z0-9_\-\.]+/[A-Za-z0-9_\-\.]+)/pull/(\d+)\z} GITLAB_MERGE_REQUEST_URL = %r{\Ahttps://gitlab\.com/([A-Za-z0-9_\-\./]+)/-/merge_requests/(\d+)\z} @@ -77,12 +77,12 @@ module Bundler @gemspecs << spec - gem spec.name, name: spec.name, path: path, glob: glob + path path, "glob" => glob, "name" => spec.name do + add_dependency spec.name + end - group(development_group) do - spec.development_dependencies.each do |dep| - gem dep.name, *(dep.requirement.as_list + [type: :development]) - end + spec.development_dependencies.each do |dep| + add_dependency dep.name, dep.requirement.as_list, "gemspec_dev_dep" => true, "group" => development_group end when 0 raise InvalidOption, "There are no gemspecs at #{expanded_path}" @@ -94,79 +94,11 @@ module Bundler def gem(name, *args) options = args.last.is_a?(Hash) ? args.pop.dup : {} - options["gemfile"] = @gemfile version = args || [">= 0"] normalize_options(name, version, options) - dep = Dependency.new(name, version, options) - - # if there's already a dependency with this name we try to prefer one - if current = @dependencies.find {|d| d.name == dep.name } - if current.requirement != dep.requirement - current_requirement_open = current.requirements_list.include?(">= 0") - - gemspec_dep = [dep, current].find(&:gemspec_dev_dep?) - if gemspec_dep - gemfile_dep = [dep, current].find(&:runtime?) - - if gemfile_dep && !current_requirement_open - 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" - elsif gemfile_dep.nil? - require_relative "vendor/pub_grub/lib/pub_grub/version_range" - require_relative "vendor/pub_grub/lib/pub_grub/version_constraint" - require_relative "vendor/pub_grub/lib/pub_grub/version_union" - require_relative "vendor/pub_grub/lib/pub_grub/rubygems" - - current_gemspec_range = PubGrub::RubyGems.requirement_to_range(current.requirement) - next_gemspec_range = PubGrub::RubyGems.requirement_to_range(dep.requirement) - - if current_gemspec_range.intersects?(next_gemspec_range) - dep = Dependency.new(name, current.requirement.as_list + dep.requirement.as_list, options) - else - raise GemfileError, "Two gemspecs have conflicting requirements on the same gem: #{dep} and #{current}" - end - end - else - update_prompt = "" - - if File.basename(@gemfile) == Injector::INJECTED_GEMS - if dep.requirements_list.include?(">= 0") && !current_requirement_open - update_prompt = ". Gem already added" - else - update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`" - - update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current_requirement_open - end - end - - raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \ - "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \ - "#{update_prompt}" - end - end - - unless current.gemspec_dev_dep? && dep.gemspec_dev_dep? - # 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 " \ - "#{current.source || "an unspecified source"} and #{dep.source}\n" - else - Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \ - "You should probably keep only one of them.\n" \ - "Remove any duplicate entries and specify the gem only once.\n" \ - "While it's not a problem now, it could cause errors if you change the version of one of them later." - end - end - end - - @dependencies << dep + add_dependency(name, version, options) end def source(source, *args, &blk) @@ -301,6 +233,81 @@ module Bundler private + def add_dependency(name, version = nil, options = {}) + options["gemfile"] = @gemfile + options["source"] ||= @source + options["env"] ||= @env + + dep = Dependency.new(name, version, options) + + # if there's already a dependency with this name we try to prefer one + if current = @dependencies.find {|d| d.name == dep.name } + if current.requirement != dep.requirement + current_requirement_open = current.requirements_list.include?(">= 0") + + gemspec_dep = [dep, current].find(&:gemspec_dev_dep?) + if gemspec_dep + gemfile_dep = [dep, current].find(&:gemfile_dep?) + + if gemfile_dep && !current_requirement_open + 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" + elsif gemfile_dep.nil? + require_relative "vendor/pub_grub/lib/pub_grub/version_range" + require_relative "vendor/pub_grub/lib/pub_grub/version_constraint" + require_relative "vendor/pub_grub/lib/pub_grub/version_union" + require_relative "vendor/pub_grub/lib/pub_grub/rubygems" + + current_gemspec_range = PubGrub::RubyGems.requirement_to_range(current.requirement) + next_gemspec_range = PubGrub::RubyGems.requirement_to_range(dep.requirement) + + if current_gemspec_range.intersects?(next_gemspec_range) + dep = Dependency.new(name, current.requirement.as_list + dep.requirement.as_list, options) + else + raise GemfileError, "Two gemspec development dependencies have conflicting requirements on the same gem: #{dep} and #{current}" + end + end + else + update_prompt = "" + + if File.basename(@gemfile) == Injector::INJECTED_GEMS + if dep.requirements_list.include?(">= 0") && !current_requirement_open + update_prompt = ". Gem already added" + else + update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`" + + update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current_requirement_open + end + end + + raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \ + "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \ + "#{update_prompt}" + end + end + + unless current.gemspec_dev_dep? && dep.gemspec_dev_dep? + # 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 " \ + "#{current.source || "an unspecified source"} and #{dep.source}\n" + else + Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \ + "You should probably keep only one of them.\n" \ + "Remove any duplicate entries and specify the gem only once.\n" \ + "While it's not a problem now, it could cause errors if you change the version of one of them later." + end + end + end + + @dependencies << dep + end + def with_gemfile(gemfile) expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile&.parent) original_gemfile = @gemfile @@ -433,8 +440,6 @@ module Bundler opts["source"] = source end - opts["source"] ||= @source - opts["env"] ||= @env opts["platforms"] = platforms.dup opts["group"] = groups opts["should_include"] = install_if diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb index e4780a1c2a..c06684657d 100644 --- a/lib/bundler/endpoint_specification.rb +++ b/lib/bundler/endpoint_specification.rb @@ -6,7 +6,8 @@ module Bundler include MatchRemoteMetadata attr_reader :name, :version, :platform, :checksum - attr_accessor :remote, :dependencies, :locked_platform + attr_writer :dependencies + attr_accessor :remote, :locked_platform def initialize(name, version, platform, spec_fetcher, dependencies, metadata = nil) super() @@ -14,7 +15,8 @@ module Bundler @version = Gem::Version.create version @platform = Gem::Platform.new(platform) @spec_fetcher = spec_fetcher - @dependencies = dependencies.map {|dep, reqs| build_dependency(dep, reqs) } + @dependencies = nil + @unbuilt_dependencies = dependencies @loaded_from = nil @remote_specification = nil @@ -31,6 +33,11 @@ module Bundler @platform end + def dependencies + @dependencies ||= @unbuilt_dependencies.map! {|dep, reqs| build_dependency(dep, reqs) } + end + alias_method :runtime_dependencies, :dependencies + # needed for standalone, load required_paths from local gemspec # after the gem is installed def require_paths @@ -161,7 +168,7 @@ module Bundler end def build_dependency(name, requirements) - Gem::Dependency.new(name, requirements) + Dependency.new(name, requirements) end end end diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb index 3fa90c5eb8..9d3d89ffeb 100644 --- a/lib/bundler/errors.rb +++ b/lib/bundler/errors.rb @@ -254,6 +254,10 @@ module Bundler @spec = spec end + def message + "Bundler found incorrect dependencies in the lockfile for #{spec.full_name}" + end + status_code(41) end end diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb index 75243873f2..ad12bf89a4 100644 --- a/lib/bundler/gem_helpers.rb +++ b/lib/bundler/gem_helpers.rb @@ -4,20 +4,14 @@ module Bundler module GemHelpers GENERIC_CACHE = { Gem::Platform::RUBY => Gem::Platform::RUBY } # rubocop:disable Style/MutableConstant GENERICS = [ - [Gem::Platform.new("java"), Gem::Platform.new("java")], - [Gem::Platform.new("mswin32"), Gem::Platform.new("mswin32")], - [Gem::Platform.new("mswin64"), Gem::Platform.new("mswin64")], - [Gem::Platform.new("universal-mingw32"), Gem::Platform.new("universal-mingw32")], - [Gem::Platform.new("x64-mingw32"), Gem::Platform.new("x64-mingw32")], - [Gem::Platform.new("x86_64-mingw32"), Gem::Platform.new("x64-mingw32")], - [Gem::Platform.new("x64-mingw-ucrt"), Gem::Platform.new("x64-mingw-ucrt")], - [Gem::Platform.new("mingw32"), Gem::Platform.new("x86-mingw32")], + Gem::Platform::JAVA, + *Gem::Platform::WINDOWS, ].freeze def generic(p) GENERIC_CACHE[p] ||= begin - _, found = GENERICS.find do |match, _generic| - p.os == match.os && (!match.cpu || p.cpu == match.cpu) + found = GENERICS.find do |match| + p === match end found || Gem::Platform::RUBY end diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb index ecc65b4956..d64dbacfdb 100644 --- a/lib/bundler/gem_version_promoter.rb +++ b/lib/bundler/gem_version_promoter.rb @@ -132,8 +132,6 @@ module Bundler # Specific version moves can't always reliably be done during sorting # as not all elements are compared against each other. def post_sort(result, unlock, locked_version) - # default :major behavior in Bundler does not do this - return result if major? if unlock || locked_version.nil? result else diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index 1cd49d8897..c2da63c822 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -193,7 +193,7 @@ module Bundler def install(options) standalone = options[:standalone] force = options[:force] - local = options[:local] + local = options[:local] || options[:"prefer-local"] jobs = installation_parallelization spec_installations = ParallelInstaller.call(self, @definition.specs, jobs, standalone, force, local: local) spec_installations.each do |installation| diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index e3089f230f..5df0cce6f3 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -121,13 +121,10 @@ module Bundler out end - def materialize_strictly - source.local! + def materialize_for_cache + source.remote! - matching_specs = source.specs.search(self) - return self if matching_specs.empty? - - __materialize__(matching_specs) + materialize(self, &:first) end def materialized_for_installation @@ -140,53 +137,25 @@ module Bundler source.local! if use_exact_resolved_specifications? - materialize_strictly - else - matching_specs = source.specs.search([name, version]) - return self if matching_specs.empty? - - target_platform = source.is_a?(Source::Path) ? platform : local_platform - - installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform) - - specification = __materialize__(installable_candidates, fallback_to_non_installable: false) - return specification unless specification.nil? - - if target_platform != platform - installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform) + materialize(self) do |matching_specs| + choose_compatible(matching_specs) end + else + materialize([name, version]) do |matching_specs| + target_platform = source.is_a?(Source::Path) ? platform : local_platform - __materialize__(installable_candidates) - end - end + installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform) - # If in frozen mode, we fallback to a non-installable candidate because by - # doing this we avoid re-resolving and potentially end up changing the - # lock file, which is not allowed. In that case, we will give a proper error - # about the mismatch higher up the stack, right before trying to install the - # bad gem. - def __materialize__(candidates, fallback_to_non_installable: Bundler.frozen_bundle?) - search = candidates.reverse.find do |spec| - spec.is_a?(StubSpecification) || spec.matches_current_metadata? - end - if search.nil? && fallback_to_non_installable - search = candidates.last - elsif search && search.full_name == full_name - # We don't validate locally installed dependencies but accept what's in - # the lockfile instead for performance, since loading locally installed - # dependencies would mean evaluating all gemspecs, which would affect - # `bundler/setup` performance - if search.is_a?(StubSpecification) - search.dependencies = dependencies - else - if !source.is_a?(Source::Path) && search.runtime_dependencies.sort != dependencies.sort - raise IncorrectLockfileDependencies.new(self) + specification = choose_compatible(installable_candidates, fallback_to_non_installable: false) + return specification unless specification.nil? + + if target_platform != platform + installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform) end - search.locked_platform = platform if search.instance_of?(RemoteSpecification) || search.instance_of?(EndpointSpecification) + choose_compatible(installable_candidates) end end - search end def inspect @@ -217,5 +186,41 @@ module Bundler (most_specific_locked_platform != generic_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform] end + + def materialize(query) + matching_specs = source.specs.search(query) + return self if matching_specs.empty? + + yield matching_specs + end + + # If in frozen mode, we fallback to a non-installable candidate because by + # doing this we avoid re-resolving and potentially end up changing the + # lock file, which is not allowed. In that case, we will give a proper error + # about the mismatch higher up the stack, right before trying to install the + # bad gem. + def choose_compatible(candidates, fallback_to_non_installable: Bundler.frozen_bundle?) + search = candidates.reverse.find do |spec| + spec.is_a?(StubSpecification) || spec.matches_current_metadata? + end + if search.nil? && fallback_to_non_installable + search = candidates.last + elsif search && search.full_name == full_name + # We don't validate locally installed dependencies but accept what's in + # the lockfile instead for performance, since loading locally installed + # dependencies would mean evaluating all gemspecs, which would affect + # `bundler/setup` performance + if search.is_a?(StubSpecification) + search.dependencies = dependencies + else + if !source.is_a?(Source::Path) && search.runtime_dependencies.sort != dependencies.sort + raise IncorrectLockfileDependencies.new(self) + end + + search.locked_platform = platform if search.instance_of?(RemoteSpecification) || search.instance_of?(EndpointSpecification) + end + end + search + end end end diff --git a/lib/bundler/match_metadata.rb b/lib/bundler/match_metadata.rb index f6cc27df32..6fd2994a85 100644 --- a/lib/bundler/match_metadata.rb +++ b/lib/bundler/match_metadata.rb @@ -13,5 +13,18 @@ module Bundler def matches_current_rubygems? @required_rubygems_version.satisfied_by?(Gem.rubygems_version) end + + def expanded_dependencies + runtime_dependencies + [ + metadata_dependency("Ruby", @required_ruby_version), + metadata_dependency("RubyGems", @required_rubygems_version), + ].compact + end + + def metadata_dependency(name, requirement) + return if requirement.nil? || requirement.none? + + Gem::Dependency.new("#{name}\0", requirement) + end end end diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 56a6c7ac67..ce51056904 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -389,9 +389,18 @@ module Bundler end def filter_remote_specs(specs, package) - return specs unless package.prefer_local? + if package.prefer_local? + local_specs = specs.select {|s| s.is_a?(StubSpecification) } - specs.select {|s| s.is_a?(StubSpecification) } + if local_specs.empty? + package.consider_remote_versions! + specs + else + local_specs + end + else + specs + end end # Ignore versions that depend on themselves incorrectly diff --git a/lib/bundler/resolver/package.rb b/lib/bundler/resolver/package.rb index 7bdcd7e9eb..0e86a4f84d 100644 --- a/lib/bundler/resolver/package.rb +++ b/lib/bundler/resolver/package.rb @@ -15,7 +15,7 @@ module Bundler class Package attr_reader :name, :platforms, :dependency, :locked_version - def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, prefer_local: false, dependency: nil) + def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, prefer_local: false, dependency: nil, new_platforms: []) @name = name @platforms = platforms @locked_version = locked_specs.version_for(name) @@ -24,10 +24,14 @@ module Bundler @top_level = !dependency.nil? @prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore @prefer_local = prefer_local + @new_platforms = new_platforms end def platform_specs(specs) - platforms.map {|platform| GemHelpers.select_best_platform_match(specs, platform, prefer_locked: !unlock?) } + platforms.map do |platform| + prefer_locked = @new_platforms.include?(platform) ? false : !unlock? + GemHelpers.select_best_platform_match(specs, platform, prefer_locked: prefer_locked) + end end def to_s @@ -55,7 +59,7 @@ module Bundler end def unlock? - @unlock.empty? || @unlock.include?(name) + @unlock == true || @unlock.include?(name) end def ignores_prereleases? diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index c3b72e2798..ac6ba86c4c 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -39,9 +39,7 @@ module Bundler end def dependencies - @dependencies ||= @specs.flat_map do |spec| - __dependencies(spec) + metadata_dependencies(spec) - end.uniq.sort + @dependencies ||= @specs.flat_map(&:expanded_dependencies).uniq.sort end def ==(other) @@ -71,28 +69,6 @@ module Bundler def exemplary_spec @specs.first end - - def __dependencies(spec) - dependencies = [] - spec.dependencies.each do |dep| - next if dep.type == :development - dependencies << Dependency.new(dep.name, dep.requirement) - end - dependencies - end - - def metadata_dependencies(spec) - [ - metadata_dependency("Ruby", spec.required_ruby_version), - metadata_dependency("RubyGems", spec.required_rubygems_version), - ].compact - end - - def metadata_dependency(name, requirement) - return if requirement.nil? || requirement.none? - - Dependency.new("#{name}\0", requirement) - end end end end diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index bc7f65c7f7..50c650d378 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -58,6 +58,87 @@ module Gem end end + require "rubygems/platform" + + class Platform + JAVA = Gem::Platform.new("java") + MSWIN = Gem::Platform.new("mswin32") + MSWIN64 = Gem::Platform.new("mswin64") + MINGW = Gem::Platform.new("x86-mingw32") + X64_MINGW = [Gem::Platform.new("x64-mingw32"), + Gem::Platform.new("x64-mingw-ucrt")].freeze + UNIVERSAL_MINGW = Gem::Platform.new("universal-mingw") + WINDOWS = [MSWIN, MSWIN64, UNIVERSAL_MINGW].flatten.freeze + X64_LINUX = Gem::Platform.new("x86_64-linux") + X64_LINUX_MUSL = Gem::Platform.new("x86_64-linux-musl") + + if X64_LINUX === X64_LINUX_MUSL + remove_method :=== + + def ===(other) + return nil unless Gem::Platform === other + + # universal-mingw32 matches x64-mingw-ucrt + return true if (@cpu == "universal" || other.cpu == "universal") && + @os.start_with?("mingw") && other.os.start_with?("mingw") + + # cpu + ([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu || + (@cpu == "arm" && other.cpu.start_with?("armv"))) && + + # os + @os == other.os && + + # version + ( + (@os != "linux" && (@version.nil? || other.version.nil?)) || + (@os == "linux" && (normalized_linux_version_ext == other.normalized_linux_version_ext || ["musl#{@version}", "musleabi#{@version}", "musleabihf#{@version}"].include?(other.version))) || + @version == other.version + ) + end + + # This is a copy of RubyGems 3.3.23 or higher `normalized_linux_method`. + # Once only 3.3.23 is supported, we can use the method in RubyGems. + def normalized_linux_version_ext + return nil unless @version + + without_gnu_nor_abi_modifiers = @version.sub(/\Agnu/, "").sub(/eabi(hf)?\Z/, "") + return nil if without_gnu_nor_abi_modifiers.empty? + + without_gnu_nor_abi_modifiers + end + end + end + + Platform.singleton_class.module_eval do + unless Platform.singleton_methods.include?(:match_spec?) + def match_spec?(spec) + match_gem?(spec.platform, spec.name) + end + + def match_gem?(platform, gem_name) + match_platforms?(platform, Gem.platforms) + end + end + + match_platforms_defined = Gem::Platform.respond_to?(:match_platforms?, true) + + if !match_platforms_defined || Gem::Platform.send(:match_platforms?, Gem::Platform::X64_LINUX_MUSL, [Gem::Platform::X64_LINUX]) + + private + + remove_method :match_platforms? if match_platforms_defined + + def match_platforms?(platform, platforms) + platforms.any? do |local_platform| + platform.nil? || + local_platform == platform || + (local_platform != Gem::Platform::RUBY && platform =~ local_platform) + end + end + end + end + require "rubygems/specification" # Can be removed once RubyGems 3.5.14 support is dropped @@ -177,8 +258,8 @@ module Gem dependencies - development_dependencies end - def deleted_gem? - !default_gem? && !File.directory?(full_gem_path) + def installation_missing? + !default_gem? && (!Dir.exist?(full_gem_path) || Dir.empty?(full_gem_path)) end unless VALIDATES_FOR_RESOLUTION @@ -288,86 +369,6 @@ module Gem end end - require "rubygems/platform" - - class Platform - JAVA = Gem::Platform.new("java") - MSWIN = Gem::Platform.new("mswin32") - MSWIN64 = Gem::Platform.new("mswin64") - MINGW = Gem::Platform.new("x86-mingw32") - X64_MINGW = [Gem::Platform.new("x64-mingw32"), - Gem::Platform.new("x64-mingw-ucrt")].freeze - WINDOWS = [MSWIN, MSWIN64, MINGW, X64_MINGW].flatten.freeze - X64_LINUX = Gem::Platform.new("x86_64-linux") - X64_LINUX_MUSL = Gem::Platform.new("x86_64-linux-musl") - - if X64_LINUX === X64_LINUX_MUSL - remove_method :=== - - def ===(other) - return nil unless Gem::Platform === other - - # universal-mingw32 matches x64-mingw-ucrt - return true if (@cpu == "universal" || other.cpu == "universal") && - @os.start_with?("mingw") && other.os.start_with?("mingw") - - # cpu - ([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu || - (@cpu == "arm" && other.cpu.start_with?("armv"))) && - - # os - @os == other.os && - - # version - ( - (@os != "linux" && (@version.nil? || other.version.nil?)) || - (@os == "linux" && (normalized_linux_version_ext == other.normalized_linux_version_ext || ["musl#{@version}", "musleabi#{@version}", "musleabihf#{@version}"].include?(other.version))) || - @version == other.version - ) - end - - # This is a copy of RubyGems 3.3.23 or higher `normalized_linux_method`. - # Once only 3.3.23 is supported, we can use the method in RubyGems. - def normalized_linux_version_ext - return nil unless @version - - without_gnu_nor_abi_modifiers = @version.sub(/\Agnu/, "").sub(/eabi(hf)?\Z/, "") - return nil if without_gnu_nor_abi_modifiers.empty? - - without_gnu_nor_abi_modifiers - end - end - end - - Platform.singleton_class.module_eval do - unless Platform.singleton_methods.include?(:match_spec?) - def match_spec?(spec) - match_gem?(spec.platform, spec.name) - end - - def match_gem?(platform, gem_name) - match_platforms?(platform, Gem.platforms) - end - end - - match_platforms_defined = Gem::Platform.respond_to?(:match_platforms?, true) - - if !match_platforms_defined || Gem::Platform.send(:match_platforms?, Gem::Platform::X64_LINUX_MUSL, [Gem::Platform::X64_LINUX]) - - private - - remove_method :match_platforms? if match_platforms_defined - - def match_platforms?(platform, platforms) - platforms.any? do |local_platform| - platform.nil? || - local_platform == platform || - (local_platform != Gem::Platform::RUBY && platform =~ local_platform) - end - end - end - end - # On universal Rubies, resolve the "universal" arch to the real CPU arch, without changing the extension directory. class BasicSpecification if /^universal\.(?.*?)-/ =~ (CROSS_COMPILING || RUBY_PLATFORM) diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index a8571f5115..eddf36278c 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -177,7 +177,7 @@ module Bundler end end - def replace_gem(specs, specs_by_name) + def replace_gem(specs_by_name) executables = nil [::Kernel.singleton_class, ::Kernel].each do |kernel_class| @@ -274,7 +274,7 @@ module Bundler else Gem::BUNDLED_GEMS.replace_require(specs) if Gem::BUNDLED_GEMS.respond_to?(:replace_require) end - replace_gem(specs, specs_by_name) + replace_gem(specs_by_name) stub_rubygems(specs_by_name.values) replace_bin_path(specs_by_name) @@ -293,7 +293,6 @@ module Bundler default_spec_name = default_spec.name next if specs_by_name.key?(default_spec_name) - specs << default_spec specs_by_name[default_spec_name] = default_spec end diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb index b72d3786bd..1225eb9f50 100644 --- a/lib/bundler/runtime.rb +++ b/lib/bundler/runtime.rb @@ -50,35 +50,30 @@ module Bundler Plugin.hook(Plugin::Events::GEM_BEFORE_REQUIRE_ALL, dependencies) dependencies.each do |dep| - required_file = nil Plugin.hook(Plugin::Events::GEM_BEFORE_REQUIRE, dep) - begin - # Loop through all the specified autorequires for the - # dependency. If there are none, use the dependency's name - # as the autorequire. - Array(dep.autorequire || dep.name).each do |file| - # Allow `require: true` as an alias for `require: ` - file = dep.name if file == true - required_file = file - begin - Kernel.require file - rescue RuntimeError => e - raise e if e.is_a?(LoadError) # we handle this a little later + # Loop through all the specified autorequires for the + # dependency. If there are none, use the dependency's name + # as the autorequire. + Array(dep.autorequire || dep.name).each do |file| + # Allow `require: true` as an alias for `require: ` + file = dep.name if file == true + required_file = file + begin + Kernel.require required_file + rescue LoadError => e + if dep.autorequire.nil? && e.path == required_file + if required_file.include?("-") + required_file = required_file.tr("-", "/") + retry + end + else raise Bundler::GemRequireError.new e, "There was an error while trying to load the gem '#{file}'." end - end - rescue LoadError => e - raise if dep.autorequire || e.path != required_file - - if dep.autorequire.nil? && dep.name.include?("-") - begin - namespaced_file = dep.name.tr("-", "/") - Kernel.require namespaced_file - rescue LoadError => e - raise if e.path != namespaced_file - end + rescue RuntimeError => e + raise Bundler::GemRequireError.new e, + "There was an error while trying to load the gem '#{file}'." end end diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb index 115dbd1378..232873503b 100644 --- a/lib/bundler/source.rb +++ b/lib/bundler/source.rb @@ -35,6 +35,8 @@ module Bundler spec.source == self end + def prefer_local!; end + def local!; end def local_only!; end diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index 36185561fa..19800e9c58 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -19,6 +19,7 @@ module Bundler @allow_remote = false @allow_cached = false @allow_local = options["allow_local"] || false + @prefer_local = false @checksum_store = Checksum::Store.new Array(options["remotes"]).reverse_each {|r| add_remote(r) } @@ -30,6 +31,10 @@ module Bundler @caches ||= [cache_path, *Bundler.rubygems.gem_cache] end + def prefer_local! + @prefer_local = true + end + def local_only! @specs = nil @allow_local = true @@ -37,6 +42,10 @@ module Bundler @allow_remote = false end + def local_only? + @allow_local && !@allow_remote + end + def local! return if @allow_local @@ -139,9 +148,15 @@ module Bundler index.merge!(cached_specs) if @allow_cached index.merge!(installed_specs) if @allow_local - # complete with default specs, only if not already available in the - # index through remote, cached, or installed specs - index.use(default_specs) if @allow_local + if @allow_local + if @prefer_local + index.merge!(default_specs) + else + # complete with default specs, only if not already available in the + # index through remote, cached, or installed specs + index.use(default_specs) + end + end index end @@ -439,7 +454,7 @@ module Bundler end def installed?(spec) - installed_specs[spec].any? && !spec.deleted_gem? + installed_specs[spec].any? && !spec.installation_missing? end def rubygems_dir diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb index 343bb73975..b7b2c4209a 100644 --- a/lib/bundler/source_list.rb +++ b/lib/bundler/source_list.rb @@ -141,6 +141,10 @@ module Bundler different_sources?(lock_sources, replacement_sources) end + def prefer_local! + all_sources.each(&:prefer_local!) + end + def local_only! all_sources.each(&:local_only!) end diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index f0082d46d5..8d75c29420 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -83,15 +83,13 @@ module Bundler end def []=(key, value) - @specs << value + delete_by_name(key) - reset! + add_spec(value) end def delete(specs) - Array(specs).each {|spec| @specs.delete(spec) } - - reset! + Array(specs).each {|spec| remove_spec(spec) } end def sort! @@ -117,8 +115,7 @@ module Bundler def materialized_for_all_platforms @specs.map do |s| next s unless s.is_a?(LazySpecification) - s.source.remote! - spec = s.materialize_strictly + spec = s.materialize_for_cache raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec spec end @@ -169,8 +166,10 @@ module Bundler def delete_by_name(name) @specs.reject! {|spec| spec.name == name } + @sorted&.reject! {|spec| spec.name == name } + return if @lookup.nil? - reset! + @lookup[name] = nil end def version_for(name) @@ -212,6 +211,10 @@ module Bundler s.matches_current_metadata? && valid_dependencies?(s) end + def to_s + map(&:full_name).to_s + end + private def materialize_dependencies(dependencies, platforms = [nil], skips: []) @@ -245,11 +248,6 @@ module Bundler @materializations.filter_map(&:materialized_spec) end - def reset! - @sorted = nil - @lookup = nil - end - def complete_platform(platform) new_specs = [] @@ -269,9 +267,7 @@ module Bundler end if valid_platform && new_specs.any? - @specs.concat(new_specs) - - reset! + new_specs.each {|spec| add_spec(spec) } end valid_platform @@ -292,15 +288,12 @@ module Bundler end def sorted - rake = @specs.find {|s| s.name == "rake" } - begin - @sorted ||= ([rake] + tsort).compact.uniq - rescue TSort::Cyclic => error - cgems = extract_circular_gems(error) - raise CyclicDependencyError, "Your bundle requires gems that depend" \ - " on each other, creating an infinite loop. Please remove either" \ - " gem '#{cgems[0]}' or gem '#{cgems[1]}' and try again." - end + @sorted ||= ([@specs.find {|s| s.name == "rake" }] + tsort).compact.uniq + rescue TSort::Cyclic => error + cgems = extract_circular_gems(error) + raise CyclicDependencyError, "Your bundle requires gems that depend" \ + " on each other, creating an infinite loop. Please remove either" \ + " gem '#{cgems[0]}' or gem '#{cgems[1]}' and try again." end def extract_circular_gems(error) @@ -311,8 +304,7 @@ module Bundler @lookup ||= begin lookup = {} @specs.each do |s| - lookup[s.name] ||= [] - lookup[s.name] << s + index_spec(lookup, s.name, s) end lookup end @@ -333,5 +325,36 @@ module Bundler specs_for_name.each {|s2| yield s2 } end end + + def add_spec(spec) + @specs << spec + + name = spec.name + + @sorted&.insert(@sorted.bsearch_index {|s| s.name >= name } || @sorted.size, spec) + return if @lookup.nil? + + index_spec(@lookup, name, spec) + end + + def remove_spec(spec) + @specs.delete(spec) + @sorted&.delete(spec) + return if @lookup.nil? + + indexed_specs = @lookup[spec.name] + return unless indexed_specs + + if indexed_specs.size > 1 + @lookup[spec.name].delete(spec) + else + @lookup[spec.name] = nil + end + end + + def index_spec(hash, key, value) + hash[key] ||= [] + hash[key] << value + end end end diff --git a/lib/bundler/templates/newgem/Gemfile.tt b/lib/bundler/templates/newgem/Gemfile.tt index de82a63c5f..ea7a33ee28 100644 --- a/lib/bundler/templates/newgem/Gemfile.tt +++ b/lib/bundler/templates/newgem/Gemfile.tt @@ -5,6 +5,7 @@ source "https://rubygems.org" # Specify your gem's dependencies in <%= config[:name] %>.gemspec gemspec +gem "irb" gem "rake", "~> 13.0" <%- if config[:ext] -%> diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index 55101f2566..93cc456062 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "2.6.3".freeze + VERSION = "2.6.4".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 83e5fded42..749efe6f89 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -9,7 +9,7 @@ require "rbconfig" module Gem - VERSION = "3.6.3" + VERSION = "3.6.4" end # Must be first since it unloads the prelude from 1.9.2 diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index 4bbfbfd7c4..0d3f98eb0f 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -106,13 +106,15 @@ class Gem::Requirement unless PATTERN =~ obj.to_s raise BadRequirementError, "Illformed requirement [#{obj.inspect}]" end + op = -($1 || "=") + version = -$2 - if $1 == ">=" && $2 == "0" + if op == ">=" && version == "0" DefaultRequirement - elsif $1 == ">=" && $2 == "0.a" + elsif op == ">=" && version == "0.a" DefaultPrereleaseRequirement else - [-($1 || "="), Gem::Version.new($2)] + [op, Gem::Version.new(version)] end end diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 35e165ff6a..121b72f90a 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -1415,13 +1415,11 @@ class Gem::Specification < Gem::BasicSpecification raise e end - begin - specs = spec_dep.to_specs.uniq(&:full_name) - rescue Gem::MissingSpecError => e - raise Gem::MissingSpecError.new(e.name, e.requirement, "at: #{spec_file}") - end + specs = spec_dep.matching_specs(true).uniq(&:full_name) - if specs.size == 1 + if specs.size == 0 + raise Gem::MissingSpecError.new(spec_dep.name, spec_dep.requirement, "at: #{spec_file}") + elsif specs.size == 1 specs.first.activate else name = spec_dep.name diff --git a/spec/bundler/bundler/current_ruby_spec.rb b/spec/bundler/bundler/current_ruby_spec.rb new file mode 100644 index 0000000000..4df8150697 --- /dev/null +++ b/spec/bundler/bundler/current_ruby_spec.rb @@ -0,0 +1,140 @@ +# frozen_string_literal: true + +RSpec.describe Bundler::CurrentRuby do + describe "PLATFORM_MAP" do + subject { described_class::PLATFORM_MAP } + + # rubocop:disable Naming/VariableNumber + let(:platforms) do + { ruby: Gem::Platform::RUBY, + ruby_18: Gem::Platform::RUBY, + ruby_19: Gem::Platform::RUBY, + ruby_20: Gem::Platform::RUBY, + ruby_21: Gem::Platform::RUBY, + ruby_22: Gem::Platform::RUBY, + ruby_23: Gem::Platform::RUBY, + ruby_24: Gem::Platform::RUBY, + ruby_25: Gem::Platform::RUBY, + ruby_26: Gem::Platform::RUBY, + ruby_27: Gem::Platform::RUBY, + ruby_30: Gem::Platform::RUBY, + ruby_31: Gem::Platform::RUBY, + ruby_32: Gem::Platform::RUBY, + ruby_33: Gem::Platform::RUBY, + ruby_34: Gem::Platform::RUBY, + ruby_35: Gem::Platform::RUBY, + mri: Gem::Platform::RUBY, + mri_18: Gem::Platform::RUBY, + mri_19: Gem::Platform::RUBY, + mri_20: Gem::Platform::RUBY, + mri_21: Gem::Platform::RUBY, + mri_22: Gem::Platform::RUBY, + mri_23: Gem::Platform::RUBY, + mri_24: Gem::Platform::RUBY, + mri_25: Gem::Platform::RUBY, + mri_26: Gem::Platform::RUBY, + mri_27: Gem::Platform::RUBY, + mri_30: Gem::Platform::RUBY, + mri_31: Gem::Platform::RUBY, + mri_32: Gem::Platform::RUBY, + mri_33: Gem::Platform::RUBY, + mri_34: Gem::Platform::RUBY, + mri_35: Gem::Platform::RUBY, + rbx: Gem::Platform::RUBY, + truffleruby: Gem::Platform::RUBY, + jruby: Gem::Platform::JAVA, + jruby_18: Gem::Platform::JAVA, + jruby_19: Gem::Platform::JAVA, + windows: Gem::Platform::WINDOWS, + windows_18: Gem::Platform::WINDOWS, + windows_19: Gem::Platform::WINDOWS, + windows_20: Gem::Platform::WINDOWS, + windows_21: Gem::Platform::WINDOWS, + windows_22: Gem::Platform::WINDOWS, + windows_23: Gem::Platform::WINDOWS, + windows_24: Gem::Platform::WINDOWS, + windows_25: Gem::Platform::WINDOWS, + windows_26: Gem::Platform::WINDOWS, + windows_27: Gem::Platform::WINDOWS, + windows_30: Gem::Platform::WINDOWS, + windows_31: Gem::Platform::WINDOWS, + windows_32: Gem::Platform::WINDOWS, + windows_33: Gem::Platform::WINDOWS, + windows_34: Gem::Platform::WINDOWS, + windows_35: Gem::Platform::WINDOWS } + end + + let(:deprecated) do + { mswin: Gem::Platform::MSWIN, + mswin_18: Gem::Platform::MSWIN, + mswin_19: Gem::Platform::MSWIN, + mswin_20: Gem::Platform::MSWIN, + mswin_21: Gem::Platform::MSWIN, + mswin_22: Gem::Platform::MSWIN, + mswin_23: Gem::Platform::MSWIN, + mswin_24: Gem::Platform::MSWIN, + mswin_25: Gem::Platform::MSWIN, + mswin_26: Gem::Platform::MSWIN, + mswin_27: Gem::Platform::MSWIN, + mswin_30: Gem::Platform::MSWIN, + mswin_31: Gem::Platform::MSWIN, + mswin_32: Gem::Platform::MSWIN, + mswin_33: Gem::Platform::MSWIN, + mswin_34: Gem::Platform::MSWIN, + mswin_35: Gem::Platform::MSWIN, + mswin64: Gem::Platform::MSWIN64, + mswin64_19: Gem::Platform::MSWIN64, + mswin64_20: Gem::Platform::MSWIN64, + mswin64_21: Gem::Platform::MSWIN64, + mswin64_22: Gem::Platform::MSWIN64, + mswin64_23: Gem::Platform::MSWIN64, + mswin64_24: Gem::Platform::MSWIN64, + mswin64_25: Gem::Platform::MSWIN64, + mswin64_26: Gem::Platform::MSWIN64, + mswin64_27: Gem::Platform::MSWIN64, + mswin64_30: Gem::Platform::MSWIN64, + mswin64_31: Gem::Platform::MSWIN64, + mswin64_32: Gem::Platform::MSWIN64, + mswin64_33: Gem::Platform::MSWIN64, + mswin64_34: Gem::Platform::MSWIN64, + mswin64_35: Gem::Platform::MSWIN64, + mingw: Gem::Platform::UNIVERSAL_MINGW, + mingw_18: Gem::Platform::UNIVERSAL_MINGW, + mingw_19: Gem::Platform::UNIVERSAL_MINGW, + mingw_20: Gem::Platform::UNIVERSAL_MINGW, + mingw_21: Gem::Platform::UNIVERSAL_MINGW, + mingw_22: Gem::Platform::UNIVERSAL_MINGW, + mingw_23: Gem::Platform::UNIVERSAL_MINGW, + mingw_24: Gem::Platform::UNIVERSAL_MINGW, + mingw_25: Gem::Platform::UNIVERSAL_MINGW, + mingw_26: Gem::Platform::UNIVERSAL_MINGW, + mingw_27: Gem::Platform::UNIVERSAL_MINGW, + mingw_30: Gem::Platform::UNIVERSAL_MINGW, + mingw_31: Gem::Platform::UNIVERSAL_MINGW, + mingw_32: Gem::Platform::UNIVERSAL_MINGW, + mingw_33: Gem::Platform::UNIVERSAL_MINGW, + mingw_34: Gem::Platform::UNIVERSAL_MINGW, + mingw_35: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_20: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_21: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_22: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_23: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_24: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_25: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_26: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_27: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_30: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_31: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_32: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_33: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_34: Gem::Platform::UNIVERSAL_MINGW, + x64_mingw_35: Gem::Platform::UNIVERSAL_MINGW } + end + # rubocop:enable Naming/VariableNumber + + it "includes all platforms" do + expect(subject).to eq(platforms.merge(deprecated)) + end + end +end diff --git a/spec/bundler/bundler/dependency_spec.rb b/spec/bundler/bundler/dependency_spec.rb index 1185c4e5dd..f930459571 100644 --- a/spec/bundler/bundler/dependency_spec.rb +++ b/spec/bundler/bundler/dependency_spec.rb @@ -35,140 +35,15 @@ RSpec.describe Bundler::Dependency do end end - describe "PLATFORM_MAP" do - subject { described_class::PLATFORM_MAP } + it "is on the current platform" do + engine = Gem.win_platform? ? "windows" : RUBY_ENGINE - # rubocop:disable Naming/VariableNumber - let(:platforms) do - { ruby: Gem::Platform::RUBY, - ruby_18: Gem::Platform::RUBY, - ruby_19: Gem::Platform::RUBY, - ruby_20: Gem::Platform::RUBY, - ruby_21: Gem::Platform::RUBY, - ruby_22: Gem::Platform::RUBY, - ruby_23: Gem::Platform::RUBY, - ruby_24: Gem::Platform::RUBY, - ruby_25: Gem::Platform::RUBY, - ruby_26: Gem::Platform::RUBY, - ruby_27: Gem::Platform::RUBY, - ruby_30: Gem::Platform::RUBY, - ruby_31: Gem::Platform::RUBY, - ruby_32: Gem::Platform::RUBY, - ruby_33: Gem::Platform::RUBY, - ruby_34: Gem::Platform::RUBY, - ruby_35: Gem::Platform::RUBY, - mri: Gem::Platform::RUBY, - mri_18: Gem::Platform::RUBY, - mri_19: Gem::Platform::RUBY, - mri_20: Gem::Platform::RUBY, - mri_21: Gem::Platform::RUBY, - mri_22: Gem::Platform::RUBY, - mri_23: Gem::Platform::RUBY, - mri_24: Gem::Platform::RUBY, - mri_25: Gem::Platform::RUBY, - mri_26: Gem::Platform::RUBY, - mri_27: Gem::Platform::RUBY, - mri_30: Gem::Platform::RUBY, - mri_31: Gem::Platform::RUBY, - mri_32: Gem::Platform::RUBY, - mri_33: Gem::Platform::RUBY, - mri_34: Gem::Platform::RUBY, - mri_35: Gem::Platform::RUBY, - rbx: Gem::Platform::RUBY, - truffleruby: Gem::Platform::RUBY, - jruby: Gem::Platform::JAVA, - jruby_18: Gem::Platform::JAVA, - jruby_19: Gem::Platform::JAVA, - windows: Gem::Platform::WINDOWS, - windows_18: Gem::Platform::WINDOWS, - windows_19: Gem::Platform::WINDOWS, - windows_20: Gem::Platform::WINDOWS, - windows_21: Gem::Platform::WINDOWS, - windows_22: Gem::Platform::WINDOWS, - windows_23: Gem::Platform::WINDOWS, - windows_24: Gem::Platform::WINDOWS, - windows_25: Gem::Platform::WINDOWS, - windows_26: Gem::Platform::WINDOWS, - windows_27: Gem::Platform::WINDOWS, - windows_30: Gem::Platform::WINDOWS, - windows_31: Gem::Platform::WINDOWS, - windows_32: Gem::Platform::WINDOWS, - windows_33: Gem::Platform::WINDOWS, - windows_34: Gem::Platform::WINDOWS, - windows_35: Gem::Platform::WINDOWS } - end + dep = described_class.new( + "test_gem", + "1.0.0", + { "platforms" => "#{engine}_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}" }, + ) - let(:deprecated) do - { mswin: Gem::Platform::MSWIN, - mswin_18: Gem::Platform::MSWIN, - mswin_19: Gem::Platform::MSWIN, - mswin_20: Gem::Platform::MSWIN, - mswin_21: Gem::Platform::MSWIN, - mswin_22: Gem::Platform::MSWIN, - mswin_23: Gem::Platform::MSWIN, - mswin_24: Gem::Platform::MSWIN, - mswin_25: Gem::Platform::MSWIN, - mswin_26: Gem::Platform::MSWIN, - mswin_27: Gem::Platform::MSWIN, - mswin_30: Gem::Platform::MSWIN, - mswin_31: Gem::Platform::MSWIN, - mswin_32: Gem::Platform::MSWIN, - mswin_33: Gem::Platform::MSWIN, - mswin_34: Gem::Platform::MSWIN, - mswin_35: Gem::Platform::MSWIN, - mswin64: Gem::Platform::MSWIN64, - mswin64_19: Gem::Platform::MSWIN64, - mswin64_20: Gem::Platform::MSWIN64, - mswin64_21: Gem::Platform::MSWIN64, - mswin64_22: Gem::Platform::MSWIN64, - mswin64_23: Gem::Platform::MSWIN64, - mswin64_24: Gem::Platform::MSWIN64, - mswin64_25: Gem::Platform::MSWIN64, - mswin64_26: Gem::Platform::MSWIN64, - mswin64_27: Gem::Platform::MSWIN64, - mswin64_30: Gem::Platform::MSWIN64, - mswin64_31: Gem::Platform::MSWIN64, - mswin64_32: Gem::Platform::MSWIN64, - mswin64_33: Gem::Platform::MSWIN64, - mswin64_34: Gem::Platform::MSWIN64, - mswin64_35: Gem::Platform::MSWIN64, - mingw: Gem::Platform::MINGW, - mingw_18: Gem::Platform::MINGW, - mingw_19: Gem::Platform::MINGW, - mingw_20: Gem::Platform::MINGW, - mingw_21: Gem::Platform::MINGW, - mingw_22: Gem::Platform::MINGW, - mingw_23: Gem::Platform::MINGW, - mingw_24: Gem::Platform::MINGW, - mingw_25: Gem::Platform::MINGW, - mingw_26: Gem::Platform::MINGW, - mingw_27: Gem::Platform::MINGW, - mingw_30: Gem::Platform::MINGW, - mingw_31: Gem::Platform::MINGW, - mingw_32: Gem::Platform::MINGW, - mingw_33: Gem::Platform::MINGW, - mingw_34: Gem::Platform::MINGW, - mingw_35: Gem::Platform::MINGW, - x64_mingw: Gem::Platform::X64_MINGW, - x64_mingw_20: Gem::Platform::X64_MINGW, - x64_mingw_21: Gem::Platform::X64_MINGW, - x64_mingw_22: Gem::Platform::X64_MINGW, - x64_mingw_23: Gem::Platform::X64_MINGW, - x64_mingw_24: Gem::Platform::X64_MINGW, - x64_mingw_25: Gem::Platform::X64_MINGW, - x64_mingw_26: Gem::Platform::X64_MINGW, - x64_mingw_27: Gem::Platform::X64_MINGW, - x64_mingw_30: Gem::Platform::X64_MINGW, - x64_mingw_31: Gem::Platform::X64_MINGW, - x64_mingw_32: Gem::Platform::X64_MINGW, - x64_mingw_33: Gem::Platform::X64_MINGW, - x64_mingw_34: Gem::Platform::X64_MINGW, - x64_mingw_35: Gem::Platform::X64_MINGW } - end - # rubocop:enable Naming/VariableNumber - - it "includes all platforms" do - expect(subject).to eq(platforms.merge(deprecated)) - end + expect(dep.current_platform?).to be_truthy end end diff --git a/spec/bundler/bundler/dsl_spec.rb b/spec/bundler/bundler/dsl_spec.rb index c4f9d0dbb5..f5fc0c7bbe 100644 --- a/spec/bundler/bundler/dsl_spec.rb +++ b/spec/bundler/bundler/dsl_spec.rb @@ -201,8 +201,8 @@ RSpec.describe Bundler::Dsl do describe "#gem" do # rubocop:disable Naming/VariableNumber [:ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24, :ruby_25, :ruby_26, :ruby_27, - :ruby_30, :ruby_31, :ruby_32, :ruby_33, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24, - :mri_25, :mri_26, :mri_27, :mri_30, :mri_31, :mri_32, :mri_33, :jruby, :rbx, :truffleruby].each do |platform| + :ruby_30, :ruby_31, :ruby_32, :ruby_33, :ruby_34, :ruby_35, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24, + :mri_25, :mri_26, :mri_27, :mri_30, :mri_31, :mri_32, :mri_33, :mri_34, :mri_35, :jruby, :rbx, :truffleruby].each do |platform| it "allows #{platform} as a valid platform" do subject.gem("foo", platform: platform) end @@ -211,7 +211,9 @@ RSpec.describe Bundler::Dsl do it "allows platforms matching the running Ruby version" do platform = "ruby_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}" - subject.gem("foo", platform: platform) + + expect { subject.gem("foo", platform: platform) }.not_to raise_error + expect(Bundler.current_ruby.respond_to?("#{platform}?")).to be_truthy end it "rejects invalid platforms" do diff --git a/spec/bundler/bundler/fetcher/downloader_spec.rb b/spec/bundler/bundler/fetcher/downloader_spec.rb index 13f8cc74aa..6164025ac6 100644 --- a/spec/bundler/bundler/fetcher/downloader_spec.rb +++ b/spec/bundler/bundler/fetcher/downloader_spec.rb @@ -83,7 +83,7 @@ RSpec.describe Bundler::Fetcher::Downloader do /Authentication is required for www.uri-to-fetch.com/) end - it "should raise a Bundler::Fetcher::AuthenticationRequiredError with advices" do + it "should raise a Bundler::Fetcher::AuthenticationRequiredError with advice" do expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError, /`bundle config set --global www\.uri-to-fetch\.com username:password`.*`BUNDLE_WWW__URI___TO___FETCH__COM`/m) end diff --git a/spec/bundler/bundler/gem_version_promoter_spec.rb b/spec/bundler/bundler/gem_version_promoter_spec.rb index 917daba95d..0e1b7c9cc8 100644 --- a/spec/bundler/bundler/gem_version_promoter_spec.rb +++ b/spec/bundler/bundler/gem_version_promoter_spec.rb @@ -20,13 +20,13 @@ RSpec.describe Bundler::GemVersionPromoter do end end - def build_package(name, version, locked = []) - Bundler::Resolver::Package.new(name, [], locked_specs: Bundler::SpecSet.new(build_spec(name, version)), unlock: locked) + def build_package(name, version, unlock) + Bundler::Resolver::Package.new(name, [], locked_specs: Bundler::SpecSet.new(build_spec(name, version)), unlock: unlock) end - def sorted_versions(candidates:, current:, name: "foo", locked: []) + def sorted_versions(candidates:, current:, unlock: true) gvp.sort_versions( - build_package(name, current, locked), + build_package("foo", current, unlock), build_candidates(candidates) ).flatten.map(&:version).map(&:to_s) end @@ -127,7 +127,7 @@ RSpec.describe Bundler::GemVersionPromoter do before { gvp.level = :minor } it "keeps the current version first" do - versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.1.0 2.0.1], current: "0.3.0", locked: ["bar"]) + versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.1.0 2.0.1], current: "0.3.0", unlock: []) expect(versions.first).to eq("0.3.0") end end diff --git a/spec/bundler/bundler/lockfile_parser_spec.rb b/spec/bundler/bundler/lockfile_parser_spec.rb index 88932bf009..b4e414d568 100644 --- a/spec/bundler/bundler/lockfile_parser_spec.rb +++ b/spec/bundler/bundler/lockfile_parser_spec.rb @@ -111,11 +111,11 @@ RSpec.describe Bundler::LockfileParser do end let(:specs) do [ - Bundler::LazySpecification.new("peiji-san", v("1.2.0"), rb), - Bundler::LazySpecification.new("rake", v("10.3.2"), rb), + Bundler::LazySpecification.new("peiji-san", v("1.2.0"), Gem::Platform::RUBY), + Bundler::LazySpecification.new("rake", v("10.3.2"), Gem::Platform::RUBY), ] end - let(:platforms) { [rb] } + let(:platforms) { [Gem::Platform::RUBY] } let(:bundler_version) { Gem::Version.new("1.12.0.rc.2") } let(:ruby_version) { "ruby 2.1.3p242" } let(:lockfile_path) { Bundler.default_lockfile.relative_path_from(Dir.pwd) } diff --git a/spec/bundler/bundler/source_spec.rb b/spec/bundler/bundler/source_spec.rb index 3b49c37431..01b57ce9e8 100644 --- a/spec/bundler/bundler/source_spec.rb +++ b/spec/bundler/bundler/source_spec.rb @@ -21,7 +21,7 @@ RSpec.describe Bundler::Source do end describe "#version_message" do - let(:spec) { double(:spec, name: "nokogiri", version: ">= 1.6", platform: rb) } + let(:spec) { double(:spec, name: "nokogiri", version: ">= 1.6", platform: Gem::Platform::RUBY) } shared_examples_for "the lockfile specs are not relevant" do it "should return a string with the spec name and version" do @@ -70,7 +70,7 @@ RSpec.describe Bundler::Source do end context "with a more recent version" do - let(:spec) { double(:spec, name: "nokogiri", version: "1.6.1", platform: rb) } + let(:spec) { double(:spec, name: "nokogiri", version: "1.6.1", platform: Gem::Platform::RUBY) } let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") } context "with color", :no_color_tty do @@ -97,7 +97,7 @@ RSpec.describe Bundler::Source do end context "with an older version" do - let(:spec) { double(:spec, name: "nokogiri", version: "1.7.1", platform: rb) } + let(:spec) { double(:spec, name: "nokogiri", version: "1.7.1", platform: Gem::Platform::RUBY) } let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") } context "with color", :no_color_tty do diff --git a/spec/bundler/cache/gems_spec.rb b/spec/bundler/cache/gems_spec.rb index a694df2700..c68b20225a 100644 --- a/spec/bundler/cache/gems_spec.rb +++ b/spec/bundler/cache/gems_spec.rb @@ -113,6 +113,11 @@ RSpec.describe "bundle cache" do expect(out).to include("Using json #{default_json_version}") end + it "does not use remote gems when installing with --prefer-local flag" do + install_gemfile %(source "https://gem.repo2"; gem 'json', '#{default_json_version}'), verbose: true, "prefer-local": true + expect(out).to include("Using json #{default_json_version}") + end + it "caches remote and builtin gems" do install_gemfile <<-G source "https://gem.repo2" @@ -167,7 +172,7 @@ RSpec.describe "bundle cache" do G bundle :cache, raise_on_error: false - expect(exitstatus).to_not eq(0) + expect(last_command).to be_failure expect(err).to include("json-#{default_json_version} is built in to Ruby, and can't be cached") end end diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb index ab8eb06838..ee05bf2e49 100644 --- a/spec/bundler/commands/cache_spec.rb +++ b/spec/bundler/commands/cache_spec.rb @@ -211,7 +211,7 @@ RSpec.describe "bundle cache" do end context "with --all-platforms" do - it "puts the gems in vendor/cache even for other rubies", bundler: ">= 2.4.0" do + it "puts the gems in vendor/cache even for other rubies" do gemfile <<-D source "https://gem.repo1" gem 'myrack', :platforms => [:ruby_20, :windows_20] @@ -221,7 +221,7 @@ RSpec.describe "bundle cache" do expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist end - it "puts the gems in vendor/cache even for legacy windows rubies", bundler: ">= 2.4.0" do + it "puts the gems in vendor/cache even for legacy windows rubies" do gemfile <<-D source "https://gem.repo1" gem 'myrack', :platforms => [:ruby_20, :x64_mingw_20] @@ -482,6 +482,53 @@ RSpec.describe "bundle install with gem sources" do expect(the_bundle).to include_gems("platform_specific 1.0 ruby") end + it "keeps gems that are locked and cached for the current platform, even if incompatible with the current ruby" do + build_repo4 do + build_gem "bcrypt_pbkdf", "1.1.1" + build_gem "bcrypt_pbkdf", "1.1.1" do |s| + s.platform = "arm64-darwin" + s.required_ruby_version = "< #{current_ruby_minor}" + end + end + + app_cache = bundled_app("vendor/cache") + FileUtils.mkdir_p app_cache + FileUtils.cp gem_repo4("gems/bcrypt_pbkdf-1.1.1-arm64-darwin.gem"), app_cache + FileUtils.cp gem_repo4("gems/bcrypt_pbkdf-1.1.1.gem"), app_cache + + bundle "config cache_all_platforms true" + + lockfile <<~L + GEM + remote: https://gem.repo4/ + specs: + bcrypt_pbkdf (1.1.1) + bcrypt_pbkdf (1.1.1-arm64-darwin) + + PLATFORMS + arm64-darwin + ruby + + DEPENDENCIES + bcrypt_pbkdf + + BUNDLED WITH + #{Bundler::VERSION} + L + + simulate_platform "arm64-darwin-23" do + install_gemfile <<~G, verbose: true + source "https://gem.repo4" + gem "bcrypt_pbkdf" + G + + expect(out).to include("Updating files in vendor/cache") + expect(err).to be_empty + expect(app_cache.join("bcrypt_pbkdf-1.1.1-arm64-darwin.gem")).to exist + expect(app_cache.join("bcrypt_pbkdf-1.1.1.gem")).to exist + end + end + it "does not update the cache if --no-cache is passed" do gemfile <<-G source "https://gem.repo1" diff --git a/spec/bundler/commands/console_spec.rb b/spec/bundler/commands/console_spec.rb index ba5f5f514b..dd7461ced4 100644 --- a/spec/bundler/commands/console_spec.rb +++ b/spec/bundler/commands/console_spec.rb @@ -36,106 +36,145 @@ RSpec.describe "bundle console", readline: true do RUBY end end - - install_gemfile <<-G - source "https://gem.repo2" - gem "myrack" - gem "activesupport", :group => :test - gem "myrack_middleware", :group => :development - G end - it "starts IRB with the default group loaded" do - bundle "console" do |input, _, _| - input.puts("puts MYRACK") - input.puts("exit") + context "when the library has an unrelated error" do + before do + build_lib "loadfuuu", "1.0.0" do |s| + s.write "lib/loadfuuu.rb", "require_relative 'loadfuuu/bar'" + s.write "lib/loadfuuu/bar.rb", "require 'not-in-bundle'" + end + + install_gemfile <<-G + source "https://gem.repo1" + path "#{lib_path}" do + gem "loadfuuu", require: true + end + G end - expect(out).to include("0.9.1") - end - it "uses IRB as default console" do - bundle "console" do |input, _, _| - input.puts("__FILE__") - input.puts("exit") - end - expect(out).to include("(irb)") - end - - it "starts another REPL if configured as such" do - install_gemfile <<-G - source "https://gem.repo2" - gem "pry" - G - bundle "config set console pry" - - bundle "console" do |input, _, _| - input.puts("__method__") - input.puts("exit") - end - expect(out).to include(":__pry__") - end - - it "falls back to IRB if the other REPL isn't available" do - bundle "config set console pry" - # make sure pry isn't there - - bundle "console" do |input, _, _| - input.puts("__FILE__") - input.puts("exit") - end - expect(out).to include("(irb)") - end - - it "doesn't load any other groups" do - bundle "console" do |input, _, _| - input.puts("puts ACTIVESUPPORT") - input.puts("exit") - end - expect(out).to include("NameError") - end - - describe "when given a group" do - it "loads the given group" do - bundle "console test" do |input, _, _| - input.puts("puts ACTIVESUPPORT") + it "does not show the bug report template" do + bundle("console", raise_on_error: false) do |input, _, _| input.puts("exit") end - expect(out).to include("2.3.5") + + expect(err).not_to include("ERROR REPORT TEMPLATE") + end + end + + context "when the library does not have any errors" do + before do + install_gemfile <<-G + source "https://gem.repo2" + gem "myrack" + gem "activesupport", :group => :test + gem "myrack_middleware", :group => :development + G end - it "loads the default group" do - bundle "console test" do |input, _, _| + it "starts IRB with the default group loaded" do + bundle "console" do |input, _, _| input.puts("puts MYRACK") input.puts("exit") end expect(out).to include("0.9.1") end - it "doesn't load other groups" do - bundle "console test" do |input, _, _| - input.puts("puts MYRACK_MIDDLEWARE") + it "uses IRB as default console" do + bundle "console" do |input, _, _| + input.puts("__FILE__") + input.puts("exit") + end + expect(out).to include("(irb)") + end + + it "starts another REPL if configured as such" do + install_gemfile <<-G + source "https://gem.repo2" + gem "pry" + G + bundle "config set console pry" + + bundle "console" do |input, _, _| + input.puts("__method__") + input.puts("exit") + end + expect(out).to include(":__pry__") + end + + it "falls back to IRB if the other REPL isn't available" do + bundle "config set console pry" + # make sure pry isn't there + + bundle "console" do |input, _, _| + input.puts("__FILE__") + input.puts("exit") + end + expect(out).to include("(irb)") + end + + it "does not try IRB twice if no console is configured and IRB is not available" do + create_file("irb.rb", "raise LoadError, 'irb is not available'") + + bundle("console", env: { "RUBYOPT" => "-I#{bundled_app} #{ENV["RUBYOPT"]}" }, raise_on_error: false) do |input, _, _| + input.puts("puts ACTIVESUPPORT") + input.puts("exit") + end + expect(err).not_to include("falling back to irb") + expect(err).to include("irb is not available") + end + + it "doesn't load any other groups" do + bundle "console" do |input, _, _| + input.puts("puts ACTIVESUPPORT") input.puts("exit") end expect(out).to include("NameError") end - end - it "performs an automatic bundle install" do - gemfile <<-G - source "https://gem.repo2" - gem "myrack" - gem "activesupport", :group => :test - gem "myrack_middleware", :group => :development - gem "foo" - G + describe "when given a group" do + it "loads the given group" do + bundle "console test" do |input, _, _| + input.puts("puts ACTIVESUPPORT") + input.puts("exit") + end + expect(out).to include("2.3.5") + end - bundle "config set auto_install 1" - bundle :console do |input, _, _| - input.puts("puts 'hello'") - input.puts("exit") + it "loads the default group" do + bundle "console test" do |input, _, _| + input.puts("puts MYRACK") + input.puts("exit") + end + expect(out).to include("0.9.1") + end + + it "doesn't load other groups" do + bundle "console test" do |input, _, _| + input.puts("puts MYRACK_MIDDLEWARE") + input.puts("exit") + end + expect(out).to include("NameError") + end + end + + it "performs an automatic bundle install" do + gemfile <<-G + source "https://gem.repo2" + gem "myrack" + gem "activesupport", :group => :test + gem "myrack_middleware", :group => :development + gem "foo" + G + + bundle "config set auto_install 1" + bundle :console do |input, _, _| + input.puts("puts 'hello'") + input.puts("exit") + end + expect(out).to include("Installing foo 1.0") + expect(out).to include("hello") + expect(the_bundle).to include_gems "foo 1.0" end - expect(out).to include("Installing foo 1.0") - expect(out).to include("hello") - expect(the_bundle).to include_gems "foo 1.0" end end diff --git a/spec/bundler/commands/doctor_spec.rb b/spec/bundler/commands/doctor_spec.rb index 744510cd40..c040602bde 100644 --- a/spec/bundler/commands/doctor_spec.rb +++ b/spec/bundler/commands/doctor_spec.rb @@ -46,7 +46,9 @@ RSpec.describe "bundle doctor" do end it "exits with no message if the installed gem has no C extensions" do - expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error + doctor = Bundler::CLI::Doctor.new({}) + allow(doctor).to receive(:lookup_with_fiddle).and_return(false) + expect { doctor.run }.not_to raise_error expect(@stdout.string).to be_empty end @@ -54,7 +56,7 @@ RSpec.describe "bundle doctor" do doctor = Bundler::CLI::Doctor.new({}) expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/myrack/myrack.bundle"] expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/lib/libSystem.dylib"] - allow(Fiddle).to receive(:dlopen).with("/usr/lib/libSystem.dylib").and_return(true) + allow(doctor).to receive(:lookup_with_fiddle).with("/usr/lib/libSystem.dylib").and_return(false) expect { doctor.run }.not_to raise_error expect(@stdout.string).to be_empty end @@ -63,7 +65,7 @@ RSpec.describe "bundle doctor" do doctor = Bundler::CLI::Doctor.new({}) expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/myrack/myrack.bundle"] expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"] - allow(Fiddle).to receive(:dlopen).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_raise(Fiddle::DLError) + allow(doctor).to receive(:lookup_with_fiddle).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(true) expect { doctor.run }.to raise_error(Bundler::ProductionError, <<~E.strip), @stdout.string The following gems are missing OS dependencies: * bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib @@ -82,7 +84,9 @@ RSpec.describe "bundle doctor" do end it "exits with an error if home contains files that are not readable/writable" do - expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error + doctor = Bundler::CLI::Doctor.new({}) + allow(doctor).to receive(:lookup_with_fiddle).and_return(false) + expect { doctor.run }.not_to raise_error expect(@stdout.string).to include( "Broken links exist in the Bundler home. Please report them to the offending gem's upstream repo. These files are:\n - #{@broken_symlink}" ) @@ -102,10 +106,12 @@ RSpec.describe "bundle doctor" do end it "exits with an error if home contains files that are not readable/writable" do + doctor = Bundler::CLI::Doctor.new({}) + allow(doctor).to receive(:lookup_with_fiddle).and_return(false) allow(@stat).to receive(:uid) { Process.uid } allow(File).to receive(:writable?).with(@unwritable_file) { false } allow(File).to receive(:readable?).with(@unwritable_file) { false } - expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error + expect { doctor.run }.not_to raise_error expect(@stdout.string).to include( "Files exist in the Bundler home that are not readable/writable by the current user. These files are:\n - #{@unwritable_file}" ) @@ -118,9 +124,11 @@ RSpec.describe "bundle doctor" do end it "exits with an error if home contains files that are not readable/writable and are not owned by the current user" do + doctor = Bundler::CLI::Doctor.new({}) + allow(doctor).to receive(:lookup_with_fiddle).and_return(false) allow(File).to receive(:writable?).with(@unwritable_file) { false } allow(File).to receive(:readable?).with(@unwritable_file) { false } - expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error + expect { doctor.run }.not_to raise_error expect(@stdout.string).to include( "Files exist in the Bundler home that are owned by another user, and are not readable/writable. These files are:\n - #{@unwritable_file}" ) @@ -128,9 +136,11 @@ RSpec.describe "bundle doctor" do end it "exits with a warning if home contains files that are read/write but not owned by current user" do + doctor = Bundler::CLI::Doctor.new({}) + allow(doctor).to receive(:lookup_with_fiddle).and_return(false) allow(File).to receive(:writable?).with(@unwritable_file) { true } allow(File).to receive(:readable?).with(@unwritable_file) { true } - expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error + expect { doctor.run }.not_to raise_error expect(@stdout.string).to include( "Files exist in the Bundler home that are owned by another user, but are still readable/writable. These files are:\n - #{@unwritable_file}" ) diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb index 42f288a1d8..4016cc5291 100644 --- a/spec/bundler/commands/info_spec.rb +++ b/spec/bundler/commands/info_spec.rb @@ -56,12 +56,12 @@ RSpec.describe "bundle info" do expect(out).to eq("2.3.2") end - it "doesn't claim that bundler has been deleted, even if using a custom path without bundler there" do + it "doesn't claim that bundler is missing, even if using a custom path without bundler there" do bundle "config set --local path vendor/bundle" bundle "install" bundle "info bundler" expect(out).to include("\tPath: #{root}") - expect(err).not_to match(/The gem bundler has been deleted/i) + expect(err).not_to match(/The gem bundler is missing/i) end it "complains if gem not in bundle" do @@ -69,27 +69,27 @@ RSpec.describe "bundle info" do expect(err).to eq("Could not find gem 'missing'.") end - it "warns if path no longer exists on disk" do + it "warns if path does not exist on disk, but specification is there" do FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2")) bundle "info rails --path" - expect(err).to include("The gem rails has been deleted.") + expect(err).to include("The gem rails is missing.") expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s) bundle "info rail --path" - expect(err).to include("The gem rails has been deleted.") + expect(err).to include("The gem rails is missing.") expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s) bundle "info rails" - expect(err).to include("The gem rails has been deleted.") + expect(err).to include("The gem rails is missing.") expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s) end context "given a default gem shippped in ruby", :ruby_repo do it "prints information about the default gem" do - bundle "info rdoc" - expect(out).to include("* rdoc") + bundle "info json" + expect(out).to include("* json") expect(out).to include("Default Gem: yes") end end diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index 992777722c..00418da27f 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -100,12 +100,23 @@ RSpec.describe "bundle install with gem sources" do gem 'myrack' G - FileUtils.rm_rf(default_bundle_path("gems/myrack-1.0.0")) + gem_dir = default_bundle_path("gems/myrack-1.0.0") + + FileUtils.rm_rf(gem_dir) bundle "install --verbose" expect(out).to include("Installing myrack 1.0.0") - expect(default_bundle_path("gems/myrack-1.0.0")).to exist + expect(gem_dir).to exist + expect(the_bundle).to include_gems("myrack 1.0.0") + + FileUtils.rm_rf(gem_dir) + Dir.mkdir(gem_dir) + + bundle "install --verbose" + + expect(out).to include("Installing myrack 1.0.0") + expect(gem_dir).to exist expect(the_bundle).to include_gems("myrack 1.0.0") end @@ -281,7 +292,7 @@ RSpec.describe "bundle install with gem sources" do end it "installs gems for windows" do - simulate_platform x86_mswin32 do + simulate_platform "x86-mswin32" do install_gemfile <<-G source "https://gem.repo1" gem "platform_specific" @@ -290,6 +301,17 @@ RSpec.describe "bundle install with gem sources" do expect(the_bundle).to include_gems("platform_specific 1.0 x86-mswin32") end end + + it "installs gems for aarch64-mingw-ucrt" do + simulate_platform "aarch64-mingw-ucrt" do + install_gemfile <<-G + source "https://gem.repo1" + gem "platform_specific" + G + end + + expect(out).to include("Installing platform_specific 1.0 (aarch64-mingw-ucrt)") + end end describe "doing bundle install foo" do @@ -559,7 +581,7 @@ RSpec.describe "bundle install with gem sources" do bundle :install, raise_on_error: false - expect(err).to include("Two gemspecs have conflicting requirements on the same gem: rubocop (~> 1.36.0, development) and rubocop (~> 2.0, development). Bundler cannot continue.") + expect(err).to include("Two gemspec development dependencies have conflicting requirements on the same gem: rubocop (~> 1.36.0) and rubocop (~> 2.0). Bundler cannot continue.") end it "warns when a Gemfile dependency is overriding a gemspec development dependency, with different requirements" do @@ -1584,6 +1606,26 @@ RSpec.describe "bundle install with gem sources" do expect(out).to include("Fetching foo 1.0.1").and include("Installing foo 1.0.1").and include("Fetching b 1.0.0").and include("Installing b 1.0.0") expect(last_command).to be_success end + + it "resolves to the latest version if no gems are available locally" do + build_repo4 do + build_gem "myreline", "0.3.8" + build_gem "debug", "0.2.1" + + build_gem "debug", "1.10.0" do |s| + s.add_dependency "myreline" + end + end + + install_gemfile <<~G, "prefer-local": true, verbose: true + source "https://gem.repo4" + + gem "debug" + G + + expect(out).to include("Fetching debug 1.10.0").and include("Installing debug 1.10.0").and include("Fetching myreline 0.3.8").and include("Installing myreline 0.3.8") + expect(last_command).to be_success + end end context "with a symlinked configured as bundle path and a gem with symlinks" do diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index db600e356f..cd20faa9a2 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -645,13 +645,13 @@ RSpec.describe "bundle lock" do it "single gem updates dependent gem to minor" do bundle "lock --update foo --patch" - expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.4.5 bar-2.1.1 qux-1.0.0].sort) + expect(the_bundle.locked_specs).to eq(%w[foo-1.4.5 bar-2.1.1 qux-1.0.0].sort) end it "minor preferred with strict" do bundle "lock --update --minor --strict" - expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort) + expect(the_bundle.locked_specs).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort) end it "shows proper error when Gemfile changes forbid patch upgrades, and --patch --strict is given" do @@ -674,25 +674,25 @@ RSpec.describe "bundle lock" do it "defaults to major" do bundle "lock --update --pre" - expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort) + expect(the_bundle.locked_specs).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort) end it "patch preferred" do bundle "lock --update --patch --pre" - expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.4.5 bar-2.1.2.pre qux-1.0.1].sort) + expect(the_bundle.locked_specs).to eq(%w[foo-1.4.5 bar-2.1.2.pre qux-1.0.1].sort) end it "minor preferred" do bundle "lock --update --minor --pre" - expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.1 bar-3.1.0.pre qux-1.1.0].sort) + expect(the_bundle.locked_specs).to eq(%w[foo-1.5.1 bar-3.1.0.pre qux-1.1.0].sort) end it "major preferred" do bundle "lock --update --major --pre" - expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort) + expect(the_bundle.locked_specs).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort) end end end @@ -734,7 +734,7 @@ RSpec.describe "bundle lock" do it "adds the latest version of the new dependency" do bundle "lock --minor --update sequel" - expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[sequel-5.72.0 bigdecimal-99.1.4].sort) + expect(the_bundle.locked_specs).to eq(%w[sequel-5.72.0 bigdecimal-99.1.4].sort) end end @@ -765,8 +765,7 @@ RSpec.describe "bundle lock" do bundle "lock --add-platform java x86-mingw32" allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) - lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32)) + expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32")) end it "supports adding new platforms, when most specific locked platform is not the current platform, and current resolve is not compatible with the target platform" do @@ -845,16 +844,14 @@ RSpec.describe "bundle lock" do bundle "lock --add-platform java x86-mingw32" allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) - lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to contain_exactly(rb, linux, java, x86_mingw32) + expect(the_bundle.locked_platforms).to contain_exactly(Gem::Platform::RUBY, "x86_64-linux", "java", "x86-mingw32") end it "supports adding the `ruby` platform" do bundle "lock --add-platform ruby" allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) - lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to match_array(default_platform_list("ruby")) + expect(the_bundle.locked_platforms).to match_array(default_platform_list("ruby")) end it "fails when adding an unknown platform" do @@ -867,13 +864,11 @@ RSpec.describe "bundle lock" do bundle "lock --add-platform java x86-mingw32" allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) - lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32)) + expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32")) bundle "lock --remove-platform java" - lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to match_array(default_platform_list(x86_mingw32)) + expect(the_bundle.locked_platforms).to match_array(default_platform_list("x86-mingw32")) end it "also cleans up redundant platform gems when removing platforms" do @@ -948,7 +943,7 @@ RSpec.describe "bundle lock" do build_repo4 do build_gem "ffi", "1.9.14" build_gem "ffi", "1.9.14" do |s| - s.platform = x86_mingw32 + s.platform = "x86-mingw32" end build_gem "gssapi", "0.1" @@ -980,7 +975,7 @@ RSpec.describe "bundle lock" do gem "gssapi" G - simulate_platform(x86_mingw32) { bundle :lock } + simulate_platform("x86-mingw32") { bundle :lock } checksums = checksums_section_when_enabled do |c| c.checksum gem_repo4, "ffi", "1.9.14", "x86-mingw32" diff --git a/spec/bundler/commands/show_spec.rb b/spec/bundler/commands/show_spec.rb index 7f32d0563b..8ccda50c9c 100644 --- a/spec/bundler/commands/show_spec.rb +++ b/spec/bundler/commands/show_spec.rb @@ -35,12 +35,12 @@ RSpec.describe "bundle show", bundler: "< 3" do expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s) end - it "warns if path no longer exists on disk" do + it "warns if specification is installed, but path does not exist on disk" do FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2")) bundle "show rails" - expect(err).to match(/has been deleted/i) + expect(err).to match(/is missing/i) expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s) end diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb index d28c74ed62..f6d0188794 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -428,6 +428,72 @@ RSpec.describe "bundle update" do expect(out).to include("Installing sneakers 2.11.0").and include("Installing rake 13.0.6") end + it "does not downgrade direct dependencies unnecessarily" do + build_repo4 do + build_gem "redis", "4.8.1" + build_gem "redis", "5.3.0" + + build_gem "sidekiq", "6.5.5" do |s| + s.add_dependency "redis", ">= 4.5.0" + end + + build_gem "sidekiq", "6.5.12" do |s| + s.add_dependency "redis", ">= 4.5.0", "< 5" + end + + # one version of sidekiq above Gemfile's range is needed to make the + # resolver choose `redis` first and trying to upgrade it, reproducing + # the accidental sidekiq downgrade as a result + build_gem "sidekiq", "7.0.0 " do |s| + s.add_dependency "redis", ">= 4.2.0" + end + + build_gem "sentry-sidekiq", "5.22.0" do |s| + s.add_dependency "sidekiq", ">= 3.0" + end + + build_gem "sentry-sidekiq", "5.22.4" do |s| + s.add_dependency "sidekiq", ">= 3.0" + end + end + + gemfile <<~G + source "https://gem.repo4" + + gem "redis" + gem "sidekiq", "~> 6.5" + gem "sentry-sidekiq" + G + + original_lockfile = <<~L + GEM + remote: https://gem.repo4/ + specs: + redis (4.8.1) + sentry-sidekiq (5.22.0) + sidekiq (>= 3.0) + sidekiq (6.5.12) + redis (>= 4.5.0, < 5) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + redis + sentry-sidekiq + sidekiq (~> 6.5) + + BUNDLED WITH + #{Bundler::VERSION} + L + + lockfile original_lockfile + + bundle "lock --update sentry-sidekiq" + + expect(lockfile).to eq(original_lockfile.sub("sentry-sidekiq (5.22.0)", "sentry-sidekiq (5.22.4)")) + end + it "does not downgrade indirect dependencies unnecessarily" do build_repo4 do build_gem "a" do |s| @@ -694,28 +760,36 @@ RSpec.describe "bundle update" do bundle "update", all: true, raise_on_error: false expect(last_command).to be_failure - expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/) - expect(err).to match(/freeze by running `bundle config set frozen false`./) + expect(err).to eq <<~ERROR.strip + Bundler is unlocking, but the lockfile can't be updated because frozen mode is set + + If this is a development machine, remove the Gemfile.lock freeze by running `bundle config set frozen false`. + ERROR end it "should fail loudly when frozen is set globally" do bundle "config set --global frozen 1" bundle "update", all: true, raise_on_error: false - expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/). - and match(/freeze by running `bundle config set frozen false`./) + expect(err).to eq <<~ERROR.strip + Bundler is unlocking, but the lockfile can't be updated because frozen mode is set + + If this is a development machine, remove the Gemfile.lock freeze by running `bundle config set frozen false`. + ERROR end it "should fail loudly when deployment is set globally" do bundle "config set --global deployment true" bundle "update", all: true, raise_on_error: false - expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/). - and match(/freeze by running `bundle config set frozen false`./) + expect(err).to eq <<~ERROR.strip + Bundler is unlocking, but the lockfile can't be updated because frozen mode is set + + If this is a development machine, remove the Gemfile.lock freeze by running `bundle config set frozen false`. + ERROR end it "should not suggest any command to unfreeze bundler if frozen is set through ENV" do bundle "update", all: true, raise_on_error: false, env: { "BUNDLE_FROZEN" => "true" } - expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/) - expect(err).not_to match(/by running/) + expect(err).to eq("Bundler is unlocking, but the lockfile can't be updated because frozen mode is set") end end @@ -1132,7 +1206,7 @@ RSpec.describe "bundle update in more complicated situations" do a L - simulate_platform linux, &example + simulate_platform "x86_64-linux", &example end it "allows updating" do @@ -1173,7 +1247,7 @@ RSpec.describe "bundle update in more complicated situations" do end it "is not updated because it is not actually included in the bundle" do - simulate_platform linux do + simulate_platform "x86_64-linux" do bundle "update a" expect(last_command.stdboth).to include "Bundler attempted to update a but it was not considered because it is for a different platform from the current one" expect(the_bundle).to_not include_gem "a" diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb index be0694cb55..4e83b7e9c3 100644 --- a/spec/bundler/install/gemfile/gemspec_spec.rb +++ b/spec/bundler/install/gemfile/gemspec_spec.rb @@ -8,34 +8,6 @@ RSpec.describe "bundle install from an existing gemspec" do end end - let(:x64_mingw_archs) do - if RUBY_PLATFORM == "x64-mingw-ucrt" - - ["x64-mingw-ucrt", "x64-mingw32"] - - else - ["x64-mingw32"] - end - end - - let(:x64_mingw_gems) do - x64_mingw_archs.map {|p| "platform_specific (1.0-#{p})" }.join("\n ") - end - - let(:x64_mingw_platforms) do - x64_mingw_archs.join("\n ") - end - - def x64_mingw_checksums(checksums) - x64_mingw_archs.each do |arch| - if arch == "x64-mingw-ucrt" - checksums.no_checksum "platform_specific", "1.0", arch - else - checksums.checksum gem_repo2, "platform_specific", "1.0", arch - end - end - end - it "should install runtime and development dependencies" do build_lib("foo", path: tmp("foo")) do |s| s.write("Gemfile", "source :rubygems\ngemspec") @@ -178,7 +150,7 @@ RSpec.describe "bundle install from an existing gemspec" do end it "should match a lockfile without needing to re-resolve with development dependencies" do - simulate_platform java do + simulate_platform "java" do build_lib("foo", path: tmp("foo")) do |s| s.add_dependency "myrack" s.add_development_dependency "thin" @@ -383,7 +355,7 @@ RSpec.describe "bundle install from an existing gemspec" do myrack (1.0.0) PLATFORMS - #{generic_local_platform} + ruby DEPENDENCIES foo! @@ -448,7 +420,8 @@ RSpec.describe "bundle install from an existing gemspec" do simulate_new_machine simulate_platform("jruby") { bundle "install" } - simulate_platform(x64_mingw32) { bundle "install" } + expect(lockfile).to include("platform_specific (1.0-java)") + simulate_platform("x64-mingw32") { bundle "install" } end context "on ruby" do @@ -465,7 +438,7 @@ RSpec.describe "bundle install from an existing gemspec" do c.no_checksum "foo", "1.0" c.checksum gem_repo2, "platform_specific", "1.0" c.checksum gem_repo2, "platform_specific", "1.0", "java" - x64_mingw_checksums(c) + c.checksum gem_repo2, "platform_specific", "1.0", "x64-mingw32" end expect(lockfile).to eq <<~L @@ -480,12 +453,12 @@ RSpec.describe "bundle install from an existing gemspec" do specs: platform_specific (1.0) platform_specific (1.0-java) - #{x64_mingw_gems} + platform_specific (1.0-x64-mingw32) PLATFORMS java ruby - #{x64_mingw_platforms} + x64-mingw32 DEPENDENCIES foo! @@ -506,7 +479,7 @@ RSpec.describe "bundle install from an existing gemspec" do c.no_checksum "foo", "1.0" c.checksum gem_repo2, "platform_specific", "1.0" c.checksum gem_repo2, "platform_specific", "1.0", "java" - x64_mingw_checksums(c) + c.checksum gem_repo2, "platform_specific", "1.0", "x64-mingw32" end expect(lockfile).to eq <<~L @@ -520,12 +493,12 @@ RSpec.describe "bundle install from an existing gemspec" do specs: platform_specific (1.0) platform_specific (1.0-java) - #{x64_mingw_gems} + platform_specific (1.0-x64-mingw32) PLATFORMS java ruby - #{x64_mingw_platforms} + x64-mingw32 DEPENDENCIES foo! @@ -549,7 +522,7 @@ RSpec.describe "bundle install from an existing gemspec" do c.checksum gem_repo2, "indirect_platform_specific", "1.0" c.checksum gem_repo2, "platform_specific", "1.0" c.checksum gem_repo2, "platform_specific", "1.0", "java" - x64_mingw_checksums(c) + c.checksum gem_repo2, "platform_specific", "1.0", "x64-mingw32" end expect(lockfile).to eq <<~L @@ -565,12 +538,12 @@ RSpec.describe "bundle install from an existing gemspec" do platform_specific platform_specific (1.0) platform_specific (1.0-java) - #{x64_mingw_gems} + platform_specific (1.0-x64-mingw32) PLATFORMS java ruby - #{x64_mingw_platforms} + x64-mingw32 DEPENDENCIES foo! @@ -659,9 +632,7 @@ RSpec.describe "bundle install from an existing gemspec" do win32-api (1.5.3-universal-mingw32) PLATFORMS - ruby - #{x64_mingw_platforms} - x86-mingw32 + #{lockfile_platforms("ruby", "x64-mingw32", "x86-mingw32")} DEPENDENCIES chef! diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb index 83b3c06cbe..ada74c311c 100644 --- a/spec/bundler/install/gemfile/git_spec.rb +++ b/spec/bundler/install/gemfile/git_spec.rb @@ -1068,7 +1068,7 @@ RSpec.describe "bundle install with git sources" do gem "foo", :git => "#{lib_path("foo-1.0")}" G - expect(exitstatus).to_not eq(0) + expect(last_command).to be_failure expect(err).to include("Bundler could not install a gem because it " \ "needs to create a directory, but a file exists " \ "- #{default_bundle_path("bundler")}") diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb index 1e536e6052..64a544a54f 100644 --- a/spec/bundler/install/gemfile/platform_spec.rb +++ b/spec/bundler/install/gemfile/platform_spec.rb @@ -195,7 +195,7 @@ RSpec.describe "bundle install across platforms" do build_gem("ffi", "1.9.23") end - simulate_platform java do + simulate_platform "java" do install_gemfile <<-G source "https://gem.repo4" @@ -330,7 +330,7 @@ RSpec.describe "bundle install across platforms" do end it "works with gems with platform-specific dependency having different requirements order" do - simulate_platform x64_mac do + simulate_platform "x86_64-darwin-15" do update_repo2 do build_gem "fspath", "3" build_gem "image_optim_pack", "1.2.3" do |s| @@ -616,7 +616,7 @@ end RSpec.describe "when a gem has no architecture" do it "still installs correctly" do - simulate_platform x86_mswin32 do + simulate_platform "x86-mswin32" do build_repo2 do # The rcov gem is platform mswin32, but has no arch build_gem "rcov" do |s| diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb index 04495e2b72..bda37949ad 100644 --- a/spec/bundler/install/gemfile/specific_platform_spec.rb +++ b/spec/bundler/install/gemfile/specific_platform_spec.rb @@ -11,7 +11,7 @@ RSpec.describe "bundle install with specific platforms" do setup_multiplatform_gem install_gemfile(google_protobuf) allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) - expect(the_bundle.locked_gems.platforms).to include(pl("x86_64-darwin-15")) + expect(the_bundle.locked_platforms).to include("universal-darwin") expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin") expect(the_bundle.locked_gems.specs.map(&:full_name)).to include( "google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin" @@ -351,7 +351,7 @@ RSpec.describe "bundle install with specific platforms" do G allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) - expect(the_bundle.locked_gems.platforms).to include(pl("x86_64-darwin-15")) + expect(the_bundle.locked_platforms).to include("universal-darwin") expect(the_bundle).to include_gems("facter 2.4.6 universal-darwin", "CFPropertyList 1.0") expect(the_bundle.locked_gems.specs.map(&:full_name)).to include("CFPropertyList-1.0", "facter-2.4.6-universal-darwin") @@ -367,9 +367,9 @@ RSpec.describe "bundle install with specific platforms" do simulate_platform "x86_64-darwin-15" do setup_multiplatform_gem install_gemfile(google_protobuf) - bundle "lock --add-platform=#{x64_mingw32}" + bundle "lock --add-platform=x64-mingw32" - expect(the_bundle.locked_gems.platforms).to include(x64_mingw32, pl("x86_64-darwin-15")) + expect(the_bundle.locked_platforms).to include("x64-mingw32", "universal-darwin") expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(*%w[ google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw32 @@ -381,9 +381,9 @@ RSpec.describe "bundle install with specific platforms" do simulate_platform "x86_64-darwin-15" do setup_multiplatform_gem install_gemfile(google_protobuf) - bundle "lock --add-platform=#{java}" + bundle "lock --add-platform=java" - expect(the_bundle.locked_gems.platforms).to include(java, pl("x86_64-darwin-15")) + expect(the_bundle.locked_platforms).to include("java", "universal-darwin") expect(the_bundle.locked_gems.specs.map(&:full_name)).to include( "google-protobuf-3.0.0.alpha.5.0.5.1", "google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin" @@ -1077,7 +1077,7 @@ RSpec.describe "bundle install with specific platforms" do myrack (3.0.7) PLATFORMS - #{lockfile_platforms("ruby", generic_local_platform, defaults: [])} + #{lockfile_platforms(generic_default_locked_platform || local_platform, defaults: ["ruby"])} DEPENDENCIES concurrent-ruby diff --git a/spec/bundler/install/gemfile_spec.rb b/spec/bundler/install/gemfile_spec.rb index 96ed174e9b..0e3b847767 100644 --- a/spec/bundler/install/gemfile_spec.rb +++ b/spec/bundler/install/gemfile_spec.rb @@ -53,17 +53,37 @@ RSpec.describe "bundle install" do end end - context "with deprecated features" do - it "reports that lib is an invalid option" do - gemfile <<-G - source "https://gem.repo1" + it "reports that lib is an invalid option" do + gemfile <<-G + source "https://gem.repo1" - gem "myrack", :lib => "myrack" - G + gem "myrack", :lib => "myrack" + G - bundle :install, raise_on_error: false - expect(err).to match(/You passed :lib as an option for gem 'myrack', but it is invalid/) - end + bundle :install, raise_on_error: false + expect(err).to match(/You passed :lib as an option for gem 'myrack', but it is invalid/) + end + + it "reports that type is an invalid option" do + gemfile <<-G + source "https://gem.repo1" + + gem "myrack", :type => "development" + G + + bundle :install, raise_on_error: false + expect(err).to match(/You passed :type as an option for gem 'myrack', but it is invalid/) + end + + it "reports that gemfile is an invalid option" do + gemfile <<-G + source "https://gem.repo1" + + gem "myrack", :gemfile => "foo" + G + + bundle :install, raise_on_error: false + expect(err).to match(/You passed :gemfile as an option for gem 'myrack', but it is invalid/) end context "when an internal error happens" do diff --git a/spec/bundler/install/gems/dependency_api_spec.rb b/spec/bundler/install/gems/dependency_api_spec.rb index 4b561064d9..01331d1c4b 100644 --- a/spec/bundler/install/gems/dependency_api_spec.rb +++ b/spec/bundler/install/gems/dependency_api_spec.rb @@ -119,7 +119,7 @@ RSpec.describe "gemcutter's dependency API" do end it "falls back when the API errors out" do - simulate_platform x86_mswin32 do + simulate_platform "x86-mswin32" do build_repo2 do # The rcov gem is platform mswin32, but has no arch build_gem "rcov" do |s| diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb index b7e1b5f1bd..522ee6b779 100644 --- a/spec/bundler/install/gems/flex_spec.rb +++ b/spec/bundler/install/gems/flex_spec.rb @@ -295,6 +295,30 @@ RSpec.describe "bundle flex_install" do it "should work when you update" do bundle "update myrack" + + checksums = checksums_section_when_enabled do |c| + c.checksum gem_repo1, "myrack", "0.9.1" + c.checksum gem_repo1, "myrack-obama", "1.0" + end + + expect(lockfile).to eq <<~L + GEM + remote: https://gem.repo1/ + specs: + myrack (0.9.1) + myrack-obama (1.0) + myrack + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + myrack (= 0.9.1) + myrack-obama + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L end end diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb index b4af8ab6d4..df55a03bbf 100644 --- a/spec/bundler/install/gems/resolving_spec.rb +++ b/spec/bundler/install/gems/resolving_spec.rb @@ -606,13 +606,13 @@ RSpec.describe "bundle install with install-time dependencies" do s.required_ruby_version = "> 9000" end build_gem "myrack", "1.2" do |s| - s.platform = x86_mingw32 + s.platform = "x86-mingw32" s.required_ruby_version = "> 9000" end build_gem "myrack", "1.2" end - simulate_platform x86_mingw32 do + simulate_platform "x86-mingw32" do install_gemfile <<-G, artifice: "compact_index" ruby "#{Gem.ruby_version}" source "https://gem.repo4" diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index 2fa9425914..00ec05a8a1 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -1587,6 +1587,32 @@ RSpec.describe "the lockfile format" do L end + it "raises a clear error when frozen mode is set and lockfile is missing deps, and does not install any gems" do + lockfile <<-L + GEM + remote: https://gem.repo2/ + specs: + myrack_middleware (1.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + myrack_middleware + + BUNDLED WITH + #{Bundler::VERSION} + L + + install_gemfile <<-G, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false + source "https://gem.repo2" + gem "myrack_middleware" + G + + expect(err).to eq("Bundler found incorrect dependencies in the lockfile for myrack_middleware-1.0") + expect(the_bundle).not_to include_gems "myrack_middleware 1.0" + end + it "automatically fixes the lockfile when it's missing deps, they conflict with other locked deps, but conflicts are fixable" do build_repo4 do build_gem "other_dep", "0.9" @@ -1798,7 +1824,7 @@ RSpec.describe "the lockfile format" do indirect_dependency (1.2.3) PLATFORMS - #{lockfile_platforms("ruby", generic_local_platform, defaults: [])} + #{lockfile_platforms(generic_default_locked_platform || local_platform, defaults: ["ruby"])} DEPENDENCIES direct_dependency @@ -1849,6 +1875,62 @@ RSpec.describe "the lockfile format" do L end + it "automatically fixes the lockfile when it has incorrect deps, keeping the locked version" do + build_repo4 do + build_gem "net-smtp", "0.5.0" do |s| + s.add_dependency "net-protocol" + end + + build_gem "net-smtp", "0.5.1" do |s| + s.add_dependency "net-protocol" + end + + build_gem "net-protocol", "0.2.2" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + gem "net-smtp" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + net-protocol (0.2.2) + net-smtp (0.5.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + net-smtp + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install" + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + net-protocol (0.2.2) + net-smtp (0.5.0) + net-protocol + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + net-smtp + + BUNDLED WITH + #{Bundler::VERSION} + L + end + shared_examples_for "a lockfile missing dependent specs" do it "auto-heals" do build_repo4 do diff --git a/spec/bundler/other/ext_spec.rb b/spec/bundler/other/ext_spec.rb index 9fc0414b4d..bdfc83d47e 100644 --- a/spec/bundler/other/ext_spec.rb +++ b/spec/bundler/other/ext_spec.rb @@ -34,19 +34,21 @@ RSpec.describe "Bundler::GemHelpers#generic" do expect(generic(pl("x86-mswin32"))).to eq(pl("x86-mswin32")) end - it "converts 32-bit mingw platform variants into x86-mingw32" do - expect(generic(pl("mingw32"))).to eq(pl("x86-mingw32")) - expect(generic(pl("i386-mingw32"))).to eq(pl("x86-mingw32")) - expect(generic(pl("x86-mingw32"))).to eq(pl("x86-mingw32")) + it "converts 32-bit mingw platform variants into universal-mingw" do + expect(generic(pl("i386-mingw32"))).to eq(pl("universal-mingw")) + expect(generic(pl("x86-mingw32"))).to eq(pl("universal-mingw")) end - it "converts 64-bit mingw platform variants into x64-mingw32" do - expect(generic(pl("x64-mingw32"))).to eq(pl("x64-mingw32")) - expect(generic(pl("x86_64-mingw32"))).to eq(pl("x64-mingw32")) + it "converts 64-bit mingw platform variants into universal-mingw" do + expect(generic(pl("x64-mingw32"))).to eq(pl("universal-mingw")) end - it "converts 64-bit mingw UCRT platform variants into x64-mingw-ucrt" do - expect(generic(pl("x64-mingw-ucrt"))).to eq(pl("x64-mingw-ucrt")) + it "converts x64 mingw UCRT platform variants into universal-mingw" do + expect(generic(pl("x64-mingw-ucrt"))).to eq(pl("universal-mingw")) + end + + it "converts aarch64 mingw UCRT platform variants into universal-mingw" do + expect(generic(pl("aarch64-mingw-ucrt"))).to eq(pl("universal-mingw")) end end diff --git a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock index 65455fdcbf..02348084f8 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.4.9.0) + jruby-jars (9.4.10.0) jruby-rack (1.1.21) rake (13.0.1) rubyzip (1.3.0) diff --git a/spec/bundler/resolver/basic_spec.rb b/spec/bundler/resolver/basic_spec.rb index 0d3eadbaf8..852c710415 100644 --- a/spec/bundler/resolver/basic_spec.rb +++ b/spec/bundler/resolver/basic_spec.rb @@ -211,12 +211,12 @@ RSpec.describe "Resolving" do it "resolves all gems to latest patch" do # strict is not set, so bar goes up a minor version due to dependency from foo 1.4.5 - should_conservative_resolve_and_include :patch, [], %w[foo-1.4.5 bar-2.1.1] + should_conservative_resolve_and_include :patch, true, %w[foo-1.4.5 bar-2.1.1] end it "resolves all gems to latest patch strict" do # strict is set, so foo can only go up to 1.4.4 to avoid bar going up a minor version, and bar can go up to 2.0.5 - should_conservative_resolve_and_include [:patch, :strict], [], %w[foo-1.4.4 bar-2.0.5] + should_conservative_resolve_and_include [:patch, :strict], true, %w[foo-1.4.4 bar-2.0.5] end it "resolves foo only to latest patch - same dependency case" do @@ -256,20 +256,20 @@ RSpec.describe "Resolving" do it "resolves all gems to latest minor" do # strict is not set, so bar goes up a major version due to dependency from foo 1.4.5 - should_conservative_resolve_and_include :minor, [], %w[foo-1.5.1 bar-3.0.0] + should_conservative_resolve_and_include :minor, true, %w[foo-1.5.1 bar-3.0.0] end it "resolves all gems to latest minor strict" do # strict is set, so foo can only go up to 1.5.0 to avoid bar going up a major version - should_conservative_resolve_and_include [:minor, :strict], [], %w[foo-1.5.0 bar-2.1.1] + should_conservative_resolve_and_include [:minor, :strict], true, %w[foo-1.5.0 bar-2.1.1] end it "resolves all gems to latest major" do - should_conservative_resolve_and_include :major, [], %w[foo-2.0.0 bar-3.0.0] + should_conservative_resolve_and_include :major, true, %w[foo-2.0.0 bar-3.0.0] end it "resolves all gems to latest major strict" do - should_conservative_resolve_and_include [:major, :strict], [], %w[foo-2.0.0 bar-3.0.0] + should_conservative_resolve_and_include [:major, :strict], true, %w[foo-2.0.0 bar-3.0.0] end # Why would this happen in real life? If bar 2.2 has a bug that the author of foo wants to bypass @@ -292,21 +292,21 @@ RSpec.describe "Resolving" do end it "could revert to a previous version level patch" do - should_conservative_resolve_and_include :patch, [], %w[foo-1.4.4 bar-2.1.1] + should_conservative_resolve_and_include :patch, true, %w[foo-1.4.4 bar-2.1.1] end it "cannot revert to a previous version in strict mode level patch" do # fall back to the locked resolution since strict means we can't regress either version - should_conservative_resolve_and_include [:patch, :strict], [], %w[foo-1.4.3 bar-2.2.3] + should_conservative_resolve_and_include [:patch, :strict], true, %w[foo-1.4.3 bar-2.2.3] end it "could revert to a previous version level minor" do - should_conservative_resolve_and_include :minor, [], %w[foo-1.5.0 bar-2.0.5] + should_conservative_resolve_and_include :minor, true, %w[foo-1.5.0 bar-2.0.5] end it "cannot revert to a previous version in strict mode level minor" do # fall back to the locked resolution since strict means we can't regress either version - should_conservative_resolve_and_include [:minor, :strict], [], %w[foo-1.4.3 bar-2.2.3] + should_conservative_resolve_and_include [:minor, :strict], true, %w[foo-1.4.3 bar-2.2.3] end end end diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb index 007733d3de..a6b7a6f07a 100644 --- a/spec/bundler/runtime/platform_spec.rb +++ b/spec/bundler/runtime/platform_spec.rb @@ -370,7 +370,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do end it "allows specifying only-ruby-platform on windows with dependency platforms" do - simulate_windows do + simulate_platform "x86-mswin32" do install_gemfile <<-G source "https://gem.repo1" gem "nokogiri", :platforms => [:windows, :mswin, :mswin64, :mingw, :x64_mingw, :jruby] @@ -397,7 +397,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do G bundle :lock - simulate_windows do + simulate_platform "x86-mswin32" do bundle "config set force_ruby_platform true" bundle "install" @@ -411,7 +411,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do s.add_dependency "platform_specific" end end - simulate_windows x64_mingw32 do + simulate_platform "x64-mingw32" do lockfile <<-L GEM remote: https://gem.repo2/ @@ -438,11 +438,9 @@ RSpec.describe "Bundler.setup with multi platform stuff" do end end - %w[x86-mswin32 x64-mswin64 x86-mingw32 x64-mingw32 x64-mingw-ucrt].each do |arch| - it "allows specifying platform windows on #{arch} arch" do - platform = send(arch.tr("-", "_")) - - simulate_windows platform do + %w[x86-mswin32 x64-mswin64 x86-mingw32 x64-mingw32 x64-mingw-ucrt aarch64-mingw-ucrt].each do |platform| + it "allows specifying platform windows on #{platform} platform" do + simulate_platform platform do lockfile <<-L GEM remote: https://gem.repo1/ @@ -463,8 +461,6 @@ RSpec.describe "Bundler.setup with multi platform stuff" do gem "platform_specific", :platforms => [:windows] G - bundle "install" - expect(the_bundle).to include_gems "platform_specific 1.0 #{platform}" end end diff --git a/spec/bundler/runtime/require_spec.rb b/spec/bundler/runtime/require_spec.rb index ece6679eb2..46613286d2 100644 --- a/spec/bundler/runtime/require_spec.rb +++ b/spec/bundler/runtime/require_spec.rb @@ -119,11 +119,9 @@ RSpec.describe "Bundler.require" do end G - load_error_run <<-R, "fail" - Bundler.require - R + run "Bundler.require", raise_on_error: false - expect(err_without_deprecations).to eq("ZOMG LOAD ERROR") + expect(err_without_deprecations).to include("cannot load such file -- fail") end it "displays a helpful message if the required gem throws an error" do @@ -155,16 +153,9 @@ RSpec.describe "Bundler.require" do end G - cmd = <<-RUBY - begin - Bundler.require - rescue LoadError => e - warn "ZOMG LOAD ERROR: \#{e.message}" - end - RUBY - run(cmd) + run "Bundler.require", raise_on_error: false - expect(err_without_deprecations).to eq("ZOMG LOAD ERROR: cannot load such file -- load-bar") + expect(err_without_deprecations).to include("cannot load such file -- load-bar") end describe "with namespaced gems" do @@ -215,10 +206,9 @@ RSpec.describe "Bundler.require" do end G - load_error_run <<-R, "jquery-rails" - Bundler.require - R - expect(err_without_deprecations).to eq("ZOMG LOAD ERROR") + run "Bundler.require", raise_on_error: false + + expect(err_without_deprecations).to include("cannot load such file -- jquery-rails") end it "handles the case where regex fails" do @@ -233,16 +223,9 @@ RSpec.describe "Bundler.require" do end G - cmd = <<-RUBY - begin - Bundler.require - rescue LoadError => e - warn "ZOMG LOAD ERROR" if e.message.include?("Could not open library 'libfuuu-1.0'") - end - RUBY - run(cmd) + run "Bundler.require", raise_on_error: false - expect(err_without_deprecations).to eq("ZOMG LOAD ERROR") + expect(err_without_deprecations).to include("libfuuu-1.0").and include("cannot open shared object file") end it "doesn't swallow the error when the library has an unrelated error" do @@ -257,16 +240,9 @@ RSpec.describe "Bundler.require" do end G - cmd = <<-RUBY - begin - Bundler.require - rescue LoadError => e - warn "ZOMG LOAD ERROR: \#{e.message}" - end - RUBY - run(cmd) + run "Bundler.require", raise_on_error: false - expect(err_without_deprecations).to eq("ZOMG LOAD ERROR: cannot load such file -- load-bar") + expect(err_without_deprecations).to include("cannot load such file -- load-bar") end end @@ -375,10 +351,9 @@ RSpec.describe "Bundler.require" do gem "busted_require" G - load_error_run <<-R, "no_such_file_omg" - Bundler.require - R - expect(err_without_deprecations).to eq("ZOMG LOAD ERROR") + run "Bundler.require", raise_on_error: false + + expect(err_without_deprecations).to include("cannot load such file -- no_such_file_omg") end end end diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb index 340b3f5dd9..0cbea69780 100644 --- a/spec/bundler/support/builders.rb +++ b/spec/bundler/support/builders.rb @@ -118,6 +118,10 @@ module Spec s.platform = "x64-mingw-ucrt" end + build_gem "platform_specific" do |s| + s.platform = "aarch64-mingw-ucrt" + end + build_gem "platform_specific" do |s| s.platform = "x86-darwin-100" end diff --git a/spec/bundler/support/filters.rb b/spec/bundler/support/filters.rb index f52cb02588..663b7fa44b 100644 --- a/spec/bundler/support/filters.rb +++ b/spec/bundler/support/filters.rb @@ -1,11 +1,18 @@ # frozen_string_literal: true class RequirementChecker < Proc - def self.against(present) + def self.against(present, major_only: false) + present = present.split(".")[0] if major_only provided = Gem::Version.new(present) new do |required| - !Gem::Requirement.new(required).satisfied_by?(provided) + requirement = Gem::Requirement.new(required) + + if major_only && !requirement.requirements.map(&:last).all? {|version| version.segments.one? } + raise "this filter only supports major versions, but #{required} was given" + end + + !requirement.satisfied_by?(provided) end.tap do |checker| checker.provided = provided end @@ -21,7 +28,7 @@ end RSpec.configure do |config| config.filter_run_excluding realworld: true - config.filter_run_excluding bundler: RequirementChecker.against(Bundler::VERSION.split(".")[0]) + config.filter_run_excluding bundler: RequirementChecker.against(Bundler::VERSION, major_only: true) config.filter_run_excluding rubygems: RequirementChecker.against(Gem::VERSION) config.filter_run_excluding ruby_repo: !ENV["GEM_COMMAND"].nil? config.filter_run_excluding no_color_tty: Gem.win_platform? || !ENV["GITHUB_ACTION"].nil? diff --git a/spec/bundler/support/hax.rb b/spec/bundler/support/hax.rb index 492c6ca8ed..715af6cab6 100644 --- a/spec/bundler/support/hax.rb +++ b/spec/bundler/support/hax.rb @@ -19,18 +19,19 @@ module Gem @default_specifications_dir = nil end - if ENV["BUNDLER_SPEC_WINDOWS"] - @@win_platform = true # rubocop:disable Style/ClassVars - end + spec_platform = ENV["BUNDLER_SPEC_PLATFORM"] + if spec_platform + if /mingw|mswin/.match?(spec_platform) + @@win_platform = nil # rubocop:disable Style/ClassVars + RbConfig::CONFIG["host_os"] = spec_platform.gsub(/^[^-]+-/, "").tr("-", "_") + end - if ENV["BUNDLER_SPEC_PLATFORM"] - previous_platforms = @platforms - previous_local = Platform.local + RbConfig::CONFIG["arch"] = spec_platform class Platform - @local = new(ENV["BUNDLER_SPEC_PLATFORM"]) + @local = nil end - @platforms = previous_platforms.map {|platform| platform == previous_local ? Platform.local : platform } + @platforms = [] end if ENV["BUNDLER_SPEC_GEM_SOURCES"] diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb index 294b932ea1..a5f7f0ad62 100644 --- a/spec/bundler/support/helpers.rb +++ b/spec/bundler/support/helpers.rb @@ -442,16 +442,6 @@ module Spec ENV["BUNDLER_SPEC_PLATFORM"] = old if block_given? end - def simulate_windows(platform = x86_mswin32) - old = ENV["BUNDLER_SPEC_WINDOWS"] - ENV["BUNDLER_SPEC_WINDOWS"] = "true" - simulate_platform platform do - yield - end - ensure - ENV["BUNDLER_SPEC_WINDOWS"] = old - end - def current_ruby_minor Gem.ruby_version.segments.tap {|s| s.delete_at(2) }.join(".") end diff --git a/spec/bundler/support/indexes.rb b/spec/bundler/support/indexes.rb index 7960bae59f..2d592808f0 100644 --- a/spec/bundler/support/indexes.rb +++ b/spec/bundler/support/indexes.rb @@ -66,7 +66,6 @@ module Spec end def should_conservative_resolve_and_include(opts, unlock, specs) - # empty unlock means unlock all opts = Array(opts) search = Bundler::GemVersionPromoter.new.tap do |s| s.level = opts.first diff --git a/spec/bundler/support/path.rb b/spec/bundler/support/path.rb index 76e28db10b..6c63666519 100644 --- a/spec/bundler/support/path.rb +++ b/spec/bundler/support/path.rb @@ -113,7 +113,7 @@ module Spec # that requires regenerating tmp/. def test_env_version - 1 + 2 end def scope diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb index 526e1c09a9..e09c6fe66a 100644 --- a/spec/bundler/support/platforms.rb +++ b/spec/bundler/support/platforms.rb @@ -4,62 +4,14 @@ module Spec module Platforms include Bundler::GemHelpers - def rb - Gem::Platform::RUBY - end - - def mac - Gem::Platform.new("x86-darwin-10") - end - - def x64_mac - Gem::Platform.new("x86_64-darwin-15") - end - - def java - Gem::Platform.new([nil, "java", nil]) - end - - def linux - Gem::Platform.new("x86_64-linux") - end - - def x86_mswin32 - Gem::Platform.new(["x86", "mswin32", nil]) - end - - def x64_mswin64 - Gem::Platform.new(["x64", "mswin64", nil]) - end - - def x86_mingw32 - Gem::Platform.new(["x86", "mingw32", nil]) - end - - def x64_mingw32 - Gem::Platform.new(["x64", "mingw32", nil]) - end - - def x64_mingw_ucrt - Gem::Platform.new(["x64", "mingw", "ucrt"]) - end - - def windows_platforms - [x86_mswin32, x64_mswin64, x86_mingw32, x64_mingw32, x64_mingw_ucrt] - end - - def all_platforms - [rb, java, linux, windows_platforms].flatten - end - def not_local - all_platforms.find {|p| p != generic_local_platform } + generic_local_platform == Gem::Platform::RUBY ? "java" : Gem::Platform::RUBY end def local_tag - if RUBY_PLATFORM == "java" + if Gem.java_platform? :jruby - elsif ["x64-mingw32", "x64-mingw-ucrt"].include?(RUBY_PLATFORM) + elsif Gem.win_platform? :windows else :ruby @@ -96,16 +48,22 @@ module Spec end def default_platform_list(*extra, defaults: default_locked_platforms) - defaults.concat(extra).uniq + defaults.concat(extra).map(&:to_s).uniq end def lockfile_platforms(*extra, defaults: default_locked_platforms) platforms = default_platform_list(*extra, defaults: defaults) - platforms.map(&:to_s).sort.join("\n ") + platforms.sort.join("\n ") end def default_locked_platforms - [local_platform, generic_local_platform] + [local_platform, generic_default_locked_platform].compact + end + + def generic_default_locked_platform + return unless generic_local_platform_is_ruby? + + Gem::Platform::RUBY end end end diff --git a/spec/bundler/support/the_bundle.rb b/spec/bundler/support/the_bundle.rb index f252a4515b..bda717f3b0 100644 --- a/spec/bundler/support/the_bundle.rb +++ b/spec/bundler/support/the_bundle.rb @@ -31,5 +31,13 @@ module Spec raise "Cannot read lockfile if it doesn't exist" unless locked? Bundler::LockfileParser.new(lockfile.read) end + + def locked_specs + locked_gems.specs.map(&:full_name) + end + + def locked_platforms + locked_gems.platforms.map(&:to_s) + end end end 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 aaa512fd34..f4e80a801f 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.107" +version = "0.9.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56aaf81d9efc195606456e91896297ee5ab2002381539f8ed1ba6b4f2e467f3b" +checksum = "56cf964f8e44115e50009921ea3d3791b6f74d1ae6d6ed37114fbe03a1cd7308" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.107" +version = "0.9.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035b513baded6df2b90a8559efb1973c47ba42e16c21c5f0863dd2aa4dbd6abe" +checksum = "161480347f56473107d4135643b6b1909331eec61445e113b256708a28b691c5" 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 07c57c144d..292a6d984d 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.107" +rb-sys = "0.9.110" 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 d6836e68f6..5ab990b894 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.107" +version = "0.9.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56aaf81d9efc195606456e91896297ee5ab2002381539f8ed1ba6b4f2e467f3b" +checksum = "56cf964f8e44115e50009921ea3d3791b6f74d1ae6d6ed37114fbe03a1cd7308" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.107" +version = "0.9.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035b513baded6df2b90a8559efb1973c47ba42e16c21c5f0863dd2aa4dbd6abe" +checksum = "161480347f56473107d4135643b6b1909331eec61445e113b256708a28b691c5" 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 45ab82869b..445bd3a641 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.107" +rb-sys = "0.9.110" diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index 43b649b9ea..697a26338c 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -564,7 +564,6 @@ end # [B] ~> 1.0 # # and should resolve using b-1.0 - # TODO: move these to specification def test_self_activate_over a = util_spec "a", "1.0", "b" => ">= 1.0", "c" => "= 1.0" @@ -653,6 +652,17 @@ end end end + def test_self_activate_missing_deps_does_not_raise_nested_exceptions + a = util_spec "a", "1.0", "b" => ">= 1.0" + install_specs a + + e = assert_raise Gem::MissingSpecError do + a.activate + end + + refute e.cause + end + def test_self_all_equals a = util_spec "foo", "1", nil, "lib/foo.rb"