Merge RubyGems-3.6.4 and Bundler-2.6.4

This commit is contained in:
Hiroshi SHIBATA 2025-02-21 09:27:47 +09:00 committed by Takashi Kokubun
parent 19c69f382b
commit a0025b6e5d
74 changed files with 1355 additions and 981 deletions

View file

@ -20,9 +20,14 @@ module Bundler
require name require name
get_constant(name) get_constant(name)
rescue LoadError rescue LoadError
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" Bundler.ui.error "Couldn't load console #{name}, falling back to irb"
require "irb" name = "irb"
get_constant("irb") retry
end
end end
def get_constant(name) def get_constant(name)
@ -32,9 +37,6 @@ module Bundler
"irb" => :IRB, "irb" => :IRB,
}[name] }[name]
Object.const_get(const_name) Object.const_get(const_name)
rescue NameError
Bundler.ui.error "Could not find constant #{const_name}"
exit 1
end end
end end
end end

View file

@ -2,7 +2,6 @@
require "rbconfig" require "rbconfig"
require "shellwords" require "shellwords"
require "fiddle"
module Bundler module Bundler
class CLI::Doctor class CLI::Doctor
@ -57,6 +56,14 @@ module Bundler
Dir.glob("#{spec.full_gem_path}/**/*.bundle") Dir.glob("#{spec.full_gem_path}/**/*.bundle")
end end
def lookup_with_fiddle(path)
require "fiddle"
Fiddle.dlopen(path)
false
rescue Fiddle::DLError
true
end
def check! def check!
require_relative "check" require_relative "check"
Bundler::CLI::Check.new({}).run Bundler::CLI::Check.new({}).run
@ -73,10 +80,7 @@ module Bundler
definition.specs.each do |spec| definition.specs.each do |spec|
bundles_for_gem(spec).each do |bundle| bundles_for_gem(spec).each do |bundle|
bad_paths = dylibs(bundle).select do |f| bad_paths = dylibs(bundle).select do |f|
Fiddle.dlopen(f) lookup_with_fiddle(f)
false
rescue Fiddle::DLError
true
end end
if bad_paths.any? if bad_paths.any?
broken_links[spec] ||= [] broken_links[spec] ||= []

View file

@ -39,8 +39,8 @@ module Bundler
path = File.expand_path("../../..", __dir__) path = File.expand_path("../../..", __dir__)
else else
path = spec.full_gem_path path = spec.full_gem_path
if spec.deleted_gem? if spec.installation_missing?
return Bundler.ui.warn "The gem #{name} has been deleted. It was installed at: #{path}" return Bundler.ui.warn "The gem #{name} is missing. It should be installed at #{path}, but was not found"
end end
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 << "\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? gem_info << "\tReverse Dependencies: \n\t\t#{gem_dependencies.join("\n\t\t")}" if gem_dependencies.any?
if name != "bundler" && spec.deleted_gem? if name != "bundler" && spec.installation_missing?
return Bundler.ui.warn "The gem #{name} has been deleted. Gemspec information is still available though:\n#{gem_info}" return Bundler.ui.warn "The gem #{name} is missing. Gemspec information is still available though:\n#{gem_info}"
end end
Bundler.ui.info gem_info Bundler.ui.info gem_info

View file

@ -10,7 +10,7 @@ module Bundler
be sure to check out these resources: be sure to check out these resources:
1. Check out our troubleshooting guide for quick fixes to common issues: 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 2. Instructions for common Bundler uses can be found on the documentation
site: https://bundler.io/ site: https://bundler.io/

View file

@ -24,7 +24,7 @@ module Bundler
return unless spec return unless spec
path = spec.full_gem_path path = spec.full_gem_path
unless File.directory?(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
end end
return Bundler.ui.info(path) return Bundler.ui.info(path)

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative "rubygems_ext"
module Bundler module Bundler
# Returns current version of Ruby # Returns current version of Ruby
# #
@ -9,38 +11,25 @@ module Bundler
end end
class CurrentRuby class CurrentRuby
KNOWN_MINOR_VERSIONS = %w[ ALL_RUBY_VERSIONS = (18..27).to_a.concat((30..35).to_a).freeze
1.8 KNOWN_MINOR_VERSIONS = ALL_RUBY_VERSIONS.map {|v| v.digits.reverse.join(".") }.freeze
1.9 KNOWN_MAJOR_VERSIONS = ALL_RUBY_VERSIONS.map {|v| v.digits.last.to_s }.uniq.freeze
2.0 PLATFORM_MAP = {
2.1 ruby: [Gem::Platform::RUBY, CurrentRuby::ALL_RUBY_VERSIONS],
2.2 mri: [Gem::Platform::RUBY, CurrentRuby::ALL_RUBY_VERSIONS],
2.3 rbx: [Gem::Platform::RUBY],
2.4 truffleruby: [Gem::Platform::RUBY],
2.5 jruby: [Gem::Platform::JAVA, [18, 19]],
2.6 windows: [Gem::Platform::WINDOWS, CurrentRuby::ALL_RUBY_VERSIONS],
2.7 # deprecated
3.0 mswin: [Gem::Platform::MSWIN, CurrentRuby::ALL_RUBY_VERSIONS],
3.1 mswin64: [Gem::Platform::MSWIN64, CurrentRuby::ALL_RUBY_VERSIONS - [18]],
3.2 mingw: [Gem::Platform::UNIVERSAL_MINGW, CurrentRuby::ALL_RUBY_VERSIONS],
3.3 x64_mingw: [Gem::Platform::UNIVERSAL_MINGW, CurrentRuby::ALL_RUBY_VERSIONS - [18, 19]],
].freeze }.each_with_object({}) do |(platform, spec), hash|
hash[platform] = spec[0]
KNOWN_MAJOR_VERSIONS = KNOWN_MINOR_VERSIONS.map {|v| v.split(".", 2).first }.uniq.freeze spec[1]&.each {|version| hash[:"#{platform}_#{version}"] = spec[0] }
end.freeze
KNOWN_PLATFORMS = %w[
jruby
maglev
mingw
mri
mswin
mswin64
rbx
ruby
truffleruby
windows
x64_mingw
].freeze
def ruby? def ruby?
return true if Bundler::GemHelpers.generic_local_platform_is_ruby? return true if Bundler::GemHelpers.generic_local_platform_is_ruby?
@ -82,7 +71,8 @@ module Bundler
RUBY_VERSION.start_with?("#{version}.") RUBY_VERSION.start_with?("#{version}.")
end end
KNOWN_PLATFORMS.each do |platform| all_platforms = PLATFORM_MAP.keys << "maglev"
all_platforms.each do |platform|
define_method(:"#{platform}_#{trimmed_version}?") do define_method(:"#{platform}_#{trimmed_version}?") do
send(:"#{platform}?") && send(:"on_#{trimmed_version}?") send(:"#{platform}?") && send(:"on_#{trimmed_version}?")
end end

View file

@ -58,17 +58,28 @@ module Bundler
# @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version # @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
# @param optional_groups [Array(String)] A list of optional groups # @param optional_groups [Array(String)] A list of optional groups
def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = []) 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_bundler = false
@unlocking = unlock @unlocking = unlock
@sources_to_unlock = []
@unlocking_ruby = false
@explicit_unlocks = []
conservative = false
else else
@unlocking_all = false
@unlocking_bundler = unlock.delete(:bundler) @unlocking_bundler = unlock.delete(:bundler)
@unlocking = unlock.any? {|_k, v| !Array(v).empty? } @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 end
@dependencies = dependencies @dependencies = dependencies
@sources = sources @sources = sources
@unlock = unlock
@optional_groups = optional_groups @optional_groups = optional_groups
@prefer_local = false @prefer_local = false
@specs = nil @specs = nil
@ -93,29 +104,24 @@ module Bundler
@platforms = @locked_platforms.dup @platforms = @locked_platforms.dup
@locked_bundler_version = @locked_gems.bundler_version @locked_bundler_version = @locked_gems.bundler_version
@locked_ruby_version = @locked_gems.ruby_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) @originally_locked_specs = SpecSet.new(@locked_gems.specs)
@locked_checksums = @locked_gems.checksums @locked_checksums = @locked_gems.checksums
if unlock != true if @unlocking_all
@locked_deps = @originally_locked_deps
@locked_specs = @originally_locked_specs
@locked_sources = @locked_gems.sources
else
@unlock = {}
@locked_deps = {}
@locked_specs = SpecSet.new([]) @locked_specs = SpecSet.new([])
@locked_sources = [] @locked_sources = []
else
@locked_specs = @originally_locked_specs
@locked_sources = @locked_gems.sources
end end
else else
@unlock = {}
@locked_gems = nil @locked_gems = nil
@locked_platforms = [] @locked_platforms = []
@most_specific_locked_platform = nil @most_specific_locked_platform = nil
@platforms = [] @platforms = []
@locked_deps = {} @locked_deps = {}
@locked_specs = SpecSet.new([]) @locked_specs = SpecSet.new([])
@originally_locked_deps = {}
@originally_locked_specs = @locked_specs @originally_locked_specs = @locked_specs
@locked_sources = [] @locked_sources = []
@locked_checksums = Bundler.feature_flag.lockfile_checksums? @locked_checksums = Bundler.feature_flag.lockfile_checksums?
@ -134,11 +140,10 @@ module Bundler
@sources.merged_gem_lockfile_sections!(locked_gem_sources.first) @sources.merged_gem_lockfile_sections!(locked_gem_sources.first)
end end
@sources_to_unlock = @unlock.delete(:sources) || [] @unlocking_ruby ||= if @ruby_version && locked_ruby_version_object
@unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object
@ruby_version.diff(locked_ruby_version_object) @ruby_version.diff(locked_ruby_version_object)
end 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? @current_platform_missing = add_current_platform unless Bundler.frozen_bundle?
@ -146,9 +151,7 @@ module Bundler
@path_changes = converge_paths @path_changes = converge_paths
@source_changes = converge_sources @source_changes = converge_sources
@explicit_unlocks = @unlock.delete(:gems) || [] if conservative
if @unlock[:conservative]
@gems_to_unlock = @explicit_unlocks.any? ? @explicit_unlocks : @dependencies.map(&:name) @gems_to_unlock = @explicit_unlocks.any? ? @explicit_unlocks : @dependencies.map(&:name)
else else
eager_unlock = @explicit_unlocks.map {|name| Dependency.new(name, ">= 0") } eager_unlock = @explicit_unlocks.map {|name| Dependency.new(name, ">= 0") }
@ -220,6 +223,8 @@ module Bundler
def prefer_local! def prefer_local!
@prefer_local = true @prefer_local = true
sources.prefer_local!
end end
# For given dependency list returns a SpecSet with Gemspec of all the required # For given dependency list returns a SpecSet with Gemspec of all the required
@ -374,7 +379,7 @@ module Bundler
def locked_ruby_version def locked_ruby_version
return unless ruby_version return unless ruby_version
if @unlock[:ruby] || !@locked_ruby_version if @unlocking_ruby || !@locked_ruby_version
Bundler::RubyVersion.system Bundler::RubyVersion.system
else else
@locked_ruby_version @locked_ruby_version
@ -435,23 +440,23 @@ module Bundler
changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`" changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
end 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 = String.new
msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because frozen mode is set" 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 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 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\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 unless explicit_flag
suggested_command = unless Bundler.settings.locations("frozen").keys.include?(:env) suggested_command = unless Bundler.settings.locations("frozen").keys.include?(:env)
"bundle config set frozen false" "bundle config set frozen false"
end 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 "freeze by running `#{suggested_command}`." if suggested_command
end 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 end
def validate_runtime! def validate_runtime!
@ -620,8 +625,8 @@ module Bundler
@resolution_packages ||= begin @resolution_packages ||= begin
last_resolve = converge_locked_specs last_resolve = converge_locked_specs
remove_invalid_platforms! 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 = 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, last_resolve) packages = additional_base_requirements_to_prevent_downgrades(packages)
packages = additional_base_requirements_to_force_updates(packages) packages = additional_base_requirements_to_force_updates(packages)
packages packages
end end
@ -639,6 +644,8 @@ module Bundler
specs = begin specs = begin
resolve.materialize(dependencies) resolve.materialize(dependencies)
rescue IncorrectLockfileDependencies => e rescue IncorrectLockfileDependencies => e
raise if Bundler.frozen_bundle?
spec = e.spec spec = e.spec
raise "Infinite loop while fixing lockfile dependencies" if incorrect_spec == 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 @most_specific_non_local_locked_ruby_platform = find_most_specific_locked_ruby_platform
return if @most_specific_non_local_locked_ruby_platform return if @most_specific_non_local_locked_ruby_platform
@new_platforms << local_platform
@platforms << local_platform @platforms << local_platform
true true
end end
@ -782,7 +790,7 @@ module Bundler
unlock_reason = if unlock_targets unlock_reason = if unlock_targets
"#{unlock_targets.first}: (#{unlock_targets.last.join(", ")})" "#{unlock_targets.first}: (#{unlock_targets.last.join(", ")})"
else else
@unlock[:ruby] ? "ruby" : "" @unlocking_ruby ? "ruby" : ""
end end
return "bundler is unlocking #{unlock_reason}" return "bundler is unlocking #{unlock_reason}"
@ -852,8 +860,6 @@ module Bundler
end end
def check_lockfile def check_lockfile
@missing_lockfile_dep = nil
@locked_spec_with_invalid_deps = nil @locked_spec_with_invalid_deps = nil
@locked_spec_with_missing_deps = nil @locked_spec_with_missing_deps = nil
@ -871,10 +877,6 @@ module Bundler
@locked_specs.delete(missing) @locked_specs.delete(missing)
@locked_spec_with_missing_deps = missing.first.name @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 end
if invalid.any? if invalid.any?
@ -935,32 +937,33 @@ module Bundler
end end
def converge_dependencies def converge_dependencies
changes = false @missing_lockfile_dep = nil
@changed_dependencies = []
@dependencies.each do |dep| current_dependencies.each do |dep|
if dep.source if dep.source
dep.source = sources.get(dep.source) dep.source = sources.get(dep.source)
end end
unless locked_dep = @originally_locked_deps[dep.name] name = dep.name
changes = true
next 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 end
# Gem::Dependency#== matches Gem::Dependency#type. As the lockfile @changed_dependencies << name if dep_changed
# 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
end end
changes @changed_dependencies.any?
end end
# Remove elements from the locked specs that are expired. This will most # Remove elements from the locked specs that are expired. This will most
@ -1022,11 +1025,6 @@ module Bundler
end end
end end
if dep.nil? && requested_dep = requested_dependencies.find {|d| name == d.name }
@gems_to_unlock << name
deps << requested_dep
end
converged << s converged << s
end end
@ -1095,11 +1093,15 @@ module Bundler
current == proposed current == proposed
end 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) 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) 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 end
resolution_packages resolution_packages
end end

