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

View file

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

View file

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

View file

@ -10,7 +10,7 @@ module Bundler
be sure to check out these resources:
1. Check out our troubleshooting guide for quick fixes to common issues:
https://github.com/rubygems/rubygems/blob/master/bundler/doc/TROUBLESHOOTING.md
https://github.com/rubygems/rubygems/blob/master/doc/bundler/TROUBLESHOOTING.md
2. Instructions for common Bundler uses can be found on the documentation
site: https://bundler.io/

View file

@ -24,7 +24,7 @@ module Bundler
return unless spec
path = spec.full_gem_path
unless File.directory?(path)
return Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at: #{path}"
return Bundler.ui.warn "The gem #{gem_name} is missing. It should be installed at #{path}, but was not found"
end
end
return Bundler.ui.info(path)

View file

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

View file

@ -58,17 +58,28 @@ module Bundler
# @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
# @param optional_groups [Array(String)] A list of optional groups
def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = [])
if [true, false].include?(unlock)
unlock ||= {}
if unlock == true
@unlocking_all = true
@unlocking_bundler = false
@unlocking = unlock
@sources_to_unlock = []
@unlocking_ruby = false
@explicit_unlocks = []
conservative = false
else
@unlocking_all = false
@unlocking_bundler = unlock.delete(:bundler)
@unlocking = unlock.any? {|_k, v| !Array(v).empty? }
@sources_to_unlock = unlock.delete(:sources) || []
@unlocking_ruby = unlock.delete(:ruby)
@explicit_unlocks = unlock.delete(:gems) || []
conservative = unlock.delete(:conservative)
end
@dependencies = dependencies
@sources = sources
@unlock = unlock
@optional_groups = optional_groups
@prefer_local = false
@specs = nil
@ -93,29 +104,24 @@ module Bundler
@platforms = @locked_platforms.dup
@locked_bundler_version = @locked_gems.bundler_version
@locked_ruby_version = @locked_gems.ruby_version
@originally_locked_deps = @locked_gems.dependencies
@locked_deps = @locked_gems.dependencies
@originally_locked_specs = SpecSet.new(@locked_gems.specs)
@locked_checksums = @locked_gems.checksums
if unlock != true
@locked_deps = @originally_locked_deps
@locked_specs = @originally_locked_specs
@locked_sources = @locked_gems.sources
else
@unlock = {}
@locked_deps = {}
if @unlocking_all
@locked_specs = SpecSet.new([])
@locked_sources = []
else
@locked_specs = @originally_locked_specs
@locked_sources = @locked_gems.sources
end
else
@unlock = {}
@locked_gems = nil
@locked_gems = nil
@locked_platforms = []
@most_specific_locked_platform = nil
@platforms = []
@locked_deps = {}
@locked_specs = SpecSet.new([])
@originally_locked_deps = {}
@originally_locked_specs = @locked_specs
@locked_sources = []
@locked_checksums = Bundler.feature_flag.lockfile_checksums?
@ -134,11 +140,10 @@ module Bundler
@sources.merged_gem_lockfile_sections!(locked_gem_sources.first)
end
@sources_to_unlock = @unlock.delete(:sources) || []
@unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object
@unlocking_ruby ||= if @ruby_version && locked_ruby_version_object
@ruby_version.diff(locked_ruby_version_object)
end
@unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version)
@unlocking ||= @unlocking_ruby ||= (!@locked_ruby_version ^ !@ruby_version)
@current_platform_missing = add_current_platform unless Bundler.frozen_bundle?
@ -146,9 +151,7 @@ module Bundler
@path_changes = converge_paths
@source_changes = converge_sources
@explicit_unlocks = @unlock.delete(:gems) || []
if @unlock[:conservative]
if conservative
@gems_to_unlock = @explicit_unlocks.any? ? @explicit_unlocks : @dependencies.map(&:name)
else
eager_unlock = @explicit_unlocks.map {|name| Dependency.new(name, ">= 0") }
@ -220,6 +223,8 @@ module Bundler
def prefer_local!
@prefer_local = true
sources.prefer_local!
end
# For given dependency list returns a SpecSet with Gemspec of all the required
@ -374,7 +379,7 @@ module Bundler
def locked_ruby_version
return unless ruby_version
if @unlock[:ruby] || !@locked_ruby_version
if @unlocking_ruby || !@locked_ruby_version
Bundler::RubyVersion.system
else
@locked_ruby_version
@ -435,23 +440,23 @@ module Bundler
changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
end
reason = nothing_changed? ? "some dependencies were deleted from your gemfile" : change_reason
reason = resolve_needed? ? change_reason : "some dependencies were deleted from your gemfile"
msg = String.new
msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because frozen mode is set"
msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_gemfile_path} to version control.\n"
msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_gemfile_path} to version control.\n" unless unlocking?
unless explicit_flag
suggested_command = unless Bundler.settings.locations("frozen").keys.include?(:env)
"bundle config set frozen false"
end
msg << "If this is a development machine, remove the #{SharedHelpers.relative_lockfile_path} " \
msg << "\n\nIf this is a development machine, remove the #{SharedHelpers.relative_lockfile_path} " \
"freeze by running `#{suggested_command}`." if suggested_command
end
raise ProductionError, msg if added.any? || deleted.any? || changed.any? || !nothing_changed?
raise ProductionError, msg if added.any? || deleted.any? || changed.any? || resolve_needed?
end
def validate_runtime!
@ -620,8 +625,8 @@ module Bundler
@resolution_packages ||= begin
last_resolve = converge_locked_specs
remove_invalid_platforms!
packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local)
packages = additional_base_requirements_to_prevent_downgrades(packages, last_resolve)
packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @unlocking_all || @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: @new_platforms)
packages = additional_base_requirements_to_prevent_downgrades(packages)
packages = additional_base_requirements_to_force_updates(packages)
packages
end
@ -639,6 +644,8 @@ module Bundler
specs = begin
resolve.materialize(dependencies)
rescue IncorrectLockfileDependencies => e
raise if Bundler.frozen_bundle?
spec = e.spec
raise "Infinite loop while fixing lockfile dependencies" if incorrect_spec == spec
@ -761,6 +768,7 @@ module Bundler
@most_specific_non_local_locked_ruby_platform = find_most_specific_locked_ruby_platform
return if @most_specific_non_local_locked_ruby_platform
@new_platforms << local_platform
@platforms << local_platform
true
end
@ -782,7 +790,7 @@ module Bundler
unlock_reason = if unlock_targets
"#{unlock_targets.first}: (#{unlock_targets.last.join(", ")})"
else
@unlock[:ruby] ? "ruby" : ""
@unlocking_ruby ? "ruby" : ""
end
return "bundler is unlocking #{unlock_reason}"
@ -852,8 +860,6 @@ module Bundler
end
def check_lockfile
@missing_lockfile_dep = nil
@locked_spec_with_invalid_deps = nil
@locked_spec_with_missing_deps = nil
@ -871,10 +877,6 @@ module Bundler
@locked_specs.delete(missing)
@locked_spec_with_missing_deps = missing.first.name
elsif !@dependency_changes
@missing_lockfile_dep = current_dependencies.find do |d|
@locked_specs[d.name].empty? && d.name != "bundler"
end&.name
end
if invalid.any?
@ -935,32 +937,33 @@ module Bundler
end
def converge_dependencies
changes = false
@missing_lockfile_dep = nil
@changed_dependencies = []
@dependencies.each do |dep|
current_dependencies.each do |dep|
if dep.source
dep.source = sources.get(dep.source)
end
unless locked_dep = @originally_locked_deps[dep.name]
changes = true
next
name = dep.name
dep_changed = @locked_deps[name].nil?
unless name == "bundler"
locked_specs = @originally_locked_specs[name]
if locked_specs.any? && !dep.matches_spec?(locked_specs.first)
@gems_to_unlock << name
dep_changed = true
elsif locked_specs.empty? && dep_changed == false
@missing_lockfile_dep = name
end
end
# Gem::Dependency#== matches Gem::Dependency#type. As the lockfile
# doesn't carry a notion of the dependency type, if you use
# add_development_dependency in a gemspec that's loaded with the gemspec
# directive, the lockfile dependencies and resolved dependencies end up
# with a mismatch on #type. Work around that by setting the type on the
# dep from the lockfile.
locked_dep.instance_variable_set(:@type, dep.type)
# We already know the name matches from the hash lookup
# so we only need to check the requirement now
changes ||= dep.requirement != locked_dep.requirement
@changed_dependencies << name if dep_changed
end
changes
@changed_dependencies.any?
end
# Remove elements from the locked specs that are expired. This will most
@ -1022,11 +1025,6 @@ module Bundler
end
end
if dep.nil? && requested_dep = requested_dependencies.find {|d| name == d.name }
@gems_to_unlock << name
deps << requested_dep
end
converged << s
end
@ -1095,11 +1093,15 @@ module Bundler
current == proposed
end
def additional_base_requirements_to_prevent_downgrades(resolution_packages, last_resolve)
def additional_base_requirements_to_prevent_downgrades(resolution_packages)
return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
converge_specs(@originally_locked_specs - last_resolve).each do |locked_spec|
@originally_locked_specs.each do |locked_spec|
next if locked_spec.source.is_a?(Source::Path)
resolution_packages.base_requirements[locked_spec.name] = Gem::Requirement.new(">= #{locked_spec.version}")
name = locked_spec.name
next if @changed_dependencies.include?(name)
resolution_packages.base_requirements[name] = Gem::Requirement.new(">= #{locked_spec.version}")
end
resolution_packages
end

View file