View file

@ -2,51 +2,92 @@
require "rubygems/dependency" require "rubygems/dependency"
require_relative "shared_helpers" require_relative "shared_helpers"
require_relative "rubygems_ext"
module Bundler module Bundler
class Dependency < Gem::Dependency 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) def initialize(name, version, options = {}, &blk)
type = options["type"] || :runtime type = options["type"] || :runtime
super(name, version, type) super(name, version, type)
@autorequire = nil @options = options
@groups = Array(options["group"] || :default).map(&:to_sym) end
@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")
@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 end
RUBY_PLATFORM_ARRAY = [Gem::Platform::RUBY].freeze RUBY_PLATFORM_ARRAY = [Gem::Platform::RUBY].freeze
@ -56,37 +97,41 @@ module Bundler
# passed in the `valid_platforms` parameter # passed in the `valid_platforms` parameter
def gem_platforms(valid_platforms) def gem_platforms(valid_platforms)
return RUBY_PLATFORM_ARRAY if force_ruby_platform 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)) } valid_platforms.select {|p| expanded_platforms.include?(GemHelpers.generic(p)) }
end end
def expanded_platforms 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 end
def should_include? def should_include?
@should_include && current_env? && current_platform? should_include && current_env? && current_platform?
end end
def gemspec_dev_dep? def gemspec_dev_dep?
type == :development @gemspec_dev_dep ||= @options.fetch("gemspec_dev_dep", false)
end
def gemfile_dep?
!gemspec_dev_dep?
end end
def current_env? def current_env?
return true unless @env return true unless env
if @env.is_a?(Hash) if env.is_a?(Hash)
@env.all? do |key, val| env.all? do |key, val|
ENV[key.to_s] && (val.is_a?(String) ? ENV[key.to_s] == val : ENV[key.to_s] =~ val) ENV[key.to_s] && (val.is_a?(String) ? ENV[key.to_s] == val : ENV[key.to_s] =~ val)
end end
else else
ENV[@env.to_s] ENV[env.to_s]
end end
end end
def current_platform? def current_platform?
return true if @platforms.empty? return true if platforms.empty?
@platforms.any? do |p| platforms.any? do |p|
Bundler.current_ruby.send("#{p}?") Bundler.current_ruby.send("#{p}?")
end end
end end

View file

@ -13,10 +13,10 @@ module Bundler
builder.to_definition(lockfile, unlock) builder.to_definition(lockfile, unlock)
end 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 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} 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} GITLAB_MERGE_REQUEST_URL = %r{\Ahttps://gitlab\.com/([A-Za-z0-9_\-\./]+)/-/merge_requests/(\d+)\z}
@ -77,12 +77,12 @@ module Bundler
@gemspecs << spec @gemspecs << spec
gem spec.name, name: spec.name, path: path, glob: glob path path, "glob" => glob, "name" => spec.name do
add_dependency spec.name
group(development_group) do
spec.development_dependencies.each do |dep|
gem dep.name, *(dep.requirement.as_list + [type: :development])
end end
spec.development_dependencies.each do |dep|
add_dependency dep.name, dep.requirement.as_list, "gemspec_dev_dep" => true, "group" => development_group
end end
when 0 when 0
raise InvalidOption, "There are no gemspecs at #{expanded_path}" raise InvalidOption, "There are no gemspecs at #{expanded_path}"
@ -94,79 +94,11 @@ module Bundler
def gem(name, *args) def gem(name, *args)
options = args.last.is_a?(Hash) ? args.pop.dup : {} options = args.last.is_a?(Hash) ? args.pop.dup : {}
options["gemfile"] = @gemfile
version = args || [">= 0"] version = args || [">= 0"]
normalize_options(name, version, options) normalize_options(name, version, options)
dep = Dependency.new(name, version, options) add_dependency(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
end end
def source(source, *args, &blk) def source(source, *args, &blk)
@ -301,6 +233,81 @@ module Bundler
private 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) def with_gemfile(gemfile)
expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile&.parent) expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile&.parent)
original_gemfile = @gemfile original_gemfile = @gemfile
@ -433,8 +440,6 @@ module Bundler
opts["source"] = source opts["source"] = source
end end
opts["source"] ||= @source
opts["env"] ||= @env
opts["platforms"] = platforms.dup opts["platforms"] = platforms.dup
opts["group"] = groups opts["group"] = groups
opts["should_include"] = install_if opts["should_include"] = install_if

View file