@ -2,51 +2,92 @@
require "rubygems/dependency"
require_relative "shared_helpers"
require_relative "rubygems_ext"
module Bundler
class Dependency < Gem::Dependency
attr_reader :autorequire
attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref, :glob
ALL_RUBY_VERSIONS = (18..27).to_a.concat((30..35).to_a).freeze
PLATFORM_MAP = {
ruby: [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
mri: [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
rbx: [Gem::Platform::RUBY],
truffleruby: [Gem::Platform::RUBY],
jruby: [Gem::Platform::JAVA, [18, 19]],
windows: [Gem::Platform::WINDOWS, ALL_RUBY_VERSIONS],
# deprecated
mswin: [Gem::Platform::MSWIN, ALL_RUBY_VERSIONS],
mswin64: [Gem::Platform::MSWIN64, ALL_RUBY_VERSIONS - [18]],
mingw: [Gem::Platform::MINGW, ALL_RUBY_VERSIONS],
x64_mingw: [Gem::Platform::X64_MINGW, ALL_RUBY_VERSIONS - [18, 19]],
}.each_with_object({}) do |(platform, spec), hash|
hash[platform] = spec[0]
spec[1]&.each {|version| hash[:"#{platform}_#{version}"] = spec[0] }
end.freeze
def initialize(name, version, options = {}, &blk)
type = options["type"] || :runtime
super(name, version, type)
@autorequire = nil
@groups = Array(options["group"] || :default).map(&:to_sym)
@source = options["source"]
@path = options["path"]
@git = options["git"]
@github = options["github"]
@branch = options["branch"]
@ref = options["ref"]
@glob = options["glob"]
@platforms = Array(options["platforms"])
@env = options["env"]
@should_include = options.fetch("should_include", true)
@gemfile = options["gemfile"]
@force_ruby_platform = options["force_ruby_platform"] if options.key?("force_ruby_platform")
@options = options
end
@autorequire = Array(options["require"] || []) if options.key?("require")
def groups
@groups ||= Array(@options["group"] || :default).map(&:to_sym)
end
def source
return @source if defined?(@source)
@source = @options["source"]
end
def path
return @path if defined?(@path)
@path = @options["path"]
end
def git
return @git if defined?(@git)
@git = @options["git"]
end
def github
return @github if defined?(@github)
@github = @options["github"]
end
def branch
return @branch if defined?(@branch)
@branch = @options["branch"]
end
def ref
return @ref if defined?(@ref)
@ref = @options["ref"]
end
def glob
return @glob if defined?(@glob)
@glob = @options["glob"]
end
def platforms
@platforms ||= Array(@options["platforms"])
end
def env
return @env if defined?(@env)
@env = @options["env"]
end
def should_include
@should_include ||= @options.fetch("should_include", true)
end
def gemfile
return @gemfile if defined?(@gemfile)
@gemfile = @options["gemfile"]
end
def force_ruby_platform
return @force_ruby_platform if defined?(@force_ruby_platform)
@force_ruby_platform = @options["force_ruby_platform"]
end
def autorequire
return @autorequire if defined?(@autorequire)
@autorequire = Array(@options["require"] || []) if @options.key?("require")
end
RUBY_PLATFORM_ARRAY = [Gem::Platform::RUBY].freeze
@ -56,37 +97,41 @@ module Bundler
# passed in the `valid_platforms` parameter
def gem_platforms(valid_platforms)
return RUBY_PLATFORM_ARRAY if force_ruby_platform
return valid_platforms if @platforms.empty?
return valid_platforms if platforms.empty?
valid_platforms.select {|p| expanded_platforms.include?(GemHelpers.generic(p)) }
end
def expanded_platforms
@expanded_platforms ||= @platforms.filter_map {|pl| PLATFORM_MAP[pl] }.flatten.uniq
@expanded_platforms ||= platforms.filter_map {|pl| CurrentRuby::PLATFORM_MAP[pl] }.flatten.uniq
end
def should_include?
@should_include && current_env? && current_platform?
should_include && current_env? && current_platform?
end
def gemspec_dev_dep?
type == :development
@gemspec_dev_dep ||= @options.fetch("gemspec_dev_dep", false)
end
def gemfile_dep?
!gemspec_dev_dep?
end
def current_env?
return true unless @env
if @env.is_a?(Hash)
@env.all? do |key, val|
return true unless env
if env.is_a?(Hash)
env.all? do |key, val|
ENV[key.to_s] && (val.is_a?(String) ? ENV[key.to_s] == val : ENV[key.to_s] =~ val)
end
else
ENV[@env.to_s]
ENV[env.to_s]
end
end
def current_platform?
return true if @platforms.empty?
@platforms.any? do |p|
return true if platforms.empty?
platforms.any? do |p|
Bundler.current_ruby.send("#{p}?")
end
end

View file

@ -13,10 +13,10 @@ module Bundler
builder.to_definition(lockfile, unlock)
end
VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze
VALID_PLATFORMS = Bundler::CurrentRuby::PLATFORM_MAP.keys.freeze
VALID_KEYS = %w[group groups git path glob name branch ref tag require submodules
platform platforms type source install_if gemfile force_ruby_platform].freeze
platform platforms source install_if force_ruby_platform].freeze
GITHUB_PULL_REQUEST_URL = %r{\Ahttps://github\.com/([A-Za-z0-9_\-\.]+/[A-Za-z0-9_\-\.]+)/pull/(\d+)\z}
GITLAB_MERGE_REQUEST_URL = %r{\Ahttps://gitlab\.com/([A-Za-z0-9_\-\./]+)/-/merge_requests/(\d+)\z}
@ -77,12 +77,12 @@ module Bundler
@gemspecs << spec
gem spec.name, name: spec.name, path: path, glob: glob
path path, "glob" => glob, "name" => spec.name do
add_dependency spec.name
end
group(development_group) do
spec.development_dependencies.each do |dep|
gem dep.name, *(dep.requirement.as_list + [type: :development])
end
spec.development_dependencies.each do |dep|
add_dependency dep.name, dep.requirement.as_list, "gemspec_dev_dep" => true, "group" => development_group
end
when 0
raise InvalidOption, "There are no gemspecs at #{expanded_path}"
@ -94,79 +94,11 @@ module Bundler
def gem(name, *args)
options = args.last.is_a?(Hash) ? args.pop.dup : {}
options["gemfile"] = @gemfile
version = args || [">= 0"]
normalize_options(name, version, options)
dep = Dependency.new(name, version, options)
# if there's already a dependency with this name we try to prefer one
if current = @dependencies.find {|d| d.name == dep.name }
if current.requirement != dep.requirement
current_requirement_open = current.requirements_list.include?(">= 0")
gemspec_dep = [dep, current].find(&:gemspec_dev_dep?)
if gemspec_dep
gemfile_dep = [dep, current].find(&:runtime?)
if gemfile_dep && !current_requirement_open
Bundler.ui.warn "A gemspec development dependency (#{gemspec_dep.name}, #{gemspec_dep.requirement}) is being overridden by a Gemfile dependency (#{gemfile_dep.name}, #{gemfile_dep.requirement}).\n" \
"This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement\n"
elsif gemfile_dep.nil?
require_relative "vendor/pub_grub/lib/pub_grub/version_range"
require_relative "vendor/pub_grub/lib/pub_grub/version_constraint"
require_relative "vendor/pub_grub/lib/pub_grub/version_union"
require_relative "vendor/pub_grub/lib/pub_grub/rubygems"
current_gemspec_range = PubGrub::RubyGems.requirement_to_range(current.requirement)
next_gemspec_range = PubGrub::RubyGems.requirement_to_range(dep.requirement)
if current_gemspec_range.intersects?(next_gemspec_range)
dep = Dependency.new(name, current.requirement.as_list + dep.requirement.as_list, options)
else
raise GemfileError, "Two gemspecs have conflicting requirements on the same gem: #{dep} and #{current}"
end
end
else
update_prompt = ""
if File.basename(@gemfile) == Injector::INJECTED_GEMS
if dep.requirements_list.include?(">= 0") && !current_requirement_open
update_prompt = ". Gem already added"
else
update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`"
update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current_requirement_open
end
end
raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
"You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
"#{update_prompt}"
end
end
unless current.gemspec_dev_dep? && dep.gemspec_dev_dep?
# Always prefer the dependency from the Gemfile
if current.gemspec_dev_dep?
@dependencies.delete(current)
elsif dep.gemspec_dev_dep?
return
elsif current.source != dep.source
raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
"You specified that #{dep.name} (#{dep.requirement}) should come from " \
"#{current.source || "an unspecified source"} and #{dep.source}\n"
else
Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
"You should probably keep only one of them.\n" \
"Remove any duplicate entries and specify the gem only once.\n" \
"While it's not a problem now, it could cause errors if you change the version of one of them later."
end
end
end
@dependencies << dep
add_dependency(name, version, options)
end
def source(source, *args, &blk)
@ -301,6 +233,81 @@ module Bundler
private
def add_dependency(name, version = nil, options = {})
options["gemfile"] = @gemfile
options["source"] ||= @source
options["env"] ||= @env
dep = Dependency.new(name, version, options)
# if there's already a dependency with this name we try to prefer one
if current = @dependencies.find {|d| d.name == dep.name }
if current.requirement != dep.requirement
current_requirement_open = current.requirements_list.include?(">= 0")
gemspec_dep = [dep, current].find(&:gemspec_dev_dep?)
if gemspec_dep
gemfile_dep = [dep, current].find(&:gemfile_dep?)
if gemfile_dep && !current_requirement_open
Bundler.ui.warn "A gemspec development dependency (#{gemspec_dep.name}, #{gemspec_dep.requirement}) is being overridden by a Gemfile dependency (#{gemfile_dep.name}, #{gemfile_dep.requirement}).\n" \
"This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement\n"
elsif gemfile_dep.nil?
require_relative "vendor/pub_grub/lib/pub_grub/version_range"
require_relative "vendor/pub_grub/lib/pub_grub/version_constraint"
require_relative "vendor/pub_grub/lib/pub_grub/version_union"
require_relative "vendor/pub_grub/lib/pub_grub/rubygems"
current_gemspec_range = PubGrub::RubyGems.requirement_to_range(current.requirement)
next_gemspec_range = PubGrub::RubyGems.requirement_to_range(dep.requirement)
if current_gemspec_range.intersects?(next_gemspec_range)
dep = Dependency.new(name, current.requirement.as_list + dep.requirement.as_list, options)
else
raise GemfileError, "Two gemspec development dependencies have conflicting requirements on the same gem: #{dep} and #{current}"
end
end
else
update_prompt = ""
if File.basename(@gemfile) == Injector::INJECTED_GEMS
if dep.requirements_list.include?(">= 0") && !current_requirement_open
update_prompt = ". Gem already added"
else
update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`"
update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current_requirement_open
end
end
raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
"You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
"#{update_prompt}"
end
end
unless current.gemspec_dev_dep? && dep.gemspec_dev_dep?
# Always prefer the dependency from the Gemfile
if current.gemspec_dev_dep?
@dependencies.delete(current)
elsif dep.gemspec_dev_dep?
return
elsif current.source != dep.source
raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
"You specified that #{dep.name} (#{dep.requirement}) should come from " \
"#{current.source || "an unspecified source"} and #{dep.source}\n"
else
Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
"You should probably keep only one of them.\n" \
"Remove any duplicate entries and specify the gem only once.\n" \
"While it's not a problem now, it could cause errors if you change the version of one of them later."
end
end
end
@dependencies << dep
end
def with_gemfile(gemfile)
expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile&.parent)
original_gemfile = @gemfile
@ -433,8 +440,6 @@ module Bundler
opts["source"] = source
end
opts["source"] ||= @source
opts["env"] ||= @env
opts["platforms"] = platforms.dup
opts["group"] = groups
opts["should_include"] = install_if

View file

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

View file

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

View file

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

View file

@ -132,8 +132,6 @@ module Bundler
# Specific version moves can't always reliably be done during sorting
# as not all elements are compared against each other.
def post_sort(result, unlock, locked_version)
# default :major behavior in Bundler does not do this
return result if major?
if unlock || locked_version.nil?
result
else

View file

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

View file

@ -121,13 +121,10 @@ module Bundler
out
end
def materialize_strictly
source.local!
def materialize_for_cache
source.remote!
matching_specs = source.specs.search(self)
return self if matching_specs.empty?
__materialize__(matching_specs)
materialize(self, &:first)
end
def materialized_for_installation
@ -140,53 +137,25 @@ module Bundler
source.local!
if use_exact_resolved_specifications?
materialize_strictly
else
matching_specs = source.specs.search([name, version])
return self if matching_specs.empty?
target_platform = source.is_a?(Source::Path) ? platform : local_platform
installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform)
specification = __materialize__(installable_candidates, fallback_to_non_installable: false)
return specification unless specification.nil?
if target_platform != platform
installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform)
materialize(self) do |matching_specs|
choose_compatible(matching_specs)
end
else
materialize([name, version]) do |matching_specs|
target_platform = source.is_a?(Source::Path) ? platform : local_platform
__materialize__(installable_candidates)
end
end
installable_candidates = GemHelpers.select_best_platform_match(matching_specs, target_platform)
# If in frozen mode, we fallback to a non-installable candidate because by
# doing this we avoid re-resolving and potentially end up changing the
# lock file, which is not allowed. In that case, we will give a proper error
# about the mismatch higher up the stack, right before trying to install the
# bad gem.
def __materialize__(candidates, fallback_to_non_installable: Bundler.frozen_bundle?)
search = candidates.reverse.find do |spec|
spec.is_a?(StubSpecification) || spec.matches_current_metadata?
end
if search.nil? && fallback_to_non_installable
search = candidates.last
elsif search && search.full_name == full_name
# We don't validate locally installed dependencies but accept what's in
# the lockfile instead for performance, since loading locally installed
# dependencies would mean evaluating all gemspecs, which would affect
# `bundler/setup` performance
if search.is_a?(StubSpecification)
search.dependencies = dependencies
else
if !source.is_a?(Source::Path) && search.runtime_dependencies.sort != dependencies.sort
raise IncorrectLockfileDependencies.new(self)
specification = choose_compatible(installable_candidates, fallback_to_non_installable: false)
return specification unless specification.nil?
if target_platform != platform
installable_candidates = GemHelpers.select_best_platform_match(matching_specs, platform)
end
search.locked_platform = platform if search.instance_of?(RemoteSpecification) || search.instance_of?(EndpointSpecification)
choose_compatible(installable_candidates)
end
end
search
end
def inspect
@ -217,5 +186,41 @@ module Bundler
(most_specific_locked_platform != generic_platform) || force_ruby_platform || Bundler.settings[:force_ruby_platform]
end
def materialize(query)
matching_specs = source.specs.search(query)
return self if matching_specs.empty?
yield matching_specs
end
# If in frozen mode, we fallback to a non-installable candidate because by
# doing this we avoid re-resolving and potentially end up changing the
# lock file, which is not allowed. In that case, we will give a proper error
# about the mismatch higher up the stack, right before trying to install the
# bad gem.
def choose_compatible(candidates, fallback_to_non_installable: Bundler.frozen_bundle?)
search = candidates.reverse.find do |spec|
spec.is_a?(StubSpecification) || spec.matches_current_metadata?
end
if search.nil? && fallback_to_non_installable
search = candidates.last
elsif search && search.full_name == full_name
# We don't validate locally installed dependencies but accept what's in
# the lockfile instead for performance, since loading locally installed
# dependencies would mean evaluating all gemspecs, which would affect
# `bundler/setup` performance
if search.is_a?(StubSpecification)
search.dependencies = dependencies
else
if !source.is_a?(Source::Path) && search.runtime_dependencies.sort != dependencies.sort
raise IncorrectLockfileDependencies.new(self)
end
search.locked_platform = platform if search.instance_of?(RemoteSpecification) || search.instance_of?(EndpointSpecification)
end
end
search
end
end
end

View file

@ -13,5 +13,18 @@ module Bundler
def matches_current_rubygems?
@required_rubygems_version.satisfied_by?(Gem.rubygems_version)
end
def expanded_dependencies
runtime_dependencies + [
metadata_dependency("Ruby", @required_ruby_version),
metadata_dependency("RubyGems", @required_rubygems_version),
].compact
end
def metadata_dependency(name, requirement)
return if requirement.nil? || requirement.none?
Gem::Dependency.new("#{name}\0", requirement)
end
end
end

View file

@ -389,9 +389,18 @@ module Bundler
end
def filter_remote_specs(specs, package)
return specs unless package.prefer_local?
if package.prefer_local?
local_specs = specs.select {|s| s.is_a?(StubSpecification) }
specs.select {|s| s.is_a?(StubSpecification) }
if local_specs.empty?
package.consider_remote_versions!
specs
else
local_specs
end
else
specs
end
end
# Ignore versions that depend on themselves incorrectly

View file

@ -15,7 +15,7 @@ module Bundler
class Package
attr_reader :name, :platforms, :dependency, :locked_version
def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, prefer_local: false, dependency: nil)
def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, prefer_local: false, dependency: nil, new_platforms: [])
@name = name
@platforms = platforms
@locked_version = locked_specs.version_for(name)
@ -24,10 +24,14 @@ module Bundler
@top_level = !dependency.nil?
@prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore
@prefer_local = prefer_local
@new_platforms = new_platforms
end
def platform_specs(specs)
platforms.map {|platform| GemHelpers.select_best_platform_match(specs, platform, prefer_locked: !unlock?) }
platforms.map do |platform|
prefer_locked = @new_platforms.include?(platform) ? false : !unlock?
GemHelpers.select_best_platform_match(specs, platform, prefer_locked: prefer_locked)
end
end
def to_s
@ -55,7 +59,7 @@ module Bundler
end
def unlock?
@unlock.empty? || @unlock.include?(name)
@unlock == true || @unlock.include?(name)
end
def ignores_prereleases?

View file

@ -39,9 +39,7 @@ module Bundler
end
def dependencies
@dependencies ||= @specs.flat_map do |spec|
__dependencies(spec) + metadata_dependencies(spec)
end.uniq.sort
@dependencies ||= @specs.flat_map(&:expanded_dependencies).uniq.sort
end
def ==(other)
@ -71,28 +69,6 @@ module Bundler
def exemplary_spec
@specs.first
end
def __dependencies(spec)
dependencies = []
spec.dependencies.each do |dep|
next if dep.type == :development
dependencies << Dependency.new(dep.name, dep.requirement)
end
dependencies
end
def metadata_dependencies(spec)
[
metadata_dependency("Ruby", spec.required_ruby_version),
metadata_dependency("RubyGems", spec.required_rubygems_version),
].compact
end
def metadata_dependency(name, requirement)
return if requirement.nil? || requirement.none?
Dependency.new("#{name}\0", requirement)
end
end
end
end

View file