@ -6,7 +6,8 @@ module Bundler
include MatchRemoteMetadata include MatchRemoteMetadata
attr_reader :name, :version, :platform, :checksum 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) def initialize(name, version, platform, spec_fetcher, dependencies, metadata = nil)
super() super()
@ -14,7 +15,8 @@ module Bundler
@version = Gem::Version.create version @version = Gem::Version.create version
@platform = Gem::Platform.new(platform) @platform = Gem::Platform.new(platform)
@spec_fetcher = spec_fetcher @spec_fetcher = spec_fetcher
@dependencies = dependencies.map {|dep, reqs| build_dependency(dep, reqs) } @dependencies = nil
@unbuilt_dependencies = dependencies
@loaded_from = nil @loaded_from = nil
@remote_specification = nil @remote_specification = nil
@ -31,6 +33,11 @@ module Bundler
@platform @platform
end 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 # needed for standalone, load required_paths from local gemspec
# after the gem is installed # after the gem is installed
def require_paths def require_paths
@ -161,7 +168,7 @@ module Bundler
end end
def build_dependency(name, requirements) def build_dependency(name, requirements)
Gem::Dependency.new(name, requirements) Dependency.new(name, requirements)
end end
end end
end end

View file

@ -254,6 +254,10 @@ module Bundler
@spec = spec @spec = spec
end end
def message
"Bundler found incorrect dependencies in the lockfile for #{spec.full_name}"
end
status_code(41) status_code(41)
end end
end end

View file

@ -4,20 +4,14 @@ module Bundler
module GemHelpers module GemHelpers
GENERIC_CACHE = { Gem::Platform::RUBY => Gem::Platform::RUBY } # rubocop:disable Style/MutableConstant GENERIC_CACHE = { Gem::Platform::RUBY => Gem::Platform::RUBY } # rubocop:disable Style/MutableConstant
GENERICS = [ GENERICS = [
[Gem::Platform.new("java"), Gem::Platform.new("java")], Gem::Platform::JAVA,
[Gem::Platform.new("mswin32"), Gem::Platform.new("mswin32")], *Gem::Platform::WINDOWS,
[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")],
].freeze ].freeze
def generic(p) def generic(p)
GENERIC_CACHE[p] ||= begin GENERIC_CACHE[p] ||= begin
_, found = GENERICS.find do |match, _generic| found = GENERICS.find do |match|
p.os == match.os && (!match.cpu || p.cpu == match.cpu) p === match
end end
found || Gem::Platform::RUBY found || Gem::Platform::RUBY
end end

View file

@ -132,8 +132,6 @@ module Bundler
# Specific version moves can't always reliably be done during sorting # Specific version moves can't always reliably be done during sorting
# as not all elements are compared against each other. # as not all elements are compared against each other.
def post_sort(result, unlock, locked_version) 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? if unlock || locked_version.nil?
result result
else else

View file

@ -193,7 +193,7 @@ module Bundler
def install(options) def install(options)
standalone = options[:standalone] standalone = options[:standalone]
force = options[:force] force = options[:force]
local = options[:local] local = options[:local] || options[:"prefer-local"]
jobs = installation_parallelization jobs = installation_parallelization
spec_installations = ParallelInstaller.call(self, @definition.specs, jobs, standalone, force, local: local) spec_installations = ParallelInstaller.call(self, @definition.specs, jobs, standalone, force, local: local)
spec_installations.each do |installation| spec_installations.each do |installation|

View file

@ -121,13 +121,10 @@ module Bundler
out out
end end
def materialize_strictly def materialize_for_cache
source.local! source.remote!
matching_specs = source.specs.search(self) materialize(self, &:first)
return self if matching_specs.empty?
__materialize__(matching_specs)
end end
def materialized_for_installation def materialized_for_installation
@ -140,53 +137,25 @@ module Bundler
source.local! source.local!
if use_exact_resolved_specifications? if use_exact_resolved_specifications?
materialize_strictly materialize(self) do |matching_specs|
choose_compatible(matching_specs)
end
else else
matching_specs = source.specs.search([name, version]) materialize([name, version]) do |matching_specs|
return self if matching_specs.empty?
target_platform = source.is_a?(Source::Path) ? platform : local_platform target_platform = source.is_a?(Source::Path) ? platform : local_platform
installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform) installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform)
specification = __materialize__(installable_candidates, fallback_to_non_installable: false) specification = choose_compatible(installable_candidates, fallback_to_non_installable: false)
return specification unless specification.nil? return specification unless specification.nil?
if target_platform != platform if target_platform != platform
installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform) installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform)
end end
__materialize__(installable_candidates) choose_compatible(installable_candidates)
end end
end 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 __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)
end
search.locked_platform = platform if search.instance_of?(RemoteSpecification) || search.instance_of?(EndpointSpecification)
end
end
search
end end
def inspect def inspect
@ -217,5 +186,41 @@ module Bundler
(most_specific_locked_platform != generic_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform] (most_specific_locked_platform != generic_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform]
end 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
end end

View file

@ -13,5 +13,18 @@ module Bundler
def matches_current_rubygems? def matches_current_rubygems?
@required_rubygems_version.satisfied_by?(Gem.rubygems_version) @required_rubygems_version.satisfied_by?(Gem.rubygems_version)
end 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
end end

View file

@ -389,9 +389,18 @@ module Bundler
end end
def filter_remote_specs(specs, package) 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 end
# Ignore versions that depend on themselves incorrectly # Ignore versions that depend on themselves incorrectly

View file

@ -15,7 +15,7 @@ module Bundler
class Package class Package
attr_reader :name, :platforms, :dependency, :locked_version 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 @name = name
@platforms = platforms @platforms = platforms
@locked_version = locked_specs.version_for(name) @locked_version = locked_specs.version_for(name)
@ -24,10 +24,14 @@ module Bundler
@top_level = !dependency.nil? @top_level = !dependency.nil?
@prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore @prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore
@prefer_local = prefer_local @prefer_local = prefer_local
@new_platforms = new_platforms
end end
def platform_specs(specs) 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 end
def to_s def to_s
@ -55,7 +59,7 @@ module Bundler
end end
def unlock? def unlock?
@unlock.empty? || @unlock.include?(name) @unlock == true || @unlock.include?(name)
end end
def ignores_prereleases? def ignores_prereleases?

View file

@ -39,9 +39,7 @@ module Bundler
end end
def dependencies def dependencies
@dependencies ||= @specs.flat_map do |spec| @dependencies ||= @specs.flat_map(&:expanded_dependencies).uniq.sort
__dependencies(spec) + metadata_dependencies(spec)
end.uniq.sort
end end
def ==(other) def ==(other)
@ -71,28 +69,6 @@ module Bundler
def exemplary_spec def exemplary_spec
@specs.first @specs.first
end 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 end
end end

View file

@ -58,6 +58,87 @@ module Gem
end end
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" require "rubygems/specification"
# Can be removed once RubyGems 3.5.14 support is dropped # Can be removed once RubyGems 3.5.14 support is dropped
@ -177,8 +258,8 @@ module Gem
dependencies - development_dependencies dependencies - development_dependencies
end end
def deleted_gem? def installation_missing?
!default_gem? && !File.directory?(full_gem_path) !default_gem? && (!Dir.exist?(full_gem_path) || Dir.empty?(full_gem_path))
end end
unless VALIDATES_FOR_RESOLUTION unless VALIDATES_FOR_RESOLUTION
@ -288,86 +369,6 @@ module Gem
end end
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. # On universal Rubies, resolve the "universal" arch to the real CPU arch, without changing the extension directory.
class BasicSpecification class BasicSpecification
if /^universal\.(?<arch>.*?)-/ =~ (CROSS_COMPILING || RUBY_PLATFORM) if /^universal\.(?<arch>.*?)-/ =~ (CROSS_COMPILING || RUBY_PLATFORM)

View file

@ -177,7 +177,7 @@ module Bundler
end end
end end
def replace_gem(specs, specs_by_name) def replace_gem(specs_by_name)
executables = nil executables = nil
[::Kernel.singleton_class, ::Kernel].each do |kernel_class| [::Kernel.singleton_class, ::Kernel].each do |kernel_class|
@ -274,7 +274,7 @@ module Bundler
else else
Gem::BUNDLED_GEMS.replace_require(specs) if Gem::BUNDLED_GEMS.respond_to?(:replace_require) Gem::BUNDLED_GEMS.replace_require(specs) if Gem::BUNDLED_GEMS.respond_to?(:replace_require)
end end
replace_gem(specs, specs_by_name) replace_gem(specs_by_name)
stub_rubygems(specs_by_name.values) stub_rubygems(specs_by_name.values)
replace_bin_path(specs_by_name) replace_bin_path(specs_by_name)
@ -293,7 +293,6 @@ module Bundler
default_spec_name = default_spec.name default_spec_name = default_spec.name
next if specs_by_name.key?(default_spec_name) next if specs_by_name.key?(default_spec_name)
specs << default_spec
specs_by_name[default_spec_name] = default_spec specs_by_name[default_spec_name] = default_spec
end end

View file

@ -50,10 +50,8 @@ module Bundler
Plugin.hook(Plugin::Events::GEM_BEFORE_REQUIRE_ALL, dependencies) Plugin.hook(Plugin::Events::GEM_BEFORE_REQUIRE_ALL, dependencies)
dependencies.each do |dep| dependencies.each do |dep|
required_file = nil
Plugin.hook(Plugin::Events::GEM_BEFORE_REQUIRE, dep) Plugin.hook(Plugin::Events::GEM_BEFORE_REQUIRE, dep)
begin
# Loop through all the specified autorequires for the # Loop through all the specified autorequires for the
# dependency. If there are none, use the dependency's name # dependency. If there are none, use the dependency's name
# as the autorequire. # as the autorequire.
@ -62,23 +60,20 @@ module Bundler
file = dep.name if file == true file = dep.name if file == true
required_file = file required_file = file
begin begin
Kernel.require file Kernel.require required_file
rescue RuntimeError => e rescue LoadError => e
raise e if e.is_a?(LoadError) # we handle this a little later 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, raise Bundler::GemRequireError.new e,
"There was an error while trying to load the gem '#{file}'." "There was an error while trying to load the gem '#{file}'."
end end
end rescue RuntimeError => e
rescue LoadError => e raise Bundler::GemRequireError.new e,
raise if dep.autorequire || e.path != required_file "There was an error while trying to load the gem '#{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
end end
end end

View file

@ -35,6 +35,8 @@ module Bundler
spec.source == self spec.source == self
end end
def prefer_local!; end
def local!; end def local!; end
def local_only!; end def local_only!; end

View file

@ -19,6 +19,7 @@ module Bundler
@allow_remote = false @allow_remote = false
@allow_cached = false @allow_cached = false
@allow_local = options["allow_local"] || false @allow_local = options["allow_local"] || false
@prefer_local = false
@checksum_store = Checksum::Store.new @checksum_store = Checksum::Store.new
Array(options["remotes"]).reverse_each {|r| add_remote(r) } Array(options["remotes"]).reverse_each {|r| add_remote(r) }
@ -30,6 +31,10 @@ module Bundler
@caches ||= [cache_path, *Bundler.rubygems.gem_cache] @caches ||= [cache_path, *Bundler.rubygems.gem_cache]
end end
def prefer_local!
@prefer_local = true
end
def local_only! def local_only!
@specs = nil @specs = nil
@allow_local = true @allow_local = true
@ -37,6 +42,10 @@ module Bundler
@allow_remote = false @allow_remote = false
end end
def local_only?
@allow_local && !@allow_remote
end
def local! def local!
return if @allow_local return if @allow_local
@ -139,9 +148,15 @@ module Bundler
index.merge!(cached_specs) if @allow_cached index.merge!(cached_specs) if @allow_cached
index.merge!(installed_specs) if @allow_local index.merge!(installed_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 # complete with default specs, only if not already available in the
# index through remote, cached, or installed specs # index through remote, cached, or installed specs
index.use(default_specs) if @allow_local index.use(default_specs)
end
end
index index
end end
@ -439,7 +454,7 @@ module Bundler
end end
def installed?(spec) def installed?(spec)
installed_specs[spec].any? && !spec.deleted_gem? installed_specs[spec].any? && !spec.installation_missing?
end end
def rubygems_dir def rubygems_dir

View file

@ -141,6 +141,10 @@ module Bundler
different_sources?(lock_sources, replacement_sources) different_sources?(lock_sources, replacement_sources)
end end
def prefer_local!
all_sources.each(&:prefer_local!)
end
def local_only! def local_only!
all_sources.each(&:local_only!) all_sources.each(&:local_only!)
end end

View file

@ -83,15 +83,13 @@ module Bundler
end end
def []=(key, value) def []=(key, value)
@specs << value delete_by_name(key)
reset! add_spec(value)
end end
def delete(specs) def delete(specs)
Array(specs).each {|spec| @specs.delete(spec) } Array(specs).each {|spec| remove_spec(spec) }
reset!
end end
def sort! def sort!
@ -117,8 +115,7 @@ module Bundler
def materialized_for_all_platforms def materialized_for_all_platforms
@specs.map do |s| @specs.map do |s|
next s unless s.is_a?(LazySpecification) next s unless s.is_a?(LazySpecification)
s.source.remote! spec = s.materialize_for_cache
spec = s.materialize_strictly
raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec
spec spec
end end
@ -169,8 +166,10 @@ module Bundler
def delete_by_name(name) def delete_by_name(name)
@specs.reject! {|spec| spec.name == name } @specs.reject! {|spec| spec.name == name }
@sorted&.reject! {|spec| spec.name == name }
return if @lookup.nil?
reset! @lookup[name] = nil
end end
def version_for(name) def version_for(name)
@ -212,6 +211,10 @@ module Bundler
s.matches_current_metadata? && valid_dependencies?(s) s.matches_current_metadata? && valid_dependencies?(s)
end end
def to_s
map(&:full_name).to_s
end
private private
def materialize_dependencies(dependencies, platforms = [nil], skips: []) def materialize_dependencies(dependencies, platforms = [nil], skips: [])
@ -245,11 +248,6 @@ module Bundler
@materializations.filter_map(&:materialized_spec) @materializations.filter_map(&:materialized_spec)
end end
def reset!
@sorted = nil
@lookup = nil
end
def complete_platform(platform) def complete_platform(platform)
new_specs = [] new_specs = []
@ -269,9 +267,7 @@ module Bundler
end end
if valid_platform && new_specs.any? if valid_platform && new_specs.any?
@specs.concat(new_specs) new_specs.each {|spec| add_spec(spec) }
reset!
end end
valid_platform valid_platform
@ -292,16 +288,13 @@ module Bundler
end end
def sorted def sorted
rake = @specs.find {|s| s.name == "rake" } @sorted ||= ([@specs.find {|s| s.name == "rake" }] + tsort).compact.uniq
begin
@sorted ||= ([rake] + tsort).compact.uniq
rescue TSort::Cyclic => error rescue TSort::Cyclic => error
cgems = extract_circular_gems(error) cgems = extract_circular_gems(error)
raise CyclicDependencyError, "Your bundle requires gems that depend" \ raise CyclicDependencyError, "Your bundle requires gems that depend" \
" on each other, creating an infinite loop. Please remove either" \ " on each other, creating an infinite loop. Please remove either" \
" gem '#{cgems[0]}' or gem '#{cgems[1]}' and try again." " gem '#{cgems[0]}' or gem '#{cgems[1]}' and try again."
end end
end
def extract_circular_gems(error) def extract_circular_gems(error)
error.message.scan(/@name="(.*?)"/).flatten error.message.scan(/@name="(.*?)"/).flatten
@ -311,8 +304,7 @@ module Bundler
@lookup ||= begin @lookup ||= begin
lookup = {} lookup = {}
@specs.each do |s| @specs.each do |s|
lookup[s.name] ||= [] index_spec(lookup, s.name, s)
lookup[s.name] << s
end end
lookup lookup
end end
@ -333,5 +325,36 @@ module Bundler
specs_for_name.each {|s2| yield s2 } specs_for_name.each {|s2| yield s2 }
end end
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
end end

View file

@ -5,6 +5,7 @@ source "https://rubygems.org"
# Specify your gem's dependencies in <%= config[:name] %>.gemspec # Specify your gem's dependencies in <%= config[:name] %>.gemspec
gemspec gemspec
gem "irb"
gem "rake", "~> 13.0" gem "rake", "~> 13.0"
<%- if config[:ext] -%> <%- if config[:ext] -%>

View file

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

View file

@ -9,7 +9,7 @@
require "rbconfig" require "rbconfig"
module Gem module Gem
VERSION = "3.6.3" VERSION = "3.6.4"
end end
# Must be first since it unloads the prelude from 1.9.2 # Must be first since it unloads the prelude from 1.9.2

View file

@ -106,13 +106,15 @@ class Gem::Requirement
unless PATTERN =~ obj.to_s unless PATTERN =~ obj.to_s
raise BadRequirementError, "Illformed requirement [#{obj.inspect}]" raise BadRequirementError, "Illformed requirement [#{obj.inspect}]"
end end
op = -($1 || "=")
version = -$2
if $1 == ">=" && $2 == "0" if op == ">=" && version == "0"
DefaultRequirement DefaultRequirement
elsif $1 == ">=" && $2 == "0.a" elsif op == ">=" && version == "0.a"
DefaultPrereleaseRequirement DefaultPrereleaseRequirement
else else
[-($1 || "="), Gem::Version.new($2)] [op, Gem::Version.new(version)]
end end
end end

View file

@ -1415,13 +1415,11 @@ class Gem::Specification < Gem::BasicSpecification
raise e raise e
end end
begin specs = spec_dep.matching_specs(true).uniq(&:full_name)
specs = spec_dep.to_specs.uniq(&:full_name)
rescue Gem::MissingSpecError => e
raise Gem::MissingSpecError.new(e.name, e.requirement, "at: #{spec_file}")
end
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 specs.first.activate
else else
name = spec_dep.name name = spec_dep.name

View file

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

View file

@ -35,140 +35,15 @@ RSpec.describe Bundler::Dependency do
end end
end end
describe "PLATFORM_MAP" do it "is on the current platform" do
subject { described_class::PLATFORM_MAP } engine = Gem.win_platform? ? "windows" : RUBY_ENGINE
# rubocop:disable Naming/VariableNumber dep = described_class.new(
let(:platforms) do "test_gem",
{ ruby: Gem::Platform::RUBY, "1.0.0",
ruby_18: Gem::Platform::RUBY, { "platforms" => "#{engine}_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}" },
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 expect(dep.current_platform?).to be_truthy
{ 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
end end
end end

View file

@ -201,8 +201,8 @@ RSpec.describe Bundler::Dsl do
describe "#gem" do describe "#gem" do
# rubocop:disable Naming/VariableNumber # 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, :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, :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, :jruby, :rbx, :truffleruby].each do |platform| :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 it "allows #{platform} as a valid platform" do
subject.gem("foo", platform: platform) subject.gem("foo", platform: platform)
end end
@ -211,7 +211,9 @@ RSpec.describe Bundler::Dsl do
it "allows platforms matching the running Ruby version" do it "allows platforms matching the running Ruby version" do
platform = "ruby_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}" 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 end
it "rejects invalid platforms" do it "rejects invalid platforms" do

View file

@ -83,7 +83,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
/Authentication is required for www.uri-to-fetch.com/) /Authentication is required for www.uri-to-fetch.com/)
end 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, 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) /`bundle config set --global www\.uri-to-fetch\.com username:password`.*`BUNDLE_WWW__URI___TO___FETCH__COM`/m)
end end

View file