@ -58,6 +58,87 @@ module Gem
end
end
require "rubygems/platform"
class Platform
JAVA = Gem::Platform.new("java")
MSWIN = Gem::Platform.new("mswin32")
MSWIN64 = Gem::Platform.new("mswin64")
MINGW = Gem::Platform.new("x86-mingw32")
X64_MINGW = [Gem::Platform.new("x64-mingw32"),
Gem::Platform.new("x64-mingw-ucrt")].freeze
UNIVERSAL_MINGW = Gem::Platform.new("universal-mingw")
WINDOWS = [MSWIN, MSWIN64, UNIVERSAL_MINGW].flatten.freeze
X64_LINUX = Gem::Platform.new("x86_64-linux")
X64_LINUX_MUSL = Gem::Platform.new("x86_64-linux-musl")
if X64_LINUX === X64_LINUX_MUSL
remove_method :===
def ===(other)
return nil unless Gem::Platform === other
# universal-mingw32 matches x64-mingw-ucrt
return true if (@cpu == "universal" || other.cpu == "universal") &&
@os.start_with?("mingw") && other.os.start_with?("mingw")
# cpu
([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu ||
(@cpu == "arm" && other.cpu.start_with?("armv"))) &&
# os
@os == other.os &&
# version
(
(@os != "linux" && (@version.nil? || other.version.nil?)) ||
(@os == "linux" && (normalized_linux_version_ext == other.normalized_linux_version_ext || ["musl#{@version}", "musleabi#{@version}", "musleabihf#{@version}"].include?(other.version))) ||
@version == other.version
)
end
# This is a copy of RubyGems 3.3.23 or higher `normalized_linux_method`.
# Once only 3.3.23 is supported, we can use the method in RubyGems.
def normalized_linux_version_ext
return nil unless @version
without_gnu_nor_abi_modifiers = @version.sub(/\Agnu/, "").sub(/eabi(hf)?\Z/, "")
return nil if without_gnu_nor_abi_modifiers.empty?
without_gnu_nor_abi_modifiers
end
end
end
Platform.singleton_class.module_eval do
unless Platform.singleton_methods.include?(:match_spec?)
def match_spec?(spec)
match_gem?(spec.platform, spec.name)
end
def match_gem?(platform, gem_name)
match_platforms?(platform, Gem.platforms)
end
end
match_platforms_defined = Gem::Platform.respond_to?(:match_platforms?, true)
if !match_platforms_defined || Gem::Platform.send(:match_platforms?, Gem::Platform::X64_LINUX_MUSL, [Gem::Platform::X64_LINUX])
private
remove_method :match_platforms? if match_platforms_defined
def match_platforms?(platform, platforms)
platforms.any? do |local_platform|
platform.nil? ||
local_platform == platform ||
(local_platform != Gem::Platform::RUBY && platform =~ local_platform)
end
end
end
end
require "rubygems/specification"
# Can be removed once RubyGems 3.5.14 support is dropped
@ -177,8 +258,8 @@ module Gem
dependencies - development_dependencies
end
def deleted_gem?
!default_gem? && !File.directory?(full_gem_path)
def installation_missing?
!default_gem? && (!Dir.exist?(full_gem_path) || Dir.empty?(full_gem_path))
end
unless VALIDATES_FOR_RESOLUTION
@ -288,86 +369,6 @@ module Gem
end
end
require "rubygems/platform"
class Platform
JAVA = Gem::Platform.new("java")
MSWIN = Gem::Platform.new("mswin32")
MSWIN64 = Gem::Platform.new("mswin64")
MINGW = Gem::Platform.new("x86-mingw32")
X64_MINGW = [Gem::Platform.new("x64-mingw32"),
Gem::Platform.new("x64-mingw-ucrt")].freeze
WINDOWS = [MSWIN, MSWIN64, MINGW, X64_MINGW].flatten.freeze
X64_LINUX = Gem::Platform.new("x86_64-linux")
X64_LINUX_MUSL = Gem::Platform.new("x86_64-linux-musl")
if X64_LINUX === X64_LINUX_MUSL
remove_method :===
def ===(other)
return nil unless Gem::Platform === other
# universal-mingw32 matches x64-mingw-ucrt
return true if (@cpu == "universal" || other.cpu == "universal") &&
@os.start_with?("mingw") && other.os.start_with?("mingw")
# cpu
([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu ||
(@cpu == "arm" && other.cpu.start_with?("armv"))) &&
# os
@os == other.os &&
# version
(
(@os != "linux" && (@version.nil? || other.version.nil?)) ||
(@os == "linux" && (normalized_linux_version_ext == other.normalized_linux_version_ext || ["musl#{@version}", "musleabi#{@version}", "musleabihf#{@version}"].include?(other.version))) ||
@version == other.version
)
end
# This is a copy of RubyGems 3.3.23 or higher `normalized_linux_method`.
# Once only 3.3.23 is supported, we can use the method in RubyGems.
def normalized_linux_version_ext
return nil unless @version
without_gnu_nor_abi_modifiers = @version.sub(/\Agnu/, "").sub(/eabi(hf)?\Z/, "")
return nil if without_gnu_nor_abi_modifiers.empty?
without_gnu_nor_abi_modifiers
end
end
end
Platform.singleton_class.module_eval do
unless Platform.singleton_methods.include?(:match_spec?)
def match_spec?(spec)
match_gem?(spec.platform, spec.name)
end
def match_gem?(platform, gem_name)
match_platforms?(platform, Gem.platforms)
end
end
match_platforms_defined = Gem::Platform.respond_to?(:match_platforms?, true)
if !match_platforms_defined || Gem::Platform.send(:match_platforms?, Gem::Platform::X64_LINUX_MUSL, [Gem::Platform::X64_LINUX])
private
remove_method :match_platforms? if match_platforms_defined
def match_platforms?(platform, platforms)
platforms.any? do |local_platform|
platform.nil? ||
local_platform == platform ||
(local_platform != Gem::Platform::RUBY && platform =~ local_platform)
end
end
end
end
# On universal Rubies, resolve the "universal" arch to the real CPU arch, without changing the extension directory.
class BasicSpecification
if /^universal\.(?<arch>.*?)-/ =~ (CROSS_COMPILING || RUBY_PLATFORM)

View file

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

View file

@ -50,35 +50,30 @@ module Bundler
Plugin.hook(Plugin::Events::GEM_BEFORE_REQUIRE_ALL, dependencies)
dependencies.each do |dep|
required_file = nil
Plugin.hook(Plugin::Events::GEM_BEFORE_REQUIRE, dep)
begin
# Loop through all the specified autorequires for the
# dependency. If there are none, use the dependency's name
# as the autorequire.
Array(dep.autorequire || dep.name).each do |file|
# Allow `require: true` as an alias for `require: <name>`
file = dep.name if file == true
required_file = file
begin
Kernel.require file
rescue RuntimeError => e
raise e if e.is_a?(LoadError) # we handle this a little later
# Loop through all the specified autorequires for the
# dependency. If there are none, use the dependency's name
# as the autorequire.
Array(dep.autorequire || dep.name).each do |file|
# Allow `require: true` as an alias for `require: <name>`
file = dep.name if file == true
required_file = file
begin
Kernel.require required_file
rescue LoadError => e
if dep.autorequire.nil? && e.path == required_file
if required_file.include?("-")
required_file = required_file.tr("-", "/")
retry
end
else
raise Bundler::GemRequireError.new e,
"There was an error while trying to load the gem '#{file}'."
end
end
rescue LoadError => e
raise if dep.autorequire || e.path != required_file
if dep.autorequire.nil? && dep.name.include?("-")
begin
namespaced_file = dep.name.tr("-", "/")
Kernel.require namespaced_file
rescue LoadError => e
raise if e.path != namespaced_file
end
rescue RuntimeError => e
raise Bundler::GemRequireError.new e,
"There was an error while trying to load the gem '#{file}'."
end
end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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
describe "PLATFORM_MAP" do
subject { described_class::PLATFORM_MAP }
it "is on the current platform" do
engine = Gem.win_platform? ? "windows" : RUBY_ENGINE
# rubocop:disable Naming/VariableNumber
let(:platforms) do
{ ruby: Gem::Platform::RUBY,
ruby_18: Gem::Platform::RUBY,
ruby_19: Gem::Platform::RUBY,
ruby_20: Gem::Platform::RUBY,
ruby_21: Gem::Platform::RUBY,
ruby_22: Gem::Platform::RUBY,
ruby_23: Gem::Platform::RUBY,
ruby_24: Gem::Platform::RUBY,
ruby_25: Gem::Platform::RUBY,
ruby_26: Gem::Platform::RUBY,
ruby_27: Gem::Platform::RUBY,
ruby_30: Gem::Platform::RUBY,
ruby_31: Gem::Platform::RUBY,
ruby_32: Gem::Platform::RUBY,
ruby_33: Gem::Platform::RUBY,
ruby_34: Gem::Platform::RUBY,
ruby_35: Gem::Platform::RUBY,
mri: Gem::Platform::RUBY,
mri_18: Gem::Platform::RUBY,
mri_19: Gem::Platform::RUBY,
mri_20: Gem::Platform::RUBY,
mri_21: Gem::Platform::RUBY,
mri_22: Gem::Platform::RUBY,
mri_23: Gem::Platform::RUBY,
mri_24: Gem::Platform::RUBY,
mri_25: Gem::Platform::RUBY,
mri_26: Gem::Platform::RUBY,
mri_27: Gem::Platform::RUBY,
mri_30: Gem::Platform::RUBY,
mri_31: Gem::Platform::RUBY,
mri_32: Gem::Platform::RUBY,
mri_33: Gem::Platform::RUBY,
mri_34: Gem::Platform::RUBY,
mri_35: Gem::Platform::RUBY,
rbx: Gem::Platform::RUBY,
truffleruby: Gem::Platform::RUBY,
jruby: Gem::Platform::JAVA,
jruby_18: Gem::Platform::JAVA,
jruby_19: Gem::Platform::JAVA,
windows: Gem::Platform::WINDOWS,
windows_18: Gem::Platform::WINDOWS,
windows_19: Gem::Platform::WINDOWS,
windows_20: Gem::Platform::WINDOWS,
windows_21: Gem::Platform::WINDOWS,
windows_22: Gem::Platform::WINDOWS,
windows_23: Gem::Platform::WINDOWS,
windows_24: Gem::Platform::WINDOWS,
windows_25: Gem::Platform::WINDOWS,
windows_26: Gem::Platform::WINDOWS,
windows_27: Gem::Platform::WINDOWS,
windows_30: Gem::Platform::WINDOWS,
windows_31: Gem::Platform::WINDOWS,
windows_32: Gem::Platform::WINDOWS,
windows_33: Gem::Platform::WINDOWS,
windows_34: Gem::Platform::WINDOWS,
windows_35: Gem::Platform::WINDOWS }
end
dep = described_class.new(
"test_gem",
"1.0.0",
{ "platforms" => "#{engine}_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}" },
)
let(:deprecated) do
{ mswin: Gem::Platform::MSWIN,
mswin_18: Gem::Platform::MSWIN,
mswin_19: Gem::Platform::MSWIN,
mswin_20: Gem::Platform::MSWIN,
mswin_21: Gem::Platform::MSWIN,
mswin_22: Gem::Platform::MSWIN,
mswin_23: Gem::Platform::MSWIN,
mswin_24: Gem::Platform::MSWIN,
mswin_25: Gem::Platform::MSWIN,
mswin_26: Gem::Platform::MSWIN,
mswin_27: Gem::Platform::MSWIN,
mswin_30: Gem::Platform::MSWIN,
mswin_31: Gem::Platform::MSWIN,
mswin_32: Gem::Platform::MSWIN,
mswin_33: Gem::Platform::MSWIN,
mswin_34: Gem::Platform::MSWIN,
mswin_35: Gem::Platform::MSWIN,
mswin64: Gem::Platform::MSWIN64,
mswin64_19: Gem::Platform::MSWIN64,
mswin64_20: Gem::Platform::MSWIN64,
mswin64_21: Gem::Platform::MSWIN64,
mswin64_22: Gem::Platform::MSWIN64,
mswin64_23: Gem::Platform::MSWIN64,
mswin64_24: Gem::Platform::MSWIN64,
mswin64_25: Gem::Platform::MSWIN64,
mswin64_26: Gem::Platform::MSWIN64,
mswin64_27: Gem::Platform::MSWIN64,
mswin64_30: Gem::Platform::MSWIN64,
mswin64_31: Gem::Platform::MSWIN64,
mswin64_32: Gem::Platform::MSWIN64,
mswin64_33: Gem::Platform::MSWIN64,
mswin64_34: Gem::Platform::MSWIN64,
mswin64_35: Gem::Platform::MSWIN64,
mingw: Gem::Platform::MINGW,
mingw_18: Gem::Platform::MINGW,
mingw_19: Gem::Platform::MINGW,
mingw_20: Gem::Platform::MINGW,
mingw_21: Gem::Platform::MINGW,
mingw_22: Gem::Platform::MINGW,
mingw_23: Gem::Platform::MINGW,
mingw_24: Gem::Platform::MINGW,
mingw_25: Gem::Platform::MINGW,
mingw_26: Gem::Platform::MINGW,
mingw_27: Gem::Platform::MINGW,
mingw_30: Gem::Platform::MINGW,
mingw_31: Gem::Platform::MINGW,
mingw_32: Gem::Platform::MINGW,
mingw_33: Gem::Platform::MINGW,
mingw_34: Gem::Platform::MINGW,
mingw_35: Gem::Platform::MINGW,
x64_mingw: Gem::Platform::X64_MINGW,
x64_mingw_20: Gem::Platform::X64_MINGW,
x64_mingw_21: Gem::Platform::X64_MINGW,
x64_mingw_22: Gem::Platform::X64_MINGW,
x64_mingw_23: Gem::Platform::X64_MINGW,
x64_mingw_24: Gem::Platform::X64_MINGW,
x64_mingw_25: Gem::Platform::X64_MINGW,
x64_mingw_26: Gem::Platform::X64_MINGW,
x64_mingw_27: Gem::Platform::X64_MINGW,
x64_mingw_30: Gem::Platform::X64_MINGW,
x64_mingw_31: Gem::Platform::X64_MINGW,
x64_mingw_32: Gem::Platform::X64_MINGW,
x64_mingw_33: Gem::Platform::X64_MINGW,
x64_mingw_34: Gem::Platform::X64_MINGW,
x64_mingw_35: Gem::Platform::X64_MINGW }
end
# rubocop:enable Naming/VariableNumber
it "includes all platforms" do
expect(subject).to eq(platforms.merge(deprecated))
end
expect(dep.current_platform?).to be_truthy
end
end

View file

@ -201,8 +201,8 @@ RSpec.describe Bundler::Dsl do
describe "#gem" do
# rubocop:disable Naming/VariableNumber
[:ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24, :ruby_25, :ruby_26, :ruby_27,
:ruby_30, :ruby_31, :ruby_32, :ruby_33, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24,
:mri_25, :mri_26, :mri_27, :mri_30, :mri_31, :mri_32, :mri_33, :jruby, :rbx, :truffleruby].each do |platform|
:ruby_30, :ruby_31, :ruby_32, :ruby_33, :ruby_34, :ruby_35, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24,
:mri_25, :mri_26, :mri_27, :mri_30, :mri_31, :mri_32, :mri_33, :mri_34, :mri_35, :jruby, :rbx, :truffleruby].each do |platform|
it "allows #{platform} as a valid platform" do
subject.gem("foo", platform: platform)
end
@ -211,7 +211,9 @@ RSpec.describe Bundler::Dsl do
it "allows platforms matching the running Ruby version" do
platform = "ruby_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}"
subject.gem("foo", platform: platform)
expect { subject.gem("foo", platform: platform) }.not_to raise_error
expect(Bundler.current_ruby.respond_to?("#{platform}?")).to be_truthy
end
it "rejects invalid platforms" do

View file

@ -83,7 +83,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
/Authentication is required for www.uri-to-fetch.com/)
end
it "should raise a Bundler::Fetcher::AuthenticationRequiredError with advices" do
it "should raise a Bundler::Fetcher::AuthenticationRequiredError with advice" do
expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
/`bundle config set --global www\.uri-to-fetch\.com username:password`.*`BUNDLE_WWW__URI___TO___FETCH__COM`/m)
end