@ -20,13 +20,13 @@ RSpec.describe Bundler::GemVersionPromoter do
end end
end end
def build_package(name, version, locked = []) def build_package(name, version, unlock)
Bundler::Resolver::Package.new(name, [], locked_specs: Bundler::SpecSet.new(build_spec(name, version)), unlock: locked) Bundler::Resolver::Package.new(name, [], locked_specs: Bundler::SpecSet.new(build_spec(name, version)), unlock: unlock)
end end
def sorted_versions(candidates:, current:, name: "foo", locked: []) def sorted_versions(candidates:, current:, unlock: true)
gvp.sort_versions( gvp.sort_versions(
build_package(name, current, locked), build_package("foo", current, unlock),
build_candidates(candidates) build_candidates(candidates)
).flatten.map(&:version).map(&:to_s) ).flatten.map(&:version).map(&:to_s)
end end
@ -127,7 +127,7 @@ RSpec.describe Bundler::GemVersionPromoter do
before { gvp.level = :minor } before { gvp.level = :minor }
it "keeps the current version first" do 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") expect(versions.first).to eq("0.3.0")
end end
end end

View file

@ -111,11 +111,11 @@ RSpec.describe Bundler::LockfileParser do
end end
let(:specs) do let(:specs) do
[ [
Bundler::LazySpecification.new("peiji-san", v("1.2.0"), rb), Bundler::LazySpecification.new("peiji-san", v("1.2.0"), Gem::Platform::RUBY),
Bundler::LazySpecification.new("rake", v("10.3.2"), rb), Bundler::LazySpecification.new("rake", v("10.3.2"), Gem::Platform::RUBY),
] ]
end end
let(:platforms) { [rb] } let(:platforms) { [Gem::Platform::RUBY] }
let(:bundler_version) { Gem::Version.new("1.12.0.rc.2") } let(:bundler_version) { Gem::Version.new("1.12.0.rc.2") }
let(:ruby_version) { "ruby 2.1.3p242" } let(:ruby_version) { "ruby 2.1.3p242" }
let(:lockfile_path) { Bundler.default_lockfile.relative_path_from(Dir.pwd) } let(:lockfile_path) { Bundler.default_lockfile.relative_path_from(Dir.pwd) }

View file

@ -21,7 +21,7 @@ RSpec.describe Bundler::Source do
end end
describe "#version_message" do 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 shared_examples_for "the lockfile specs are not relevant" do
it "should return a string with the spec name and version" do it "should return a string with the spec name and version" do
@ -70,7 +70,7 @@ RSpec.describe Bundler::Source do
end end
context "with a more recent version" do 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") } let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") }
context "with color", :no_color_tty do context "with color", :no_color_tty do
@ -97,7 +97,7 @@ RSpec.describe Bundler::Source do
end end
context "with an older version" do 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") } let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") }
context "with color", :no_color_tty do context "with color", :no_color_tty do

View file

@ -113,6 +113,11 @@ RSpec.describe "bundle cache" do
expect(out).to include("Using json #{default_json_version}") expect(out).to include("Using json #{default_json_version}")
end 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 it "caches remote and builtin gems" do
install_gemfile <<-G install_gemfile <<-G
source "https://gem.repo2" source "https://gem.repo2"
@ -167,7 +172,7 @@ RSpec.describe "bundle cache" do
G G
bundle :cache, raise_on_error: false 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") expect(err).to include("json-#{default_json_version} is built in to Ruby, and can't be cached")
end end
end end

View file

@ -211,7 +211,7 @@ RSpec.describe "bundle cache" do
end end
context "with --all-platforms" do 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 gemfile <<-D
source "https://gem.repo1" source "https://gem.repo1"
gem 'myrack', :platforms => [:ruby_20, :windows_20] 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 expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
end 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 gemfile <<-D
source "https://gem.repo1" source "https://gem.repo1"
gem 'myrack', :platforms => [:ruby_20, :x64_mingw_20] 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") expect(the_bundle).to include_gems("platform_specific 1.0 ruby")
end 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 it "does not update the cache if --no-cache is passed" do
gemfile <<-G gemfile <<-G
source "https://gem.repo1" source "https://gem.repo1"

View file

@ -36,7 +36,34 @@ RSpec.describe "bundle console", readline: true do
RUBY RUBY
end end
end end
end
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
it "does not show the bug report template" do
bundle("console", raise_on_error: false) do |input, _, _|
input.puts("exit")
end
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 install_gemfile <<-G
source "https://gem.repo2" source "https://gem.repo2"
gem "myrack" gem "myrack"
@ -86,6 +113,17 @@ RSpec.describe "bundle console", readline: true do
expect(out).to include("(irb)") expect(out).to include("(irb)")
end 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 it "doesn't load any other groups" do
bundle "console" do |input, _, _| bundle "console" do |input, _, _|
input.puts("puts ACTIVESUPPORT") input.puts("puts ACTIVESUPPORT")
@ -139,3 +177,4 @@ RSpec.describe "bundle console", readline: true do
expect(the_bundle).to include_gems "foo 1.0" expect(the_bundle).to include_gems "foo 1.0"
end end
end end
end

View file

@ -46,7 +46,9 @@ RSpec.describe "bundle doctor" do
end end
it "exits with no message if the installed gem has no C extensions" do 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 expect(@stdout.string).to be_empty
end end
@ -54,7 +56,7 @@ RSpec.describe "bundle doctor" do
doctor = Bundler::CLI::Doctor.new({}) 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(: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"] 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 { doctor.run }.not_to raise_error
expect(@stdout.string).to be_empty expect(@stdout.string).to be_empty
end end
@ -63,7 +65,7 @@ RSpec.describe "bundle doctor" do
doctor = Bundler::CLI::Doctor.new({}) 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(: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"] 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 expect { doctor.run }.to raise_error(Bundler::ProductionError, <<~E.strip), @stdout.string
The following gems are missing OS dependencies: The following gems are missing OS dependencies:
* bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib * bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
@ -82,7 +84,9 @@ RSpec.describe "bundle doctor" do
end end
it "exits with an error if home contains files that are not readable/writable" do 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( 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}" "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 end
it "exits with an error if home contains files that are not readable/writable" do 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(@stat).to receive(:uid) { Process.uid }
allow(File).to receive(:writable?).with(@unwritable_file) { false } allow(File).to receive(:writable?).with(@unwritable_file) { false }
allow(File).to receive(:readable?).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( 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}" "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 end
it "exits with an error if home contains files that are not readable/writable and are not owned by the current user" do 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(:writable?).with(@unwritable_file) { false }
allow(File).to receive(:readable?).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( 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}" "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 end
it "exits with a warning if home contains files that are read/write but not owned by current user" do 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(:writable?).with(@unwritable_file) { true }
allow(File).to receive(:readable?).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( 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}" "Files exist in the Bundler home that are owned by another user, but are still readable/writable. These files are:\n - #{@unwritable_file}"
) )

View file

@ -56,12 +56,12 @@ RSpec.describe "bundle info" do
expect(out).to eq("2.3.2") expect(out).to eq("2.3.2")
end 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 "config set --local path vendor/bundle"
bundle "install" bundle "install"
bundle "info bundler" bundle "info bundler"
expect(out).to include("\tPath: #{root}") 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 end
it "complains if gem not in bundle" do 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'.") expect(err).to eq("Could not find gem 'missing'.")
end 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")) FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2"))
bundle "info rails --path" 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) expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
bundle "info rail --path" 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) expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
bundle "info rails" 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) expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
end end
context "given a default gem shippped in ruby", :ruby_repo do context "given a default gem shippped in ruby", :ruby_repo do
it "prints information about the default gem" do it "prints information about the default gem" do
bundle "info rdoc" bundle "info json"
expect(out).to include("* rdoc") expect(out).to include("* json")
expect(out).to include("Default Gem: yes") expect(out).to include("Default Gem: yes")
end end
end end

View file

@ -100,12 +100,23 @@ RSpec.describe "bundle install with gem sources" do
gem 'myrack' gem 'myrack'
G 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" bundle "install --verbose"
expect(out).to include("Installing myrack 1.0.0") 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") expect(the_bundle).to include_gems("myrack 1.0.0")
end end
@ -281,7 +292,7 @@ RSpec.describe "bundle install with gem sources" do
end end
it "installs gems for windows" do it "installs gems for windows" do
simulate_platform x86_mswin32 do simulate_platform "x86-mswin32" do
install_gemfile <<-G install_gemfile <<-G
source "https://gem.repo1" source "https://gem.repo1"
gem "platform_specific" 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") expect(the_bundle).to include_gems("platform_specific 1.0 x86-mswin32")
end end
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 end
describe "doing bundle install foo" do describe "doing bundle install foo" do
@ -559,7 +581,7 @@ RSpec.describe "bundle install with gem sources" do
bundle :install, raise_on_error: false 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 end
it "warns when a Gemfile dependency is overriding a gemspec development dependency, with different requirements" do 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(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 expect(last_command).to be_success
end 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 end
context "with a symlinked configured as bundle path and a gem with symlinks" do context "with a symlinked configured as bundle path and a gem with symlinks" do

View file

@ -645,13 +645,13 @@ RSpec.describe "bundle lock" do
it "single gem updates dependent gem to minor" do it "single gem updates dependent gem to minor" do
bundle "lock --update foo --patch" 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 end
it "minor preferred with strict" do it "minor preferred with strict" do
bundle "lock --update --minor --strict" 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 end
it "shows proper error when Gemfile changes forbid patch upgrades, and --patch --strict is given" do 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 it "defaults to major" do
bundle "lock --update --pre" 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 end
it "patch preferred" do it "patch preferred" do
bundle "lock --update --patch --pre" 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 end
it "minor preferred" do it "minor preferred" do
bundle "lock --update --minor --pre" 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 end
it "major preferred" do it "major preferred" do
bundle "lock --update --major --pre" 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 end
end end
@ -734,7 +734,7 @@ RSpec.describe "bundle lock" do
it "adds the latest version of the new dependency" do it "adds the latest version of the new dependency" do
bundle "lock --minor --update sequel" 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
end end
@ -765,8 +765,7 @@ RSpec.describe "bundle lock" do
bundle "lock --add-platform java x86-mingw32" bundle "lock --add-platform java x86-mingw32"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile) expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32"))
expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32))
end 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 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" bundle "lock --add-platform java x86-mingw32"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile) expect(the_bundle.locked_platforms).to contain_exactly(Gem::Platform::RUBY, "x86_64-linux", "java", "x86-mingw32")
expect(lockfile.platforms).to contain_exactly(rb, linux, java, x86_mingw32)
end end
it "supports adding the `ruby` platform" do it "supports adding the `ruby` platform" do
bundle "lock --add-platform ruby" bundle "lock --add-platform ruby"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile) expect(the_bundle.locked_platforms).to match_array(default_platform_list("ruby"))
expect(lockfile.platforms).to match_array(default_platform_list("ruby"))
end end
it "fails when adding an unknown platform" do it "fails when adding an unknown platform" do
@ -867,13 +864,11 @@ RSpec.describe "bundle lock" do
bundle "lock --add-platform java x86-mingw32" bundle "lock --add-platform java x86-mingw32"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile) expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32"))
expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32))
bundle "lock --remove-platform java" bundle "lock --remove-platform java"
lockfile = Bundler::LockfileParser.new(read_lockfile) expect(the_bundle.locked_platforms).to match_array(default_platform_list("x86-mingw32"))
expect(lockfile.platforms).to match_array(default_platform_list(x86_mingw32))
end end
it "also cleans up redundant platform gems when removing platforms" do it "also cleans up redundant platform gems when removing platforms" do
@ -948,7 +943,7 @@ RSpec.describe "bundle lock" do
build_repo4 do build_repo4 do
build_gem "ffi", "1.9.14" build_gem "ffi", "1.9.14"
build_gem "ffi", "1.9.14" do |s| build_gem "ffi", "1.9.14" do |s|
s.platform = x86_mingw32 s.platform = "x86-mingw32"
end end
build_gem "gssapi", "0.1" build_gem "gssapi", "0.1"
@ -980,7 +975,7 @@ RSpec.describe "bundle lock" do
gem "gssapi" gem "gssapi"
G G
simulate_platform(x86_mingw32) { bundle :lock } simulate_platform("x86-mingw32") { bundle :lock }
checksums = checksums_section_when_enabled do |c| checksums = checksums_section_when_enabled do |c|
c.checksum gem_repo4, "ffi", "1.9.14", "x86-mingw32" c.checksum gem_repo4, "ffi", "1.9.14", "x86-mingw32"

View file

@ -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) expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
end 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")) FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2"))
bundle "show rails" 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) expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s)
end end

View file

@ -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") expect(out).to include("Installing sneakers 2.11.0").and include("Installing rake 13.0.6")
end 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 it "does not downgrade indirect dependencies unnecessarily" do
build_repo4 do build_repo4 do
build_gem "a" do |s| build_gem "a" do |s|
@ -694,28 +760,36 @@ RSpec.describe "bundle update" do
bundle "update", all: true, raise_on_error: false bundle "update", all: true, raise_on_error: false
expect(last_command).to be_failure 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 eq <<~ERROR.strip
expect(err).to match(/freeze by running `bundle config set frozen false`./) 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 end
it "should fail loudly when frozen is set globally" do it "should fail loudly when frozen is set globally" do
bundle "config set --global frozen 1" bundle "config set --global frozen 1"
bundle "update", all: true, raise_on_error: false 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/). expect(err).to eq <<~ERROR.strip
and match(/freeze by running `bundle config set frozen false`./) 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 end
it "should fail loudly when deployment is set globally" do it "should fail loudly when deployment is set globally" do
bundle "config set --global deployment true" bundle "config set --global deployment true"
bundle "update", all: true, raise_on_error: false 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/). expect(err).to eq <<~ERROR.strip
and match(/freeze by running `bundle config set frozen false`./) 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 end
it "should not suggest any command to unfreeze bundler if frozen is set through ENV" do 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" } 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).to eq("Bundler is unlocking, but the lockfile can't be updated because frozen mode is set")
expect(err).not_to match(/by running/)
end end
end end
@ -1132,7 +1206,7 @@ RSpec.describe "bundle update in more complicated situations" do
a a
L L
simulate_platform linux, &example simulate_platform "x86_64-linux", &example
end end
it "allows updating" do it "allows updating" do
@ -1173,7 +1247,7 @@ RSpec.describe "bundle update in more complicated situations" do
end end
it "is not updated because it is not actually included in the bundle" do 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" 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(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" expect(the_bundle).to_not include_gem "a"

View file

@ -8,34 +8,6 @@ RSpec.describe "bundle install from an existing gemspec" do
end end
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 it "should install runtime and development dependencies" do
build_lib("foo", path: tmp("foo")) do |s| build_lib("foo", path: tmp("foo")) do |s|
s.write("Gemfile", "source :rubygems\ngemspec") s.write("Gemfile", "source :rubygems\ngemspec")
@ -178,7 +150,7 @@ RSpec.describe "bundle install from an existing gemspec" do
end end
it "should match a lockfile without needing to re-resolve with development dependencies" do 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| build_lib("foo", path: tmp("foo")) do |s|
s.add_dependency "myrack" s.add_dependency "myrack"
s.add_development_dependency "thin" s.add_development_dependency "thin"
@ -383,7 +355,7 @@ RSpec.describe "bundle install from an existing gemspec" do
myrack (1.0.0) myrack (1.0.0)
PLATFORMS PLATFORMS
#{generic_local_platform} ruby
DEPENDENCIES DEPENDENCIES
foo! foo!
@ -448,7 +420,8 @@ RSpec.describe "bundle install from an existing gemspec" do
simulate_new_machine simulate_new_machine
simulate_platform("jruby") { bundle "install" } 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 end
context "on ruby" do context "on ruby" do
@ -465,7 +438,7 @@ RSpec.describe "bundle install from an existing gemspec" do
c.no_checksum "foo", "1.0" c.no_checksum "foo", "1.0"
c.checksum gem_repo2, "platform_specific", "1.0" c.checksum gem_repo2, "platform_specific", "1.0"
c.checksum gem_repo2, "platform_specific", "1.0", "java" c.checksum gem_repo2, "platform_specific", "1.0", "java"
x64_mingw_checksums(c) c.checksum gem_repo2, "platform_specific", "1.0", "x64-mingw32"
end end
expect(lockfile).to eq <<~L expect(lockfile).to eq <<~L
@ -480,12 +453,12 @@ RSpec.describe "bundle install from an existing gemspec" do
specs: specs:
platform_specific (1.0) platform_specific (1.0)
platform_specific (1.0-java) platform_specific (1.0-java)
#{x64_mingw_gems} platform_specific (1.0-x64-mingw32)
PLATFORMS PLATFORMS
java java
ruby ruby
#{x64_mingw_platforms} x64-mingw32
DEPENDENCIES DEPENDENCIES
foo! foo!
@ -506,7 +479,7 @@ RSpec.describe "bundle install from an existing gemspec" do
c.no_checksum "foo", "1.0" c.no_checksum "foo", "1.0"
c.checksum gem_repo2, "platform_specific", "1.0" c.checksum gem_repo2, "platform_specific", "1.0"
c.checksum gem_repo2, "platform_specific", "1.0", "java" c.checksum gem_repo2, "platform_specific", "1.0", "java"
x64_mingw_checksums(c) c.checksum gem_repo2, "platform_specific", "1.0", "x64-mingw32"
end end
expect(lockfile).to eq <<~L expect(lockfile).to eq <<~L
@ -520,12 +493,12 @@ RSpec.describe "bundle install from an existing gemspec" do
specs: specs:
platform_specific (1.0) platform_specific (1.0)
platform_specific (1.0-java) platform_specific (1.0-java)
#{x64_mingw_gems} platform_specific (1.0-x64-mingw32)
PLATFORMS PLATFORMS
java java
ruby ruby
#{x64_mingw_platforms} x64-mingw32
DEPENDENCIES DEPENDENCIES
foo! 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, "indirect_platform_specific", "1.0"
c.checksum gem_repo2, "platform_specific", "1.0" c.checksum gem_repo2, "platform_specific", "1.0"
c.checksum gem_repo2, "platform_specific", "1.0", "java" c.checksum gem_repo2, "platform_specific", "1.0", "java"
x64_mingw_checksums(c) c.checksum gem_repo2, "platform_specific", "1.0", "x64-mingw32"
end end
expect(lockfile).to eq <<~L expect(lockfile).to eq <<~L
@ -565,12 +538,12 @@ RSpec.describe "bundle install from an existing gemspec" do
platform_specific platform_specific
platform_specific (1.0) platform_specific (1.0)
platform_specific (1.0-java) platform_specific (1.0-java)
#{x64_mingw_gems} platform_specific (1.0-x64-mingw32)
PLATFORMS PLATFORMS
java java
ruby ruby
#{x64_mingw_platforms} x64-mingw32
DEPENDENCIES DEPENDENCIES
foo! foo!
@ -659,9 +632,7 @@ RSpec.describe "bundle install from an existing gemspec" do
win32-api (1.5.3-universal-mingw32) win32-api (1.5.3-universal-mingw32)
PLATFORMS PLATFORMS
ruby #{lockfile_platforms("ruby", "x64-mingw32", "x86-mingw32")}
#{x64_mingw_platforms}
x86-mingw32
DEPENDENCIES DEPENDENCIES
chef! chef!

View file

@ -1068,7 +1068,7 @@ RSpec.describe "bundle install with git sources" do
gem "foo", :git => "#{lib_path("foo-1.0")}" gem "foo", :git => "#{lib_path("foo-1.0")}"
G 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 " \ expect(err).to include("Bundler could not install a gem because it " \
"needs to create a directory, but a file exists " \ "needs to create a directory, but a file exists " \
"- #{default_bundle_path("bundler")}") "- #{default_bundle_path("bundler")}")