View file

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

View file

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

View file

@ -21,7 +21,7 @@ RSpec.describe Bundler::Source do
end
describe "#version_message" do
let(:spec) { double(:spec, name: "nokogiri", version: ">= 1.6", platform: rb) }
let(:spec) { double(:spec, name: "nokogiri", version: ">= 1.6", platform: Gem::Platform::RUBY) }
shared_examples_for "the lockfile specs are not relevant" do
it "should return a string with the spec name and version" do
@ -70,7 +70,7 @@ RSpec.describe Bundler::Source do
end
context "with a more recent version" do
let(:spec) { double(:spec, name: "nokogiri", version: "1.6.1", platform: rb) }
let(:spec) { double(:spec, name: "nokogiri", version: "1.6.1", platform: Gem::Platform::RUBY) }
let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") }
context "with color", :no_color_tty do
@ -97,7 +97,7 @@ RSpec.describe Bundler::Source do
end
context "with an older version" do
let(:spec) { double(:spec, name: "nokogiri", version: "1.7.1", platform: rb) }
let(:spec) { double(:spec, name: "nokogiri", version: "1.7.1", platform: Gem::Platform::RUBY) }
let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") }
context "with color", :no_color_tty do

View file

@ -113,6 +113,11 @@ RSpec.describe "bundle cache" do
expect(out).to include("Using json #{default_json_version}")
end
it "does not use remote gems when installing with --prefer-local flag" do
install_gemfile %(source "https://gem.repo2"; gem 'json', '#{default_json_version}'), verbose: true, "prefer-local": true
expect(out).to include("Using json #{default_json_version}")
end
it "caches remote and builtin gems" do
install_gemfile <<-G
source "https://gem.repo2"
@ -167,7 +172,7 @@ RSpec.describe "bundle cache" do
G
bundle :cache, raise_on_error: false
expect(exitstatus).to_not eq(0)
expect(last_command).to be_failure
expect(err).to include("json-#{default_json_version} is built in to Ruby, and can't be cached")
end
end

View file

@ -211,7 +211,7 @@ RSpec.describe "bundle cache" do
end
context "with --all-platforms" do
it "puts the gems in vendor/cache even for other rubies", bundler: ">= 2.4.0" do
it "puts the gems in vendor/cache even for other rubies" do
gemfile <<-D
source "https://gem.repo1"
gem 'myrack', :platforms => [:ruby_20, :windows_20]
@ -221,7 +221,7 @@ RSpec.describe "bundle cache" do
expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
end
it "puts the gems in vendor/cache even for legacy windows rubies", bundler: ">= 2.4.0" do
it "puts the gems in vendor/cache even for legacy windows rubies" do
gemfile <<-D
source "https://gem.repo1"
gem 'myrack', :platforms => [:ruby_20, :x64_mingw_20]
@ -482,6 +482,53 @@ RSpec.describe "bundle install with gem sources" do
expect(the_bundle).to include_gems("platform_specific 1.0 ruby")
end
it "keeps gems that are locked and cached for the current platform, even if incompatible with the current ruby" do
build_repo4 do
build_gem "bcrypt_pbkdf", "1.1.1"
build_gem "bcrypt_pbkdf", "1.1.1" do |s|
s.platform = "arm64-darwin"
s.required_ruby_version = "< #{current_ruby_minor}"
end
end
app_cache = bundled_app("vendor/cache")
FileUtils.mkdir_p app_cache
FileUtils.cp gem_repo4("gems/bcrypt_pbkdf-1.1.1-arm64-darwin.gem"), app_cache
FileUtils.cp gem_repo4("gems/bcrypt_pbkdf-1.1.1.gem"), app_cache
bundle "config cache_all_platforms true"
lockfile <<~L
GEM
remote: https://gem.repo4/
specs:
bcrypt_pbkdf (1.1.1)
bcrypt_pbkdf (1.1.1-arm64-darwin)
PLATFORMS
arm64-darwin
ruby
DEPENDENCIES
bcrypt_pbkdf
BUNDLED WITH
#{Bundler::VERSION}
L
simulate_platform "arm64-darwin-23" do
install_gemfile <<~G, verbose: true
source "https://gem.repo4"
gem "bcrypt_pbkdf"
G
expect(out).to include("Updating files in vendor/cache")
expect(err).to be_empty
expect(app_cache.join("bcrypt_pbkdf-1.1.1-arm64-darwin.gem")).to exist
expect(app_cache.join("bcrypt_pbkdf-1.1.1.gem")).to exist
end
end
it "does not update the cache if --no-cache is passed" do
gemfile <<-G
source "https://gem.repo1"

View file

@ -36,106 +36,145 @@ RSpec.describe "bundle console", readline: true do
RUBY
end
end
install_gemfile <<-G
source "https://gem.repo2"
gem "myrack"
gem "activesupport", :group => :test
gem "myrack_middleware", :group => :development
G
end
it "starts IRB with the default group loaded" do
bundle "console" do |input, _, _|
input.puts("puts MYRACK")
input.puts("exit")
context "when the library has an unrelated error" do
before do
build_lib "loadfuuu", "1.0.0" do |s|
s.write "lib/loadfuuu.rb", "require_relative 'loadfuuu/bar'"
s.write "lib/loadfuuu/bar.rb", "require 'not-in-bundle'"
end
install_gemfile <<-G
source "https://gem.repo1"
path "#{lib_path}" do
gem "loadfuuu", require: true
end
G
end
expect(out).to include("0.9.1")
end
it "uses IRB as default console" do
bundle "console" do |input, _, _|
input.puts("__FILE__")
input.puts("exit")
end
expect(out).to include("(irb)")
end
it "starts another REPL if configured as such" do
install_gemfile <<-G
source "https://gem.repo2"
gem "pry"
G
bundle "config set console pry"
bundle "console" do |input, _, _|
input.puts("__method__")
input.puts("exit")
end
expect(out).to include(":__pry__")
end
it "falls back to IRB if the other REPL isn't available" do
bundle "config set console pry"
# make sure pry isn't there
bundle "console" do |input, _, _|
input.puts("__FILE__")
input.puts("exit")
end
expect(out).to include("(irb)")
end
it "doesn't load any other groups" do
bundle "console" do |input, _, _|
input.puts("puts ACTIVESUPPORT")
input.puts("exit")
end
expect(out).to include("NameError")
end
describe "when given a group" do
it "loads the given group" do
bundle "console test" do |input, _, _|
input.puts("puts ACTIVESUPPORT")
it "does not show the bug report template" do
bundle("console", raise_on_error: false) do |input, _, _|
input.puts("exit")
end
expect(out).to include("2.3.5")
expect(err).not_to include("ERROR REPORT TEMPLATE")
end
end
context "when the library does not have any errors" do
before do
install_gemfile <<-G
source "https://gem.repo2"
gem "myrack"
gem "activesupport", :group => :test
gem "myrack_middleware", :group => :development
G
end
it "loads the default group" do
bundle "console test" do |input, _, _|
it "starts IRB with the default group loaded" do
bundle "console" do |input, _, _|
input.puts("puts MYRACK")
input.puts("exit")
end
expect(out).to include("0.9.1")
end
it "doesn't load other groups" do
bundle "console test" do |input, _, _|
input.puts("puts MYRACK_MIDDLEWARE")
it "uses IRB as default console" do
bundle "console" do |input, _, _|
input.puts("__FILE__")
input.puts("exit")
end
expect(out).to include("(irb)")
end
it "starts another REPL if configured as such" do
install_gemfile <<-G
source "https://gem.repo2"
gem "pry"
G
bundle "config set console pry"
bundle "console" do |input, _, _|
input.puts("__method__")
input.puts("exit")
end
expect(out).to include(":__pry__")
end
it "falls back to IRB if the other REPL isn't available" do
bundle "config set console pry"
# make sure pry isn't there
bundle "console" do |input, _, _|
input.puts("__FILE__")
input.puts("exit")
end
expect(out).to include("(irb)")
end
it "does not try IRB twice if no console is configured and IRB is not available" do
create_file("irb.rb", "raise LoadError, 'irb is not available'")
bundle("console", env: { "RUBYOPT" => "-I#{bundled_app} #{ENV["RUBYOPT"]}" }, raise_on_error: false) do |input, _, _|
input.puts("puts ACTIVESUPPORT")
input.puts("exit")
end
expect(err).not_to include("falling back to irb")
expect(err).to include("irb is not available")
end
it "doesn't load any other groups" do
bundle "console" do |input, _, _|
input.puts("puts ACTIVESUPPORT")
input.puts("exit")
end
expect(out).to include("NameError")
end
end
it "performs an automatic bundle install" do
gemfile <<-G
source "https://gem.repo2"
gem "myrack"
gem "activesupport", :group => :test
gem "myrack_middleware", :group => :development
gem "foo"
G
describe "when given a group" do
it "loads the given group" do
bundle "console test" do |input, _, _|
input.puts("puts ACTIVESUPPORT")
input.puts("exit")
end
expect(out).to include("2.3.5")
end
bundle "config set auto_install 1"
bundle :console do |input, _, _|
input.puts("puts 'hello'")
input.puts("exit")
it "loads the default group" do
bundle "console test" do |input, _, _|
input.puts("puts MYRACK")
input.puts("exit")
end
expect(out).to include("0.9.1")
end
it "doesn't load other groups" do
bundle "console test" do |input, _, _|
input.puts("puts MYRACK_MIDDLEWARE")
input.puts("exit")
end
expect(out).to include("NameError")
end
end
it "performs an automatic bundle install" do
gemfile <<-G
source "https://gem.repo2"
gem "myrack"
gem "activesupport", :group => :test
gem "myrack_middleware", :group => :development
gem "foo"
G
bundle "config set auto_install 1"
bundle :console do |input, _, _|
input.puts("puts 'hello'")
input.puts("exit")
end
expect(out).to include("Installing foo 1.0")
expect(out).to include("hello")
expect(the_bundle).to include_gems "foo 1.0"
end
expect(out).to include("Installing foo 1.0")
expect(out).to include("hello")
expect(the_bundle).to include_gems "foo 1.0"
end
end

View file

@ -46,7 +46,9 @@ RSpec.describe "bundle doctor" do
end
it "exits with no message if the installed gem has no C extensions" do
expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
doctor = Bundler::CLI::Doctor.new({})
allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
expect { doctor.run }.not_to raise_error
expect(@stdout.string).to be_empty
end
@ -54,7 +56,7 @@ RSpec.describe "bundle doctor" do
doctor = Bundler::CLI::Doctor.new({})
expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/myrack/myrack.bundle"]
expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/lib/libSystem.dylib"]
allow(Fiddle).to receive(:dlopen).with("/usr/lib/libSystem.dylib").and_return(true)
allow(doctor).to receive(:lookup_with_fiddle).with("/usr/lib/libSystem.dylib").and_return(false)
expect { doctor.run }.not_to raise_error
expect(@stdout.string).to be_empty
end
@ -63,7 +65,7 @@ RSpec.describe "bundle doctor" do
doctor = Bundler::CLI::Doctor.new({})
expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/myrack/myrack.bundle"]
expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"]
allow(Fiddle).to receive(:dlopen).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_raise(Fiddle::DLError)
allow(doctor).to receive(:lookup_with_fiddle).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(true)
expect { doctor.run }.to raise_error(Bundler::ProductionError, <<~E.strip), @stdout.string
The following gems are missing OS dependencies:
* bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
@ -82,7 +84,9 @@ RSpec.describe "bundle doctor" do
end
it "exits with an error if home contains files that are not readable/writable" do
expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
doctor = Bundler::CLI::Doctor.new({})
allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
expect { doctor.run }.not_to raise_error
expect(@stdout.string).to include(
"Broken links exist in the Bundler home. Please report them to the offending gem's upstream repo. These files are:\n - #{@broken_symlink}"
)
@ -102,10 +106,12 @@ RSpec.describe "bundle doctor" do
end
it "exits with an error if home contains files that are not readable/writable" do
doctor = Bundler::CLI::Doctor.new({})
allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
allow(@stat).to receive(:uid) { Process.uid }
allow(File).to receive(:writable?).with(@unwritable_file) { false }
allow(File).to receive(:readable?).with(@unwritable_file) { false }
expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
expect { doctor.run }.not_to raise_error
expect(@stdout.string).to include(
"Files exist in the Bundler home that are not readable/writable by the current user. These files are:\n - #{@unwritable_file}"
)
@ -118,9 +124,11 @@ RSpec.describe "bundle doctor" do
end
it "exits with an error if home contains files that are not readable/writable and are not owned by the current user" do
doctor = Bundler::CLI::Doctor.new({})
allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
allow(File).to receive(:writable?).with(@unwritable_file) { false }
allow(File).to receive(:readable?).with(@unwritable_file) { false }
expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
expect { doctor.run }.not_to raise_error
expect(@stdout.string).to include(
"Files exist in the Bundler home that are owned by another user, and are not readable/writable. These files are:\n - #{@unwritable_file}"
)
@ -128,9 +136,11 @@ RSpec.describe "bundle doctor" do
end
it "exits with a warning if home contains files that are read/write but not owned by current user" do
doctor = Bundler::CLI::Doctor.new({})
allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
allow(File).to receive(:writable?).with(@unwritable_file) { true }
allow(File).to receive(:readable?).with(@unwritable_file) { true }
expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
expect { doctor.run }.not_to raise_error
expect(@stdout.string).to include(
"Files exist in the Bundler home that are owned by another user, but are still readable/writable. These files are:\n - #{@unwritable_file}"
)

View file

@ -56,12 +56,12 @@ RSpec.describe "bundle info" do
expect(out).to eq("2.3.2")
end
it "doesn't claim that bundler has been deleted, even if using a custom path without bundler there" do
it "doesn't claim that bundler is missing, even if using a custom path without bundler there" do
bundle "config set --local path vendor/bundle"
bundle "install"
bundle "info bundler"
expect(out).to include("\tPath: #{root}")
expect(err).not_to match(/The gem bundler has been deleted/i)
expect(err).not_to match(/The gem bundler is missing/i)
end
it "complains if gem not in bundle" do
@ -69,27 +69,27 @@ RSpec.describe "bundle info" do
expect(err).to eq("Could not find gem 'missing'.")
end
it "warns if path no longer exists on disk" do
it "warns if path does not exist on disk, but specification is there" do
FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2"))
bundle "info rails --path"
expect(err).to include("The gem rails has been deleted.")
expect(err).to include("The gem rails is missing.")
expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
bundle "info rail --path"
expect(err).to include("The gem rails has been deleted.")
expect(err).to include("The gem rails is missing.")
expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
bundle "info rails"
expect(err).to include("The gem rails has been deleted.")
expect(err).to include("The gem rails is missing.")
expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
end
context "given a default gem shippped in ruby", :ruby_repo do
it "prints information about the default gem" do
bundle "info rdoc"
expect(out).to include("* rdoc")
bundle "info json"
expect(out).to include("* json")
expect(out).to include("Default Gem: yes")
end
end

View file

@ -100,12 +100,23 @@ RSpec.describe "bundle install with gem sources" do
gem 'myrack'
G
FileUtils.rm_rf(default_bundle_path("gems/myrack-1.0.0"))
gem_dir = default_bundle_path("gems/myrack-1.0.0")
FileUtils.rm_rf(gem_dir)
bundle "install --verbose"
expect(out).to include("Installing myrack 1.0.0")
expect(default_bundle_path("gems/myrack-1.0.0")).to exist
expect(gem_dir).to exist
expect(the_bundle).to include_gems("myrack 1.0.0")
FileUtils.rm_rf(gem_dir)
Dir.mkdir(gem_dir)
bundle "install --verbose"
expect(out).to include("Installing myrack 1.0.0")
expect(gem_dir).to exist
expect(the_bundle).to include_gems("myrack 1.0.0")
end
@ -281,7 +292,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "installs gems for windows" do
simulate_platform x86_mswin32 do
simulate_platform "x86-mswin32" do
install_gemfile <<-G
source "https://gem.repo1"
gem "platform_specific"
@ -290,6 +301,17 @@ RSpec.describe "bundle install with gem sources" do
expect(the_bundle).to include_gems("platform_specific 1.0 x86-mswin32")
end
end
it "installs gems for aarch64-mingw-ucrt" do
simulate_platform "aarch64-mingw-ucrt" do
install_gemfile <<-G
source "https://gem.repo1"
gem "platform_specific"
G
end
expect(out).to include("Installing platform_specific 1.0 (aarch64-mingw-ucrt)")
end
end
describe "doing bundle install foo" do
@ -559,7 +581,7 @@ RSpec.describe "bundle install with gem sources" do
bundle :install, raise_on_error: false
expect(err).to include("Two gemspecs have conflicting requirements on the same gem: rubocop (~> 1.36.0, development) and rubocop (~> 2.0, development). Bundler cannot continue.")
expect(err).to include("Two gemspec development dependencies have conflicting requirements on the same gem: rubocop (~> 1.36.0) and rubocop (~> 2.0). Bundler cannot continue.")
end
it "warns when a Gemfile dependency is overriding a gemspec development dependency, with different requirements" do
@ -1584,6 +1606,26 @@ RSpec.describe "bundle install with gem sources" do
expect(out).to include("Fetching foo 1.0.1").and include("Installing foo 1.0.1").and include("Fetching b 1.0.0").and include("Installing b 1.0.0")
expect(last_command).to be_success
end
it "resolves to the latest version if no gems are available locally" do
build_repo4 do
build_gem "myreline", "0.3.8"
build_gem "debug", "0.2.1"
build_gem "debug", "1.10.0" do |s|
s.add_dependency "myreline"
end
end
install_gemfile <<~G, "prefer-local": true, verbose: true
source "https://gem.repo4"
gem "debug"
G
expect(out).to include("Fetching debug 1.10.0").and include("Installing debug 1.10.0").and include("Fetching myreline 0.3.8").and include("Installing myreline 0.3.8")
expect(last_command).to be_success
end
end
context "with a symlinked configured as bundle path and a gem with symlinks" do

View file