View file

@ -195,7 +195,7 @@ RSpec.describe "bundle install across platforms" do
build_gem("ffi", "1.9.23") build_gem("ffi", "1.9.23")
end end
simulate_platform java do simulate_platform "java" do
install_gemfile <<-G install_gemfile <<-G
source "https://gem.repo4" source "https://gem.repo4"
@ -330,7 +330,7 @@ RSpec.describe "bundle install across platforms" do
end end
it "works with gems with platform-specific dependency having different requirements order" do 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 update_repo2 do
build_gem "fspath", "3" build_gem "fspath", "3"
build_gem "image_optim_pack", "1.2.3" do |s| build_gem "image_optim_pack", "1.2.3" do |s|
@ -616,7 +616,7 @@ end
RSpec.describe "when a gem has no architecture" do RSpec.describe "when a gem has no architecture" do
it "still installs correctly" do it "still installs correctly" do
simulate_platform x86_mswin32 do simulate_platform "x86-mswin32" do
build_repo2 do build_repo2 do
# The rcov gem is platform mswin32, but has no arch # The rcov gem is platform mswin32, but has no arch
build_gem "rcov" do |s| build_gem "rcov" do |s|

View file

@ -11,7 +11,7 @@ RSpec.describe "bundle install with specific platforms" do
setup_multiplatform_gem setup_multiplatform_gem
install_gemfile(google_protobuf) install_gemfile(google_protobuf)
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) 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).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( expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(
"google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin" "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 G
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) 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).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", expect(the_bundle.locked_gems.specs.map(&:full_name)).to include("CFPropertyList-1.0",
"facter-2.4.6-universal-darwin") "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 simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem setup_multiplatform_gem
install_gemfile(google_protobuf) 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[ 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-universal-darwin
google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw32 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 simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem setup_multiplatform_gem
install_gemfile(google_protobuf) 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( 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",
"google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin" "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) myrack (3.0.7)
PLATFORMS PLATFORMS
#{lockfile_platforms("ruby", generic_local_platform, defaults: [])} #{lockfile_platforms(generic_default_locked_platform || local_platform, defaults: ["ruby"])}
DEPENDENCIES DEPENDENCIES
concurrent-ruby concurrent-ruby

View file

@ -53,7 +53,6 @@ RSpec.describe "bundle install" do
end end
end end
context "with deprecated features" do
it "reports that lib is an invalid option" do it "reports that lib is an invalid option" do
gemfile <<-G gemfile <<-G
source "https://gem.repo1" source "https://gem.repo1"
@ -64,6 +63,27 @@ RSpec.describe "bundle install" do
bundle :install, raise_on_error: false bundle :install, raise_on_error: false
expect(err).to match(/You passed :lib as an option for gem 'myrack', but it is invalid/) expect(err).to match(/You passed :lib as an option for gem 'myrack', but it is invalid/)
end 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 end
context "when an internal error happens" do context "when an internal error happens" do

View file

@ -119,7 +119,7 @@ RSpec.describe "gemcutter's dependency API" do
end end
it "falls back when the API errors out" do it "falls back when the API errors out" do
simulate_platform x86_mswin32 do simulate_platform "x86-mswin32" do
build_repo2 do build_repo2 do
# The rcov gem is platform mswin32, but has no arch # The rcov gem is platform mswin32, but has no arch
build_gem "rcov" do |s| build_gem "rcov" do |s|

View file

@ -295,6 +295,30 @@ RSpec.describe "bundle flex_install" do
it "should work when you update" do it "should work when you update" do
bundle "update myrack" 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
end end

View file

@ -606,13 +606,13 @@ RSpec.describe "bundle install with install-time dependencies" do
s.required_ruby_version = "> 9000" s.required_ruby_version = "> 9000"
end end
build_gem "myrack", "1.2" do |s| build_gem "myrack", "1.2" do |s|
s.platform = x86_mingw32 s.platform = "x86-mingw32"
s.required_ruby_version = "> 9000" s.required_ruby_version = "> 9000"
end end
build_gem "myrack", "1.2" build_gem "myrack", "1.2"
end end
simulate_platform x86_mingw32 do simulate_platform "x86-mingw32" do
install_gemfile <<-G, artifice: "compact_index" install_gemfile <<-G, artifice: "compact_index"
ruby "#{Gem.ruby_version}" ruby "#{Gem.ruby_version}"
source "https://gem.repo4" source "https://gem.repo4"

View file

@ -1587,6 +1587,32 @@ RSpec.describe "the lockfile format" do
L L
end 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 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_repo4 do
build_gem "other_dep", "0.9" build_gem "other_dep", "0.9"
@ -1798,7 +1824,7 @@ RSpec.describe "the lockfile format" do
indirect_dependency (1.2.3) indirect_dependency (1.2.3)
PLATFORMS PLATFORMS
#{lockfile_platforms("ruby", generic_local_platform, defaults: [])} #{lockfile_platforms(generic_default_locked_platform || local_platform, defaults: ["ruby"])}
DEPENDENCIES DEPENDENCIES
direct_dependency direct_dependency
@ -1849,6 +1875,62 @@ RSpec.describe "the lockfile format" do
L L
end 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 shared_examples_for "a lockfile missing dependent specs" do
it "auto-heals" do it "auto-heals" do
build_repo4 do build_repo4 do

View file

@ -34,19 +34,21 @@ RSpec.describe "Bundler::GemHelpers#generic" do
expect(generic(pl("x86-mswin32"))).to eq(pl("x86-mswin32")) expect(generic(pl("x86-mswin32"))).to eq(pl("x86-mswin32"))
end end
it "converts 32-bit mingw platform variants into x86-mingw32" do it "converts 32-bit mingw platform variants into universal-mingw" do
expect(generic(pl("mingw32"))).to eq(pl("x86-mingw32")) expect(generic(pl("i386-mingw32"))).to eq(pl("universal-mingw"))
expect(generic(pl("i386-mingw32"))).to eq(pl("x86-mingw32")) expect(generic(pl("x86-mingw32"))).to eq(pl("universal-mingw"))
expect(generic(pl("x86-mingw32"))).to eq(pl("x86-mingw32"))
end end
it "converts 64-bit mingw platform variants into x64-mingw32" do it "converts 64-bit mingw platform variants into universal-mingw" do
expect(generic(pl("x64-mingw32"))).to eq(pl("x64-mingw32")) expect(generic(pl("x64-mingw32"))).to eq(pl("universal-mingw"))
expect(generic(pl("x86_64-mingw32"))).to eq(pl("x64-mingw32"))
end end
it "converts 64-bit mingw UCRT platform variants into x64-mingw-ucrt" do it "converts x64 mingw UCRT platform variants into universal-mingw" do
expect(generic(pl("x64-mingw-ucrt"))).to eq(pl("x64-mingw-ucrt")) 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
end end

View file

@ -6,7 +6,7 @@ PATH
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
jruby-jars (9.4.9.0) jruby-jars (9.4.10.0)
jruby-rack (1.1.21) jruby-rack (1.1.21)
rake (13.0.1) rake (13.0.1)
rubyzip (1.3.0) rubyzip (1.3.0)

View file

@ -211,12 +211,12 @@ RSpec.describe "Resolving" do
it "resolves all gems to latest patch" 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 # 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 end
it "resolves all gems to latest patch strict" do 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 # 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 end
it "resolves foo only to latest patch - same dependency case" do 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 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 # 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 end
it "resolves all gems to latest minor strict" do 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 # 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 end
it "resolves all gems to latest major" do 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 end
it "resolves all gems to latest major strict" do 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 end
# Why would this happen in real life? If bar 2.2 has a bug that the author of foo wants to bypass # 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 end
it "could revert to a previous version level patch" do 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 end
it "cannot revert to a previous version in strict mode level patch" do 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 # 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 end
it "could revert to a previous version level minor" do 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 end
it "cannot revert to a previous version in strict mode level minor" do 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 # 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 end
end end

View file

@ -370,7 +370,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
end end
it "allows specifying only-ruby-platform on windows with dependency platforms" do it "allows specifying only-ruby-platform on windows with dependency platforms" do
simulate_windows do simulate_platform "x86-mswin32" do
install_gemfile <<-G install_gemfile <<-G
source "https://gem.repo1" source "https://gem.repo1"
gem "nokogiri", :platforms => [:windows, :mswin, :mswin64, :mingw, :x64_mingw, :jruby] gem "nokogiri", :platforms => [:windows, :mswin, :mswin64, :mingw, :x64_mingw, :jruby]
@ -397,7 +397,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
G G
bundle :lock bundle :lock
simulate_windows do simulate_platform "x86-mswin32" do
bundle "config set force_ruby_platform true" bundle "config set force_ruby_platform true"
bundle "install" bundle "install"
@ -411,7 +411,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
s.add_dependency "platform_specific" s.add_dependency "platform_specific"
end end
end end
simulate_windows x64_mingw32 do simulate_platform "x64-mingw32" do
lockfile <<-L lockfile <<-L
GEM GEM
remote: https://gem.repo2/ remote: https://gem.repo2/
@ -438,11 +438,9 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
end end
end end
%w[x86-mswin32 x64-mswin64 x86-mingw32 x64-mingw32 x64-mingw-ucrt].each do |arch| %w[x86-mswin32 x64-mswin64 x86-mingw32 x64-mingw32 x64-mingw-ucrt aarch64-mingw-ucrt].each do |platform|
it "allows specifying platform windows on #{arch} arch" do it "allows specifying platform windows on #{platform} platform" do
platform = send(arch.tr("-", "_")) simulate_platform platform do
simulate_windows platform do
lockfile <<-L lockfile <<-L
GEM GEM
remote: https://gem.repo1/ remote: https://gem.repo1/
@ -463,8 +461,6 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
gem "platform_specific", :platforms => [:windows] gem "platform_specific", :platforms => [:windows]
G G
bundle "install"
expect(the_bundle).to include_gems "platform_specific 1.0 #{platform}" expect(the_bundle).to include_gems "platform_specific 1.0 #{platform}"
end end
end end

View file

@ -119,11 +119,9 @@ RSpec.describe "Bundler.require" do
end end
G G
load_error_run <<-R, "fail" run "Bundler.require", raise_on_error: false
Bundler.require
R
expect(err_without_deprecations).to eq("ZOMG LOAD ERROR") expect(err_without_deprecations).to include("cannot load such file -- fail")
end end
it "displays a helpful message if the required gem throws an error" do it "displays a helpful message if the required gem throws an error" do
@ -155,16 +153,9 @@ RSpec.describe "Bundler.require" do
end end
G G
cmd = <<-RUBY run "Bundler.require", raise_on_error: false
begin
Bundler.require
rescue LoadError => e
warn "ZOMG LOAD ERROR: \#{e.message}"
end
RUBY
run(cmd)
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
describe "with namespaced gems" do describe "with namespaced gems" do
@ -215,10 +206,9 @@ RSpec.describe "Bundler.require" do
end end
G G
load_error_run <<-R, "jquery-rails" run "Bundler.require", raise_on_error: false
Bundler.require
R expect(err_without_deprecations).to include("cannot load such file -- jquery-rails")
expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
end end
it "handles the case where regex fails" do it "handles the case where regex fails" do
@ -233,16 +223,9 @@ RSpec.describe "Bundler.require" do
end end
G G
cmd = <<-RUBY run "Bundler.require", raise_on_error: false
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)
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 end
it "doesn't swallow the error when the library has an unrelated error" do it "doesn't swallow the error when the library has an unrelated error" do
@ -257,16 +240,9 @@ RSpec.describe "Bundler.require" do
end end
G G
cmd = <<-RUBY run "Bundler.require", raise_on_error: false
begin
Bundler.require
rescue LoadError => e
warn "ZOMG LOAD ERROR: \#{e.message}"
end
RUBY
run(cmd)
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
end end
@ -375,10 +351,9 @@ RSpec.describe "Bundler.require" do
gem "busted_require" gem "busted_require"
G G
load_error_run <<-R, "no_such_file_omg" run "Bundler.require", raise_on_error: false
Bundler.require
R expect(err_without_deprecations).to include("cannot load such file -- no_such_file_omg")
expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
end end
end end
end end

View file

@ -118,6 +118,10 @@ module Spec
s.platform = "x64-mingw-ucrt" s.platform = "x64-mingw-ucrt"
end end
build_gem "platform_specific" do |s|
s.platform = "aarch64-mingw-ucrt"
end
build_gem "platform_specific" do |s| build_gem "platform_specific" do |s|
s.platform = "x86-darwin-100" s.platform = "x86-darwin-100"
end end

View file

@ -1,11 +1,18 @@
# frozen_string_literal: true # frozen_string_literal: true
class RequirementChecker < Proc 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) provided = Gem::Version.new(present)
new do |required| 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| end.tap do |checker|
checker.provided = provided checker.provided = provided
end end
@ -21,7 +28,7 @@ end
RSpec.configure do |config| RSpec.configure do |config|
config.filter_run_excluding realworld: true 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 rubygems: RequirementChecker.against(Gem::VERSION)
config.filter_run_excluding ruby_repo: !ENV["GEM_COMMAND"].nil? config.filter_run_excluding ruby_repo: !ENV["GEM_COMMAND"].nil?
config.filter_run_excluding no_color_tty: Gem.win_platform? || !ENV["GITHUB_ACTION"].nil? config.filter_run_excluding no_color_tty: Gem.win_platform? || !ENV["GITHUB_ACTION"].nil?