@ -645,13 +645,13 @@ RSpec.describe "bundle lock" do
it "single gem updates dependent gem to minor" do
bundle "lock --update foo --patch"
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.4.5 bar-2.1.1 qux-1.0.0].sort)
expect(the_bundle.locked_specs).to eq(%w[foo-1.4.5 bar-2.1.1 qux-1.0.0].sort)
end
it "minor preferred with strict" do
bundle "lock --update --minor --strict"
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort)
expect(the_bundle.locked_specs).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort)
end
it "shows proper error when Gemfile changes forbid patch upgrades, and --patch --strict is given" do
@ -674,25 +674,25 @@ RSpec.describe "bundle lock" do
it "defaults to major" do
bundle "lock --update --pre"
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
expect(the_bundle.locked_specs).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
end
it "patch preferred" do
bundle "lock --update --patch --pre"
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.4.5 bar-2.1.2.pre qux-1.0.1].sort)
expect(the_bundle.locked_specs).to eq(%w[foo-1.4.5 bar-2.1.2.pre qux-1.0.1].sort)
end
it "minor preferred" do
bundle "lock --update --minor --pre"
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.1 bar-3.1.0.pre qux-1.1.0].sort)
expect(the_bundle.locked_specs).to eq(%w[foo-1.5.1 bar-3.1.0.pre qux-1.1.0].sort)
end
it "major preferred" do
bundle "lock --update --major --pre"
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
expect(the_bundle.locked_specs).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
end
end
end
@ -734,7 +734,7 @@ RSpec.describe "bundle lock" do
it "adds the latest version of the new dependency" do
bundle "lock --minor --update sequel"
expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[sequel-5.72.0 bigdecimal-99.1.4].sort)
expect(the_bundle.locked_specs).to eq(%w[sequel-5.72.0 bigdecimal-99.1.4].sort)
end
end
@ -765,8 +765,7 @@ RSpec.describe "bundle lock" do
bundle "lock --add-platform java x86-mingw32"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32))
expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32"))
end
it "supports adding new platforms, when most specific locked platform is not the current platform, and current resolve is not compatible with the target platform" do
@ -845,16 +844,14 @@ RSpec.describe "bundle lock" do
bundle "lock --add-platform java x86-mingw32"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
expect(lockfile.platforms).to contain_exactly(rb, linux, java, x86_mingw32)
expect(the_bundle.locked_platforms).to contain_exactly(Gem::Platform::RUBY, "x86_64-linux", "java", "x86-mingw32")
end
it "supports adding the `ruby` platform" do
bundle "lock --add-platform ruby"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
expect(lockfile.platforms).to match_array(default_platform_list("ruby"))
expect(the_bundle.locked_platforms).to match_array(default_platform_list("ruby"))
end
it "fails when adding an unknown platform" do
@ -867,13 +864,11 @@ RSpec.describe "bundle lock" do
bundle "lock --add-platform java x86-mingw32"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
lockfile = Bundler::LockfileParser.new(read_lockfile)
expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32))
expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32"))
bundle "lock --remove-platform java"
lockfile = Bundler::LockfileParser.new(read_lockfile)
expect(lockfile.platforms).to match_array(default_platform_list(x86_mingw32))
expect(the_bundle.locked_platforms).to match_array(default_platform_list("x86-mingw32"))
end
it "also cleans up redundant platform gems when removing platforms" do
@ -948,7 +943,7 @@ RSpec.describe "bundle lock" do
build_repo4 do
build_gem "ffi", "1.9.14"
build_gem "ffi", "1.9.14" do |s|
s.platform = x86_mingw32
s.platform = "x86-mingw32"
end
build_gem "gssapi", "0.1"
@ -980,7 +975,7 @@ RSpec.describe "bundle lock" do
gem "gssapi"
G
simulate_platform(x86_mingw32) { bundle :lock }
simulate_platform("x86-mingw32") { bundle :lock }
checksums = checksums_section_when_enabled do |c|
c.checksum gem_repo4, "ffi", "1.9.14", "x86-mingw32"

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)
end
it "warns if path no longer exists on disk" do
it "warns if specification is installed, but path does not exist on disk" do
FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2"))
bundle "show rails"
expect(err).to match(/has been deleted/i)
expect(err).to match(/is missing/i)
expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s)
end

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")
end
it "does not downgrade direct dependencies unnecessarily" do
build_repo4 do
build_gem "redis", "4.8.1"
build_gem "redis", "5.3.0"
build_gem "sidekiq", "6.5.5" do |s|
s.add_dependency "redis", ">= 4.5.0"
end
build_gem "sidekiq", "6.5.12" do |s|
s.add_dependency "redis", ">= 4.5.0", "< 5"
end
# one version of sidekiq above Gemfile's range is needed to make the
# resolver choose `redis` first and trying to upgrade it, reproducing
# the accidental sidekiq downgrade as a result
build_gem "sidekiq", "7.0.0 " do |s|
s.add_dependency "redis", ">= 4.2.0"
end
build_gem "sentry-sidekiq", "5.22.0" do |s|
s.add_dependency "sidekiq", ">= 3.0"
end
build_gem "sentry-sidekiq", "5.22.4" do |s|
s.add_dependency "sidekiq", ">= 3.0"
end
end
gemfile <<~G
source "https://gem.repo4"
gem "redis"
gem "sidekiq", "~> 6.5"
gem "sentry-sidekiq"
G
original_lockfile = <<~L
GEM
remote: https://gem.repo4/
specs:
redis (4.8.1)
sentry-sidekiq (5.22.0)
sidekiq (>= 3.0)
sidekiq (6.5.12)
redis (>= 4.5.0, < 5)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
redis
sentry-sidekiq
sidekiq (~> 6.5)
BUNDLED WITH
#{Bundler::VERSION}
L
lockfile original_lockfile
bundle "lock --update sentry-sidekiq"
expect(lockfile).to eq(original_lockfile.sub("sentry-sidekiq (5.22.0)", "sentry-sidekiq (5.22.4)"))
end
it "does not downgrade indirect dependencies unnecessarily" do
build_repo4 do
build_gem "a" do |s|
@ -694,28 +760,36 @@ RSpec.describe "bundle update" do
bundle "update", all: true, raise_on_error: false
expect(last_command).to be_failure
expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/)
expect(err).to match(/freeze by running `bundle config set frozen false`./)
expect(err).to eq <<~ERROR.strip
Bundler is unlocking, but the lockfile can't be updated because frozen mode is set
If this is a development machine, remove the Gemfile.lock freeze by running `bundle config set frozen false`.
ERROR
end
it "should fail loudly when frozen is set globally" do
bundle "config set --global frozen 1"
bundle "update", all: true, raise_on_error: false
expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/).
and match(/freeze by running `bundle config set frozen false`./)
expect(err).to eq <<~ERROR.strip
Bundler is unlocking, but the lockfile can't be updated because frozen mode is set
If this is a development machine, remove the Gemfile.lock freeze by running `bundle config set frozen false`.
ERROR
end
it "should fail loudly when deployment is set globally" do
bundle "config set --global deployment true"
bundle "update", all: true, raise_on_error: false
expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/).
and match(/freeze by running `bundle config set frozen false`./)
expect(err).to eq <<~ERROR.strip
Bundler is unlocking, but the lockfile can't be updated because frozen mode is set
If this is a development machine, remove the Gemfile.lock freeze by running `bundle config set frozen false`.
ERROR
end
it "should not suggest any command to unfreeze bundler if frozen is set through ENV" do
bundle "update", all: true, raise_on_error: false, env: { "BUNDLE_FROZEN" => "true" }
expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/)
expect(err).not_to match(/by running/)
expect(err).to eq("Bundler is unlocking, but the lockfile can't be updated because frozen mode is set")
end
end
@ -1132,7 +1206,7 @@ RSpec.describe "bundle update in more complicated situations" do
a
L
simulate_platform linux, &example
simulate_platform "x86_64-linux", &example
end
it "allows updating" do
@ -1173,7 +1247,7 @@ RSpec.describe "bundle update in more complicated situations" do
end
it "is not updated because it is not actually included in the bundle" do
simulate_platform linux do
simulate_platform "x86_64-linux" do
bundle "update a"
expect(last_command.stdboth).to include "Bundler attempted to update a but it was not considered because it is for a different platform from the current one"
expect(the_bundle).to_not include_gem "a"

View file

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

View file

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

View file

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

View file