View file

@ -19,18 +19,19 @@ module Gem
@default_specifications_dir = nil @default_specifications_dir = nil
end end
if ENV["BUNDLER_SPEC_WINDOWS"] spec_platform = ENV["BUNDLER_SPEC_PLATFORM"]
@@win_platform = true # rubocop:disable Style/ClassVars 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 end
if ENV["BUNDLER_SPEC_PLATFORM"] RbConfig::CONFIG["arch"] = spec_platform
previous_platforms = @platforms
previous_local = Platform.local
class Platform class Platform
@local = new(ENV["BUNDLER_SPEC_PLATFORM"]) @local = nil
end end
@platforms = previous_platforms.map {|platform| platform == previous_local ? Platform.local : platform } @platforms = []
end end
if ENV["BUNDLER_SPEC_GEM_SOURCES"] if ENV["BUNDLER_SPEC_GEM_SOURCES"]

View file

@ -442,16 +442,6 @@ module Spec
ENV["BUNDLER_SPEC_PLATFORM"] = old if block_given? ENV["BUNDLER_SPEC_PLATFORM"] = old if block_given?
end 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 def current_ruby_minor
Gem.ruby_version.segments.tap {|s| s.delete_at(2) }.join(".") Gem.ruby_version.segments.tap {|s| s.delete_at(2) }.join(".")
end end

View file

@ -66,7 +66,6 @@ module Spec
end end
def should_conservative_resolve_and_include(opts, unlock, specs) def should_conservative_resolve_and_include(opts, unlock, specs)
# empty unlock means unlock all
opts = Array(opts) opts = Array(opts)
search = Bundler::GemVersionPromoter.new.tap do |s| search = Bundler::GemVersionPromoter.new.tap do |s|
s.level = opts.first s.level = opts.first

View file

@ -113,7 +113,7 @@ module Spec
# that requires regenerating tmp/. # that requires regenerating tmp/.
def test_env_version def test_env_version
1 2
end end
def scope def scope

View file

@ -4,62 +4,14 @@ module Spec
module Platforms module Platforms
include Bundler::GemHelpers 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 def not_local
all_platforms.find {|p| p != generic_local_platform } generic_local_platform == Gem::Platform::RUBY ? "java" : Gem::Platform::RUBY
end end
def local_tag def local_tag
if RUBY_PLATFORM == "java" if Gem.java_platform?
:jruby :jruby
elsif ["x64-mingw32", "x64-mingw-ucrt"].include?(RUBY_PLATFORM) elsif Gem.win_platform?
:windows :windows
else else
:ruby :ruby
@ -96,16 +48,22 @@ module Spec
end end
def default_platform_list(*extra, defaults: default_locked_platforms) def default_platform_list(*extra, defaults: default_locked_platforms)
defaults.concat(extra).uniq defaults.concat(extra).map(&:to_s).uniq
end end
def lockfile_platforms(*extra, defaults: default_locked_platforms) def lockfile_platforms(*extra, defaults: default_locked_platforms)
platforms = default_platform_list(*extra, defaults: defaults) platforms = default_platform_list(*extra, defaults: defaults)
platforms.map(&:to_s).sort.join("\n ") platforms.sort.join("\n ")
end end
def default_locked_platforms 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 end
end end

View file

@ -31,5 +31,13 @@ module Spec
raise "Cannot read lockfile if it doesn't exist" unless locked? raise "Cannot read lockfile if it doesn't exist" unless locked?
Bundler::LockfileParser.new(lockfile.read) Bundler::LockfileParser.new(lockfile.read)
end end
def locked_specs
locked_gems.specs.map(&:full_name)
end
def locked_platforms
locked_gems.platforms.map(&:to_s)
end
end end
end end

View file

@ -152,18 +152,18 @@ dependencies = [
[[package]] [[package]]
name = "rb-sys" name = "rb-sys"
version = "0.9.107" version = "0.9.110"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56aaf81d9efc195606456e91896297ee5ab2002381539f8ed1ba6b4f2e467f3b" checksum = "56cf964f8e44115e50009921ea3d3791b6f74d1ae6d6ed37114fbe03a1cd7308"
dependencies = [ dependencies = [
"rb-sys-build", "rb-sys-build",
] ]
[[package]] [[package]]
name = "rb-sys-build" name = "rb-sys-build"
version = "0.9.107" version = "0.9.110"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "035b513baded6df2b90a8559efb1973c47ba42e16c21c5f0863dd2aa4dbd6abe" checksum = "161480347f56473107d4135643b6b1909331eec61445e113b256708a28b691c5"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"lazy_static", "lazy_static",

View file

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

View file

@ -145,18 +145,18 @@ dependencies = [
[[package]] [[package]]
name = "rb-sys" name = "rb-sys"
version = "0.9.107" version = "0.9.110"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56aaf81d9efc195606456e91896297ee5ab2002381539f8ed1ba6b4f2e467f3b" checksum = "56cf964f8e44115e50009921ea3d3791b6f74d1ae6d6ed37114fbe03a1cd7308"
dependencies = [ dependencies = [
"rb-sys-build", "rb-sys-build",
] ]
[[package]] [[package]]
name = "rb-sys-build" name = "rb-sys-build"
version = "0.9.107" version = "0.9.110"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "035b513baded6df2b90a8559efb1973c47ba42e16c21c5f0863dd2aa4dbd6abe" checksum = "161480347f56473107d4135643b6b1909331eec61445e113b256708a28b691c5"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"lazy_static", "lazy_static",

View file

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

View file

@ -564,7 +564,6 @@ end
# [B] ~> 1.0 # [B] ~> 1.0
# #
# and should resolve using b-1.0 # and should resolve using b-1.0
# TODO: move these to specification
def test_self_activate_over def test_self_activate_over
a = util_spec "a", "1.0", "b" => ">= 1.0", "c" => "= 1.0" a = util_spec "a", "1.0", "b" => ">= 1.0", "c" => "= 1.0"
@ -653,6 +652,17 @@ end
end 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 def test_self_all_equals
a = util_spec "foo", "1", nil, "lib/foo.rb" a = util_spec "foo", "1", nil, "lib/foo.rb"