@ -11,7 +11,7 @@ RSpec.describe "bundle install with specific platforms" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
expect(the_bundle.locked_gems.platforms).to include(pl("x86_64-darwin-15"))
expect(the_bundle.locked_platforms).to include("universal-darwin")
expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(
"google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin"
@ -351,7 +351,7 @@ RSpec.describe "bundle install with specific platforms" do
G
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
expect(the_bundle.locked_gems.platforms).to include(pl("x86_64-darwin-15"))
expect(the_bundle.locked_platforms).to include("universal-darwin")
expect(the_bundle).to include_gems("facter 2.4.6 universal-darwin", "CFPropertyList 1.0")
expect(the_bundle.locked_gems.specs.map(&:full_name)).to include("CFPropertyList-1.0",
"facter-2.4.6-universal-darwin")
@ -367,9 +367,9 @@ RSpec.describe "bundle install with specific platforms" do
simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
bundle "lock --add-platform=#{x64_mingw32}"
bundle "lock --add-platform=x64-mingw32"
expect(the_bundle.locked_gems.platforms).to include(x64_mingw32, pl("x86_64-darwin-15"))
expect(the_bundle.locked_platforms).to include("x64-mingw32", "universal-darwin")
expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(*%w[
google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw32
@ -381,9 +381,9 @@ RSpec.describe "bundle install with specific platforms" do
simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
bundle "lock --add-platform=#{java}"
bundle "lock --add-platform=java"
expect(the_bundle.locked_gems.platforms).to include(java, pl("x86_64-darwin-15"))
expect(the_bundle.locked_platforms).to include("java", "universal-darwin")
expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(
"google-protobuf-3.0.0.alpha.5.0.5.1",
"google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin"
@ -1077,7 +1077,7 @@ RSpec.describe "bundle install with specific platforms" do
myrack (3.0.7)
PLATFORMS
#{lockfile_platforms("ruby", generic_local_platform, defaults: [])}
#{lockfile_platforms(generic_default_locked_platform || local_platform, defaults: ["ruby"])}
DEPENDENCIES
concurrent-ruby

View file

@ -53,17 +53,37 @@ RSpec.describe "bundle install" do
end
end
context "with deprecated features" do
it "reports that lib is an invalid option" do
gemfile <<-G
source "https://gem.repo1"
it "reports that lib is an invalid option" do
gemfile <<-G
source "https://gem.repo1"
gem "myrack", :lib => "myrack"
G
gem "myrack", :lib => "myrack"
G
bundle :install, raise_on_error: false
expect(err).to match(/You passed :lib as an option for gem 'myrack', but it is invalid/)
end
bundle :install, raise_on_error: false
expect(err).to match(/You passed :lib as an option for gem 'myrack', but it is invalid/)
end
it "reports that type is an invalid option" do
gemfile <<-G
source "https://gem.repo1"
gem "myrack", :type => "development"
G
bundle :install, raise_on_error: false
expect(err).to match(/You passed :type as an option for gem 'myrack', but it is invalid/)
end
it "reports that gemfile is an invalid option" do
gemfile <<-G
source "https://gem.repo1"
gem "myrack", :gemfile => "foo"
G
bundle :install, raise_on_error: false
expect(err).to match(/You passed :gemfile as an option for gem 'myrack', but it is invalid/)
end
context "when an internal error happens" do

View file

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

View file

@ -295,6 +295,30 @@ RSpec.describe "bundle flex_install" do
it "should work when you update" do
bundle "update myrack"
checksums = checksums_section_when_enabled do |c|
c.checksum gem_repo1, "myrack", "0.9.1"
c.checksum gem_repo1, "myrack-obama", "1.0"
end
expect(lockfile).to eq <<~L
GEM
remote: https://gem.repo1/
specs:
myrack (0.9.1)
myrack-obama (1.0)
myrack
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
myrack (= 0.9.1)
myrack-obama
#{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
end
end

View file

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

View file

@ -1587,6 +1587,32 @@ RSpec.describe "the lockfile format" do
L
end
it "raises a clear error when frozen mode is set and lockfile is missing deps, and does not install any gems" do
lockfile <<-L
GEM
remote: https://gem.repo2/
specs:
myrack_middleware (1.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
myrack_middleware
BUNDLED WITH
#{Bundler::VERSION}
L
install_gemfile <<-G, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
source "https://gem.repo2"
gem "myrack_middleware"
G
expect(err).to eq("Bundler found incorrect dependencies in the lockfile for myrack_middleware-1.0")
expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
end
it "automatically fixes the lockfile when it's missing deps, they conflict with other locked deps, but conflicts are fixable" do
build_repo4 do
build_gem "other_dep", "0.9"
@ -1798,7 +1824,7 @@ RSpec.describe "the lockfile format" do
indirect_dependency (1.2.3)
PLATFORMS
#{lockfile_platforms("ruby", generic_local_platform, defaults: [])}
#{lockfile_platforms(generic_default_locked_platform || local_platform, defaults: ["ruby"])}
DEPENDENCIES
direct_dependency
@ -1849,6 +1875,62 @@ RSpec.describe "the lockfile format" do
L
end
it "automatically fixes the lockfile when it has incorrect deps, keeping the locked version" do
build_repo4 do
build_gem "net-smtp", "0.5.0" do |s|
s.add_dependency "net-protocol"
end
build_gem "net-smtp", "0.5.1" do |s|
s.add_dependency "net-protocol"
end
build_gem "net-protocol", "0.2.2"
end
gemfile <<~G
source "#{file_uri_for(gem_repo4)}"
gem "net-smtp"
G
lockfile <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
net-protocol (0.2.2)
net-smtp (0.5.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
net-smtp
BUNDLED WITH
#{Bundler::VERSION}
L
bundle "install"
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
net-protocol (0.2.2)
net-smtp (0.5.0)
net-protocol
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
net-smtp
BUNDLED WITH
#{Bundler::VERSION}
L
end
shared_examples_for "a lockfile missing dependent specs" do
it "auto-heals" do
build_repo4 do

View file

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

View file

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

View file

@ -211,12 +211,12 @@ RSpec.describe "Resolving" do
it "resolves all gems to latest patch" do
# strict is not set, so bar goes up a minor version due to dependency from foo 1.4.5
should_conservative_resolve_and_include :patch, [], %w[foo-1.4.5 bar-2.1.1]
should_conservative_resolve_and_include :patch, true, %w[foo-1.4.5 bar-2.1.1]
end
it "resolves all gems to latest patch strict" do
# strict is set, so foo can only go up to 1.4.4 to avoid bar going up a minor version, and bar can go up to 2.0.5
should_conservative_resolve_and_include [:patch, :strict], [], %w[foo-1.4.4 bar-2.0.5]
should_conservative_resolve_and_include [:patch, :strict], true, %w[foo-1.4.4 bar-2.0.5]
end
it "resolves foo only to latest patch - same dependency case" do
@ -256,20 +256,20 @@ RSpec.describe "Resolving" do
it "resolves all gems to latest minor" do
# strict is not set, so bar goes up a major version due to dependency from foo 1.4.5
should_conservative_resolve_and_include :minor, [], %w[foo-1.5.1 bar-3.0.0]
should_conservative_resolve_and_include :minor, true, %w[foo-1.5.1 bar-3.0.0]
end
it "resolves all gems to latest minor strict" do
# strict is set, so foo can only go up to 1.5.0 to avoid bar going up a major version
should_conservative_resolve_and_include [:minor, :strict], [], %w[foo-1.5.0 bar-2.1.1]
should_conservative_resolve_and_include [:minor, :strict], true, %w[foo-1.5.0 bar-2.1.1]
end
it "resolves all gems to latest major" do
should_conservative_resolve_and_include :major, [], %w[foo-2.0.0 bar-3.0.0]
should_conservative_resolve_and_include :major, true, %w[foo-2.0.0 bar-3.0.0]
end
it "resolves all gems to latest major strict" do
should_conservative_resolve_and_include [:major, :strict], [], %w[foo-2.0.0 bar-3.0.0]
should_conservative_resolve_and_include [:major, :strict], true, %w[foo-2.0.0 bar-3.0.0]
end
# Why would this happen in real life? If bar 2.2 has a bug that the author of foo wants to bypass
@ -292,21 +292,21 @@ RSpec.describe "Resolving" do
end
it "could revert to a previous version level patch" do
should_conservative_resolve_and_include :patch, [], %w[foo-1.4.4 bar-2.1.1]
should_conservative_resolve_and_include :patch, true, %w[foo-1.4.4 bar-2.1.1]
end
it "cannot revert to a previous version in strict mode level patch" do
# fall back to the locked resolution since strict means we can't regress either version
should_conservative_resolve_and_include [:patch, :strict], [], %w[foo-1.4.3 bar-2.2.3]
should_conservative_resolve_and_include [:patch, :strict], true, %w[foo-1.4.3 bar-2.2.3]
end
it "could revert to a previous version level minor" do
should_conservative_resolve_and_include :minor, [], %w[foo-1.5.0 bar-2.0.5]
should_conservative_resolve_and_include :minor, true, %w[foo-1.5.0 bar-2.0.5]
end
it "cannot revert to a previous version in strict mode level minor" do
# fall back to the locked resolution since strict means we can't regress either version
should_conservative_resolve_and_include [:minor, :strict], [], %w[foo-1.4.3 bar-2.2.3]
should_conservative_resolve_and_include [:minor, :strict], true, %w[foo-1.4.3 bar-2.2.3]
end
end
end

View file

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

View file

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

View file

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

View file

@ -1,11 +1,18 @@
# frozen_string_literal: true
class RequirementChecker < Proc
def self.against(present)
def self.against(present, major_only: false)
present = present.split(".")[0] if major_only
provided = Gem::Version.new(present)
new do |required|
!Gem::Requirement.new(required).satisfied_by?(provided)
requirement = Gem::Requirement.new(required)
if major_only && !requirement.requirements.map(&:last).all? {|version| version.segments.one? }
raise "this filter only supports major versions, but #{required} was given"
end
!requirement.satisfied_by?(provided)
end.tap do |checker|
checker.provided = provided
end
@ -21,7 +28,7 @@ end
RSpec.configure do |config|
config.filter_run_excluding realworld: true
config.filter_run_excluding bundler: RequirementChecker.against(Bundler::VERSION.split(".")[0])
config.filter_run_excluding bundler: RequirementChecker.against(Bundler::VERSION, major_only: true)
config.filter_run_excluding rubygems: RequirementChecker.against(Gem::VERSION)
config.filter_run_excluding ruby_repo: !ENV["GEM_COMMAND"].nil?
config.filter_run_excluding no_color_tty: Gem.win_platform? || !ENV["GITHUB_ACTION"].nil?

View file

@ -19,18 +19,19 @@ module Gem
@default_specifications_dir = nil
end
if ENV["BUNDLER_SPEC_WINDOWS"]
@@win_platform = true # rubocop:disable Style/ClassVars
end
spec_platform = ENV["BUNDLER_SPEC_PLATFORM"]
if spec_platform
if /mingw|mswin/.match?(spec_platform)
@@win_platform = nil # rubocop:disable Style/ClassVars
RbConfig::CONFIG["host_os"] = spec_platform.gsub(/^[^-]+-/, "").tr("-", "_")
end
if ENV["BUNDLER_SPEC_PLATFORM"]
previous_platforms = @platforms
previous_local = Platform.local
RbConfig::CONFIG["arch"] = spec_platform
class Platform
@local = new(ENV["BUNDLER_SPEC_PLATFORM"])
@local = nil
end
@platforms = previous_platforms.map {|platform| platform == previous_local ? Platform.local : platform }
@platforms = []
end
if ENV["BUNDLER_SPEC_GEM_SOURCES"]

View file

@ -442,16 +442,6 @@ module Spec
ENV["BUNDLER_SPEC_PLATFORM"] = old if block_given?
end
def simulate_windows(platform = x86_mswin32)
old = ENV["BUNDLER_SPEC_WINDOWS"]
ENV["BUNDLER_SPEC_WINDOWS"] = "true"
simulate_platform platform do
yield
end
ensure
ENV["BUNDLER_SPEC_WINDOWS"] = old
end
def current_ruby_minor
Gem.ruby_version.segments.tap {|s| s.delete_at(2) }.join(".")
end

View file

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

View file

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

View file

@ -4,62 +4,14 @@ module Spec
module Platforms
include Bundler::GemHelpers
def rb
Gem::Platform::RUBY
end
def mac
Gem::Platform.new("x86-darwin-10")
end
def x64_mac
Gem::Platform.new("x86_64-darwin-15")
end
def java
Gem::Platform.new([nil, "java", nil])
end
def linux
Gem::Platform.new("x86_64-linux")
end
def x86_mswin32
Gem::Platform.new(["x86", "mswin32", nil])
end
def x64_mswin64
Gem::Platform.new(["x64", "mswin64", nil])
end
def x86_mingw32
Gem::Platform.new(["x86", "mingw32", nil])
end
def x64_mingw32
Gem::Platform.new(["x64", "mingw32", nil])
end
def x64_mingw_ucrt
Gem::Platform.new(["x64", "mingw", "ucrt"])
end
def windows_platforms
[x86_mswin32, x64_mswin64, x86_mingw32, x64_mingw32, x64_mingw_ucrt]
end
def all_platforms
[rb, java, linux, windows_platforms].flatten
end
def not_local
all_platforms.find {|p| p != generic_local_platform }
generic_local_platform == Gem::Platform::RUBY ? "java" : Gem::Platform::RUBY
end
def local_tag
if RUBY_PLATFORM == "java"
if Gem.java_platform?
:jruby
elsif ["x64-mingw32", "x64-mingw-ucrt"].include?(RUBY_PLATFORM)
elsif Gem.win_platform?
:windows
else
:ruby
@ -96,16 +48,22 @@ module Spec
end
def default_platform_list(*extra, defaults: default_locked_platforms)
defaults.concat(extra).uniq
defaults.concat(extra).map(&:to_s).uniq
end
def lockfile_platforms(*extra, defaults: default_locked_platforms)
platforms = default_platform_list(*extra, defaults: defaults)
platforms.map(&:to_s).sort.join("\n ")
platforms.sort.join("\n ")
end
def default_locked_platforms
[local_platform, generic_local_platform]
[local_platform, generic_default_locked_platform].compact
end
def generic_default_locked_platform
return unless generic_local_platform_is_ruby?
Gem::Platform::RUBY
end
end
end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -564,7 +564,6 @@ end
# [B] ~> 1.0
#
# and should resolve using b-1.0
# TODO: move these to specification
def test_self_activate_over
a = util_spec "a", "1.0", "b" => ">= 1.0", "c" => "= 1.0"
@ -653,6 +652,17 @@ end
end
end
def test_self_activate_missing_deps_does_not_raise_nested_exceptions
a = util_spec "a", "1.0", "b" => ">= 1.0"
install_specs a
e = assert_raise Gem::MissingSpecError do
a.activate
end
refute e.cause
end
def test_self_all_equals
a = util_spec "foo", "1", nil, "lib/foo.rb"