From 53468cc11147b0d285fc376fc546b677dad600ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 1 Feb 2021 16:17:16 +0100 Subject: [PATCH] Sync latest development version of bundler & rubygems --- lib/bundler.rb | 3 +- lib/bundler/cli.rb | 1 + lib/bundler/cli/cache.rb | 1 + lib/bundler/cli/gem.rb | 54 ++-- lib/bundler/compact_index_client/updater.rb | 10 +- lib/bundler/definition.rb | 95 +++--- lib/bundler/dep_proxy.rb | 23 +- lib/bundler/dsl.rb | 58 ++-- lib/bundler/feature_flag.rb | 1 - lib/bundler/fetcher.rb | 1 - lib/bundler/gem_helper.rb | 16 +- lib/bundler/gem_version_promoter.rb | 4 +- lib/bundler/index.rb | 11 +- lib/bundler/inline.rb | 1 + lib/bundler/installer.rb | 19 +- lib/bundler/installer/standalone.rb | 15 + lib/bundler/lazy_specification.rb | 27 +- lib/bundler/lockfile_parser.rb | 20 +- lib/bundler/man/bundle-add.1 | 2 +- lib/bundler/man/bundle-binstubs.1 | 2 +- lib/bundler/man/bundle-cache.1 | 2 +- lib/bundler/man/bundle-check.1 | 2 +- lib/bundler/man/bundle-clean.1 | 2 +- lib/bundler/man/bundle-config.1 | 8 +- lib/bundler/man/bundle-config.1.ronn | 6 +- lib/bundler/man/bundle-doctor.1 | 2 +- lib/bundler/man/bundle-exec.1 | 2 +- lib/bundler/man/bundle-gem.1 | 2 +- lib/bundler/man/bundle-info.1 | 2 +- lib/bundler/man/bundle-init.1 | 2 +- lib/bundler/man/bundle-inject.1 | 2 +- lib/bundler/man/bundle-install.1 | 2 +- lib/bundler/man/bundle-list.1 | 2 +- lib/bundler/man/bundle-lock.1 | 2 +- lib/bundler/man/bundle-open.1 | 2 +- lib/bundler/man/bundle-outdated.1 | 2 +- lib/bundler/man/bundle-platform.1 | 2 +- lib/bundler/man/bundle-pristine.1 | 2 +- lib/bundler/man/bundle-remove.1 | 2 +- lib/bundler/man/bundle-show.1 | 2 +- lib/bundler/man/bundle-update.1 | 2 +- lib/bundler/man/bundle-viz.1 | 2 +- lib/bundler/man/bundle.1 | 2 +- lib/bundler/man/gemfile.5 | 2 +- lib/bundler/plugin.rb | 1 + lib/bundler/plugin/installer.rb | 18 +- lib/bundler/plugin/source_list.rb | 4 + lib/bundler/resolver.rb | 197 +++++++----- lib/bundler/resolver/spec_group.rb | 102 ++++--- lib/bundler/rubygems_ext.rb | 16 + lib/bundler/rubygems_gem_installer.rb | 47 +++ lib/bundler/settings.rb | 2 +- lib/bundler/shared_helpers.rb | 4 +- lib/bundler/source/git.rb | 36 +-- lib/bundler/source/git/git_proxy.rb | 52 ++-- lib/bundler/source/path/installer.rb | 2 + lib/bundler/source/rubygems.rb | 3 +- lib/bundler/source_list.rb | 37 +-- lib/bundler/spec_set.rb | 9 +- lib/bundler/stub_specification.rb | 8 + lib/bundler/templates/newgem/CHANGELOG.md.tt | 5 + lib/bundler/templates/newgem/Gemfile.tt | 2 +- lib/bundler/templates/newgem/README.md.tt | 8 +- .../templates/newgem/newgem.gemspec.tt | 2 +- lib/bundler/templates/newgem/rubocop.yml.tt | 3 + .../delegates/specification_provider.rb | 7 + .../lib/molinillo/dependency_graph.rb | 1 - .../lib/molinillo/dependency_graph/vertex.rb | 16 +- .../vendor/molinillo/lib/molinillo/errors.rb | 4 +- .../modules/specification_provider.rb | 11 + .../molinillo/lib/molinillo/resolution.rb | 18 +- lib/bundler/vendor/thor/lib/thor.rb | 11 +- lib/bundler/vendor/thor/lib/thor/actions.rb | 2 +- .../lib/thor/actions/file_manipulation.rb | 6 +- lib/bundler/vendor/thor/lib/thor/error.rb | 2 +- .../vendor/thor/lib/thor/parser/arguments.rb | 6 +- .../vendor/thor/lib/thor/parser/options.rb | 17 +- .../vendor/thor/lib/thor/shell/basic.rb | 7 +- .../vendor/thor/lib/thor/shell/color.rb | 6 +- lib/bundler/vendor/thor/lib/thor/version.rb | 2 +- lib/rubygems.rb | 4 +- lib/rubygems/command.rb | 1 + lib/rubygems/config_file.rb | 9 + lib/rubygems/core_ext/tcpsocket_init.rb | 52 ++++ lib/rubygems/dependency.rb | 6 +- lib/rubygems/ext/builder.rb | 3 +- lib/rubygems/installer_uninstaller_utils.rb | 7 +- lib/rubygems/platform.rb | 14 +- lib/rubygems/remote_fetcher.rb | 3 +- lib/rubygems/requirement.rb | 2 +- lib/rubygems/resolver/index_specification.rb | 5 +- .../delegates/specification_provider.rb | 7 + .../lib/molinillo/dependency_graph.rb | 1 - .../lib/molinillo/dependency_graph/vertex.rb | 14 +- .../molinillo/lib/molinillo/errors.rb | 2 +- .../modules/specification_provider.rb | 11 + .../molinillo/lib/molinillo/resolution.rb | 18 +- lib/rubygems/specification.rb | 56 ++-- lib/rubygems/test_case.rb | 15 +- spec/bundler/bundler/cli_spec.rb | 4 +- .../compact_index_client/updater_spec.rb | 25 +- spec/bundler/bundler/dep_proxy_spec.rb | 22 +- spec/bundler/bundler/dsl_spec.rb | 15 +- spec/bundler/bundler/gem_helper_spec.rb | 11 +- .../bundler/gem_version_promoter_spec.rb | 4 +- spec/bundler/bundler/plugin_spec.rb | 1 + .../bundler/source/git/git_proxy_spec.rb | 8 +- spec/bundler/bundler/source/git_spec.rb | 4 +- .../bundler/stub_specification_spec.rb | 27 ++ spec/bundler/cache/git_spec.rb | 2 +- spec/bundler/commands/cache_spec.rb | 13 + spec/bundler/commands/exec_spec.rb | 131 ++++++-- spec/bundler/commands/lock_spec.rb | 69 +++++ spec/bundler/commands/newgem_spec.rb | 149 ++++++---- .../commands/post_bundle_message_spec.rb | 11 +- spec/bundler/commands/update_spec.rb | 13 +- spec/bundler/install/bundler_spec.rb | 74 +++-- spec/bundler/install/deploy_spec.rb | 20 +- spec/bundler/install/gemfile/gemspec_spec.rb | 15 +- spec/bundler/install/gemfile/git_spec.rb | 20 +- spec/bundler/install/gemfile/path_spec.rb | 69 ++++- spec/bundler/install/gemfile/platform_spec.rb | 67 +++-- spec/bundler/install/gemfile/sources_spec.rb | 280 +++++++++++++++--- .../install/gemfile/specific_platform_spec.rb | 20 -- spec/bundler/install/gems/resolving_spec.rb | 26 +- spec/bundler/install/gems/standalone_spec.rb | 39 +++ spec/bundler/install/gemspecs_spec.rb | 2 - spec/bundler/install/git_spec.rb | 21 +- spec/bundler/lock/lockfile_spec.rb | 2 +- spec/bundler/other/major_deprecation_spec.rb | 58 ++-- spec/bundler/quality_spec.rb | 2 +- spec/bundler/realworld/double_check_spec.rb | 2 +- spec/bundler/realworld/edgecases_spec.rb | 217 +++++++++++++- spec/bundler/realworld/parallel_spec.rb | 2 +- spec/bundler/realworld/slow_perf_spec.rb | 20 ++ spec/bundler/resolver/basic_spec.rb | 2 +- spec/bundler/resolver/platform_spec.rb | 168 +++++++++++ spec/bundler/runtime/inline_spec.rb | 4 - spec/bundler/runtime/platform_spec.rb | 110 ++++++- spec/bundler/runtime/setup_spec.rb | 40 +-- spec/bundler/spec_helper.rb | 3 +- spec/bundler/support/builders.rb | 2 + spec/bundler/support/filters.rb | 3 +- spec/bundler/support/helpers.rb | 2 - spec/bundler/support/indexes.rb | 4 +- spec/bundler/support/matchers.rb | 33 ++- .../support/rubygems_version_manager.rb | 1 + spec/bundler/support/sometimes.rb | 57 ---- .../null-required-ruby-version.gemspec.rz | Bin 0 -> 403 bytes test/rubygems/test_gem.rb | 93 +++++- .../test_gem_commands_outdated_command.rb | 18 ++ test/rubygems/test_gem_config_file.rb | 10 + .../rubygems/test_gem_dependency_installer.rb | 45 +-- test/rubygems/test_gem_ext_builder.rb | 24 ++ test/rubygems/test_gem_installer.rb | 2 + test/rubygems/test_gem_platform.rb | 37 +++ test/rubygems/test_gem_remote_fetcher.rb | 6 + test/rubygems/test_gem_requirement.rb | 36 ++- test/rubygems/test_gem_specification.rb | 59 ++-- test/rubygems/test_gem_stream_ui.rb | 2 +- test/rubygems/test_gem_util.rb | 8 +- 161 files changed, 2544 insertions(+), 1016 deletions(-) create mode 100644 lib/bundler/templates/newgem/CHANGELOG.md.tt create mode 100644 lib/rubygems/core_ext/tcpsocket_init.rb create mode 100644 spec/bundler/realworld/slow_perf_spec.rb delete mode 100644 spec/bundler/support/sometimes.rb create mode 100644 test/rubygems/data/null-required-ruby-version.gemspec.rz diff --git a/lib/bundler.rb b/lib/bundler.rb index c72ad27c40..d370d8a53a 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -63,7 +63,6 @@ module Bundler autoload :Resolver, File.expand_path("bundler/resolver", __dir__) autoload :Retry, File.expand_path("bundler/retry", __dir__) autoload :RubyDsl, File.expand_path("bundler/ruby_dsl", __dir__) - autoload :RubyGemsGemInstaller, File.expand_path("bundler/rubygems_gem_installer", __dir__) autoload :RubyVersion, File.expand_path("bundler/ruby_version", __dir__) autoload :Runtime, File.expand_path("bundler/runtime", __dir__) autoload :Settings, File.expand_path("bundler/settings", __dir__) @@ -441,7 +440,7 @@ EOF end def local_platform - return Gem::Platform::RUBY if settings[:force_ruby_platform] + return Gem::Platform::RUBY if settings[:force_ruby_platform] || Gem.platforms == [Gem::Platform::RUBY] Gem::Platform.local end diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 249f5b2027..421f42cb52 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -586,6 +586,7 @@ module Bundler method_option :git, :type => :boolean, :default => true, :desc => "Initialize a git repo inside your library." method_option :mit, :type => :boolean, :desc => "Generate an MIT license file. Set a default with `bundle config set --global gem.mit true`." method_option :rubocop, :type => :boolean, :desc => "Add rubocop to the generated Rakefile and gemspec. Set a default with `bundle config set --global gem.rubocop true`." + method_option :changelog, :type => :boolean, :desc => "Generate changelog file. Set a default with `bundle config set --global gem.changelog true`." method_option :test, :type => :string, :lazy_default => Bundler.settings["gem.test"] || "", :aliases => "-t", :banner => "Use the specified test framework for your library", :desc => "Generate a test directory for your library, either rspec, minitest or test-unit. Set a default with `bundle config set --global gem.test (rspec|minitest|test-unit)`." method_option :ci, :type => :string, :lazy_default => Bundler.settings["gem.ci"] || "", diff --git a/lib/bundler/cli/cache.rb b/lib/bundler/cli/cache.rb index c14c8877f0..9cd6133879 100644 --- a/lib/bundler/cli/cache.rb +++ b/lib/bundler/cli/cache.rb @@ -30,6 +30,7 @@ module Bundler require_relative "install" options = self.options.dup options["local"] = false if Bundler.settings[:cache_all_platforms] + options["no-cache"] = true Bundler::CLI::Install.new(options).run end diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb index 6ef1473b1e..5b3d9c332e 100644 --- a/lib/bundler/cli/gem.rb +++ b/lib/bundler/cli/gem.rb @@ -39,11 +39,11 @@ module Bundler constant_name = name.gsub(/-[_-]*(?![_-]|$)/) { "::" }.gsub(/([_-]+|(::)|^)(.|$)/) { $2.to_s + $3.upcase } constant_array = constant_name.split("::") - git_installed = Bundler.git_present? + use_git = Bundler.git_present? && options[:git] - git_author_name = git_installed ? `git config user.name`.chomp : "" - github_username = git_installed ? `git config github.user`.chomp : "" - git_user_email = git_installed ? `git config user.email`.chomp : "" + git_author_name = use_git ? `git config user.name`.chomp : "" + github_username = use_git ? `git config github.user`.chomp : "" + git_user_email = use_git ? `git config user.email`.chomp : "" config = { :name => name, @@ -58,7 +58,9 @@ module Bundler :ext => options[:ext], :exe => options[:exe], :bundler_version => bundler_dependency_version, + :git => use_git, :github_username => github_username.empty? ? "[USERNAME]" : github_username, + :required_ruby_version => Gem.ruby_version < Gem::Version.new("2.4.a") ? "2.3.0" : "2.4.0", } ensure_safe_gem_name(name, constant_array) @@ -78,7 +80,7 @@ module Bundler bin/setup ] - templates.merge!("gitignore.tt" => ".gitignore") if Bundler.git_present? + templates.merge!("gitignore.tt" => ".gitignore") if use_git if test_framework = ask_and_set_test_framework config[:test] = test_framework @@ -141,12 +143,25 @@ module Bundler templates.merge!("CODE_OF_CONDUCT.md.tt" => "CODE_OF_CONDUCT.md") end + if ask_and_set(:changelog, "Do you want to include a changelog?", + "A changelog is a file which contains a curated, chronologically ordered list of notable " \ + "changes for each version of a project. To make it easier for users and contributors to" \ + " see precisely what notable changes have been made between each release (or version) of" \ + " the project. Whether consumers or developers, the end users of software are" \ + " human beings who care about what's in the software. When the software changes, people " \ + "want to know why and how. see https://keepachangelog.com") + config[:changelog] = true + Bundler.ui.info "Changelog enabled in config" + templates.merge!("CHANGELOG.md.tt" => "CHANGELOG.md") + end + if ask_and_set(:rubocop, "Do you want to add rubocop as a dependency for gems you generate?", "RuboCop is a static code analyzer that has out-of-the-box rules for many " \ "of the guidelines in the community style guide. " \ "For more information, see the RuboCop docs (https://docs.rubocop.org/en/stable/) " \ "and the Ruby Style Guides (https://github.com/rubocop-hq/ruby-style-guide).") config[:rubocop] = true + config[:rubocop_version] = Gem.ruby_version < Gem::Version.new("2.4.a") ? "0.81.0" : "1.7" Bundler.ui.info "RuboCop enabled in config" templates.merge!("rubocop.yml.tt" => ".rubocop.yml") end @@ -161,24 +176,31 @@ module Bundler ) end + if File.exist?(target) && !File.directory?(target) + Bundler.ui.error "Couldn't create a new gem named `#{gem_name}` because there's an existing file named `#{gem_name}`." + exit Bundler::BundlerError.all_errors[Bundler::GenericSystemCallError] + end + + if use_git + Bundler.ui.info "Initializing git repo in #{target}" + `git init #{target}` + + config[:git_default_branch] = File.read("#{target}/.git/HEAD").split("/").last.chomp + end + templates.each do |src, dst| destination = target.join(dst) - SharedHelpers.filesystem_access(destination) do - thor.template("newgem/#{src}", destination, config) - end + thor.template("newgem/#{src}", destination, config) end executables.each do |file| - SharedHelpers.filesystem_access(target.join(file)) do |path| - executable = (path.stat.mode | 0o111) - path.chmod(executable) - end + path = target.join(file) + executable = (path.stat.mode | 0o111) + path.chmod(executable) end - if Bundler.git_present? && options[:git] - Bundler.ui.info "Initializing git repo in #{target}" + if use_git Dir.chdir(target) do - `git init` `git add .` end end @@ -188,8 +210,6 @@ module Bundler Bundler.ui.info "Gem '#{name}' was successfully created. " \ "For more information on making a RubyGem visit https://bundler.io/guides/creating_gem.html" - rescue Errno::EEXIST => e - raise GenericSystemCallError.new(e, "There was a conflict while creating the new gem.") end private diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb index 66d1735583..9e0180fac7 100644 --- a/lib/bundler/compact_index_client/updater.rb +++ b/lib/bundler/compact_index_client/updater.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true require_relative "../vendored_fileutils" -require "stringio" -require "zlib" module Bundler class CompactIndexClient @@ -45,24 +43,18 @@ module Bundler else "bytes=#{local_temp_path.size}-" end - else - # Fastly ignores Range when Accept-Encoding: gzip is set - headers["Accept-Encoding"] = "gzip" end response = @fetcher.call(remote_path, headers) return nil if response.is_a?(Net::HTTPNotModified) content = response.body - if response["Content-Encoding"] == "gzip" - content = Zlib::GzipReader.new(StringIO.new(content)).read - end SharedHelpers.filesystem_access(local_temp_path) do if response.is_a?(Net::HTTPPartialContent) && local_temp_path.size.nonzero? local_temp_path.open("a") {|f| f << slice_body(content, 1..-1) } else - local_temp_path.open("w") {|f| f << content } + local_temp_path.open("wb") {|f| f << content } end end diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 349fa632a0..5f02b15e5f 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require_relative "lockfile_parser" -require "set" module Bundler class Definition @@ -88,11 +87,7 @@ module Bundler @lockfile_contents = Bundler.read_file(lockfile) @locked_gems = LockfileParser.new(@lockfile_contents) @locked_platforms = @locked_gems.platforms - if Bundler.settings[:force_ruby_platform] - @platforms = [Gem::Platform::RUBY] - else - @platforms = @locked_platforms.dup - end + @platforms = @locked_platforms.dup @locked_bundler_version = @locked_gems.bundler_version @locked_ruby_version = @locked_gems.ruby_version @@ -264,23 +259,18 @@ module Bundler def resolve @resolve ||= begin last_resolve = converge_locked_specs - resolve = - if Bundler.frozen_bundle? - Bundler.ui.debug "Frozen, using resolution from the lockfile" - last_resolve - elsif !unlocking? && nothing_changed? - Bundler.ui.debug("Found no changes, using resolution from the lockfile") - last_resolve - else - # Run a resolve against the locally available gems - Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}") - expanded_dependencies = expand_dependencies(dependencies + metadata_dependencies, @remote) - last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms) - end - - # filter out gems that _can_ be installed on multiple platforms, but don't need - # to be - resolve.for(expand_dependencies(dependencies, true), [], false, false, false) + if Bundler.frozen_bundle? + Bundler.ui.debug "Frozen, using resolution from the lockfile" + last_resolve + elsif !unlocking? && nothing_changed? + Bundler.ui.debug("Found no changes, using resolution from the lockfile") + last_resolve + else + # Run a resolve against the locally available gems + Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}") + expanded_dependencies = expand_dependencies(dependencies + metadata_dependencies, @remote) + Resolver.resolve(expanded_dependencies, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms) + end end end @@ -611,7 +601,7 @@ module Bundler deps_for_source = @dependencies.select {|s| s.source == source } locked_deps_for_source = @locked_deps.values.select {|dep| dep.source == locked_source } - Set.new(deps_for_source) != Set.new(locked_deps_for_source) + deps_for_source.uniq.sort != locked_deps_for_source.sort end def specs_for_source_changed?(source) @@ -673,19 +663,20 @@ module Bundler def converge_rubygems_sources return false if Bundler.feature_flag.disable_multisource? - changes = false - # Get the RubyGems sources from the Gemfile.lock locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) } + return false if locked_gem_sources.empty? + # Get the RubyGems remotes from the Gemfile actual_remotes = sources.rubygems_remotes + return false if actual_remotes.empty? + + changes = false # If there is a RubyGems source in both - if !locked_gem_sources.empty? && !actual_remotes.empty? - locked_gem_sources.each do |locked_gem| - # Merge the remotes from the Gemfile into the Gemfile.lock - changes |= locked_gem.replace_remotes(actual_remotes, Bundler.settings[:allow_deployment_source_credential_changes]) - end + locked_gem_sources.each do |locked_gem| + # Merge the remotes from the Gemfile into the Gemfile.lock + changes |= locked_gem.replace_remotes(actual_remotes, Bundler.settings[:allow_deployment_source_credential_changes]) end changes @@ -825,11 +816,6 @@ module Bundler # commonly happens if the version changed in the gemspec next unless new_spec - new_runtime_deps = new_spec.dependencies.select {|d| d.type != :development } - old_runtime_deps = s.dependencies.select {|d| d.type != :development } - # If the dependencies of the path source have changed and locked spec can't satisfy new dependencies, unlock it - next unless new_runtime_deps.sort == old_runtime_deps.sort || new_runtime_deps.all? {|d| satisfies_locked_spec?(d) } - s.dependencies.replace(new_spec.dependencies) end @@ -896,7 +882,7 @@ module Bundler dependencies.each do |dep| dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name) next unless remote || dep.current_platform? - target_platforms = dep.gem_platforms(remote ? Resolver.sort_platforms(@platforms) : [generic_local_platform]) + target_platforms = dep.gem_platforms(remote ? @platforms : [generic_local_platform]) deps += expand_dependency_with_platforms(dep, target_platforms) end deps @@ -904,7 +890,7 @@ module Bundler def expand_dependency_with_platforms(dep, platforms) platforms.map do |p| - DepProxy.new(dep, p) + DepProxy.get_proxy(dep, p) end end @@ -915,29 +901,18 @@ module Bundler # Record the specs available in each gem's source, so that those # specs will be available later when the resolver knows where to # look for that gemspec (or its dependencies) - default = sources.default_source - source_requirements = { :default => default } - default = nil unless Bundler.feature_flag.disable_multisource? - dependencies.each do |dep| - next unless source = dep.source || default - source_requirements[dep.name] = source - end + source_requirements = { :default => sources.default_source }.merge(dependency_source_requirements) metadata_dependencies.each do |dep| source_requirements[dep.name] = sources.metadata_source end + source_requirements[:global] = index unless Bundler.feature_flag.disable_multisource? + source_requirements[:default_bundler] = source_requirements["bundler"] || source_requirements[:default] source_requirements["bundler"] = sources.metadata_source # needs to come last to override source_requirements end def pinned_spec_names(skip = nil) - pinned_names = [] - default = Bundler.feature_flag.disable_multisource? && sources.default_source - @dependencies.each do |dep| - next unless dep_source = dep.source || default - next if dep_source == skip - pinned_names << dep.name - end - pinned_names + dependency_source_requirements.reject {|_, source| source == skip }.keys end def requested_groups @@ -984,7 +959,7 @@ module Bundler next requirements if @locked_gems.dependencies[name] != dependency next requirements if dependency.source.is_a?(Source::Path) dep = Gem::Dependency.new(name, ">= #{locked_spec.version}") - requirements[name] = DepProxy.new(dep, locked_spec.platform) + requirements[name] = DepProxy.get_proxy(dep, locked_spec.platform) requirements end.values end @@ -994,5 +969,17 @@ module Bundler Bundler.settings[:allow_deployment_source_credential_changes] && source.equivalent_remotes?(sources.rubygems_remotes) end + + def dependency_source_requirements + @dependency_source_requirements ||= begin + source_requirements = {} + default = sources.default_source + dependencies.each do |dep| + dep_source = dep.source || default + source_requirements[dep.name] = dep_source + end + source_requirements + end + end end end diff --git a/lib/bundler/dep_proxy.rb b/lib/bundler/dep_proxy.rb index bb09fe9ea6..a32dc37b49 100644 --- a/lib/bundler/dep_proxy.rb +++ b/lib/bundler/dep_proxy.rb @@ -4,19 +4,18 @@ module Bundler class DepProxy attr_reader :__platform, :dep + @proxies = {} + + def self.get_proxy(dep, platform) + @proxies[[dep, platform]] ||= new(dep, platform).freeze + end + def initialize(dep, platform) @dep = dep @__platform = platform end - def hash - @hash ||= [dep, __platform].hash - end - - def ==(other) - return false if other.class != self.class - dep == other.dep && __platform == other.__platform - end + private_class_method :new alias_method :eql?, :== @@ -39,6 +38,14 @@ module Bundler s end + def dup + raise NoMethodError.new("DepProxy cannot be duplicated") + end + + def clone + raise NoMethodError.new("DepProxy cannot be cloned") + end + private def method_missing(*args, &blk) diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index 1cc7908b8a..23fba99ffa 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -24,6 +24,9 @@ module Bundler def initialize @source = nil @sources = SourceList.new + + @global_rubygems_sources = [] + @git_sources = {} @dependencies = [] @groups = [] @@ -45,6 +48,7 @@ module Bundler @gemfiles << expanded_gemfile_path contents ||= Bundler.read_file(@gemfile.to_s) instance_eval(contents.dup.tap{|x| x.untaint if RUBY_VERSION < "2.7" }, gemfile.to_s, 1) + check_primary_source_safety rescue Exception => e # rubocop:disable Lint/RescueException message = "There was an error " \ "#{e.is_a?(GemfileEvalError) ? "evaluating" : "parsing"} " \ @@ -164,8 +168,7 @@ module Bundler elsif block_given? with_source(@sources.add_rubygems_source("remotes" => source), &blk) else - check_primary_source_safety(@sources) - @sources.global_rubygems_source = source + @global_rubygems_sources << source end end @@ -183,24 +186,14 @@ module Bundler end def path(path, options = {}, &blk) - unless block_given? - msg = "You can no longer specify a path source by itself. Instead, \n" \ - "either use the :path option on a gem, or specify the gems that \n" \ - "bundler should find in the path source by passing a block to \n" \ - "the path method, like: \n\n" \ - " path 'dir/containing/rails' do\n" \ - " gem 'rails'\n" \ - " end\n\n" - - raise DeprecatedError, msg if Bundler.feature_flag.disable_multisource? - SharedHelpers.major_deprecation(2, msg.strip) - end - source_options = normalize_hash(options).merge( "path" => Pathname.new(path), "root_path" => gemfile_root, "gemspec" => gemspecs.find {|g| g.name == options["name"] } ) + + source_options["global"] = true unless block_given? + source = @sources.add_path_source(source_options) with_source(source, &blk) end @@ -279,6 +272,11 @@ module Bundler raise GemfileError, "Undefined local variable or method `#{name}' for Gemfile" end + def check_primary_source_safety + check_path_source_safety + check_rubygems_source_safety + end + private def add_git_sources @@ -440,17 +438,33 @@ repo_name ||= user_name end end - def check_primary_source_safety(source_list) - return if source_list.rubygems_primary_remotes.empty? && source_list.global_rubygems_source.nil? + def check_path_source_safety + return if @sources.global_path_source.nil? + + msg = "You can no longer specify a path source by itself. Instead, \n" \ + "either use the :path option on a gem, or specify the gems that \n" \ + "bundler should find in the path source by passing a block to \n" \ + "the path method, like: \n\n" \ + " path 'dir/containing/rails' do\n" \ + " gem 'rails'\n" \ + " end\n\n" + + SharedHelpers.major_deprecation(2, msg.strip) + end + + def check_rubygems_source_safety + @sources.global_rubygems_source = @global_rubygems_sources.shift + return if @global_rubygems_sources.empty? + + @global_rubygems_sources.each do |source| + @sources.add_rubygems_remote(source) + end if Bundler.feature_flag.disable_multisource? msg = "This Gemfile contains multiple primary sources. " \ "Each source after the first must include a block to indicate which gems " \ - "should come from that source" - unless Bundler.feature_flag.bundler_2_mode? - msg += ". To downgrade this error to a warning, run " \ - "`bundle config unset disable_multisource`" - end + "should come from that source. To downgrade this error to a warning, run " \ + "`bundle config unset disable_multisource`" raise GemfileEvalError, msg else Bundler::SharedHelpers.major_deprecation 2, "Your Gemfile contains multiple primary sources. " \ diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb index a92ec8756d..a1b443b042 100644 --- a/lib/bundler/feature_flag.rb +++ b/lib/bundler/feature_flag.rb @@ -27,7 +27,6 @@ module Bundler (1..10).each {|v| define_method("bundler_#{v}_mode?") { major_version >= v } } - settings_flag(:allow_bundler_dependency_conflicts) { bundler_3_mode? } settings_flag(:allow_offline_install) { bundler_3_mode? } settings_flag(:auto_clean_without_path) { bundler_3_mode? } settings_flag(:cache_all) { bundler_3_mode? } diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb index 2d9b4c2945..f9c54f7ad2 100644 --- a/lib/bundler/fetcher.rb +++ b/lib/bundler/fetcher.rb @@ -137,7 +137,6 @@ module Bundler end specs.each do |name, version, platform, dependencies, metadata| - next if name == "bundler" spec = if dependencies EndpointSpecification.new(name, version, platform, dependencies, metadata) else diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb index 0acaa69f25..d3e30124f9 100644 --- a/lib/bundler/gem_helper.rb +++ b/lib/bundler/gem_helper.rb @@ -116,19 +116,21 @@ module Bundler def git_push(remote = nil) remote ||= default_remote - perform_git_push remote + perform_git_push "#{remote} refs/heads/#{current_branch}" perform_git_push "#{remote} refs/tags/#{version_tag}" Bundler.ui.confirm "Pushed git commits and release tag." end def default_remote + remote_for_branch, status = sh_with_status(%W[git config --get branch.#{current_branch}.remote]) + return "origin" unless status.success? + + remote_for_branch.strip + end + + def current_branch # We can replace this with `git branch --show-current` once we drop support for git < 2.22.0 - current_branch = sh(%w[git rev-parse --abbrev-ref HEAD]).gsub(%r{\Aheads/}, "").strip - - remote_for_branch = sh(%W[git config --get branch.#{current_branch}.remote]).strip - return "origin" if remote_for_branch.empty? - - remote_for_branch + sh(%w[git rev-parse --abbrev-ref HEAD]).gsub(%r{\Aheads/}, "").strip end def allowed_push_host diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb index 2e87b7d443..3cce3f2139 100644 --- a/lib/bundler/gem_version_promoter.rb +++ b/lib/bundler/gem_version_promoter.rb @@ -81,8 +81,8 @@ module Bundler sort_dep_specs(spec_groups, locked_spec) end.tap do |specs| if DEBUG - warn before_result - warn " after sort_versions: #{debug_format_result(dep, specs).inspect}" + puts before_result + puts " after sort_versions: #{debug_format_result(dep, specs).inspect}" end end end diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb index f26f542fde..f945176037 100644 --- a/lib/bundler/index.rb +++ b/lib/bundler/index.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require "set" - module Bundler class Index include Enumerable @@ -65,11 +63,14 @@ module Bundler def unsorted_search(query, base) results = local_search(query, base) - seen = results.map(&:full_name).to_set unless @sources.empty? + seen = results.map(&:full_name).uniq unless @sources.empty? @sources.each do |source| source.unsorted_search(query, base).each do |spec| - results << spec if seen.add?(spec.full_name) + next if seen.include?(spec.full_name) + + seen << spec.full_name + results << spec end end @@ -170,7 +171,7 @@ module Bundler def dependencies_eql?(spec, other_spec) deps = spec.dependencies.select {|d| d.type != :development } other_deps = other_spec.dependencies.select {|d| d.type != :development } - Set.new(deps) == Set.new(other_deps) + deps.sort == other_deps.sort end def add_source(index) diff --git a/lib/bundler/inline.rb b/lib/bundler/inline.rb index 59211193d4..02da06cee9 100644 --- a/lib/bundler/inline.rb +++ b/lib/bundler/inline.rb @@ -50,6 +50,7 @@ def gemfile(install = false, options = {}, &gemfile) Bundler::Plugin.gemfile_install(&gemfile) if Bundler.feature_flag.plugins? builder = Bundler::Dsl.new builder.instance_eval(&gemfile) + builder.check_primary_source_safety Bundler.settings.temporary(:frozen => false) do definition = builder.to_definition(nil, true) diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index 048b0786a7..09c8b1c157 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -82,7 +82,6 @@ module Bundler if resolve_if_needed(options) ensure_specs_are_compatible! - warn_on_incompatible_bundler_deps load_plugins options.delete(:jobs) else @@ -90,6 +89,8 @@ module Bundler end install(options) + Gem::Specification.reset # invalidate gem specification cache so that installed gems are immediately available + lock unless Bundler.frozen_bundle? Standalone.new(options[:standalone], @definition).generate if options[:standalone] end @@ -265,22 +266,6 @@ module Bundler end end - def warn_on_incompatible_bundler_deps - bundler_version = Gem::Version.create(Bundler::VERSION) - @definition.specs.each do |spec| - spec.dependencies.each do |dep| - next if dep.type == :development - next unless dep.name == "bundler".freeze - next if dep.requirement.satisfied_by?(bundler_version) - - Bundler.ui.warn "#{spec.name} (#{spec.version}) has dependency" \ - " #{SharedHelpers.pretty_dependency(dep)}" \ - ", which is unsatisfied by the current bundler version #{VERSION}" \ - ", so the dependency is being ignored" - end - end - end - def install_in_parallel(size, standalone, force = false) spec_installations = ParallelInstaller.call(self, @definition.specs, size, standalone, force) spec_installations.each do |installation| diff --git a/lib/bundler/installer/standalone.rb b/lib/bundler/installer/standalone.rb index 0720d6d38a..2a3f5cfe35 100644 --- a/lib/bundler/installer/standalone.rb +++ b/lib/bundler/installer/standalone.rb @@ -15,6 +15,7 @@ module Bundler file.puts "ruby_engine = RUBY_ENGINE" file.puts "ruby_version = RbConfig::CONFIG[\"ruby_version\"]" file.puts "path = File.expand_path('..', __FILE__)" + file.puts reverse_rubygems_kernel_mixin paths.each do |path| file.puts %($:.unshift File.expand_path("\#{path}/#{path}")) end @@ -48,5 +49,19 @@ module Bundler error_message = "#{spec.name} #{spec.version} has an invalid gemspec" raise Gem::InvalidSpecificationException.new(error_message) end + + def reverse_rubygems_kernel_mixin + <<~END + kernel = (class << ::Kernel; self; end) + [kernel, ::Kernel].each do |k| + if k.private_method_defined?(:gem_original_require) + private_require = k.private_method_defined?(:require) + k.send(:remove_method, :require) + k.send(:define_method, :require, k.instance_method(:gem_original_require)) + k.send(:private, :require) if private_require + end + end + END + end end end diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index 1081910816..04ba2a2364 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -4,22 +4,6 @@ require_relative "match_platform" module Bundler class LazySpecification - Identifier = Struct.new(:name, :version, :platform) - class Identifier - include Comparable - def <=>(other) - return unless other.is_a?(Identifier) - [name, version, platform_string] <=> [other.name, other.version, other.platform_string] - end - - protected - - def platform_string - platform_string = platform.to_s - platform_string == Index::RUBY ? Index::NULL : platform_string - end - end - include MatchPlatform attr_reader :name, :version, :dependencies, :platform @@ -108,7 +92,7 @@ module Bundler end def identifier - @__identifier ||= Identifier.new(name, version, platform) + @__identifier ||= [name, version, platform_string] end def git_version @@ -116,6 +100,13 @@ module Bundler " #{source.revision[0..6]}" end + protected + + def platform_string + platform_string = platform.to_s + platform_string == Index::RUBY ? Index::NULL : platform_string + end + private def to_ary @@ -140,7 +131,7 @@ module Bundler # explicitly add a more specific platform. # def ruby_platform_materializes_to_ruby_platform? - !Bundler.most_specific_locked_platform?(Gem::Platform::RUBY) + !Bundler.most_specific_locked_platform?(Gem::Platform::RUBY) || Bundler.settings[:force_ruby_platform] end end end diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb index f836737621..058d353bbe 100644 --- a/lib/bundler/lockfile_parser.rb +++ b/lib/bundler/lockfile_parser.rb @@ -64,8 +64,6 @@ module Bundler @state = nil @specs = {} - @rubygems_aggregate = Source::Rubygems.new - if lockfile.match(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/) raise LockfileError, "Your #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} contains merge conflicts.\n" \ "Run `git checkout HEAD -- #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` first to get a clean lock." @@ -89,7 +87,6 @@ module Bundler send("parse_#{@state}", line) end end - @sources << @rubygems_aggregate unless Bundler.feature_flag.disable_multisource? @specs = @specs.values.sort_by(&:identifier) warn_for_outdated_bundler_version rescue ArgumentError => e @@ -134,16 +131,19 @@ module Bundler @sources << @current_source end when GEM - if Bundler.feature_flag.disable_multisource? + source_remotes = Array(@opts["remote"]) + + if source_remotes.size == 1 @opts["remotes"] = @opts.delete("remote") @current_source = TYPES[@type].from_lock(@opts) - @sources << @current_source else - Array(@opts["remote"]).each do |url| - @rubygems_aggregate.add_remote(url) + source_remotes.each do |url| + rubygems_aggregate.add_remote(url) end - @current_source = @rubygems_aggregate + @current_source = rubygems_aggregate end + + @sources << @current_source when PLUGIN @current_source = Plugin.source_from_lock(@opts) @sources << @current_source @@ -245,5 +245,9 @@ module Bundler def parse_ruby(line) @ruby_version = line.strip end + + def rubygems_aggregate + @rubygems_aggregate ||= Source::Rubygems.new + end end end diff --git a/lib/bundler/man/bundle-add.1 b/lib/bundler/man/bundle-add.1 index 29b2cadf33..b44295b2d3 100644 --- a/lib/bundler/man/bundle-add.1 +++ b/lib/bundler/man/bundle-add.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-ADD" "1" "December 2020" "" "" +.TH "BUNDLE\-ADD" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install diff --git a/lib/bundler/man/bundle-binstubs.1 b/lib/bundler/man/bundle-binstubs.1 index 6ba4ed914d..99876d023e 100644 --- a/lib/bundler/man/bundle-binstubs.1 +++ b/lib/bundler/man/bundle-binstubs.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-BINSTUBS" "1" "December 2020" "" "" +.TH "BUNDLE\-BINSTUBS" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-binstubs\fR \- Install the binstubs of the listed gems diff --git a/lib/bundler/man/bundle-cache.1 b/lib/bundler/man/bundle-cache.1 index 8aa45ca213..3b3c6f9734 100644 --- a/lib/bundler/man/bundle-cache.1 +++ b/lib/bundler/man/bundle-cache.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CACHE" "1" "December 2020" "" "" +.TH "BUNDLE\-CACHE" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application diff --git a/lib/bundler/man/bundle-check.1 b/lib/bundler/man/bundle-check.1 index 5013cd8cda..4436c3e971 100644 --- a/lib/bundler/man/bundle-check.1 +++ b/lib/bundler/man/bundle-check.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CHECK" "1" "December 2020" "" "" +.TH "BUNDLE\-CHECK" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems diff --git a/lib/bundler/man/bundle-clean.1 b/lib/bundler/man/bundle-clean.1 index 231ce273a8..b7e882ecbf 100644 --- a/lib/bundler/man/bundle-clean.1 +++ b/lib/bundler/man/bundle-clean.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CLEAN" "1" "December 2020" "" "" +.TH "BUNDLE\-CLEAN" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory diff --git a/lib/bundler/man/bundle-config.1 b/lib/bundler/man/bundle-config.1 index f690b0a8f0..ad36c6bde0 100644 --- a/lib/bundler/man/bundle-config.1 +++ b/lib/bundler/man/bundle-config.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CONFIG" "1" "December 2020" "" "" +.TH "BUNDLE\-CONFIG" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-config\fR \- Set bundler configuration options @@ -136,9 +136,6 @@ Any periods in the configuration keys must be replaced with two underscores when The following is a list of all configuration keys and their purpose\. You can learn more about their operation in bundle install(1) \fIbundle\-install\.1\.html\fR\. . .IP "\(bu" 4 -\fBallow_bundler_dependency_conflicts\fR (\fBBUNDLE_ALLOW_BUNDLER_DEPENDENCY_CONFLICTS\fR): Allow resolving to specifications that have dependencies on \fBbundler\fR that are incompatible with the running Bundler version\. -. -.IP "\(bu" 4 \fBallow_deployment_source_credential_changes\fR (\fBBUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES\fR): When in deployment mode, allow changing the credentials to a gem\'s source\. Ex: \fBhttps://some\.host\.com/gems/path/\fR \-> \fBhttps://user_name:password@some\.host\.com/gems/path\fR . .IP "\(bu" 4 @@ -184,6 +181,9 @@ The following is a list of all configuration keys and their purpose\. You can le \fBdisable_local_branch_check\fR (\fBBUNDLE_DISABLE_LOCAL_BRANCH_CHECK\fR): Allow Bundler to use a local git override without a branch specified in the Gemfile\. . .IP "\(bu" 4 +\fBdisable_local_revision_check\fR (\fBBUNDLE_DISABLE_LOCAL_REVISION_CHECK\fR): Allow Bundler to use a local git override without checking if the revision present in the lockfile is present in the repository\. +. +.IP "\(bu" 4 \fBdisable_multisource\fR (\fBBUNDLE_DISABLE_MULTISOURCE\fR): When set, Gemfiles containing multiple sources will produce errors instead of warnings\. Use \fBbundle config unset disable_multisource\fR to unset\. . .IP "\(bu" 4 diff --git a/lib/bundler/man/bundle-config.1.ronn b/lib/bundler/man/bundle-config.1.ronn index 7ca5dbed72..90ec5ab59e 100644 --- a/lib/bundler/man/bundle-config.1.ronn +++ b/lib/bundler/man/bundle-config.1.ronn @@ -133,9 +133,6 @@ the environment variable `BUNDLE_LOCAL__RACK`. The following is a list of all configuration keys and their purpose. You can learn more about their operation in [bundle install(1)](bundle-install.1.html). -* `allow_bundler_dependency_conflicts` (`BUNDLE_ALLOW_BUNDLER_DEPENDENCY_CONFLICTS`): - Allow resolving to specifications that have dependencies on `bundler` that - are incompatible with the running Bundler version. * `allow_deployment_source_credential_changes` (`BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES`): When in deployment mode, allow changing the credentials to a gem's source. Ex: `https://some.host.com/gems/path/` -> `https://user_name:password@some.host.com/gems/path` @@ -178,6 +175,9 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html). * `disable_local_branch_check` (`BUNDLE_DISABLE_LOCAL_BRANCH_CHECK`): Allow Bundler to use a local git override without a branch specified in the Gemfile. +* `disable_local_revision_check` (`BUNDLE_DISABLE_LOCAL_REVISION_CHECK`): + Allow Bundler to use a local git override without checking if the revision + present in the lockfile is present in the repository. * `disable_multisource` (`BUNDLE_DISABLE_MULTISOURCE`): When set, Gemfiles containing multiple sources will produce errors instead of warnings. diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1 index dd3471cfa9..2923517608 100644 --- a/lib/bundler/man/bundle-doctor.1 +++ b/lib/bundler/man/bundle-doctor.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-DOCTOR" "1" "December 2020" "" "" +.TH "BUNDLE\-DOCTOR" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-doctor\fR \- Checks the bundle for common problems diff --git a/lib/bundler/man/bundle-exec.1 b/lib/bundler/man/bundle-exec.1 index 374d2b11ed..aa0c509278 100644 --- a/lib/bundler/man/bundle-exec.1 +++ b/lib/bundler/man/bundle-exec.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-EXEC" "1" "December 2020" "" "" +.TH "BUNDLE\-EXEC" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-exec\fR \- Execute a command in the context of the bundle diff --git a/lib/bundler/man/bundle-gem.1 b/lib/bundler/man/bundle-gem.1 index 2ec18ecc65..c1abf90f5c 100644 --- a/lib/bundler/man/bundle-gem.1 +++ b/lib/bundler/man/bundle-gem.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-GEM" "1" "December 2020" "" "" +.TH "BUNDLE\-GEM" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem diff --git a/lib/bundler/man/bundle-info.1 b/lib/bundler/man/bundle-info.1 index e7386e3699..c715af4063 100644 --- a/lib/bundler/man/bundle-info.1 +++ b/lib/bundler/man/bundle-info.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-INFO" "1" "December 2020" "" "" +.TH "BUNDLE\-INFO" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-info\fR \- Show information for the given gem in your bundle diff --git a/lib/bundler/man/bundle-init.1 b/lib/bundler/man/bundle-init.1 index 756dc2c67c..6c8f441bdd 100644 --- a/lib/bundler/man/bundle-init.1 +++ b/lib/bundler/man/bundle-init.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-INIT" "1" "December 2020" "" "" +.TH "BUNDLE\-INIT" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-init\fR \- Generates a Gemfile into the current working directory diff --git a/lib/bundler/man/bundle-inject.1 b/lib/bundler/man/bundle-inject.1 index 4c66eaf78f..2342b44716 100644 --- a/lib/bundler/man/bundle-inject.1 +++ b/lib/bundler/man/bundle-inject.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-INJECT" "1" "December 2020" "" "" +.TH "BUNDLE\-INJECT" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-inject\fR \- Add named gem(s) with version requirements to Gemfile diff --git a/lib/bundler/man/bundle-install.1 b/lib/bundler/man/bundle-install.1 index 34dca1f0bc..c223185b6f 100644 --- a/lib/bundler/man/bundle-install.1 +++ b/lib/bundler/man/bundle-install.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-INSTALL" "1" "December 2020" "" "" +.TH "BUNDLE\-INSTALL" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-install\fR \- Install the dependencies specified in your Gemfile diff --git a/lib/bundler/man/bundle-list.1 b/lib/bundler/man/bundle-list.1 index ec507a566b..929b0f79f8 100644 --- a/lib/bundler/man/bundle-list.1 +++ b/lib/bundler/man/bundle-list.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-LIST" "1" "December 2020" "" "" +.TH "BUNDLE\-LIST" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-list\fR \- List all the gems in the bundle diff --git a/lib/bundler/man/bundle-lock.1 b/lib/bundler/man/bundle-lock.1 index 6f468db581..bcf588a3cf 100644 --- a/lib/bundler/man/bundle-lock.1 +++ b/lib/bundler/man/bundle-lock.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-LOCK" "1" "December 2020" "" "" +.TH "BUNDLE\-LOCK" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-lock\fR \- Creates / Updates a lockfile without installing diff --git a/lib/bundler/man/bundle-open.1 b/lib/bundler/man/bundle-open.1 index 7131bfad9f..27308ff624 100644 --- a/lib/bundler/man/bundle-open.1 +++ b/lib/bundler/man/bundle-open.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-OPEN" "1" "December 2020" "" "" +.TH "BUNDLE\-OPEN" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-open\fR \- Opens the source directory for a gem in your bundle diff --git a/lib/bundler/man/bundle-outdated.1 b/lib/bundler/man/bundle-outdated.1 index 8c6ce260c8..40b23568d5 100644 --- a/lib/bundler/man/bundle-outdated.1 +++ b/lib/bundler/man/bundle-outdated.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-OUTDATED" "1" "December 2020" "" "" +.TH "BUNDLE\-OUTDATED" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-outdated\fR \- List installed gems with newer versions available diff --git a/lib/bundler/man/bundle-platform.1 b/lib/bundler/man/bundle-platform.1 index cd8eeb29c0..d0ec5c643c 100644 --- a/lib/bundler/man/bundle-platform.1 +++ b/lib/bundler/man/bundle-platform.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-PLATFORM" "1" "December 2020" "" "" +.TH "BUNDLE\-PLATFORM" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-platform\fR \- Displays platform compatibility information diff --git a/lib/bundler/man/bundle-pristine.1 b/lib/bundler/man/bundle-pristine.1 index 4fc59626bd..9fd42d04a4 100644 --- a/lib/bundler/man/bundle-pristine.1 +++ b/lib/bundler/man/bundle-pristine.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-PRISTINE" "1" "December 2020" "" "" +.TH "BUNDLE\-PRISTINE" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-pristine\fR \- Restores installed gems to their pristine condition diff --git a/lib/bundler/man/bundle-remove.1 b/lib/bundler/man/bundle-remove.1 index 764b33527a..366e5f4be2 100644 --- a/lib/bundler/man/bundle-remove.1 +++ b/lib/bundler/man/bundle-remove.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-REMOVE" "1" "December 2020" "" "" +.TH "BUNDLE\-REMOVE" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-remove\fR \- Removes gems from the Gemfile diff --git a/lib/bundler/man/bundle-show.1 b/lib/bundler/man/bundle-show.1 index 25cbddf5fb..e2de241438 100644 --- a/lib/bundler/man/bundle-show.1 +++ b/lib/bundler/man/bundle-show.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-SHOW" "1" "December 2020" "" "" +.TH "BUNDLE\-SHOW" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem diff --git a/lib/bundler/man/bundle-update.1 b/lib/bundler/man/bundle-update.1 index a97692911c..a78633bbf8 100644 --- a/lib/bundler/man/bundle-update.1 +++ b/lib/bundler/man/bundle-update.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-UPDATE" "1" "December 2020" "" "" +.TH "BUNDLE\-UPDATE" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-update\fR \- Update your gems to the latest available versions diff --git a/lib/bundler/man/bundle-viz.1 b/lib/bundler/man/bundle-viz.1 index c84a0bd8b1..fead09e6ad 100644 --- a/lib/bundler/man/bundle-viz.1 +++ b/lib/bundler/man/bundle-viz.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-VIZ" "1" "December 2020" "" "" +.TH "BUNDLE\-VIZ" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\-viz\fR \- Generates a visual dependency graph for your Gemfile diff --git a/lib/bundler/man/bundle.1 b/lib/bundler/man/bundle.1 index 58043ff91d..50aa6e17c7 100644 --- a/lib/bundler/man/bundle.1 +++ b/lib/bundler/man/bundle.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE" "1" "December 2020" "" "" +.TH "BUNDLE" "1" "January 2021" "" "" . .SH "NAME" \fBbundle\fR \- Ruby Dependency Management diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5 index d6c26d91ef..460295da4d 100644 --- a/lib/bundler/man/gemfile.5 +++ b/lib/bundler/man/gemfile.5 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GEMFILE" "5" "December 2020" "" "" +.TH "GEMFILE" "5" "January 2021" "" "" . .SH "NAME" \fBGemfile\fR \- A format for describing gem dependencies for Ruby programs diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb index da3f468da5..dddb468582 100644 --- a/lib/bundler/plugin.rb +++ b/lib/bundler/plugin.rb @@ -105,6 +105,7 @@ module Bundler else builder.eval_gemfile(gemfile) end + builder.check_primary_source_safety definition = builder.to_definition(nil, true) return if definition.dependencies.empty? diff --git a/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb index 26cec4f18c..980b0e29b8 100644 --- a/lib/bundler/plugin/installer.rb +++ b/lib/bundler/plugin/installer.rb @@ -16,15 +16,13 @@ module Bundler version = options[:version] || [">= 0"] - Bundler.settings.temporary(:disable_multisource => false) do - if options[:git] - install_git(names, version, options) - elsif options[:local_git] - install_local_git(names, version, options) - else - sources = options[:source] || Bundler.rubygems.sources - install_rubygems(names, version, sources) - end + if options[:git] + install_git(names, version, options) + elsif options[:local_git] + install_local_git(names, version, options) + else + sources = options[:source] || Bundler.rubygems.sources + install_rubygems(names, version, sources) end end @@ -79,7 +77,7 @@ module Bundler source_list = SourceList.new source_list.add_git_source(git_source_options) if git_source_options - source_list.add_rubygems_source("remotes" => rubygems_source) if rubygems_source + source_list.global_rubygems_source = rubygems_source if rubygems_source deps = names.map {|name| Dependency.new name, version } diff --git a/lib/bundler/plugin/source_list.rb b/lib/bundler/plugin/source_list.rb index b90a331d28..547661cf2f 100644 --- a/lib/bundler/plugin/source_list.rb +++ b/lib/bundler/plugin/source_list.rb @@ -17,6 +17,10 @@ module Bundler path_sources + git_sources + rubygems_sources + [metadata_source] end + def default_source + git_sources.first || global_rubygems_source + end + private def rubygems_aggregate_class diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 636dc8af46..69899dc6c3 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -5,6 +5,8 @@ module Bundler require_relative "vendored_molinillo" require_relative "resolver/spec_group" + include GemHelpers + # Figures out the best possible configuration of gems that satisfies # the list of passed dependencies and any child dependencies without # causing any gem activation errors. @@ -15,31 +17,38 @@ module Bundler # ==== Returns # ,nil:: If the list of dependencies can be resolved, a # collection of gemspecs is returned. Otherwise, nil is returned. - def self.resolve(requirements, index, source_requirements = {}, base = [], gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [], platforms = nil) - platforms = Set.new(platforms) if platforms + def self.resolve(requirements, source_requirements = {}, base = [], gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [], platforms = nil) base = SpecSet.new(base) unless base.is_a?(SpecSet) - resolver = new(index, source_requirements, base, gem_version_promoter, additional_base_requirements, platforms) + resolver = new(source_requirements, base, gem_version_promoter, additional_base_requirements, platforms) result = resolver.start(requirements) SpecSet.new(result) end - def initialize(index, source_requirements, base, gem_version_promoter, additional_base_requirements, platforms) - @index = index + def initialize(source_requirements, base, gem_version_promoter, additional_base_requirements, platforms) @source_requirements = source_requirements + + @index_requirements = source_requirements.each_with_object({}) do |source_requirement, index_requirements| + name, source = source_requirement + index_requirements[name] = name == :global ? source : source.specs + end + @base = base @resolver = Molinillo::Resolver.new(self, self) @search_for = {} @base_dg = Molinillo::DependencyGraph.new @base.each do |ls| dep = Dependency.new(ls.name, ls.version) - @base_dg.add_vertex(ls.name, DepProxy.new(dep, ls.platform), true) + @base_dg.add_vertex(ls.name, DepProxy.get_proxy(dep, ls.platform), true) end additional_base_requirements.each {|d| @base_dg.add_vertex(d.name, d) } - @platforms = platforms + @platforms = platforms.reject {|p| p != Gem::Platform::RUBY && (platforms - [p]).any? {|pl| generic(pl) == p } } + @resolving_only_for_ruby = platforms == [Gem::Platform::RUBY] @gem_version_promoter = gem_version_promoter - @allow_bundler_dependency_conflicts = Bundler.feature_flag.allow_bundler_dependency_conflicts? @use_gvp = Bundler.feature_flag.use_gem_version_promoter_for_major_updates? || !@gem_version_promoter.major? - @lockfile_uses_separate_rubygems_sources = Bundler.feature_flag.disable_multisource? + @no_aggregate_global_source = @source_requirements[:global].nil? + + @variant_specific_names = [] + @generic_names = ["Ruby\0", "RubyGems\0"] end def start(requirements) @@ -75,7 +84,7 @@ module Bundler return unless debug? debug_info = yield debug_info = debug_info.inspect unless debug_info.is_a?(String) - puts debug_info.split("\n").map {|s| "BUNDLER: " + " " * depth + s } + puts debug_info.split("\n").map {|s| depth == 0 ? "BUNDLER: #{s}" : "BUNDLER(#{depth}): #{s}" } end def debug? @@ -103,16 +112,25 @@ module Bundler include Molinillo::SpecificationProvider def dependencies_for(specification) - specification.dependencies_for_activated_platforms + all_dependencies = specification.dependencies_for_activated_platforms + + if @variant_specific_names.include?(specification.name) + @variant_specific_names |= all_dependencies.map(&:name) - @generic_names + else + generic_names, variant_specific_names = specification.partitioned_dependency_names_for_activated_platforms + @variant_specific_names |= variant_specific_names - @generic_names + @generic_names |= generic_names + end + + all_dependencies end def search_for(dependency_proxy) platform = dependency_proxy.__platform dependency = dependency_proxy.dep - @search_for[dependency_proxy] ||= begin - name = dependency.name - index = index_for(dependency) - results = index.search(dependency, @base[name]) + name = dependency.name + search_result = @search_for[dependency_proxy] ||= begin + results = results_for(dependency, @base[name]) if vertex = @base_dg.vertex_named(name) locked_requirement = vertex.payload.requirement @@ -137,55 +155,67 @@ module Bundler end nested.reduce([]) do |groups, (version, specs)| next groups if locked_requirement && !locked_requirement.satisfied_by?(version) - spec_group = SpecGroup.new(specs) - spec_group.ignores_bundler_dependencies = @allow_bundler_dependency_conflicts - groups << spec_group + + specs_by_platform = Hash.new do |current_specs, current_platform| + current_specs[current_platform] = select_best_platform_match(specs, current_platform) + end + + spec_group_ruby = SpecGroup.create_for(specs_by_platform, [Gem::Platform::RUBY], Gem::Platform::RUBY) + groups << spec_group_ruby if spec_group_ruby + + next groups if @resolving_only_for_ruby + + spec_group = SpecGroup.create_for(specs_by_platform, @platforms, platform) + groups << spec_group if spec_group + + groups end else [] end # GVP handles major itself, but it's still a bit risky to trust it with it # until we get it settled with new behavior. For 2.x it can take over all cases. - search = if !@use_gvp + if !@use_gvp spec_groups else @gem_version_promoter.sort_versions(dependency, spec_groups) end - selected_sgs = [] - search.each do |sg| - next unless sg.for?(platform) - sg_all_platforms = sg.copy_for(self.class.sort_platforms(@platforms).reverse) - next unless sg_all_platforms - - selected_sgs << sg_all_platforms - - next if sg_all_platforms.activated_platforms == [Gem::Platform::RUBY] - # Add a spec group for "non platform specific spec" as the fallback - # spec group. - sg_ruby = sg.copy_for([Gem::Platform::RUBY]) - selected_sgs.insert(-2, sg_ruby) if sg_ruby - end - selected_sgs end + + unless search_result.empty? + specific_dependency = @variant_specific_names.include?(name) + return search_result unless specific_dependency + + search_result.each do |sg| + if @generic_names.include?(name) + @variant_specific_names -= [name] + sg.activate_all_platforms! + else + sg.activate_platform!(platform) + end + end + end + + search_result end def index_for(dependency) - source = @source_requirements[dependency.name] + source = @index_requirements[dependency.name] if source - source.specs - elsif @lockfile_uses_separate_rubygems_sources + source + elsif @no_aggregate_global_source Index.build do |idx| - if dependency.all_sources - dependency.all_sources.each {|s| idx.add_source(s.specs) if s } - else - idx.add_source @source_requirements[:default].specs - end + dependency.all_sources.each {|s| idx.add_source(s.specs) } end else - @index + @index_requirements[:global] end end + def results_for(dependency, base) + index_for(dependency).search(dependency, base) + end + def name_for(dependency) dependency.name end @@ -206,21 +236,27 @@ module Bundler requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec) end + def dependencies_equal?(dependencies, other_dependencies) + dependencies.map(&:dep) == other_dependencies.map(&:dep) + end + def relevant_sources_for_vertex(vertex) if vertex.root? [@source_requirements[vertex.name]] - elsif @lockfile_uses_separate_rubygems_sources + elsif @no_aggregate_global_source vertex.recursive_predecessors.map do |v| @source_requirements[v.name] - end << @source_requirements[:default] + end.compact << @source_requirements[:default] + else + [] end end def sort_dependencies(dependencies, activated, conflicts) dependencies.sort_by do |dependency| - dependency.all_sources = relevant_sources_for_vertex(activated.vertex_named(dependency.name)) name = name_for(dependency) vertex = activated.vertex_named(name) + dependency.all_sources = relevant_sources_for_vertex(vertex) [ @base_dg.vertex_named(name) ? 0 : 1, vertex.payload ? 0 : 1, @@ -233,13 +269,6 @@ module Bundler end end - # Sort platforms from most general to most specific - def self.sort_platforms(platforms) - platforms.sort_by do |platform| - platform_sort_key(platform) - end - end - def self.platform_sort_key(platform) # Prefer specific platform to not specific platform return ["99-LAST", "", "", ""] if Gem::Platform::RUBY == platform @@ -294,7 +323,7 @@ module Bundler "If you are updating multiple gems in your Gemfile at once,\n" \ "try passing them all to `bundle update`" elsif source = @source_requirements[name] - specs = source.specs[name] + specs = source.specs.search(name) versions_with_platforms = specs.map {|s| [s.version, s.platform] } message = String.new("Could not find gem '#{SharedHelpers.pretty_dependency(requirement)}' in #{source}#{cache_message}.\n") message << if versions_with_platforms.any? @@ -303,7 +332,7 @@ module Bundler "The source does not contain any versions of '#{name}'" end else - message = "Could not find gem '#{requirement}' in any of the gem sources " \ + message = "Could not find gem '#{SharedHelpers.pretty_dependency(requirement)}' in any of the gem sources " \ "listed in your Gemfile#{cache_message}." end raise GemNotFound, message @@ -324,10 +353,16 @@ module Bundler def version_conflict_message(e) # only show essential conflicts, if possible conflicts = e.conflicts.dup - conflicts.delete_if do |_name, conflict| - deps = conflict.requirement_trees.map(&:last).flatten(1) - !Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement))) + + if conflicts["bundler"] + conflicts.replace("bundler" => conflicts["bundler"]) + else + conflicts.delete_if do |_name, conflict| + deps = conflict.requirement_trees.map(&:last).flatten(1) + !Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement))) + end end + e = Molinillo::VersionConflict.new(conflicts, e.specification_provider) unless conflicts.empty? solver_name = "Bundler" @@ -355,15 +390,25 @@ module Bundler :additional_message_for_conflict => lambda do |o, name, conflict| if name == "bundler" o << %(\n Current Bundler version:\n bundler (#{Bundler::VERSION})) - other_bundler_required = !conflict.requirement.requirement.satisfied_by?(Gem::Version.new(Bundler::VERSION)) - end - if name == "bundler" && other_bundler_required - o << "\n" - o << "This Gemfile requires a different version of Bundler.\n" - o << "Perhaps you need to update Bundler by running `gem install bundler`?\n" - end - if conflict.locked_requirement + conflict_dependency = conflict.requirement + conflict_requirement = conflict_dependency.requirement + other_bundler_required = !conflict_requirement.satisfied_by?(Gem::Version.new(Bundler::VERSION)) + + if other_bundler_required + o << "\n\n" + + candidate_specs = @index_requirements[:default_bundler].search(conflict_dependency) + if candidate_specs.any? + target_version = candidate_specs.last.version + new_command = [File.basename($PROGRAM_NAME), "_#{target_version}_", *ARGV].join(" ") + o << "Your bundle requires a different version of Bundler than the one you're running.\n" + o << "Install the necessary version with `gem install bundler:#{target_version}` and rerun bundler using `#{new_command}`\n" + else + o << "Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.\n" + end + end + elsif conflict.locked_requirement o << "\n" o << %(Running `bundle update` will rebuild your snapshot from scratch, using only\n) o << %(the gems in your Gemfile, which may resolve the conflict.\n) @@ -372,14 +417,8 @@ module Bundler relevant_sources = if conflict.requirement.source [conflict.requirement.source] - elsif conflict.requirement.all_sources - conflict.requirement.all_sources - elsif @lockfile_uses_separate_rubygems_sources - # every conflict should have an explicit group of sources when we - # enforce strict pinning - raise "no source set for #{conflict}" else - [] + conflict.requirement.all_sources end.compact.map(&:to_s).uniq.sort metadata_requirement = name.end_with?("\0") @@ -416,23 +455,21 @@ module Bundler def validate_resolved_specs!(resolved_specs) resolved_specs.each do |v| name = v.name - next unless sources = relevant_sources_for_vertex(v) - sources.compact! + sources = relevant_sources_for_vertex(v) + next unless sources.any? if default_index = sources.index(@source_requirements[:default]) sources.delete_at(default_index) end - sources.reject! {|s| s.specs[name].empty? } + sources.reject! {|s| s.specs.search(name).empty? } sources.uniq! next if sources.size <= 1 - multisource_disabled = Bundler.feature_flag.disable_multisource? - msg = ["The gem '#{name}' was found in multiple relevant sources."] msg.concat sources.map {|s| " * #{s}" }.sort - msg << "You #{multisource_disabled ? :must : :should} add this gem to the source block for the source you wish it to be installed from." + msg << "You #{@no_aggregate_global_source ? :must : :should} add this gem to the source block for the source you wish it to be installed from." msg = msg.join("\n") - raise SecurityError, msg if multisource_disabled + raise SecurityError, msg if @no_aggregate_global_source Bundler.ui.warn "Warning: #{msg}" end end diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index 34780f9528..73ffec5838 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -3,28 +3,37 @@ module Bundler class Resolver class SpecGroup - include GemHelpers - attr_accessor :name, :version, :source - attr_accessor :ignores_bundler_dependencies, :activated_platforms + attr_accessor :activated_platforms - def initialize(all_specs) - @all_specs = all_specs - raise ArgumentError, "cannot initialize with an empty value" unless exemplary_spec = all_specs.first + def self.create_for(specs, all_platforms, specific_platform) + specific_platform_specs = specs[specific_platform] + return unless specific_platform_specs.any? + + platforms = all_platforms.select {|p| specs[p].any? } + + new(specific_platform_specs.first, specs, platforms) + end + + def initialize(exemplary_spec, specs, relevant_platforms) + @exemplary_spec = exemplary_spec @name = exemplary_spec.name @version = exemplary_spec.version @source = exemplary_spec.source - @activated_platforms = [] - @dependencies = nil - @specs = Hash.new do |specs, platform| - specs[platform] = select_best_platform_match(all_specs, platform) + @all_platforms = relevant_platforms + @activated_platforms = relevant_platforms + @dependencies = Hash.new do |dependencies, platforms| + dependencies[platforms] = dependencies_for(platforms) end - @ignores_bundler_dependencies = true + @partitioned_dependency_names = Hash.new do |partitioned_dependency_names, platforms| + partitioned_dependency_names[platforms] = partitioned_dependency_names_for(platforms) + end + @specs = specs end def to_specs - @activated_platforms.map do |p| + activated_platforms.map do |p| specs = @specs[p] next unless specs.any? @@ -36,18 +45,12 @@ module Bundler end.flatten.compact.uniq end - def copy_for(platforms) - platforms.select! {|p| for?(p) } - return unless platforms.any? - - copied_sg = self.class.new(@all_specs) - copied_sg.ignores_bundler_dependencies = @ignores_bundler_dependencies - copied_sg.activated_platforms = platforms - copied_sg + def activate_platform!(platform) + self.activated_platforms = [platform] end - def for?(platform) - @specs[platform].any? + def activate_all_platforms! + self.activated_platforms = @all_platforms end def to_s @@ -56,11 +59,11 @@ module Bundler end def dependencies_for_activated_platforms - dependencies = @activated_platforms.map {|p| __dependencies[p] } - metadata_dependencies = @activated_platforms.map do |platform| - metadata_dependencies(@specs[platform].first, platform) - end - dependencies.concat(metadata_dependencies).flatten + @dependencies[activated_platforms] + end + + def partitioned_dependency_names_for_activated_platforms + @partitioned_dependency_names[activated_platforms] end def ==(other) @@ -86,34 +89,43 @@ module Bundler protected def sorted_activated_platforms - @activated_platforms.sort_by(&:to_s) + activated_platforms.sort_by(&:to_s) end private - def __dependencies - @dependencies = Hash.new do |dependencies, platform| - dependencies[platform] = [] - specs = @specs[platform] - if spec = specs.first - spec.dependencies.each do |dep| - next if dep.type == :development - next if @ignores_bundler_dependencies && dep.name == "bundler".freeze - dependencies[platform] << DepProxy.new(dep, platform) - end - end - dependencies[platform] - end + def dependencies_for(platforms) + platforms.map do |platform| + __dependencies(platform) + metadata_dependencies(platform) + end.flatten end - def metadata_dependencies(spec, platform) - return [] unless spec && spec.is_a?(Gem::Specification) + def partitioned_dependency_names_for(platforms) + return @dependencies[platforms].map(&:name), [] if platforms.size == 1 + + @dependencies[platforms].partition do |dep_proxy| + @dependencies[platforms].count {|dp| dp.dep == dep_proxy.dep } == platforms.size + end.map {|deps| deps.map(&:name) } + end + + def __dependencies(platform) + dependencies = [] + @specs[platform].first.dependencies.each do |dep| + next if dep.type == :development + dependencies << DepProxy.get_proxy(dep, platform) + end + dependencies + end + + def metadata_dependencies(platform) + spec = @specs[platform].first + return [] unless spec.is_a?(Gem::Specification) dependencies = [] if !spec.required_ruby_version.nil? && !spec.required_ruby_version.none? - dependencies << DepProxy.new(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform) + dependencies << DepProxy.get_proxy(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform) end if !spec.required_rubygems_version.nil? && !spec.required_rubygems_version.none? - dependencies << DepProxy.new(Gem::Dependency.new("RubyGems\0", spec.required_rubygems_version), platform) + dependencies << DepProxy.get_proxy(Gem::Dependency.new("RubyGems\0", spec.required_rubygems_version), platform) end dependencies end diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index 0322b06d07..2bd2dcb451 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -158,6 +158,22 @@ module Gem end end + if Gem::Requirement.new("~> 2.0").hash == Gem::Requirement.new("~> 2.0.0").hash + class Requirement + module CorrectHashForLambdaOperator + def hash + if requirements.any? {|r| r.first == "~>" } + requirements.map {|r| r.first == "~>" ? [r[0], r[1].to_s] : r }.sort.hash + else + super + end + end + end + + prepend CorrectHashForLambdaOperator + end + end + class Platform JAVA = Gem::Platform.new("java") unless defined?(JAVA) MSWIN = Gem::Platform.new("mswin32") unless defined?(MSWIN) diff --git a/lib/bundler/rubygems_gem_installer.rb b/lib/bundler/rubygems_gem_installer.rb index cd5eb152f9..f5f3c53309 100644 --- a/lib/bundler/rubygems_gem_installer.rb +++ b/lib/bundler/rubygems_gem_installer.rb @@ -8,6 +8,53 @@ module Bundler # Bundler needs to install gems regardless of binstub overwriting end + def install + pre_install_checks + + run_pre_install_hooks + + spec.loaded_from = spec_file + + # Completely remove any previous gem files + FileUtils.rm_rf gem_dir + FileUtils.rm_rf spec.extension_dir + + FileUtils.mkdir_p gem_dir, :mode => 0o755 + + extract_files + + build_extensions + write_build_info_file + run_post_build_hooks + + generate_bin + generate_plugins + + write_spec + write_cache_file + + say spec.post_install_message unless spec.post_install_message.nil? + + run_post_install_hooks + + spec + end + + def generate_plugins + return unless Gem::Installer.instance_methods(false).include?(:generate_plugins) + + latest = Gem::Specification.stubs_for(spec.name).first + return if latest && latest.version > spec.version + + ensure_writable_dir @plugins_dir + + if spec.plugins.empty? + remove_plugins_for(spec, @plugins_dir) + else + regenerate_plugins_for(spec, @plugins_dir) + end + end + def pre_install_checks super && validate_bundler_checksum(options[:bundler_expected_checksum]) end diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index 1c669491f6..749e4eb60e 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -7,7 +7,6 @@ module Bundler autoload :Validator, File.expand_path("settings/validator", __dir__) BOOL_KEYS = %w[ - allow_bundler_dependency_conflicts allow_deployment_source_credential_changes allow_offline_install auto_clean_without_path @@ -20,6 +19,7 @@ module Bundler disable_checksum_validation disable_exec_load disable_local_branch_check + disable_local_revision_check disable_multisource disable_shared_gems disable_version_check diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb index a56a3bff4b..f43e3301ef 100644 --- a/lib/bundler/shared_helpers.rb +++ b/lib/bundler/shared_helpers.rb @@ -187,11 +187,11 @@ module Bundler return @md5_available if defined?(@md5_available) @md5_available = begin require "openssl" - OpenSSL::Digest.digest("MD5", "") + ::OpenSSL::Digest.digest("MD5", "") true rescue LoadError true - rescue OpenSSL::Digest::DigestError + rescue ::OpenSSL::Digest::DigestError false end end diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb index 0157995cb0..fb13ca0578 100644 --- a/lib/bundler/source/git.rb +++ b/lib/bundler/source/git.rb @@ -22,7 +22,7 @@ module Bundler @uri = options["uri"] || "" @safe_uri = URICredentialsFilter.credential_filtered_uri(@uri) @branch = options["branch"] - @ref = options["ref"] || options["branch"] || options["tag"] || "master" + @ref = options["ref"] || options["branch"] || options["tag"] @submodules = options["submodules"] @name = options["name"] @version = options["version"].to_s.strip.gsub("-", ".pre.") @@ -60,25 +60,27 @@ module Bundler alias_method :==, :eql? def to_s - at = if local? - path - elsif user_ref = options["ref"] - if ref =~ /\A[a-z0-9]{4,}\z/i - shortref_for_display(user_ref) + begin + at = if local? + path + elsif user_ref = options["ref"] + if ref =~ /\A[a-z0-9]{4,}\z/i + shortref_for_display(user_ref) + else + user_ref + end + elsif ref + ref else - user_ref + git_proxy.branch end - else - ref + + rev = " (at #{at}@#{shortref_for_display(revision)})" + rescue GitError + "" end - rev = begin - "@#{shortref_for_display(revision)}" - rescue GitError - nil - end - - "#{@safe_uri} (at #{at}#{rev})" + "#{@safe_uri}#{rev}" end def name @@ -146,7 +148,7 @@ module Bundler changed = cached_revision && cached_revision != git_proxy.revision - if changed && !@unlocked && !git_proxy.contains?(cached_revision) + if !Bundler.settings[:disable_local_revision_check] && changed && !@unlocked && !git_proxy.contains?(cached_revision) raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \ "but the current branch in your local override for #{name} does not contain such commit. " \ "Please make sure your branch is up to date." diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb index dd65c674d8..ae21770306 100644 --- a/lib/bundler/source/git/git_proxy.rb +++ b/lib/bundler/source/git/git_proxy.rb @@ -17,7 +17,7 @@ module Bundler class GitNotAllowedError < GitError def initialize(command) msg = String.new - msg << "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, " + msg << "Bundler is trying to run `#{command}` at runtime. You probably need to run `bundle install`. However, " msg << "this error message could probably be more useful. Please submit a ticket at https://github.com/rubygems/rubygems/issues/new?labels=Bundler&template=bundler-related-issue.md " msg << "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}" super msg @@ -27,11 +27,11 @@ module Bundler class GitCommandError < GitError attr_reader :command - def initialize(command, path, destination_path, extra_info = nil) + def initialize(command, path, extra_info = nil) @command = command msg = String.new - msg << "Git error: command `git #{command}` in directory #{destination_path} has failed." + msg << "Git error: command `#{command}` in directory #{path} has failed." msg << "\n#{extra_info}" if extra_info msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path.exist? super msg @@ -39,9 +39,9 @@ module Bundler end class MissingGitRevisionError < GitCommandError - def initialize(command, path, destination_path, ref, repo) + def initialize(command, destination_path, ref, repo) msg = "Revision #{ref} does not exist in the repository #{repo}. Maybe you misspelled it?" - super command, path, destination_path, msg + super command, destination_path, msg end end @@ -132,7 +132,7 @@ module Bundler begin git "reset", "--hard", @revision, :dir => destination rescue GitCommandError => e - raise MissingGitRevisionError.new(e.command, path, destination, @revision, URICredentialsFilter.credential_filtered_uri(uri)) + raise MissingGitRevisionError.new(e.command, destination, @revision, URICredentialsFilter.credential_filtered_uri(uri)) end if submodules @@ -145,32 +145,36 @@ module Bundler private - def git_null(*command, dir: SharedHelpers.pwd) + def git_null(*command, dir: nil) check_allowed(command) out, status = SharedHelpers.with_clean_git_env do - capture_and_ignore_stderr("git", "-C", dir.to_s, *command) + capture_and_ignore_stderr(*capture3_args_for(command, dir)) end [URICredentialsFilter.credential_filtered_string(out, uri), status] end - def git_retry(*command, dir: SharedHelpers.pwd) - Bundler::Retry.new("`git -C #{dir} #{URICredentialsFilter.credential_filtered_string(command.shelljoin, uri)}`", GitNotAllowedError).attempts do + def git_retry(*command, dir: nil) + command_with_no_credentials = check_allowed(command) + + Bundler::Retry.new("`#{command_with_no_credentials}` at #{dir || SharedHelpers.pwd}").attempts do git(*command, :dir => dir) end end - def git(*command, dir: SharedHelpers.pwd) + def git(*command, dir: nil) command_with_no_credentials = check_allowed(command) out, status = SharedHelpers.with_clean_git_env do - capture_and_filter_stderr("git", "-C", dir.to_s, *command) + capture_and_filter_stderr(*capture3_args_for(command, dir)) end - raise GitCommandError.new(command_with_no_credentials, path, dir) unless status.success? + filtered_out = URICredentialsFilter.credential_filtered_string(out, uri) - URICredentialsFilter.credential_filtered_string(out, uri) + raise GitCommandError.new(command_with_no_credentials, dir || SharedHelpers.pwd, filtered_out) unless status.success? + + filtered_out end def has_revision_cached? @@ -187,10 +191,10 @@ module Bundler def find_local_revision allowed_with_path do - git("rev-parse", "--verify", ref, :dir => path).strip + git("rev-parse", "--verify", ref || "HEAD", :dir => path).strip end rescue GitCommandError => e - raise MissingGitRevisionError.new(e.command, path, path, ref, URICredentialsFilter.credential_filtered_uri(uri)) + raise MissingGitRevisionError.new(e.command, path, ref, URICredentialsFilter.credential_filtered_uri(uri)) end # Adds credentials to the URI as Fetcher#configured_uri_for does @@ -220,7 +224,7 @@ module Bundler end def check_allowed(command) - command_with_no_credentials = URICredentialsFilter.credential_filtered_string(command.shelljoin, uri) + command_with_no_credentials = URICredentialsFilter.credential_filtered_string("git #{command.shelljoin}", uri) raise GitNotAllowedError.new(command_with_no_credentials) unless allow? command_with_no_credentials end @@ -237,6 +241,20 @@ module Bundler return_value, _, status = Open3.capture3(*cmd) [return_value, status] end + + def capture3_args_for(cmd, dir) + return ["git", *cmd] unless dir + + if Bundler.feature_flag.bundler_3_mode? || supports_minus_c? + ["git", "-C", dir.to_s, *cmd] + else + ["git", *cmd, { :chdir => dir.to_s }] + end + end + + def supports_minus_c? + @supports_minus_c ||= Gem::Version.new(version) >= Gem::Version.new("1.8.5") + end end end end diff --git a/lib/bundler/source/path/installer.rb b/lib/bundler/source/path/installer.rb index 6be58e2087..72bfbb4836 100644 --- a/lib/bundler/source/path/installer.rb +++ b/lib/bundler/source/path/installer.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative "../../rubygems_gem_installer" + module Bundler class Source class Path diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index 79ad278da3..5b89b1645d 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -145,6 +145,8 @@ module Bundler Bundler.mkdir_p bin_path, :no_sudo => true unless spec.executables.empty? || Bundler.rubygems.provides?(">= 2.7.5") + require_relative "../rubygems_gem_installer" + installed_spec = Bundler::RubyGemsGemInstaller.at( path, :install_dir => install_path.to_s, @@ -351,7 +353,6 @@ module Bundler def installed_specs @installed_specs ||= Index.build do |idx| Bundler.rubygems.all_specs.reverse_each do |spec| - next if spec.name == "bundler" spec.source = self if Bundler.rubygems.spec_missing_extensions?(spec, false) Bundler.ui.debug "Source #{self} is ignoring #{spec} because it is missing extensions" diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb index 731a791531..44b167ca3e 100644 --- a/lib/bundler/source_list.rb +++ b/lib/bundler/source_list.rb @@ -1,21 +1,23 @@ # frozen_string_literal: true -require "set" - module Bundler class SourceList attr_reader :path_sources, :git_sources, :plugin_sources, - :global_rubygems_source, + :global_path_source, :metadata_source + def global_rubygems_source + @global_rubygems_source ||= rubygems_aggregate_class.new + end + def initialize @path_sources = [] @git_sources = [] @plugin_sources = [] @global_rubygems_source = nil - @rubygems_aggregate = rubygems_aggregate_class.new + @global_path_source = nil @rubygems_sources = [] @metadata_source = Source::Metadata.new end @@ -24,7 +26,9 @@ module Bundler if options["gemspec"] add_source_to_list Source::Gemspec.new(options), path_sources else - add_source_to_list Source::Path.new(options), path_sources + path_source = add_source_to_list Source::Path.new(options), path_sources + @global_path_source ||= path_source if options["global"] + path_source end end @@ -43,24 +47,20 @@ module Bundler end def global_rubygems_source=(uri) - if Bundler.feature_flag.disable_multisource? - @global_rubygems_source ||= rubygems_aggregate_class.new("remotes" => uri) - end - add_rubygems_remote(uri) + @global_rubygems_source ||= rubygems_aggregate_class.new("remotes" => uri) end def add_rubygems_remote(uri) - return if Bundler.feature_flag.disable_multisource? - @rubygems_aggregate.add_remote(uri) - @rubygems_aggregate + global_rubygems_source.add_remote(uri) + global_rubygems_source end def default_source - global_rubygems_source || @rubygems_aggregate + global_path_source || global_rubygems_source end def rubygems_sources - @rubygems_sources + [default_source] + @rubygems_sources + [global_rubygems_source] end def rubygems_remotes @@ -96,10 +96,9 @@ module Bundler replacement_rubygems = !Bundler.feature_flag.disable_multisource? && replacement_sources.detect {|s| s.is_a?(Source::Rubygems) } - @rubygems_aggregate = replacement_rubygems if replacement_rubygems + @global_rubygems_source = replacement_rubygems if replacement_rubygems return true if !equal_sources?(lock_sources, replacement_sources) && !equivalent_sources?(lock_sources, replacement_sources) - return true if replacement_rubygems && rubygems_remotes.to_set != replacement_rubygems.remotes.to_set false end @@ -112,10 +111,6 @@ module Bundler all_sources.each(&:remote!) end - def rubygems_primary_remotes - @rubygems_aggregate.remotes - end - private def rubygems_aggregate_class @@ -153,7 +148,7 @@ module Bundler end def equal_sources?(lock_sources, replacement_sources) - lock_sources.to_set == replacement_sources.to_set + lock_sources.sort_by(&:to_s) == replacement_sources.sort_by(&:to_s) end def equal_source?(source, other_source) diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index a0b9552c18..399c91fea5 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "tsort" -require "set" module Bundler class SpecSet @@ -13,14 +12,16 @@ module Bundler end def for(dependencies, skip = [], check = false, match_current_platform = false, raise_on_missing = true) - handled = Set.new + handled = [] deps = dependencies.dup specs = [] skip += ["bundler"] loop do break unless dep = deps.shift - next if !handled.add?(dep) || skip.include?(dep.name) + next if handled.include?(dep) || skip.include?(dep.name) + + handled << dep specs_for_dep = spec_for_dependency(dep, match_current_platform) if specs_for_dep.any? @@ -28,7 +29,7 @@ module Bundler specs_for_dep.first.dependencies.each do |d| next if d.type == :development - d = DepProxy.new(d, dep.__platform) unless match_current_platform + d = DepProxy.get_proxy(d, dep.__platform) unless match_current_platform deps << d end elsif check diff --git a/lib/bundler/stub_specification.rb b/lib/bundler/stub_specification.rb index 2456d268da..fa071901e5 100644 --- a/lib/bundler/stub_specification.rb +++ b/lib/bundler/stub_specification.rb @@ -26,11 +26,19 @@ module Bundler # @!group Stub Delegates + def manually_installed? + # This is for manually installed gems which are gems that were fixed in place after a + # failed installation. Once the issue was resolved, the user then manually created + # the gem specification using the instructions provided by `gem help install` + installed_by_version == Gem::Version.new(0) + end + # This is defined directly to avoid having to loading the full spec def missing_extensions? return false if default_gem? return false if extensions.empty? return false if File.exist? gem_build_complete_path + return false if manually_installed? true end diff --git a/lib/bundler/templates/newgem/CHANGELOG.md.tt b/lib/bundler/templates/newgem/CHANGELOG.md.tt new file mode 100644 index 0000000000..c9ea96d453 --- /dev/null +++ b/lib/bundler/templates/newgem/CHANGELOG.md.tt @@ -0,0 +1,5 @@ +## [Unreleased] + +## [0.1.0] - <%= Time.now.strftime('%F') %> + +- Initial release diff --git a/lib/bundler/templates/newgem/Gemfile.tt b/lib/bundler/templates/newgem/Gemfile.tt index 1d55fd7a90..b09ccfff15 100644 --- a/lib/bundler/templates/newgem/Gemfile.tt +++ b/lib/bundler/templates/newgem/Gemfile.tt @@ -16,5 +16,5 @@ gem "<%= config[:test] %>", "~> <%= config[:test_framework_version] %>" <%- end -%> <%- if config[:rubocop] -%> -gem "rubocop", "~> 0.80" +gem "rubocop", "~> <%= config[:rubocop_version] %>" <%- end -%> diff --git a/lib/bundler/templates/newgem/README.md.tt b/lib/bundler/templates/newgem/README.md.tt index 315bc745a3..8fd87abe9a 100644 --- a/lib/bundler/templates/newgem/README.md.tt +++ b/lib/bundler/templates/newgem/README.md.tt @@ -29,19 +29,21 @@ TODO: Write usage instructions here After checking out the repo, run `bin/setup` to install dependencies.<% if config[:test] %> Then, run `rake <%= config[:test].sub('mini', '').sub('rspec', 'spec') %>` to run the tests.<% end %> You can also run `bin/console` for an interactive prompt that will allow you to experiment.<% if config[:bin] %> Run `bundle exec <%= config[:name] %>` to use the gem in this directory, ignoring other installed copies of this gem.<% end %> To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org). +<% if config[:git] -%> ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/<%= config[:github_username] %>/<%= config[:name] %>.<% if config[:coc] %> This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/<%= config[:github_username] %>/<%= config[:name] %>/blob/master/CODE_OF_CONDUCT.md).<% end %> +Bug reports and pull requests are welcome on GitHub at https://github.com/<%= config[:github_username] %>/<%= config[:name] %>.<% if config[:coc] %> This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/<%= config[:github_username] %>/<%= config[:name] %>/blob/<%= config[:git_default_branch] %>/CODE_OF_CONDUCT.md).<% end %> +<% end -%> <% if config[:mit] -%> ## License The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). <% end -%> -<% if config[:coc] -%> +<% if config[:git] && config[:coc] -%> ## Code of Conduct -Everyone interacting in the <%= config[:constant_name] %> project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/<%= config[:github_username] %>/<%= config[:name] %>/blob/master/CODE_OF_CONDUCT.md). +Everyone interacting in the <%= config[:constant_name] %> project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/<%= config[:github_username] %>/<%= config[:name] %>/blob/<%= config[:git_default_branch] %>/CODE_OF_CONDUCT.md). <% end -%> diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt index 4ed00bc749..5be20c7e4b 100644 --- a/lib/bundler/templates/newgem/newgem.gemspec.tt +++ b/lib/bundler/templates/newgem/newgem.gemspec.tt @@ -14,7 +14,7 @@ Gem::Specification.new do |spec| <%- if config[:mit] -%> spec.license = "MIT" <%- end -%> - spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0") + spec.required_ruby_version = Gem::Requirement.new(">= <%= config[:required_ruby_version] %>") spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'" diff --git a/lib/bundler/templates/newgem/rubocop.yml.tt b/lib/bundler/templates/newgem/rubocop.yml.tt index 00a72e3006..9ecec78807 100644 --- a/lib/bundler/templates/newgem/rubocop.yml.tt +++ b/lib/bundler/templates/newgem/rubocop.yml.tt @@ -1,3 +1,6 @@ +AllCops: + TargetRubyVersion: <%= ::Gem::Version.new(config[:required_ruby_version]).segments[0..1].join(".") %> + Style/StringLiterals: Enabled: true EnforcedStyle: double_quotes diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb b/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb index ec9c770a28..f8c695c1ed 100644 --- a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb @@ -26,6 +26,13 @@ module Bundler::Molinillo end end + # (see Bundler::Molinillo::SpecificationProvider#dependencies_equal?) + def dependencies_equal?(dependencies, other_dependencies) + with_no_such_dependency_error_handling do + specification_provider.dependencies_equal?(dependencies, other_dependencies) + end + end + # (see Bundler::Molinillo::SpecificationProvider#name_for) def name_for(dependency) with_no_such_dependency_error_handling do diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb index d1d7045daf..936399ed2f 100644 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'set' require 'tsort' require_relative 'dependency_graph/log' diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb index 41bc013143..1185a8ab05 100644 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb @@ -59,7 +59,7 @@ module Bundler::Molinillo # @param [Set] vertices the set to add the predecessors to # @return [Set] the vertices of {#graph} where `self` is a # {#descendent?} - def _recursive_predecessors(vertices = Set.new) + def _recursive_predecessors(vertices = new_vertex_set) incoming_edges.each do |edge| vertex = edge.origin next unless vertices.add?(vertex) @@ -85,7 +85,7 @@ module Bundler::Molinillo # @param [Set] vertices the set to add the successors to # @return [Set] the vertices of {#graph} where `self` is an # {#ancestor?} - def _recursive_successors(vertices = Set.new) + def _recursive_successors(vertices = new_vertex_set) outgoing_edges.each do |edge| vertex = edge.destination next unless vertices.add?(vertex) @@ -128,7 +128,7 @@ module Bundler::Molinillo # Is there a path from `self` to `other` following edges in the # dependency graph? - # @return true iff there is a path following edges within this {#graph} + # @return whether there is a path following edges within this {#graph} def path_to?(other) _path_to?(other) end @@ -138,7 +138,7 @@ module Bundler::Molinillo # @param [Vertex] other the vertex to check if there's a path to # @param [Set] visited the vertices of {#graph} that have been visited # @return [Boolean] whether there is a path to `other` from `self` - def _path_to?(other, visited = Set.new) + def _path_to?(other, visited = new_vertex_set) return false unless visited.add?(self) return true if equal?(other) successors.any? { |v| v._path_to?(other, visited) } @@ -147,12 +147,18 @@ module Bundler::Molinillo # Is there a path from `other` to `self` following edges in the # dependency graph? - # @return true iff there is a path following edges within this {#graph} + # @return whether there is a path following edges within this {#graph} def ancestor?(other) other.path_to?(self) end alias is_reachable_from? ancestor? + + def new_vertex_set + require 'set' + Set.new + end + private :new_vertex_set end end end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb index 4d64d21072..e210202b69 100644 --- a/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb @@ -34,7 +34,7 @@ module Bundler::Molinillo # An error caused by attempting to fulfil a dependency that was circular # - # @note This exception will be thrown iff a {Vertex} is added to a + # @note This exception will be thrown if and only if a {Vertex} is added to a # {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an # existing {DependencyGraph::Vertex} class CircularDependencyError < ResolverError @@ -121,7 +121,7 @@ module Bundler::Molinillo t = ''.dup depth = 2 tree.each do |req| - t << ' ' * depth << req.to_s + t << ' ' * depth << printable_requirement.call(req) unless tree.last == req if spec = conflict.activated_by_name[name_for(req)] t << %( was resolved to #{version_for_spec.call(spec)}, which) diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb index fa094c1981..edf2366b7b 100644 --- a/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb @@ -45,6 +45,17 @@ module Bundler::Molinillo true end + # Determines whether two arrays of dependencies are equal, and thus can be + # grouped. + # + # @param [Array] dependencies + # @param [Array] other_dependencies + # @return [Boolean] whether `dependencies` and `other_dependencies` should + # be considered equal. + def dependencies_equal?(dependencies, other_dependencies) + dependencies == other_dependencies + end + # Returns the name for the given `dependency`. # @note This method should be 'pure', i.e. the return value should depend # only on the `dependency` parameter. diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb index 26b8bc745c..c689ca7635 100644 --- a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb @@ -329,11 +329,11 @@ module Bundler::Molinillo # Look for past conflicts that could be unwound to affect the # requirement tree for the current conflict + all_reqs = last_detail_for_current_unwind.all_requirements + all_reqs_size = all_reqs.size relevant_unused_unwinds = unused_unwind_options.select do |alternative| - intersecting_requirements = - last_detail_for_current_unwind.all_requirements & - alternative.requirements_unwound_to_instead - next if intersecting_requirements.empty? + diff_reqs = all_reqs - alternative.requirements_unwound_to_instead + next if diff_reqs.size == all_reqs_size # Find the highest index unwind whilst looping through current_detail = alternative if alternative > current_detail alternative @@ -344,8 +344,12 @@ module Bundler::Molinillo state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 } # Update the requirements_unwound_to_instead on any relevant unused unwinds - relevant_unused_unwinds.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement } - unwind_details.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement } + relevant_unused_unwinds.each do |d| + (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq! + end + unwind_details.each do |d| + (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq! + end current_detail end @@ -803,7 +807,7 @@ module Bundler::Molinillo possibilities.reverse_each do |possibility| dependencies = dependencies_for(possibility) - if current_possibility_set && current_possibility_set.dependencies == dependencies + if current_possibility_set && dependencies_equal?(current_possibility_set.dependencies, dependencies) current_possibility_set.possibilities.unshift(possibility) else possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility])) diff --git a/lib/bundler/vendor/thor/lib/thor.rb b/lib/bundler/vendor/thor/lib/thor.rb index f2a03388cc..0794dbb522 100644 --- a/lib/bundler/vendor/thor/lib/thor.rb +++ b/lib/bundler/vendor/thor/lib/thor.rb @@ -1,7 +1,7 @@ -require "set" require_relative "thor/base" class Bundler::Thor + $thor_runner ||= false class << self # Allows for custom "Command" package naming. # @@ -323,7 +323,7 @@ class Bundler::Thor # ==== Parameters # Symbol ...:: A list of commands that should be affected. def stop_on_unknown_option!(*command_names) - stop_on_unknown_option.merge(command_names) + @stop_on_unknown_option = stop_on_unknown_option | command_names end def stop_on_unknown_option?(command) #:nodoc: @@ -337,7 +337,7 @@ class Bundler::Thor # ==== Parameters # Symbol ...:: A list of commands that should be affected. def disable_required_check!(*command_names) - disable_required_check.merge(command_names) + @disable_required_check = disable_required_check | command_names end def disable_required_check?(command) #:nodoc: @@ -347,12 +347,12 @@ class Bundler::Thor protected def stop_on_unknown_option #:nodoc: - @stop_on_unknown_option ||= Set.new + @stop_on_unknown_option ||= [] end # help command has the required check disabled by default. def disable_required_check #:nodoc: - @disable_required_check ||= Set.new([:help]) + @disable_required_check ||= [:help] end # The method responsible for dispatching given the args. @@ -398,7 +398,6 @@ class Bundler::Thor # the namespace should be displayed as arguments. # def banner(command, namespace = nil, subcommand = false) - $thor_runner ||= false command.formatted_usage(self, $thor_runner, subcommand).split("\n").map do |formatted_usage| "#{basename} #{formatted_usage}" end.join("\n") diff --git a/lib/bundler/vendor/thor/lib/thor/actions.rb b/lib/bundler/vendor/thor/lib/thor/actions.rb index a5368d07f3..de9323b2db 100644 --- a/lib/bundler/vendor/thor/lib/thor/actions.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions.rb @@ -219,7 +219,7 @@ class Bundler::Thor contents = if is_uri require "open-uri" - open(path, "Accept" => "application/x-thor-template", &:read) + URI.open(path, "Accept" => "application/x-thor-template", &:read) else open(path, &:read) end diff --git a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb index afdbd53dd0..62c82b3dba 100644 --- a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb @@ -251,7 +251,8 @@ class Bundler::Thor # path:: path of the file to be changed # flag:: the regexp or string to be replaced # replacement:: the replacement, can be also given as a block - # config:: give :verbose => false to not log the status. + # config:: give :verbose => false to not log the status, and + # :force => true, to force the replacement regardles of runner behavior. # # ==== Example # @@ -262,9 +263,10 @@ class Bundler::Thor # end # def gsub_file(path, flag, *args, &block) - return unless behavior == :invoke config = args.last.is_a?(Hash) ? args.pop : {} + return unless behavior == :invoke || config.fetch(:force, false) + path = File.expand_path(path, destination_root) say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true) diff --git a/lib/bundler/vendor/thor/lib/thor/error.rb b/lib/bundler/vendor/thor/lib/thor/error.rb index 1553afd201..7d57129b83 100644 --- a/lib/bundler/vendor/thor/lib/thor/error.rb +++ b/lib/bundler/vendor/thor/lib/thor/error.rb @@ -1,5 +1,5 @@ class Bundler::Thor - Correctable = if defined?(DidYouMean::SpellChecker) && defined?(DidYouMean::Correctable) + Correctable = if defined?(DidYouMean::SpellChecker) && defined?(DidYouMean::Correctable) # rubocop:disable Naming/ConstantName # In order to support versions of Ruby that don't have keyword # arguments, we need our own spell checker class that doesn't take key # words. Even though this code wouldn't be hit because of the check diff --git a/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb index d0f43e2d97..3a5d82cf29 100644 --- a/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb +++ b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb @@ -30,7 +30,11 @@ class Bundler::Thor arguments.each do |argument| if !argument.default.nil? - @assigns[argument.human_name] = argument.default + begin + @assigns[argument.human_name] = argument.default.dup + rescue TypeError # Compatibility shim for un-dup-able Fixnum in Ruby < 2.4 + @assigns[argument.human_name] = argument.default + end elsif argument.required? @non_assigned_required << argument end diff --git a/lib/bundler/vendor/thor/lib/thor/parser/options.rb b/lib/bundler/vendor/thor/lib/thor/parser/options.rb index 6d1342ee3c..3a8927d09c 100644 --- a/lib/bundler/vendor/thor/lib/thor/parser/options.rb +++ b/lib/bundler/vendor/thor/lib/thor/parser/options.rb @@ -133,15 +133,16 @@ class Bundler::Thor protected - def assign_result!(option, result) - if option.repeatable && option.type == :hash - (@assigns[option.human_name] ||= {}).merge!(result) - elsif option.repeatable - (@assigns[option.human_name] ||= []) << result - else - @assigns[option.human_name] = result + def assign_result!(option, result) + if option.repeatable && option.type == :hash + (@assigns[option.human_name] ||= {}).merge!(result) + elsif option.repeatable + (@assigns[option.human_name] ||= []) << result + else + @assigns[option.human_name] = result + end end - end + # Check if the current value in peek is a registered switch. # # Two booleans are returned. The first is true if the current value diff --git a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb index be48358cb1..2dddd4a53a 100644 --- a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb @@ -94,6 +94,8 @@ class Bundler::Thor # say("I know you knew that.") # def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/)) + return if quiet? + buffer = prepare_message(message, *color) buffer << "\n" if force_new_line && !message.to_s.end_with?("\n") @@ -230,8 +232,9 @@ class Bundler::Thor paras = message.split("\n\n") paras.map! do |unwrapped| - counter = 0 - unwrapped.split(" ").inject do |memo, word| + words = unwrapped.split(" ") + counter = words.first.length + words.inject do |memo, word| word = word.gsub(/\n\005/, "\n").gsub(/\005/, "\n") counter = 0 if word.include? "\n" if (counter + word.length + 1) < width diff --git a/lib/bundler/vendor/thor/lib/thor/shell/color.rb b/lib/bundler/vendor/thor/lib/thor/shell/color.rb index 29f280202d..dc167ed3cc 100644 --- a/lib/bundler/vendor/thor/lib/thor/shell/color.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell/color.rb @@ -97,7 +97,11 @@ class Bundler::Thor protected def can_display_colors? - stdout.tty? && !are_colors_disabled? + are_colors_supported? && !are_colors_disabled? + end + + def are_colors_supported? + stdout.tty? && ENV["TERM"] != "dumb" end def are_colors_disabled? diff --git a/lib/bundler/vendor/thor/lib/thor/version.rb b/lib/bundler/vendor/thor/lib/thor/version.rb index 1b222da995..a3efa9f762 100644 --- a/lib/bundler/vendor/thor/lib/thor/version.rb +++ b/lib/bundler/vendor/thor/lib/thor/version.rb @@ -1,3 +1,3 @@ class Bundler::Thor - VERSION = "1.0.1" + VERSION = "1.1.0" end diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 3f2184b504..3c85284ff2 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -275,7 +275,7 @@ module Gem unless spec = specs.first msg = "can't find gem #{dep} with executable #{exec_name}" - if name == "bundler" && bundler_message = Gem::BundlerVersionFinder.missing_version_message + if dep.filters_bundler? && bundler_message = Gem::BundlerVersionFinder.missing_version_message msg = bundler_message end raise Gem::GemNotFoundException, msg @@ -469,7 +469,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} next if File.exist? subdir begin FileUtils.mkdir_p subdir, **options - rescue Errno::EACCES + rescue SystemCallError end end ensure diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb index bf55ce3205..9f935e6285 100644 --- a/lib/rubygems/command.rb +++ b/lib/rubygems/command.rb @@ -634,6 +634,7 @@ RubyGems is a package manager for Ruby. gem install rake gem list --local gem build package.gemspec + gem push package-0.0.1.gem gem help install Further help: diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb index 854d09ef3d..9dc41a2995 100644 --- a/lib/rubygems/config_file.rb +++ b/lib/rubygems/config_file.rb @@ -45,6 +45,7 @@ class Gem::ConfigFile DEFAULT_UPDATE_SOURCES = true DEFAULT_CONCURRENT_DOWNLOADS = 8 DEFAULT_CERT_EXPIRATION_LENGTH_DAYS = 365 + DEFAULT_IPV4_FALLBACK_ENABLED = false ## # For Ruby packagers to set configuration defaults. Set in @@ -140,6 +141,12 @@ class Gem::ConfigFile attr_accessor :cert_expiration_length_days + ## + # == Experimental == + # Fallback to IPv4 when IPv6 is not reachable or slow (default: false) + + attr_accessor :ipv4_fallback_enabled + ## # Path name of directory or file of openssl client certificate, used for remote https connection with client authentication @@ -175,6 +182,7 @@ class Gem::ConfigFile @update_sources = DEFAULT_UPDATE_SOURCES @concurrent_downloads = DEFAULT_CONCURRENT_DOWNLOADS @cert_expiration_length_days = DEFAULT_CERT_EXPIRATION_LENGTH_DAYS + @ipv4_fallback_enabled = ENV['IPV4_FALLBACK_ENABLED'] == 'true' || DEFAULT_IPV4_FALLBACK_ENABLED operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS) platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS) @@ -203,6 +211,7 @@ class Gem::ConfigFile @disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server @sources = @hash[:sources] if @hash.key? :sources @cert_expiration_length_days = @hash[:cert_expiration_length_days] if @hash.key? :cert_expiration_length_days + @ipv4_fallback_enabled = @hash[:ipv4_fallback_enabled] if @hash.key? :ipv4_fallback_enabled @ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode @ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert diff --git a/lib/rubygems/core_ext/tcpsocket_init.rb b/lib/rubygems/core_ext/tcpsocket_init.rb new file mode 100644 index 0000000000..3d9740c579 --- /dev/null +++ b/lib/rubygems/core_ext/tcpsocket_init.rb @@ -0,0 +1,52 @@ +require 'socket' + +module CoreExtensions + module TCPSocketExt + def self.prepended(base) + base.prepend Initializer + end + + module Initializer + CONNECTION_TIMEOUT = 5 + IPV4_DELAY_SECONDS = 0.1 + + def initialize(host, serv, *rest) + mutex = Mutex.new + addrs = [] + threads = [] + cond_var = ConditionVariable.new + + Addrinfo.foreach(host, serv, nil, :STREAM) do |addr| + Thread.report_on_exception = false if defined? Thread.report_on_exception = () + + threads << Thread.new(addr) do + # give head start to ipv6 addresses + sleep IPV4_DELAY_SECONDS if addr.ipv4? + + # raises Errno::ECONNREFUSED when ip:port is unreachable + Socket.tcp(addr.ip_address, serv, connect_timeout: CONNECTION_TIMEOUT).close + mutex.synchronize do + addrs << addr.ip_address + cond_var.signal + end + end + end + + mutex.synchronize do + timeout_time = CONNECTION_TIMEOUT + Time.now.to_f + while addrs.empty? && (remaining_time = timeout_time - Time.now.to_f) > 0 + cond_var.wait(mutex, remaining_time) + end + + host = addrs.shift unless addrs.empty? + end + + threads.each {|t| t.kill.join if t.alive? } + + super(host, serv, *rest) + end + end + end +end + +TCPSocket.prepend CoreExtensions::TCPSocketExt diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb index 68f3e3d991..3721204ab2 100644 --- a/lib/rubygems/dependency.rb +++ b/lib/rubygems/dependency.rb @@ -277,7 +277,7 @@ class Gem::Dependency requirement.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version) end.map(&:to_spec) - Gem::BundlerVersionFinder.filter!(matches) if name == "bundler".freeze && !requirement.specific? + Gem::BundlerVersionFinder.filter!(matches) if filters_bundler? if platform_only matches.reject! do |spec| @@ -295,6 +295,10 @@ class Gem::Dependency @requirement.specific? end + def filters_bundler? + name == "bundler".freeze && !specific? + end + def to_specs matches = matching_specs true diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb index e089b3d57a..14d5dde413 100644 --- a/lib/rubygems/ext/builder.rb +++ b/lib/rubygems/ext/builder.rb @@ -28,13 +28,14 @@ class Gem::Ext::Builder unless make_program make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make' end + make_program = Shellwords.split(make_program) destdir = 'DESTDIR=%s' % ENV['DESTDIR'] ['clean', '', 'install'].each do |target| # Pass DESTDIR via command line to override what's in MAKEFLAGS cmd = [ - make_program, + *make_program, destdir, target, ].reject(&:empty?) diff --git a/lib/rubygems/installer_uninstaller_utils.rb b/lib/rubygems/installer_uninstaller_utils.rb index e81ed4cba3..2c8b7c635e 100644 --- a/lib/rubygems/installer_uninstaller_utils.rb +++ b/lib/rubygems/installer_uninstaller_utils.rb @@ -6,11 +6,16 @@ module Gem::InstallerUninstallerUtils def regenerate_plugins_for(spec, plugins_dir) + plugins = spec.plugins + return if plugins.empty? + + require 'pathname' + spec.plugins.each do |plugin| plugin_script_path = File.join plugins_dir, "#{spec.name}_plugin#{File.extname(plugin)}" File.open plugin_script_path, 'wb' do |file| - file.puts "require '#{plugin}'" + file.puts "require_relative '#{Pathname.new(plugin).relative_path_from(Pathname.new(plugins_dir))}'" end verbose plugin_script_path diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb index a500fd24c8..e01d0494d6 100644 --- a/lib/rubygems/platform.rb +++ b/lib/rubygems/platform.rb @@ -66,7 +66,7 @@ class Gem::Platform when String then arch = arch.split '-' - if arch.length > 2 and arch.last !~ /\d/ # reassemble x86-linux-gnu + if arch.length > 2 and arch.last !~ /\d+(\.\d+)?$/ # reassemble x86-linux-{libc} extra = arch.pop arch.last << "-#{extra}" end @@ -121,10 +121,6 @@ class Gem::Platform end end - def inspect - "%s @cpu=%p, @os=%p, @version=%p>" % [super[0..-2], *to_a] - end - def to_a [@cpu, @os, @version] end @@ -150,7 +146,8 @@ class Gem::Platform ## # Does +other+ match this platform? Two platforms match if they have the # same CPU, or either has a CPU of 'universal', they have the same OS, and - # they have the same version, or either has no version. + # they have the same version, or either has no version (except for 'linux' + # where the version is the libc name, with no version standing for 'gnu') # # Additionally, the platform will match if the local CPU is 'arm' and the # other CPU starts with "arm" (for generic ARM family support). @@ -166,7 +163,10 @@ class Gem::Platform @os == other.os and # version - (@version.nil? or other.version.nil? or @version == other.version) + ( + (@os != 'linux' and (@version.nil? or other.version.nil?)) or + @version == other.version + ) end ## diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 53e840978c..a14c42f00e 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -78,6 +78,7 @@ class Gem::RemoteFetcher # fetching the gem. def initialize(proxy=nil, dns=nil, headers={}) + require 'rubygems/core_ext/tcpsocket_init' if Gem.configuration.ipv4_fallback_enabled require 'net/http' require 'stringio' require 'uri' @@ -295,7 +296,7 @@ class Gem::RemoteFetcher data = fetch_path(uri, mtime) - if data == nil # indicates the server returned 304 Not Modified + if data.nil? # indicates the server returned 304 Not Modified return Gem.read_binary(path) end diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index 430060e2ff..6721de4055 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -190,7 +190,7 @@ class Gem::Requirement end def hash # :nodoc: - requirements.sort.hash + requirements.map {|r| r.first == "~>" ? [r[0], r[1].to_s] : r }.sort.hash end def marshal_dump # :nodoc: diff --git a/lib/rubygems/resolver/index_specification.rb b/lib/rubygems/resolver/index_specification.rb index 2aa6b419ba..9ea76f40ba 100644 --- a/lib/rubygems/resolver/index_specification.rb +++ b/lib/rubygems/resolver/index_specification.rb @@ -35,9 +35,12 @@ class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification ## # The required_ruby_version constraint for this specification + # + # A fallback is included because when generated, some marshalled specs have it + # set to +nil+. def required_ruby_version - spec.required_ruby_version + spec.required_ruby_version || Gem::Requirement.default end ## diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb index 2ddb0ac426..b765226fb0 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb @@ -26,6 +26,13 @@ module Gem::Resolver::Molinillo end end + # (see Gem::Resolver::Molinillo::SpecificationProvider#dependencies_equal?) + def dependencies_equal?(dependencies, other_dependencies) + with_no_such_dependency_error_handling do + specification_provider.dependencies_equal?(dependencies, other_dependencies) + end + end + # (see Gem::Resolver::Molinillo::SpecificationProvider#name_for) def name_for(dependency) with_no_such_dependency_error_handling do diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb index 773bb3417f..16430a79f5 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'set' require 'tsort' require_relative 'dependency_graph/log' diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb index f4cc333dd1..77114951b2 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb @@ -59,7 +59,7 @@ module Gem::Resolver::Molinillo # @param [Set] vertices the set to add the predecessors to # @return [Set] the vertices of {#graph} where `self` is a # {#descendent?} - def _recursive_predecessors(vertices = Set.new) + def _recursive_predecessors(vertices = new_vertex_set) incoming_edges.each do |edge| vertex = edge.origin next unless vertices.add?(vertex) @@ -85,7 +85,7 @@ module Gem::Resolver::Molinillo # @param [Set] vertices the set to add the successors to # @return [Set] the vertices of {#graph} where `self` is an # {#ancestor?} - def _recursive_successors(vertices = Set.new) + def _recursive_successors(vertices = new_vertex_set) outgoing_edges.each do |edge| vertex = edge.destination next unless vertices.add?(vertex) @@ -138,7 +138,7 @@ module Gem::Resolver::Molinillo # @param [Vertex] other the vertex to check if there's a path to # @param [Set] visited the vertices of {#graph} that have been visited # @return [Boolean] whether there is a path to `other` from `self` - def _path_to?(other, visited = Set.new) + def _path_to?(other, visited = new_vertex_set) return false unless visited.add?(self) return true if equal?(other) successors.any? { |v| v._path_to?(other, visited) } @@ -147,12 +147,18 @@ module Gem::Resolver::Molinillo # Is there a path from `other` to `self` following edges in the # dependency graph? - # @return true iff there is a path following edges within this {#graph} + # @return whether there is a path following edges within this {#graph} def ancestor?(other) other.path_to?(self) end alias is_reachable_from? ancestor? + + def new_vertex_set + require 'set' + Set.new + end + private :new_vertex_set end end end diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb index a6e182e84d..ada03a901c 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb @@ -121,7 +121,7 @@ module Gem::Resolver::Molinillo t = ''.dup depth = 2 tree.each do |req| - t << ' ' * depth << req.to_s + t << ' ' * depth << printable_requirement.call(req) unless tree.last == req if spec = conflict.activated_by_name[name_for(req)] t << %( was resolved to #{version_for_spec.call(spec)}, which) diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb index a44b9c0d5d..9448dc7bf3 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb @@ -45,6 +45,17 @@ module Gem::Resolver::Molinillo true end + # Determines whether two arrays of dependencies are equal, and thus can be + # grouped. + # + # @param [Array] dependencies + # @param [Array] other_dependencies + # @return [Boolean] whether `dependencies` and `other_dependencies` should + # be considered equal. + def dependencies_equal?(dependencies, other_dependencies) + dependencies == other_dependencies + end + # Returns the name for the given `dependency`. # @note This method should be 'pure', i.e. the return value should depend # only on the `dependency` parameter. diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb index f1c60ec544..8b40e59e42 100644 --- a/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb +++ b/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb @@ -329,11 +329,11 @@ module Gem::Resolver::Molinillo # Look for past conflicts that could be unwound to affect the # requirement tree for the current conflict + all_reqs = last_detail_for_current_unwind.all_requirements + all_reqs_size = all_reqs.size relevant_unused_unwinds = unused_unwind_options.select do |alternative| - intersecting_requirements = - last_detail_for_current_unwind.all_requirements & - alternative.requirements_unwound_to_instead - next if intersecting_requirements.empty? + diff_reqs = all_reqs - alternative.requirements_unwound_to_instead + next if diff_reqs.size == all_reqs_size # Find the highest index unwind whilst looping through current_detail = alternative if alternative > current_detail alternative @@ -344,8 +344,12 @@ module Gem::Resolver::Molinillo state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 } # Update the requirements_unwound_to_instead on any relevant unused unwinds - relevant_unused_unwinds.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement } - unwind_details.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement } + relevant_unused_unwinds.each do |d| + (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq! + end + unwind_details.each do |d| + (d.requirements_unwound_to_instead << current_detail.state_requirement).uniq! + end current_detail end @@ -803,7 +807,7 @@ module Gem::Resolver::Molinillo possibilities.reverse_each do |possibility| dependencies = dependencies_for(possibility) - if current_possibility_set && current_possibility_set.dependencies == dependencies + if current_possibility_set && dependencies_equal?(current_possibility_set.dependencies, dependencies) current_possibility_set.possibilities.unshift(possibility) else possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility])) diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index d59f57c49f..7206c3eaf0 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -182,6 +182,7 @@ class Gem::Specification < Gem::BasicSpecification @@default_value[k].nil? end + @@stubs = nil @@stubs_by_name = {} # Sentinel object to represent "not found" stubs @@ -665,6 +666,9 @@ class Gem::Specification < Gem::BasicSpecification # # # Only prereleases or final releases after 2.6.0.preview2 # spec.required_ruby_version = '> 2.6.0.preview2' + # + # # This gem will work with 2.3.0 or greater, including major version 3, but lesser than 4.0.0 + # spec.required_ruby_version = '>= 2.3', '< 4' def required_ruby_version=(req) @required_ruby_version = Gem::Requirement.create req @@ -800,10 +804,8 @@ class Gem::Specification < Gem::BasicSpecification def self.stubs @@stubs ||= begin pattern = "*.gemspec" - stubs = installed_stubs(dirs, pattern) + default_stubs(pattern) - stubs = stubs.uniq {|stub| stub.full_name } + stubs = stubs_for_pattern(pattern, false) - _resort!(stubs) @@stubs_by_name = stubs.select {|s| Gem::Platform.match_spec? s }.group_by(&:name) stubs end @@ -820,31 +822,40 @@ class Gem::Specification < Gem::BasicSpecification end end - EMPTY = [].freeze # :nodoc: - ## # Returns a Gem::StubSpecification for installed gem named +name+ # only returns stubs that match Gem.platforms def self.stubs_for(name) - if @@stubs_by_name[name] - @@stubs_by_name[name] + if @@stubs + @@stubs_by_name[name] || [] else - pattern = "#{name}-*.gemspec" - stubs = installed_stubs(dirs, pattern).select {|s| Gem::Platform.match_spec? s } + default_stubs(pattern) - stubs = stubs.uniq {|stub| stub.full_name }.group_by(&:name) - stubs.each_value {|v| _resort!(v) } - - @@stubs_by_name.merge! stubs - @@stubs_by_name[name] ||= EMPTY + @@stubs_by_name[name] ||= stubs_for_pattern("#{name}-*.gemspec").select do |s| + s.name == name + end end end + ## + # Finds stub specifications matching a pattern from the standard locations, + # optionally filtering out specs not matching the current platform + # + def self.stubs_for_pattern(pattern, match_platform = true) # :nodoc: + installed_stubs = installed_stubs(Gem::Specification.dirs, pattern) + installed_stubs.select! {|s| Gem::Platform.match_spec? s } if match_platform + stubs = installed_stubs + default_stubs(pattern) + stubs = stubs.uniq {|stub| stub.full_name } + _resort!(stubs) + stubs + end + def self._resort!(specs) # :nodoc: specs.sort! do |a, b| names = a.name <=> b.name next names if names.nonzero? - b.version <=> a.version + versions = b.version <=> a.version + next versions if versions.nonzero? + b.platform == Gem::Platform::RUBY ? -1 : 1 end end @@ -1080,20 +1091,15 @@ class Gem::Specification < Gem::BasicSpecification end def self._latest_specs(specs, prerelease = false) # :nodoc: - result = Hash.new {|h,k| h[k] = {} } - native = {} + result = {} specs.reverse_each do |spec| next if spec.version.prerelease? unless prerelease - native[spec.name] = spec.version if spec.platform == Gem::Platform::RUBY - result[spec.name][spec.platform] = spec + result[spec.name] = spec end - result.map(&:last).map(&:values).flatten.reject do |spec| - minimum = native[spec.name] - minimum && spec.version < minimum - end.sort_by{|tup| tup.name } + result.map(&:last).flatten.sort_by{|tup| tup.name } end ## @@ -1552,7 +1558,6 @@ class Gem::Specification < Gem::BasicSpecification def build_extensions # :nodoc: return if default_gem? return if extensions.empty? - return if installed_by_version < Gem::Version.new('2.2.0.preview.2') return if File.exist? gem_build_complete_path return if !File.writable?(base_dir) return if !File.exist?(File.join(base_dir, 'extensions')) @@ -2123,7 +2128,6 @@ class Gem::Specification < Gem::BasicSpecification def missing_extensions? return false if default_gem? return false if extensions.empty? - return false if installed_by_version < Gem::Version.new('2.2.0.preview.2') return false if File.exist? gem_build_complete_path true @@ -2548,7 +2552,7 @@ class Gem::Specification < Gem::BasicSpecification begin dependencies.each do |dep| next unless dep.runtime? - dep.to_specs.each do |dep_spec| + dep.matching_specs(true).each do |dep_spec| next if visited.has_key?(dep_spec) visited[dep_spec] = true trail.push(dep_spec) diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb index 79bd3e2a60..e2763561c6 100644 --- a/lib/rubygems/test_case.rb +++ b/lib/rubygems/test_case.rb @@ -301,7 +301,9 @@ class Gem::TestCase < Minitest::Test def setup @orig_env = ENV.to_hash - @tmp = Dir.mktmpdir("tmp", Dir.pwd) + @tmp = File.expand_path("tmp") + + FileUtils.mkdir_p @tmp ENV['GEM_VENDOR'] = nil ENV['GEMRC'] = nil @@ -310,7 +312,6 @@ class Gem::TestCase < Minitest::Test ENV['XDG_DATA_HOME'] = nil ENV['SOURCE_DATE_EPOCH'] = nil ENV['BUNDLER_VERSION'] = nil - ENV["TMPDIR"] = @tmp @current_dir = Dir.pwd @fetcher = nil @@ -321,13 +322,10 @@ class Gem::TestCase < Minitest::Test # capture output Gem::DefaultUserInteraction.ui = Gem::MockGemUi.new - tmpdir = File.realpath Dir.tmpdir - tmpdir.tap(&Gem::UNTAINT) - - @tempdir = File.join(tmpdir, "test_rubygems_#{$$}") + @tempdir = Dir.mktmpdir("test_rubygems_", @tmp) @tempdir.tap(&Gem::UNTAINT) - FileUtils.mkdir_p @tempdir + ENV["TMPDIR"] = @tempdir @orig_SYSTEM_WIDE_CONFIG_FILE = Gem::ConfigFile::SYSTEM_WIDE_CONFIG_FILE Gem::ConfigFile.send :remove_const, :SYSTEM_WIDE_CONFIG_FILE @@ -366,7 +364,9 @@ class Gem::TestCase < Minitest::Test Dir.chdir @tempdir ENV['HOME'] = @userhome + Gem.instance_variable_set :@config_file, nil Gem.instance_variable_set :@user_home, nil + Gem.instance_variable_set :@config_home, nil Gem.instance_variable_set :@data_home, nil Gem.instance_variable_set :@gemdeps, nil Gem.instance_variable_set :@env_requirements_by_name, nil @@ -449,7 +449,6 @@ class Gem::TestCase < Minitest::Test Dir.chdir @current_dir FileUtils.rm_rf @tempdir - FileUtils.rm_rf @tmp ENV.replace(@orig_env) diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb index ccf957d6df..e49066f7dc 100644 --- a/spec/bundler/bundler/cli_spec.rb +++ b/spec/bundler/bundler/cli_spec.rb @@ -138,12 +138,12 @@ RSpec.describe "bundle executable" do it "doesn't print defaults" do install_gemfile "", :verbose => true - expect(out).to start_with("Running `bundle install --retry 0 --verbose` with bundler #{Bundler::VERSION}") + expect(out).to start_with("Running `bundle install --verbose` with bundler #{Bundler::VERSION}") end it "doesn't print defaults" do install_gemfile "", :verbose => true - expect(out).to start_with("Running `bundle install --retry 0 --verbose` with bundler #{Bundler::VERSION}") + expect(out).to start_with("Running `bundle install --verbose` with bundler #{Bundler::VERSION}") end end diff --git a/spec/bundler/bundler/compact_index_client/updater_spec.rb b/spec/bundler/bundler/compact_index_client/updater_spec.rb index acb312edb3..cecaddfba4 100644 --- a/spec/bundler/bundler/compact_index_client/updater_spec.rb +++ b/spec/bundler/bundler/compact_index_client/updater_spec.rb @@ -17,7 +17,6 @@ RSpec.describe Bundler::CompactIndexClient::Updater do let(:response) { double(:response, :body => "abc123") } it "treats the response as an update" do - expect(response).to receive(:[]).with("Content-Encoding") { "" } expect(response).to receive(:[]).with("ETag") { nil } expect(fetcher).to receive(:call) { response } @@ -29,8 +28,7 @@ RSpec.describe Bundler::CompactIndexClient::Updater do let(:response) { double(:response, :body => "") } it "raises HTTPError" do - expect(response).to receive(:[]).with("Content-Encoding") { "gzip" } - expect(fetcher).to receive(:call) { response } + expect(fetcher).to receive(:call).and_raise(Zlib::GzipFile::Error) expect do updater.update(local_path, remote_path) @@ -48,4 +46,25 @@ RSpec.describe Bundler::CompactIndexClient::Updater do end.to raise_error(Bundler::PermissionError) end end + + context "when receiving non UTF-8 data and default internal encoding set to ASCII" do + let(:response) { double(:response, :body => "\x8B".b) } + + it "works just fine" do + old_verbose = $VERBOSE + previous_internal_encoding = Encoding.default_internal + + begin + $VERBOSE = false + Encoding.default_internal = "ASCII" + expect(response).to receive(:[]).with("ETag") { nil } + expect(fetcher).to receive(:call) { response } + + updater.update(local_path, remote_path) + ensure + Encoding.default_internal = previous_internal_encoding + $VERBOSE = old_verbose + end + end + end end diff --git a/spec/bundler/bundler/dep_proxy_spec.rb b/spec/bundler/bundler/dep_proxy_spec.rb index 0f8d6b1076..84243d2ee2 100644 --- a/spec/bundler/bundler/dep_proxy_spec.rb +++ b/spec/bundler/bundler/dep_proxy_spec.rb @@ -2,10 +2,10 @@ RSpec.describe Bundler::DepProxy do let(:dep) { Bundler::Dependency.new("rake", ">= 0") } - subject { described_class.new(dep, Gem::Platform::RUBY) } + subject { described_class.get_proxy(dep, Gem::Platform::RUBY) } let(:same) { subject } - let(:other) { subject.dup } - let(:different) { described_class.new(dep, Gem::Platform::JAVA) } + let(:other) { described_class.get_proxy(dep, Gem::Platform::RUBY) } + let(:different) { described_class.get_proxy(dep, Gem::Platform::JAVA) } describe "#eql?" do it { expect(subject.eql?(same)).to be true } @@ -15,8 +15,18 @@ RSpec.describe Bundler::DepProxy do it { expect(subject.eql?("foobar")).to be false } end - describe "#hash" do - it { expect(subject.hash).to eq(same.hash) } - it { expect(subject.hash).to eq(other.hash) } + describe "must use factory methods" do + it { expect { described_class.new(dep, Gem::Platform::RUBY) }.to raise_error NoMethodError } + it { expect { subject.dup }.to raise_error NoMethodError } + it { expect { subject.clone }.to raise_error NoMethodError } + end + + describe "frozen" do + if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.5.0") + error = Object.const_get("FrozenError") + else + error = RuntimeError + end + it { expect { subject.instance_variable_set(:@__platform, {}) }.to raise_error error } end end diff --git a/spec/bundler/bundler/dsl_spec.rb b/spec/bundler/bundler/dsl_spec.rb index ff87b57886..a47dd6e399 100644 --- a/spec/bundler/bundler/dsl_spec.rb +++ b/spec/bundler/bundler/dsl_spec.rb @@ -25,7 +25,7 @@ RSpec.describe Bundler::Dsl do expect { subject.git_source(:example) }.to raise_error(Bundler::InvalidOption) end - context "default hosts", :bundler => "2" do + context "default hosts", :bundler => "< 3" do it "converts :github to URI using https" do subject.gem("sparks", :github => "indirect/sparks") github_uri = "https://github.com/indirect/sparks.git" @@ -195,19 +195,6 @@ RSpec.describe Bundler::Dsl do # gem 'spree_api' # gem 'spree_backend' # end - describe "#github", :bundler => "< 3" do - it "from github" do - spree_gems = %w[spree_core spree_api spree_backend] - subject.github "spree" do - spree_gems.each {|spree_gem| subject.send :gem, spree_gem } - end - - subject.dependencies.each do |d| - expect(d.source.uri).to eq("https://github.com/spree/spree.git") - end - end - end - describe "#github" do it "from github" do spree_gems = %w[spree_core spree_api spree_backend] diff --git a/spec/bundler/bundler/gem_helper_spec.rb b/spec/bundler/bundler/gem_helper_spec.rb index 769c461911..d718615ad2 100644 --- a/spec/bundler/bundler/gem_helper_spec.rb +++ b/spec/bundler/bundler/gem_helper_spec.rb @@ -9,7 +9,8 @@ RSpec.describe Bundler::GemHelper do let(:app_gemspec_path) { app_path.join("#{app_name}.gemspec") } before(:each) do - global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__RUBOCOP" => "false", "BUNDLE_GEM__CI" => "false" + global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__RUBOCOP" => "false", + "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__CHANGELOG" => "false" bundle "gem #{app_name}" prepare_gemspec(app_gemspec_path) end @@ -267,6 +268,14 @@ RSpec.describe Bundler::GemHelper do Rake.application["release"].invoke end + + it "also works with releasing from a branch not yet pushed" do + sys_exec("git checkout -b module_function", :dir => app_path) + + expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s) + + Rake.application["release"].invoke + end end context "on releasing with a custom tag prefix" do diff --git a/spec/bundler/bundler/gem_version_promoter_spec.rb b/spec/bundler/bundler/gem_version_promoter_spec.rb index 01e0232fba..43a3630bbb 100644 --- a/spec/bundler/bundler/gem_version_promoter_spec.rb +++ b/spec/bundler/bundler/gem_version_promoter_spec.rb @@ -28,7 +28,7 @@ RSpec.describe Bundler::GemVersionPromoter do def build_spec_groups(name, versions) versions.map do |v| - Bundler::Resolver::SpecGroup.new(build_spec(name, v)) + Bundler::Resolver::SpecGroup.create_for({ Gem::Platform::RUBY => build_spec(name, v) }, [Gem::Platform::RUBY], Gem::Platform::RUBY) end end @@ -170,7 +170,7 @@ RSpec.describe Bundler::GemVersionPromoter do context "debug output" do it "should not kerblooie on its own debug output" do gvp = unlocking(:level => :patch) - dep = Bundler::DepProxy.new(dep("foo", "1.2.0").first, "ruby") + dep = Bundler::DepProxy.get_proxy(dep("foo", "1.2.0").first, "ruby") result = gvp.send(:debug_format_result, dep, build_spec_groups("foo", %w[1.2.0 1.3.0])) expect(result.class).to eq Array end diff --git a/spec/bundler/bundler/plugin_spec.rb b/spec/bundler/bundler/plugin_spec.rb index c1c88ce480..cc28382043 100644 --- a/spec/bundler/bundler/plugin_spec.rb +++ b/spec/bundler/bundler/plugin_spec.rb @@ -110,6 +110,7 @@ RSpec.describe Bundler::Plugin do before do allow(Plugin::DSL).to receive(:new) { builder } allow(builder).to receive(:eval_gemfile).with(gemfile) + allow(builder).to receive(:check_primary_source_safety) allow(builder).to receive(:to_definition) { definition } allow(builder).to receive(:inferred_plugins) { [] } end diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb index 35702d7763..97f06973cb 100644 --- a/spec/bundler/bundler/source/git/git_proxy_spec.rb +++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb @@ -130,18 +130,20 @@ RSpec.describe Bundler::Source::Git::GitProxy do context "when given a SHA as a revision" do let(:revision) { "abcd" * 10 } let(:command) { ["reset", "--hard", revision] } + let(:command_for_display) { "git #{command.shelljoin}" } it "fails gracefully when resetting to the revision fails" do expect(subject).to receive(:git_retry).with("clone", any_args) { destination.mkpath } expect(subject).to receive(:git_retry).with("fetch", any_args, :dir => destination) - expect(subject).to receive(:git).with(*command, :dir => destination).and_raise(Bundler::Source::Git::GitCommandError.new(command, cache, destination)) + expect(subject).to receive(:git).with(*command, :dir => destination).and_raise(Bundler::Source::Git::GitCommandError.new(command_for_display, destination)) expect(subject).not_to receive(:git) expect { subject.copy_to(destination, submodules) }. to raise_error( Bundler::Source::Git::MissingGitRevisionError, - "Git error: command `git #{command}` in directory #{destination} has failed.\n" \ - "Revision #{revision} does not exist in the repository #{uri}. Maybe you misspelled it?" \ + "Git error: command `#{command_for_display}` in directory #{destination} has failed.\n" \ + "Revision #{revision} does not exist in the repository #{uri}. Maybe you misspelled it?\n" \ + "If this error persists you could try removing the cache directory '#{destination}'" ) end end diff --git a/spec/bundler/bundler/source/git_spec.rb b/spec/bundler/bundler/source/git_spec.rb index f7475a35aa..6668b6e69a 100644 --- a/spec/bundler/bundler/source/git_spec.rb +++ b/spec/bundler/bundler/source/git_spec.rb @@ -14,14 +14,14 @@ RSpec.describe Bundler::Source::Git do describe "#to_s" do it "returns a description" do - expect(subject.to_s).to eq "https://github.com/foo/bar.git (at master)" + expect(subject.to_s).to eq "https://github.com/foo/bar.git" end context "when the URI contains credentials" do let(:uri) { "https://my-secret-token:x-oauth-basic@github.com/foo/bar.git" } it "filters credentials" do - expect(subject.to_s).to eq "https://x-oauth-basic@github.com/foo/bar.git (at master)" + expect(subject.to_s).to eq "https://x-oauth-basic@github.com/foo/bar.git" end end end diff --git a/spec/bundler/bundler/stub_specification_spec.rb b/spec/bundler/bundler/stub_specification_spec.rb index 0a54f9d7b5..fb612813c2 100644 --- a/spec/bundler/bundler/stub_specification_spec.rb +++ b/spec/bundler/bundler/stub_specification_spec.rb @@ -6,6 +6,7 @@ RSpec.describe Bundler::StubSpecification do s.name = "gemname" s.version = "1.0.0" s.loaded_from = __FILE__ + s.extensions = "ext/gemname" end described_class.from_stub(gemspec) @@ -17,4 +18,30 @@ RSpec.describe Bundler::StubSpecification do expect(stub).to be(with_bundler_stub_spec) end end + + describe "#manually_installed?" do + it "returns true if installed_by_version is nil or 0" do + stub = described_class.from_stub(with_bundler_stub_spec) + expect(stub.manually_installed?).to be true + end + + it "returns false if installed_by_version is greater than 0" do + stub = described_class.from_stub(with_bundler_stub_spec) + stub.installed_by_version = Gem::Version.new(1) + expect(stub.manually_installed?).to be false + end + end + + describe "#missing_extensions?" do + it "returns false if manually_installed?" do + stub = described_class.from_stub(with_bundler_stub_spec) + expect(stub.missing_extensions?).to be false + end + + it "returns true if not manually_installed?" do + stub = described_class.from_stub(with_bundler_stub_spec) + stub.installed_by_version = Gem::Version.new(1) + expect(stub.missing_extensions?).to be true + end + end end diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb index 97d73907db..25f12a9e87 100644 --- a/spec/bundler/cache/git_spec.rb +++ b/spec/bundler/cache/git_spec.rb @@ -200,7 +200,7 @@ RSpec.describe "bundle cache with git" do gem "foo", :git => '#{lib_path("foo-1.0")}' G bundle "config set cache_all true" - bundle :cache, "all-platforms" => true, :install => false, :path => "./vendor/cache" + bundle :cache, "all-platforms" => true, :install => false simulate_new_machine with_path_as "" do diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb index f4a57039ad..3b8bce4b01 100644 --- a/spec/bundler/commands/cache_spec.rb +++ b/spec/bundler/commands/cache_spec.rb @@ -1,6 +1,19 @@ # frozen_string_literal: true RSpec.describe "bundle cache" do + it "doesn't update the cache multiple times, even if it already exists" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + G + + bundle :cache + expect(out).to include("Updating files in vendor/cache").once + + bundle :cache + expect(out).to include("Updating files in vendor/cache").once + end + context "with --gemfile" do it "finds the gemfile" do gemfile bundled_app("NotGemfile"), <<-G diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb index 6485eb6a5a..0d104ad304 100644 --- a/spec/bundler/commands/exec_spec.rb +++ b/spec/bundler/commands/exec_spec.rb @@ -668,29 +668,40 @@ RSpec.describe "bundle exec" do subject { bundle "exec #{path} arg1 arg2", :raise_on_error => false } - shared_examples_for "it runs" do - it "like a normally executed executable" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject - expect(exitstatus).to eq(exit_code) - expect(err).to eq(expected_err) - expect(out).to eq(expected) - end + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) end - it_behaves_like "it runs" - context "the executable exits explicitly" do let(:executable) { super() << "\nexit #{exit_code}\nputs 'POST_EXIT'\n" } context "with exit 0" do - it_behaves_like "it runs" + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end context "with exit 99" do let(:exit_code) { 99 } - it_behaves_like "it runs" + + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end end @@ -707,7 +718,15 @@ RSpec.describe "bundle exec" do # this is specified by C99 128 + 15 end - it_behaves_like "it runs" + + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end context "the executable is empty" do @@ -716,7 +735,15 @@ RSpec.describe "bundle exec" do let(:exit_code) { 0 } let(:expected_err) { "#{path} is empty" } let(:expected) { "" } - it_behaves_like "it runs" + + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end context "the executable raises" do @@ -743,12 +770,27 @@ RSpec.describe "bundle exec" do let(:expected_err) { "bundler: failed to load command: #{path} (#{path})\n#{system_gem_path("bin/bundle")}: Err (Err)" } let(:expected) { super() } - it_behaves_like "it runs" + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end context "when the file uses the current ruby shebang" do let(:shebang) { "#!#{Gem.ruby}" } - it_behaves_like "it runs" + + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end context "when Bundler.setup fails", :bundler => "< 3" do @@ -762,11 +804,19 @@ RSpec.describe "bundle exec" do let(:exit_code) { Bundler::GemNotFound.new.status_code } let(:expected) { "" } let(:expected_err) { <<-EOS.strip } -\e[31mCould not find gem 'rack (= 2)' in any of the gem sources listed in your Gemfile.\e[0m +\e[31mCould not find gem 'rack (= 2)' in locally installed gems. +The source contains the following versions of 'rack': 0.9.1, 1.0.0\e[0m \e[33mRun `bundle install` to install missing gems.\e[0m EOS - it_behaves_like "it runs" + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end context "when Bundler.setup fails", :bundler => "3" do @@ -785,14 +835,28 @@ The source contains the following versions of 'rack': 1.0.0\e[0m \e[33mRun `bundle install` to install missing gems.\e[0m EOS - it_behaves_like "it runs" + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end context "when the executable exits non-zero via at_exit" do let(:executable) { super() + "\n\nat_exit { $! ? raise($!) : exit(1) }" } let(:exit_code) { 1 } - it_behaves_like "it runs" + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end context "when disable_exec_load is set" do @@ -803,7 +867,14 @@ The source contains the following versions of 'rack': 1.0.0\e[0m bundle "config set disable_exec_load true" end - it_behaves_like "it runs" + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end context "regarding $0 and __FILE__" do @@ -819,12 +890,26 @@ $0: #{path.to_s.inspect} __FILE__: #{path.to_s.inspect} EOS - it_behaves_like "it runs" + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end context "when the path is relative" do let(:path) { super().relative_path_from(bundled_app) } - it_behaves_like "it runs" + it "runs" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end context "when the path is relative with a leading ./" do diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index afc3b03a53..7724170a63 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -220,6 +220,30 @@ RSpec.describe "bundle lock" do expect(lockfile.platforms).to match_array(local_platforms.unshift(java, mingw).uniq) end + it "supports adding new platforms with force_ruby_platform = true" do + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + platform_specific (1.0) + platform_specific (1.0-x86-linux) + + PLATFORMS + ruby + x86-linux + + DEPENDENCIES + platform_specific + L + + bundle "config set force_ruby_platform true" + 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, mingw) + end + it "supports adding the `ruby` platform" do bundle "lock --add-platform ruby" @@ -467,6 +491,51 @@ RSpec.describe "bundle lock" do end end + it "does not conflict on ruby requirements when adding new platforms" do + next_minor = Gem.ruby_version.segments[0..1].map.with_index {|s, i| i == 1 ? s + 1 : s }.join(".") + + build_repo4 do + build_gem "raygun-apm", "1.0.78" do |s| + s.platform = "x86_64-linux" + s.required_ruby_version = "< #{next_minor}.dev" + end + + build_gem "raygun-apm", "1.0.78" do |s| + s.platform = "universal-darwin" + s.required_ruby_version = "< #{next_minor}.dev" + end + + build_gem "raygun-apm", "1.0.78" do |s| + s.platform = "x64-mingw32" + s.required_ruby_version = "< #{next_minor}.dev" + end + end + + gemfile <<-G + source "https://localgemserver.test" + + gem "raygun-apm" + G + + lockfile <<-L + GEM + remote: https://localgemserver.test/ + specs: + raygun-apm (1.0.78-universal-darwin) + + PLATFORMS + x86_64-darwin-19 + + DEPENDENCIES + raygun-apm + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "lock --add-platform x86_64-linux", :artifice => :compact_index, :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + end + context "when an update is available" do let(:repo) { gem_repo2 } diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb index 8aca745bef..c1d5e1814d 100644 --- a/spec/bundler/commands/newgem_spec.rb +++ b/spec/bundler/commands/newgem_spec.rb @@ -12,10 +12,10 @@ RSpec.describe "bundle gem" do def bundle_exec_rubocop prepare_gemspec(bundled_app(gem_name, "#{gem_name}.gemspec")) - rubocop_version = RUBY_VERSION > "2.4" ? "0.90.0" : "0.80.1" + rubocop_version = RUBY_VERSION > "2.4" ? "1.7.0" : "0.81.0" gems = ["minitest", "rake", "rake-compiler", "rspec", "rubocop -v #{rubocop_version}", "test-unit"] gems.unshift "parallel -v 1.19.2" if RUBY_VERSION < "2.5" - gems += ["rubocop-ast -v 0.4.0"] if rubocop_version == "0.90.0" + gems += ["rubocop-ast -v 1.4.0"] if rubocop_version == "1.7.0" path = Bundler.feature_flag.default_install_uses_path? ? local_gem_path(:base => bundled_app(gem_name)) : system_gem_path realworld_system_gems gems, :path => path bundle "exec rubocop --debug --config .rubocop.yml", :dir => bundled_app(gem_name) @@ -28,44 +28,9 @@ RSpec.describe "bundle gem" do let(:require_path) { "mygem" } before do - git_config_content = <<-EOF - [user] - name = "Bundler User" - email = user@example.com - [github] - user = bundleuser - EOF - @git_config_location = ENV["GIT_CONFIG"] - path = "#{tmp}/test_git_config.txt" - File.open(path, "w") {|f| f.write(git_config_content) } - ENV["GIT_CONFIG"] = path - end - - after do - FileUtils.rm(ENV["GIT_CONFIG"]) if File.exist?(ENV["GIT_CONFIG"]) - ENV["GIT_CONFIG"] = @git_config_location - end - - shared_examples_for "git config is present" do - context "git config user.{name,email} present" do - it "sets gemspec author to git user.name if available" do - expect(generated_gemspec.authors.first).to eq("Bundler User") - end - - it "sets gemspec email to git user.email if available" do - expect(generated_gemspec.email.first).to eq("user@example.com") - end - end - end - - shared_examples_for "git config is absent" do - it "sets gemspec author to default message if git user.name is not set or empty" do - expect(generated_gemspec.authors.first).to eq("TODO: Write your name") - end - - it "sets gemspec email to default message if git user.email is not set or empty" do - expect(generated_gemspec.email.first).to eq("TODO: Write your email address") - end + sys_exec("git config --global user.name 'Bundler User'") + sys_exec("git config --global user.email user@example.com") + sys_exec("git config --global github.user bundleuser") end describe "git repo initialization" do @@ -125,19 +90,24 @@ RSpec.describe "bundle gem" do end shared_examples_for "--coc flag" do - before do - bundle "gem #{gem_name} --coc" - end it "generates a gem skeleton with MIT license" do + bundle "gem #{gem_name} --coc" gem_skeleton_assertions expect(bundled_app("#{gem_name}/CODE_OF_CONDUCT.md")).to exist end - describe "README additions" do - it "generates the README with a section for the Code of Conduct" do - expect(bundled_app("#{gem_name}/README.md").read).to include("## Code of Conduct") - expect(bundled_app("#{gem_name}/README.md").read).to include("https://github.com/bundleuser/#{gem_name}/blob/master/CODE_OF_CONDUCT.md") - end + it "generates the README with a section for the Code of Conduct" do + bundle "gem #{gem_name} --coc" + expect(bundled_app("#{gem_name}/README.md").read).to include("## Code of Conduct") + expect(bundled_app("#{gem_name}/README.md").read).to match(%r{https://github\.com/bundleuser/#{gem_name}/blob/.*/CODE_OF_CONDUCT.md}) + end + + it "generates the README with a section for the Code of Conduct, respecting the configured git default branch" do + sys_exec("git config --global init.defaultBranch main") + bundle "gem #{gem_name} --coc" + + expect(bundled_app("#{gem_name}/README.md").read).to include("## Code of Conduct") + expect(bundled_app("#{gem_name}/README.md").read).to include("https://github.com/bundleuser/#{gem_name}/blob/main/CODE_OF_CONDUCT.md") end end @@ -150,11 +120,29 @@ RSpec.describe "bundle gem" do expect(bundled_app("#{gem_name}/CODE_OF_CONDUCT.md")).to_not exist end - describe "README additions" do - it "generates the README without a section for the Code of Conduct" do - expect(bundled_app("#{gem_name}/README.md").read).not_to include("## Code of Conduct") - expect(bundled_app("#{gem_name}/README.md").read).not_to include("https://github.com/bundleuser/#{gem_name}/blob/master/CODE_OF_CONDUCT.md") - end + it "generates the README without a section for the Code of Conduct" do + expect(bundled_app("#{gem_name}/README.md").read).not_to include("## Code of Conduct") + expect(bundled_app("#{gem_name}/README.md").read).not_to match(%r{https://github\.com/bundleuser/#{gem_name}/blob/.*/CODE_OF_CONDUCT.md}) + end + end + + shared_examples_for "--changelog flag" do + before do + bundle "gem #{gem_name} --changelog" + end + it "generates a gem skeleton with a CHANGELOG", :readline do + gem_skeleton_assertions + expect(bundled_app("#{gem_name}/CHANGELOG.md")).to exist + end + end + + shared_examples_for "--no-changelog flag" do + before do + bundle "gem #{gem_name} --no-changelog" + end + it "generates a gem skeleton without a CHANGELOG", :readline do + gem_skeleton_assertions + expect(bundled_app("#{gem_name}/CHANGELOG.md")).to_not exist end end @@ -282,7 +270,7 @@ RSpec.describe "bundle gem" do context "git config github.user is absent" do before do - sys_exec("git config --unset github.user") + sys_exec("git config --global --unset github.user") bundle "gem #{gem_name}" end @@ -393,17 +381,29 @@ RSpec.describe "bundle gem" do bundle "gem #{gem_name}" end - it_should_behave_like "git config is present" + it "sets gemspec author to git user.name if available" do + expect(generated_gemspec.authors.first).to eq("Bundler User") + end + + it "sets gemspec email to git user.email if available" do + expect(generated_gemspec.email.first).to eq("user@example.com") + end end context "git config user.{name,email} is not set" do before do - sys_exec("git config --unset user.name", :dir => bundled_app) - sys_exec("git config --unset user.email", :dir => bundled_app) + sys_exec("git config --global --unset user.name") + sys_exec("git config --global --unset user.email") bundle "gem #{gem_name}" end - it_should_behave_like "git config is absent" + it "sets gemspec author to default message if git user.name is not set or empty" do + expect(generated_gemspec.authors.first).to eq("TODO: Write your name") + end + + it "sets gemspec email to default message if git user.email is not set or empty" do + expect(generated_gemspec.email.first).to eq("TODO: Write your email address") + end end it "sets gemspec metadata['allowed_push_host']" do @@ -416,9 +416,7 @@ RSpec.describe "bundle gem" do it "sets a minimum ruby version" do bundle "gem #{gem_name}" - bundler_gemspec = Bundler::GemHelper.new(gemspec_dir).gemspec - - expect(bundler_gemspec.required_ruby_version).to eq(generated_gemspec.required_ruby_version) + expect(generated_gemspec.required_ruby_version).to eq(Gem::Requirement.new(Gem.ruby_version < Gem::Version.new("2.4.a") ? ">= 2.3.0" : ">= 2.4.0")) end it "requires the version file" do @@ -904,6 +902,22 @@ RSpec.describe "bundle gem" do it_behaves_like "--rubocop flag" it_behaves_like "--no-rubocop flag" end + + context "with changelog option in bundle config settings set to true" do + before do + global_config "BUNDLE_GEM__CHANGELOG" => "true" + end + it_behaves_like "--changelog flag" + it_behaves_like "--no-changelog flag" + end + + context "with changelog option in bundle config settings set to false" do + before do + global_config "BUNDLE_GEM__CHANGELOG" => "false" + end + it_behaves_like "--changelog flag" + it_behaves_like "--no-changelog flag" + end end context "gem naming with underscore", :readline do @@ -1089,13 +1103,24 @@ Usage: "bundle gem NAME [OPTIONS]" expect(bundled_app("foobar/CODE_OF_CONDUCT.md")).to exist end + + it "asks about CHANGELOG" do + global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__RUBOCOP" => "false", + "BUNDLE_GEM__COC" => "false" + + bundle "gem foobar" do |input, _, _| + input.puts "yes" + end + + expect(bundled_app("foobar/CHANGELOG.md")).to exist + end end context "on conflicts with a previously created file", :readline do it "should fail gracefully" do FileUtils.touch(bundled_app("conflict-foobar")) bundle "gem conflict-foobar", :raise_on_error => false - expect(err).to include("Errno::ENOTDIR") + expect(err).to eq("Couldn't create a new gem named `conflict-foobar` because there's an existing file named `conflict-foobar`.") expect(exitstatus).to eql(32) end end diff --git a/spec/bundler/commands/post_bundle_message_spec.rb b/spec/bundler/commands/post_bundle_message_spec.rb index 710152e59f..104221efbe 100644 --- a/spec/bundler/commands/post_bundle_message_spec.rb +++ b/spec/bundler/commands/post_bundle_message_spec.rb @@ -113,16 +113,7 @@ RSpec.describe "post bundle message" do bundle "config set force_ruby_platform true" end - it "should report a helpful error message", :bundler => "< 3" do - install_gemfile <<-G, :raise_on_error => false - source "#{file_uri_for(gem_repo1)}" - gem "rack" - gem "not-a-gem", :group => :development - G - expect(err).to include("Could not find gem 'not-a-gem' in any of the gem sources listed in your Gemfile.") - end - - it "should report a helpful error message", :bundler => "3" do + it "should report a helpful error message" do install_gemfile <<-G, :raise_on_error => false source "#{file_uri_for(gem_repo1)}" gem "rack" diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb index b8de6507f6..521c175711 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -651,6 +651,8 @@ RSpec.describe "bundle update when a gem depends on a newer version of bundler" build_gem "rails", "3.0.1" do |s| s.add_dependency "bundler", Bundler::VERSION.succ end + + build_gem "bundler", Bundler::VERSION.succ end gemfile <<-G @@ -659,18 +661,11 @@ RSpec.describe "bundle update when a gem depends on a newer version of bundler" G end - it "should explain that bundler conflicted", :bundler => "< 3" do + it "should explain that bundler conflicted and how to resolve the conflict" do bundle "update", :all => true, :raise_on_error => false expect(last_command.stdboth).not_to match(/in snapshot/i) expect(err).to match(/current Bundler version/i). - and match(/perhaps you need to update bundler/i) - end - - it "should warn that the newer version of Bundler would conflict", :bundler => "3" do - bundle "update", :all => true - expect(err).to include("rails (3.0.1) has dependency bundler"). - and include("so the dependency is being ignored") - expect(the_bundle).to include_gem "rails 3.0.1" + and match(/Install the necessary version with `gem install bundler:#{Bundler::VERSION.succ}`/i) end end diff --git a/spec/bundler/install/bundler_spec.rb b/spec/bundler/install/bundler_spec.rb index af1ff7225a..963ce82db8 100644 --- a/spec/bundler/install/bundler_spec.rb +++ b/spec/bundler/install/bundler_spec.rb @@ -21,17 +21,57 @@ RSpec.describe "bundle install" do expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}" end - it "are not added if not already present" do + it "are forced to the current bundler version even if not already present" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack" G - expect(the_bundle).not_to include_gems "bundler #{Bundler::VERSION}" + expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}" end - it "causes a conflict if explicitly requesting a different version" do - bundle "config set force_ruby_platform true" + it "causes a conflict if explicitly requesting a different version of bundler" do + install_gemfile <<-G, :raise_on_error => false + source "#{file_uri_for(gem_repo2)}" + gem "rails", "3.0" + gem "bundler", "0.9.1" + G + nice_error = <<-E.strip.gsub(/^ {8}/, "") + Bundler could not find compatible versions for gem "bundler": + In Gemfile: + bundler (= 0.9.1) + + Current Bundler version: + bundler (#{Bundler::VERSION}) + + Your bundle requires a different version of Bundler than the one you're running. + Install the necessary version with `gem install bundler:0.9.1` and rerun bundler using `bundle _0.9.1_ install` + E + expect(err).to include(nice_error) + end + + it "causes a conflict if explicitly requesting a non matching requirement on bundler" do + install_gemfile <<-G, :raise_on_error => false + source "#{file_uri_for(gem_repo2)}" + gem "rails", "3.0" + gem "bundler", "~> 0.8" + G + + nice_error = <<-E.strip.gsub(/^ {8}/, "") + Bundler could not find compatible versions for gem "bundler": + In Gemfile: + bundler (~> 0.8) + + Current Bundler version: + bundler (#{Bundler::VERSION}) + + Your bundle requires a different version of Bundler than the one you're running. + Install the necessary version with `gem install bundler:0.9.1` and rerun bundler using `bundle _0.9.1_ install` + E + expect(err).to include(nice_error) + end + + it "causes a conflict if explicitly requesting a version of bundler that doesn't exist" do install_gemfile <<-G, :raise_on_error => false source "#{file_uri_for(gem_repo2)}" gem "rails", "3.0" @@ -45,10 +85,8 @@ RSpec.describe "bundle install" do Current Bundler version: bundler (#{Bundler::VERSION}) - This Gemfile requires a different version of Bundler. - Perhaps you need to update Bundler by running `gem install bundler`? - Could not find gem 'bundler (= 0.9.2)' in any + Your bundle requires a different version of Bundler than the one you're running, and that version could not be found. E expect(err).to include(nice_error) end @@ -197,27 +235,5 @@ RSpec.describe "bundle install" do bundle "check" expect(out).to include("The Gemfile's dependencies are satisfied") end - - context "with allow_bundler_dependency_conflicts set" do - before { bundle "config set allow_bundler_dependency_conflicts true" } - - it "are forced to the current bundler version with warnings when no compatible version is found" do - build_repo4 do - build_gem "requires_nonexistant_bundler" do |s| - s.add_runtime_dependency "bundler", "99.99.99.99" - end - end - - install_gemfile <<-G - source "#{file_uri_for(gem_repo4)}" - gem "requires_nonexistant_bundler" - G - - expect(err).to include "requires_nonexistant_bundler (1.0) has dependency bundler (= 99.99.99.99), " \ - "which is unsatisfied by the current bundler version #{Bundler::VERSION}, so the dependency is being ignored" - - expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}", "requires_nonexistant_bundler 1.0" - end - end end end diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb index 5f4fc71499..f3898c0a65 100644 --- a/spec/bundler/install/deploy_spec.rb +++ b/spec/bundler/install/deploy_spec.rb @@ -85,6 +85,18 @@ RSpec.describe "install in deployment or frozen mode" do bundle :install end + it "works when path gems are specified twice" do + build_lib "foo", :path => lib_path("nested/foo") + gemfile <<-G + gem "foo", :path => "#{lib_path("nested/foo")}" + gem "foo", :path => "#{lib_path("nested/foo")}" + G + + bundle :install + bundle "config set --local deployment true" + bundle :install + end + it "works when there are credentials in the source URL" do install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true) source "http://user:pass@localgemserver.test/" @@ -341,7 +353,7 @@ RSpec.describe "install in deployment or frozen mode" do bundle "config set --local deployment true" bundle :install, :raise_on_error => false expect(err).to include("deployment mode") - expect(err).to include("You have added to the Gemfile:\n* source: git://hubz.com (at master)") + expect(err).to include("You have added to the Gemfile:\n* source: git://hubz.com") expect(err).not_to include("You have changed in the Gemfile") end @@ -361,7 +373,7 @@ RSpec.describe "install in deployment or frozen mode" do bundle "config set --local deployment true" bundle :install, :raise_on_error => false expect(err).to include("deployment mode") - expect(err).to include("You have deleted from the Gemfile:\n* source: #{lib_path("rack-1.0")} (at master@#{revision_for(lib_path("rack-1.0"))[0..6]}") + expect(err).to include("You have deleted from the Gemfile:\n* source: #{lib_path("rack-1.0")}") expect(err).not_to include("You have added to the Gemfile") expect(err).not_to include("You have changed in the Gemfile") end @@ -385,7 +397,7 @@ RSpec.describe "install in deployment or frozen mode" do bundle "config set --local deployment true" bundle :install, :raise_on_error => false expect(err).to include("deployment mode") - expect(err).to include("You have changed in the Gemfile:\n* rack from `no specified source` to `#{lib_path("rack")} (at master@#{revision_for(lib_path("rack"))[0..6]})`") + expect(err).to include("You have changed in the Gemfile:\n* rack from `no specified source` to `#{lib_path("rack")}`") expect(err).not_to include("You have added to the Gemfile") expect(err).not_to include("You have deleted from the Gemfile") end @@ -401,7 +413,7 @@ RSpec.describe "install in deployment or frozen mode" do gem "rack-obama" G - expect(the_bundle).not_to include_gems "rack 1.0.0" + run "require 'rack'", :raise_on_error => false expect(err).to include strip_whitespace(<<-E).strip The dependencies in your gemfile changed diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb index b41a083d92..a70b950e1b 100644 --- a/spec/bundler/install/gemfile/gemspec_spec.rb +++ b/spec/bundler/install/gemfile/gemspec_spec.rb @@ -422,14 +422,13 @@ RSpec.describe "bundle install from an existing gemspec" do end end - %w[ruby jruby].each do |platform| - simulate_platform(platform) do - install_gemfile <<-G - source "#{file_uri_for(gem_repo2)}" - gemspec - G - end - end + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gemspec + G + + simulate_platform("ruby") { bundle "install" } + simulate_platform("jruby") { bundle "install" } end context "on ruby" do diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb index 2e0c0bcf6d..ba8f253b0e 100644 --- a/spec/bundler/install/gemfile/git_spec.rb +++ b/spec/bundler/install/gemfile/git_spec.rb @@ -598,6 +598,24 @@ RSpec.describe "bundle install with git sources" do bundle :install, :raise_on_error => false expect(err).to match(/The Gemfile lock is pointing to revision \w+/) end + + it "does not explode on invalid revision on install" do + build_git "rack", "0.8" + + build_git "rack", "0.8", :path => lib_path("local-rack") do |s| + s.write "lib/rack.rb", "puts :LOCAL" + end + + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + G + + bundle %(config set local.rack #{lib_path("local-rack")}) + bundle %(config set disable_local_revision_check true) + bundle :install + expect(out).to match(/Bundle complete!/) + end end describe "specified inline" do @@ -629,8 +647,6 @@ RSpec.describe "bundle install with git sources" do end it "installs dependencies from git even if a newer gem is available elsewhere" do - skip "override is not winning" if Gem.win_platform? - system_gems "rack-1.0.0" build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s| diff --git a/spec/bundler/install/gemfile/path_spec.rb b/spec/bundler/install/gemfile/path_spec.rb index fc40c5e9b9..3463c5ec06 100644 --- a/spec/bundler/install/gemfile/path_spec.rb +++ b/spec/bundler/install/gemfile/path_spec.rb @@ -136,8 +136,6 @@ RSpec.describe "bundle install with explicit source paths" do end it "installs dependencies from the path even if a newer gem is available elsewhere" do - skip "override is not winning" if Gem.win_platform? - system_gems "rack-1.0.0" build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s| @@ -581,6 +579,71 @@ RSpec.describe "bundle install with explicit source paths" do expect(the_bundle).to include_gems "rack 0.9.1" end + + it "keeps using the same version even when another dependency is added" do + build_lib "foo", "1.0", :path => lib_path("foo") do |s| + s.add_dependency "rack", "0.9.1" + end + + bundle "install" + + expect(the_bundle).to include_gems "rack 0.9.1" + + lockfile_should_be <<-G + PATH + remote: #{lib_path("foo")} + specs: + foo (1.0) + rack (= 0.9.1) + + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + rack (0.9.1) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo! + + BUNDLED WITH + #{Bundler::VERSION} + G + + build_lib "foo", "1.0", :path => lib_path("foo") do |s| + s.add_dependency "rack" + s.add_dependency "rake", "13.0.1" + end + + bundle "install" + + lockfile_should_be <<-G + PATH + remote: #{lib_path("foo")} + specs: + foo (1.0) + rack + rake (= 13.0.1) + + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + rack (0.9.1) + rake (13.0.1) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo! + + BUNDLED WITH + #{Bundler::VERSION} + G + + expect(the_bundle).to include_gems "rack 0.9.1" + end end describe "switching sources" do @@ -636,8 +699,6 @@ RSpec.describe "bundle install with explicit source paths" do describe "when there are both a gemspec and remote gems" do it "doesn't query rubygems for local gemspec name" do - skip "platform issues" if Gem.win_platform? - build_lib "private_lib", "2.2", :path => lib_path("private_lib") gemfile = <<-G source "http://localgemserver.test" diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb index 1a3794dffe..c49594183e 100644 --- a/spec/bundler/install/gemfile/platform_spec.rb +++ b/spec/bundler/install/gemfile/platform_spec.rb @@ -88,14 +88,15 @@ RSpec.describe "bundle install across platforms" do simulate_new_machine simulate_platform "ruby" - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - - gem "nokogiri" - G + bundle "install" expect(the_bundle).to include_gems "nokogiri 1.4.2" expect(the_bundle).not_to include_gems "weakling" + + simulate_platform "java" + bundle "install" + + expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3" end it "does not keep unneeded platforms for gems that are used" do @@ -241,20 +242,6 @@ RSpec.describe "bundle install across platforms" do end end - it "works the other way with gems that have different dependencies" do - simulate_platform "ruby" - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - - gem "nokogiri" - G - - simulate_platform "java" - bundle "install" - - expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3" - end - it "works with gems with platform-specific dependency having different requirements order" do simulate_platform x64_mac @@ -299,6 +286,48 @@ RSpec.describe "bundle install across platforms" do bundle :install expect(vendored_gems("gems/rack-1.0.0")).to exist end + + it "keeps existing platforms when installing with force_ruby_platform" do + lockfile <<-G + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + platform_specific (1.0-java) + + PLATFORMS + java + + DEPENDENCIES + platform_specific + G + + bundle "config set --local force_ruby_platform true" + + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "platform_specific" + G + + expect(the_bundle).to include_gem "platform_specific 1.0 RUBY" + + lockfile_should_be <<-G + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + platform_specific (1.0) + platform_specific (1.0-java) + + PLATFORMS + java + ruby + + DEPENDENCIES + platform_specific + + BUNDLED WITH + #{Bundler::VERSION} + G + end end RSpec.describe "bundle install with platform conditionals" do diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb index 75b7b67e98..b388b17881 100644 --- a/spec/bundler/install/gemfile/sources_spec.rb +++ b/spec/bundler/install/gemfile/sources_spec.rb @@ -27,7 +27,7 @@ RSpec.describe "bundle install with gems on multiple sources" do G end - it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", :bundler => "2" do + it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", :bundler => "< 3" do bundle :install expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") @@ -54,7 +54,7 @@ RSpec.describe "bundle install with gems on multiple sources" do G end - it "warns about ambiguous gems, but installs anyway", :bundler => "2" do + it "warns about ambiguous gems, but installs anyway", :bundler => "< 3" do bundle :install expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") expect(err).to include("Installed from: #{file_uri_for(gem_repo1)}") @@ -96,7 +96,7 @@ RSpec.describe "bundle install with gems on multiple sources" do it "installs the gems without any warning" do bundle :install - expect(out).not_to include("Warning") + expect(err).not_to include("Warning") expect(the_bundle).to include_gems("rack-obama 1.0.0") expect(the_bundle).to include_gems("rack 1.0.0", :source => "remote1") end @@ -128,7 +128,7 @@ RSpec.describe "bundle install with gems on multiple sources" do end end - gemfile <<-G + install_gemfile <<-G source "#{file_uri_for(gem_repo3)}" gem "rack-obama" # should come from repo3! gem "rack", :source => "#{file_uri_for(gem_repo1)}" @@ -136,8 +136,7 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "installs the gems without any warning" do - bundle :install - expect(out).not_to include("Warning") + expect(err).not_to include("Warning") expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0") end end @@ -173,8 +172,8 @@ RSpec.describe "bundle install with gems on multiple sources" do it "installs from the same source without any warning" do bundle :install - expect(out).not_to include("Warning") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") + expect(err).not_to include("Warning") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") end end @@ -188,26 +187,19 @@ RSpec.describe "bundle install with gems on multiple sources" do end end - context "when disable_multisource is set" do - before do - bundle "config set disable_multisource true" - end + it "installs from the same source without any warning" do + bundle :install - it "installs from the same source without any warning" do - bundle :install + expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") - expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.") - expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") + # In https://github.com/bundler/bundler/issues/3585 this failed + # when there is already a lock file, and the gems are missing, so try again + system_gems [] + bundle :install - # when there is already a lock file, and the gems are missing, so try again - system_gems [] - bundle :install - - expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.") - expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") - end + expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") end end end @@ -222,7 +214,7 @@ RSpec.describe "bundle install with gems on multiple sources" do context "and not in any other sources" do before do - gemfile <<-G + install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" source "#{file_uri_for(gem_repo3)}" do gem "depends_on_rack" @@ -231,8 +223,7 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "installs from the other source without any warning" do - bundle :install - expect(out).not_to include("Warning") + expect(err).not_to include("Warning") expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") end end @@ -248,7 +239,7 @@ RSpec.describe "bundle install with gems on multiple sources" do G end - it "installs from the other source and warns about ambiguous gems", :bundler => "2" do + it "installs from the other source and warns about ambiguous gems", :bundler => "< 3" do bundle :install expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") expect(err).to include("Installed from: #{file_uri_for(gem_repo2)}") @@ -280,7 +271,7 @@ RSpec.describe "bundle install with gems on multiple sources" do G end - it "installs the dependency from the pinned source without warning", :bundler => "2" do + it "installs the dependency from the pinned source without warning", :bundler => "< 3" do bundle :install expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") @@ -304,6 +295,33 @@ RSpec.describe "bundle install with gems on multiple sources" do end end + context "when a top-level gem can only be found in an scoped source" do + before do + build_repo2 + + build_repo gem_repo3 do + build_gem "private_gem_1", "1.0.0" + build_gem "private_gem_2", "1.0.0" + end + + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + + gem "private_gem_1" + + source "#{file_uri_for(gem_repo3)}" do + gem "private_gem_2" + end + G + end + + it "fails" do + bundle :install, :raise_on_error => false + expect(err).to include("Could not find gem 'private_gem_1' in rubygems repository #{file_uri_for(gem_repo2)}/ or installed locally.") + expect(err).to include("The source does not contain any versions of 'private_gem_1'") + end + end + context "when a top-level gem has an indirect dependency" do context "when disable_multisource is set" do before do @@ -381,6 +399,200 @@ RSpec.describe "bundle install with gems on multiple sources" do end end end + + context "when the lockfile has aggregated rubygems sources and newer versions of dependencies are available" do + before do + build_repo gem_repo2 do + build_gem "activesupport", "6.0.3.4" do |s| + s.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2" + s.add_dependency "i18n", ">= 0.7", "< 2" + s.add_dependency "minitest", "~> 5.1" + s.add_dependency "tzinfo", "~> 1.1" + s.add_dependency "zeitwerk", "~> 2.2", ">= 2.2.2" + end + + build_gem "activesupport", "6.1.2.1" do |s| + s.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2" + s.add_dependency "i18n", ">= 1.6", "< 2" + s.add_dependency "minitest", ">= 5.1" + s.add_dependency "tzinfo", "~> 2.0" + s.add_dependency "zeitwerk", "~> 2.3" + end + + build_gem "concurrent-ruby", "1.1.8" + build_gem "concurrent-ruby", "1.1.9" + build_gem "connection_pool", "2.2.3" + + build_gem "i18n", "1.8.9" do |s| + s.add_dependency "concurrent-ruby", "~> 1.0" + end + + build_gem "minitest", "5.14.3" + build_gem "rack", "2.2.3" + build_gem "redis", "4.2.5" + + build_gem "sidekiq", "6.1.3" do |s| + s.add_dependency "connection_pool", ">= 2.2.2" + s.add_dependency "rack", "~> 2.0" + s.add_dependency "redis", ">= 4.2.0" + end + + build_gem "thread_safe", "0.3.6" + + build_gem "tzinfo", "1.2.9" do |s| + s.add_dependency "thread_safe", "~> 0.1" + end + + build_gem "tzinfo", "2.0.4" do |s| + s.add_dependency "concurrent-ruby", "~> 1.0" + end + + build_gem "zeitwerk", "2.4.2" + end + + build_repo gem_repo3 do + build_gem "sidekiq-pro", "5.2.1" do |s| + s.add_dependency "connection_pool", ">= 2.2.3" + s.add_dependency "sidekiq", ">= 6.1.0" + end + end + + gemfile <<-G + # frozen_string_literal: true + + source "#{file_uri_for(gem_repo2)}" + + gem "activesupport" + + source "#{file_uri_for(gem_repo3)}" do + gem "sidekiq-pro" + end + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo2)}/ + remote: #{file_uri_for(gem_repo3)}/ + specs: + activesupport (6.0.3.4) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + zeitwerk (~> 2.2, >= 2.2.2) + concurrent-ruby (1.1.8) + connection_pool (2.2.3) + i18n (1.8.9) + concurrent-ruby (~> 1.0) + minitest (5.14.3) + rack (2.2.3) + redis (4.2.5) + sidekiq (6.1.3) + connection_pool (>= 2.2.2) + rack (~> 2.0) + redis (>= 4.2.0) + sidekiq-pro (5.2.1) + connection_pool (>= 2.2.3) + sidekiq (>= 6.1.0) + thread_safe (0.3.6) + tzinfo (1.2.9) + thread_safe (~> 0.1) + zeitwerk (2.4.2) + + PLATFORMS + #{specific_local_platform} + + DEPENDENCIES + activesupport + sidekiq-pro! + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "upgrades gems when running bundle update, without printing any warnings or errors" do + bundle "update --all" + expect(err).to be_empty + end + end + end + + context "when a top-level gem has an indirect dependency present in the default source, but with a different version from the one resolved", :bundler => "< 3" do + before do + build_lib "activesupport", "7.0.0.alpha", :path => lib_path("rails/activesupport") + build_lib "rails", "7.0.0.alpha", :path => lib_path("rails") do |s| + s.add_dependency "activesupport", "= 7.0.0.alpha" + end + + build_repo gem_repo2 do + build_gem "activesupport", "6.1.2" + + build_gem "webpacker", "5.2.1" do |s| + s.add_dependency "activesupport", ">= 5.2" + end + end + + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + + gemspec :path => "#{lib_path("rails")}" + + gem "webpacker", "~> 5.0" + G + end + + it "installs all gems without warning" do + bundle :install + expect(err).not_to include("Warning") + expect(the_bundle).to include_gems("activesupport 7.0.0.alpha", "rails 7.0.0.alpha") + expect(the_bundle).to include_gems("activesupport 7.0.0.alpha", :source => "path@#{lib_path("rails/activesupport")}") + expect(the_bundle).to include_gems("rails 7.0.0.alpha", :source => "path@#{lib_path("rails")}") + end + end + + context "when a pinned gem has an indirect dependency with more than one level of indirection in the default source ", :bundler => "< 3" do + before do + build_repo gem_repo3 do + build_gem "handsoap", "0.2.5.5" do |s| + s.add_dependency "nokogiri", ">= 1.2.3" + end + end + + update_repo gem_repo2 do + build_gem "nokogiri", "1.11.1" do |s| + s.add_dependency "racca", "~> 1.4" + end + + build_gem "racca", "1.5.2" + end + + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + + source "#{file_uri_for(gem_repo3)}" do + gem "handsoap" + end + + gem "nokogiri" + G + end + + it "installs from the proper sources without any warnings or errors" do + bundle "install --verbose" + expect(err).not_to include("Warning") + expect(the_bundle).to include_gems("handsoap 0.2.5.5", "nokogiri 1.11.1", "racca 1.5.2") + expect(the_bundle).to include_gems("handsoap 0.2.5.5", :source => "remote3") + expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", :source => "remote2") + + # Even if the gems are already installed + FileUtils.rm bundled_app_lock + bundle "install --verbose" + expect(err).not_to include("Warning") + expect(the_bundle).to include_gems("handsoap 0.2.5.5", "nokogiri 1.11.1", "racca 1.5.2") + expect(the_bundle).to include_gems("handsoap 0.2.5.5", :source => "remote3") + expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", :source => "remote2") + end end context "with a gem that is only found in the wrong source" do @@ -389,14 +601,13 @@ RSpec.describe "bundle install with gems on multiple sources" do build_gem "not_in_repo1", "1.0.0" end - gemfile <<-G + install_gemfile <<-G, :raise_on_error => false source "#{file_uri_for(gem_repo3)}" gem "not_in_repo1", :source => "#{file_uri_for(gem_repo1)}" G end it "does not install the gem" do - bundle :install, :raise_on_error => false expect(err).to include("Could not find gem 'not_in_repo1'") end end @@ -457,14 +668,13 @@ RSpec.describe "bundle install with gems on multiple sources" do before do system_gems "rack-0.9.1" - gemfile <<-G + install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack" # shoud come from repo1! G end it "installs the gems without any warning" do - bundle :install expect(err).not_to include("Warning") expect(the_bundle).to include_gems("rack 1.0.0") end @@ -615,7 +825,7 @@ RSpec.describe "bundle install with gems on multiple sources" do G end - it "keeps the old version", :bundler => "2" do + it "keeps the old version", :bundler => "< 3" do expect(the_bundle).to include_gems("rack 1.0.0") end diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb index b58726064f..9e30fc4fd4 100644 --- a/spec/bundler/install/gemfile/specific_platform_spec.rb +++ b/spec/bundler/install/gemfile/specific_platform_spec.rb @@ -211,37 +211,17 @@ RSpec.describe "bundle install with specific platforms" do build_repo2 do build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86_64-linux" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86-mingw32" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86-linux" } build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x64-mingw32" } build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "universal-darwin" } build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86_64-linux" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86-linux" } build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x64-mingw32" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86-mingw32" } build_gem("google-protobuf", "3.0.0.alpha.5.0.5") build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "universal-darwin" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86_64-linux" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86-mingw32" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86-linux" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x64-mingw32" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.4") - - build_gem("google-protobuf", "3.0.0.alpha.5.0.3") - build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86_64-linux" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86-mingw32" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86-linux" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x64-mingw32" } - build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "universal-darwin" } build_gem("google-protobuf", "3.0.0.alpha.4.0") build_gem("google-protobuf", "3.0.0.alpha.3.1.pre") - build_gem("google-protobuf", "3.0.0.alpha.3") - build_gem("google-protobuf", "3.0.0.alpha.2.0") - build_gem("google-protobuf", "3.0.0.alpha.1.1") - build_gem("google-protobuf", "3.0.0.alpha.1.0") end end diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb index 4384a731cd..035ed9a7f3 100644 --- a/spec/bundler/install/gems/resolving_spec.rb +++ b/spec/bundler/install/gems/resolving_spec.rb @@ -88,7 +88,7 @@ RSpec.describe "bundle install with install-time dependencies" do end it "installs plugins depended on by other plugins" do - install_gemfile <<-G + install_gemfile <<-G, :env => { "DEBUG" => "1" } source "#{file_uri_for(gem_repo2)}" gem "net_a" G @@ -97,7 +97,7 @@ RSpec.describe "bundle install with install-time dependencies" do end it "installs multiple levels of dependencies" do - install_gemfile <<-G + install_gemfile <<-G, :env => { "DEBUG" => "1" } source "#{file_uri_for(gem_repo2)}" gem "net_c" gem "net_e" @@ -114,7 +114,7 @@ RSpec.describe "bundle install with install-time dependencies" do gem "net_e" G - bundle :install, :env => { "BUNDLER_DEBUG_RESOLVER" => "1" } + bundle :install, :env => { "BUNDLER_DEBUG_RESOLVER" => "1", "DEBUG" => "1" } expect(out).to include("BUNDLER: Starting resolution") end @@ -128,7 +128,7 @@ RSpec.describe "bundle install with install-time dependencies" do gem "net_e" G - bundle :install, :env => { "DEBUG_RESOLVER" => "1" } + bundle :install, :env => { "DEBUG_RESOLVER" => "1", "DEBUG" => "1" } expect(out).to include("BUNDLER: Starting resolution") end @@ -142,10 +142,13 @@ RSpec.describe "bundle install with install-time dependencies" do gem "net_e" G - bundle :install, :env => { "DEBUG_RESOLVER_TREE" => "1" } + bundle :install, :env => { "DEBUG_RESOLVER_TREE" => "1", "DEBUG" => "1" } - activated_groups = "net_b (1.0) (ruby)" - activated_groups += ", net_b (1.0) (#{local_platforms.join(", ")})" if local_platforms.any? && local_platforms != ["ruby"] + activated_groups = if local_platforms.any? + "net_b (1.0) (ruby), net_b (1.0) (#{local_platforms.join(", ")})" + else + "net_b (1.0) (ruby)" + end expect(out).to include(" net_b"). and include("BUNDLER: Starting resolution"). @@ -157,10 +160,6 @@ RSpec.describe "bundle install with install-time dependencies" do describe "when a required ruby version" do context "allows only an older version" do - before do - skip "gem not found" if Gem.win_platform? - end - it "installs the older version" do build_repo2 do build_gem "rack", "1.2" do |s| @@ -239,7 +238,6 @@ RSpec.describe "bundle install with install-time dependencies" do let(:ruby_requirement) { %("#{RUBY_VERSION}") } let(:error_message_requirement) { "~> #{RUBY_VERSION}.0" } - let(:error_message_platform) { " #{Bundler.local_platform}" } shared_examples_for "ruby version conflicts" do it "raises an error during resolution" do @@ -256,9 +254,9 @@ RSpec.describe "bundle install with install-time dependencies" do nice_error = strip_whitespace(<<-E).strip Bundler found conflicting requirements for the Ruby\0 version: In Gemfile: - Ruby\0 (#{error_message_requirement})#{error_message_platform} + Ruby\0 (#{error_message_requirement}) - require_ruby#{error_message_platform} was resolved to 1.0, which depends on + require_ruby was resolved to 1.0, which depends on Ruby\0 (> 9000) Ruby\0 (> 9000), which is required by gem 'require_ruby', is not available in the local ruby installation diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb index 1037a98092..08d5276a54 100644 --- a/spec/bundler/install/gems/standalone_spec.rb +++ b/spec/bundler/install/gems/standalone_spec.rb @@ -7,6 +7,12 @@ RSpec.shared_examples "bundle install --standalone" do expect(the_bundle).to include_gems(*args) end + it "still makes system gems unavailable to normal bundler" do + system_gems "rack-1.0.0" + + expect(the_bundle).to_not include_gems("rack") + end + it "generates a bundle/bundler/setup.rb" do expect(bundled_app("bundle/bundler/setup.rb")).to exist end @@ -26,6 +32,39 @@ RSpec.shared_examples "bundle install --standalone" do expect(out).to eq(expected_gems.values.join("\n")) end + it "makes the gems available without bundler via Kernel.require" do + testrb = String.new <<-RUBY + $:.unshift File.expand_path("bundle") + require "bundler/setup" + + RUBY + expected_gems.each do |k, _| + testrb << "\nKernel.require \"#{k}\"" + testrb << "\nputs #{k.upcase}" + end + ruby testrb + + expect(out).to eq(expected_gems.values.join("\n")) + end + + it "makes system gems unavailable without bundler" do + system_gems "rack-1.0.0" + + testrb = String.new <<-RUBY + $:.unshift File.expand_path("bundle") + require "bundler/setup" + + begin + require "rack" + rescue LoadError + puts "LoadError" + end + RUBY + ruby testrb + + expect(out).to eq("LoadError") + end + it "works on a different system" do begin FileUtils.mv(bundled_app, "#{bundled_app}2") diff --git a/spec/bundler/install/gemspecs_spec.rb b/spec/bundler/install/gemspecs_spec.rb index 0db1f1985b..dc1934804d 100644 --- a/spec/bundler/install/gemspecs_spec.rb +++ b/spec/bundler/install/gemspecs_spec.rb @@ -28,8 +28,6 @@ RSpec.describe "bundle install" do end it "should use gemspecs in the system cache when available" do - skip "weird incompatible marshal file format error" if Gem.win_platform? - gemfile <<-G source "http://localtestserver.gem" gem 'rack' diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb index 31c9568f85..71771d3dc2 100644 --- a/spec/bundler/install/git_spec.rb +++ b/spec/bundler/install/git_spec.rb @@ -2,10 +2,10 @@ RSpec.describe "bundle install" do context "git sources" do - it "displays the revision hash of the gem repository", :bundler => "< 3" do + it "displays the revision hash of the gem repository" do build_git "foo", "1.0", :path => lib_path("foo") - install_gemfile <<-G + install_gemfile <<-G, :verbose => true gem "foo", :git => "#{file_uri_for(lib_path("foo"))}" G @@ -13,7 +13,18 @@ RSpec.describe "bundle install" do expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}" end - it "displays the ref of the gem repository when using branch~num as a ref", :bundler => "< 3" do + it "displays the correct default branch" do + build_git "foo", "1.0", :path => lib_path("foo"), :default_branch => "main" + + install_gemfile <<-G, :verbose => true + gem "foo", :git => "#{file_uri_for(lib_path("foo"))}" + G + + expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main@#{revision_for(lib_path("foo"))[0..6]})") + expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}" + end + + it "displays the ref of the gem repository when using branch~num as a ref" do skip "maybe branch~num notation doesn't work on Windows' git" if Gem.win_platform? build_git "foo", "1.0", :path => lib_path("foo") @@ -22,7 +33,7 @@ RSpec.describe "bundle install" do rev2 = revision_for(lib_path("foo"))[0..6] update_git "foo", "3.0", :path => lib_path("foo"), :gemspec => true - install_gemfile <<-G + install_gemfile <<-G, :verbose => true gem "foo", :git => "#{file_uri_for(lib_path("foo"))}", :ref => "master~2" G @@ -31,7 +42,7 @@ RSpec.describe "bundle install" do update_git "foo", "4.0", :path => lib_path("foo"), :gemspec => true - bundle :update, :all => true + bundle :update, :all => true, :verbose => true expect(out).to include("Using foo 2.0 (was 1.0) from #{file_uri_for(lib_path("foo"))} (at master~2@#{rev2})") expect(the_bundle).to include_gems "foo 2.0", :source => "git@#{lib_path("foo")}" end diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index af3a10f9e9..976db33b0e 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -1134,7 +1134,7 @@ RSpec.describe "the lockfile format" do G expect(bundled_app_lock).not_to exist - expect(err).to include "rack (>= 0) should come from an unspecified source and git://hubz.com (at master)" + expect(err).to include "rack (>= 0) should come from an unspecified source and git://hubz.com" end it "works correctly with multiple version dependencies" do diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb index d061ca7064..0758d29746 100644 --- a/spec/bundler/other/major_deprecation_spec.rb +++ b/spec/bundler/other/major_deprecation_spec.rb @@ -17,7 +17,7 @@ RSpec.describe "major deprecations" do bundle "exec ruby -e #{source.dump}" end - it "is deprecated in favor of .unbundled_env", :bundler => "2" do + it "is deprecated in favor of .unbundled_env", :bundler => "< 3" do expect(deprecations).to include \ "`Bundler.clean_env` has been deprecated in favor of `Bundler.unbundled_env`. " \ "If you instead want the environment before bundler was originally loaded, use `Bundler.original_env` " \ @@ -33,7 +33,7 @@ RSpec.describe "major deprecations" do bundle "exec ruby -e #{source.dump}" end - it "is deprecated in favor of .unbundled_env", :bundler => "2" do + it "is deprecated in favor of .unbundled_env", :bundler => "< 3" do expect(deprecations).to include( "`Bundler.with_clean_env` has been deprecated in favor of `Bundler.with_unbundled_env`. " \ "If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env` " \ @@ -50,7 +50,7 @@ RSpec.describe "major deprecations" do bundle "exec ruby -e #{source.dump}" end - it "is deprecated in favor of .unbundled_system", :bundler => "2" do + it "is deprecated in favor of .unbundled_system", :bundler => "< 3" do expect(deprecations).to include( "`Bundler.clean_system` has been deprecated in favor of `Bundler.unbundled_system`. " \ "If you instead want to run the command in the environment before bundler was originally loaded, use `Bundler.original_system` " \ @@ -67,7 +67,7 @@ RSpec.describe "major deprecations" do bundle "exec ruby -e #{source.dump}" end - it "is deprecated in favor of .unbundled_exec", :bundler => "2" do + it "is deprecated in favor of .unbundled_exec", :bundler => "< 3" do expect(deprecations).to include( "`Bundler.clean_exec` has been deprecated in favor of `Bundler.unbundled_exec`. " \ "If you instead want to exec to a command in the environment before bundler was originally loaded, use `Bundler.original_exec` " \ @@ -84,7 +84,7 @@ RSpec.describe "major deprecations" do bundle "exec ruby -e #{source.dump}" end - it "is deprecated in favor of .load", :bundler => "2" do + it "is deprecated in favor of .load", :bundler => "< 3" do expect(deprecations).to include "Bundler.environment has been removed in favor of Bundler.load (called at -e:1)" end @@ -109,7 +109,7 @@ RSpec.describe "major deprecations" do bundle "check --path vendor/bundle", :raise_on_error => false end - it "should print a deprecation warning", :bundler => "2" do + it "should print a deprecation warning", :bundler => "< 3" do expect(deprecations).to include( "The `--path` flag is deprecated because it relies on being " \ "remembered across bundler invocations, which bundler will no " \ @@ -118,7 +118,7 @@ RSpec.describe "major deprecations" do ) end - pending "should fail with a helpful error", :bundler => "3" + pending "fails with a helpful error", :bundler => "3" end context "bundle check --path=" do @@ -131,7 +131,7 @@ RSpec.describe "major deprecations" do bundle "check --path=vendor/bundle", :raise_on_error => false end - it "should print a deprecation warning", :bundler => "2" do + it "should print a deprecation warning", :bundler => "< 3" do expect(deprecations).to include( "The `--path` flag is deprecated because it relies on being " \ "remembered across bundler invocations, which bundler will no " \ @@ -140,7 +140,7 @@ RSpec.describe "major deprecations" do ) end - pending "should fail with a helpful error", :bundler => "3" + pending "fails with a helpful error", :bundler => "3" end context "bundle cache --all" do @@ -153,7 +153,7 @@ RSpec.describe "major deprecations" do bundle "cache --all", :raise_on_error => false end - it "should print a deprecation warning", :bundler => "2" do + it "should print a deprecation warning", :bundler => "< 3" do expect(deprecations).to include( "The `--all` flag is deprecated because it relies on being " \ "remembered across bundler invocations, which bundler will no " \ @@ -162,7 +162,7 @@ RSpec.describe "major deprecations" do ) end - pending "should fail with a helpful error", :bundler => "3" + pending "fails with a helpful error", :bundler => "3" end describe "bundle config" do @@ -292,7 +292,7 @@ RSpec.describe "major deprecations" do G end - it "should output a deprecation warning", :bundler => "2" do + it "should output a deprecation warning", :bundler => "< 3" do expect(deprecations).to include("The --binstubs option will be removed in favor of `bundle binstubs --all`") end @@ -356,7 +356,7 @@ RSpec.describe "major deprecations" do bundle "install #{flag_name} #{value}" end - it "should print a deprecation warning", :bundler => "2" do + it "should print a deprecation warning", :bundler => "< 3" do expect(deprecations).to include( "The `#{flag_name}` flag is deprecated because it relies on " \ "being remembered across bundler invocations, which bundler " \ @@ -365,7 +365,7 @@ RSpec.describe "major deprecations" do ) end - pending "should fail with a helpful error", :bundler => "3" + pending "fails with a helpful error", :bundler => "3" end end end @@ -378,7 +378,7 @@ RSpec.describe "major deprecations" do G end - it "shows a deprecation", :bundler => "2" do + it "shows a deprecation", :bundler => "< 3" do expect(deprecations).to include( "Your Gemfile contains multiple primary sources. " \ "Using `source` more than once without a block is a security risk, and " \ @@ -389,7 +389,7 @@ RSpec.describe "major deprecations" do ) end - pending "should fail with a helpful error", :bundler => "3" + pending "fails with a helpful error", :bundler => "3" end context "when Bundler.setup is run in a ruby script" do @@ -422,14 +422,14 @@ RSpec.describe "major deprecations" do RUBY end - it "should print a capistrano deprecation warning", :bundler => "2" do + it "should print a capistrano deprecation warning", :bundler => "< 3" do expect(deprecations).to include("Bundler no longer integrates " \ "with Capistrano, but Capistrano provides " \ "its own integration with Bundler via the " \ "capistrano-bundler gem. Use it instead.") end - pending "should fail with a helpful error", :bundler => "3" + pending "fails with a helpful error", :bundler => "3" end describe Bundler::Dsl do @@ -439,7 +439,7 @@ RSpec.describe "major deprecations" do end context "with github gems" do - it "does not warn about removal", :bundler => "2" do + it "does not warn about removal", :bundler => "< 3" do expect(Bundler.ui).not_to receive(:warn) subject.gem("sparks", :github => "indirect/sparks") github_uri = "https://github.com/indirect/sparks.git" @@ -461,7 +461,7 @@ The :github git source is deprecated, and will be removed in the future. Change end context "with bitbucket gems" do - it "does not warn about removal", :bundler => "2" do + it "does not warn about removal", :bundler => "< 3" do expect(Bundler.ui).not_to receive(:warn) subject.gem("not-really-a-gem", :bitbucket => "mcorp/flatlab-rails") end @@ -483,7 +483,7 @@ The :bitbucket git source is deprecated, and will be removed in the future. Add end context "with gist gems" do - it "does not warn about removal", :bundler => "2" do + it "does not warn about removal", :bundler => "< 3" do expect(Bundler.ui).not_to receive(:warn) subject.gem("not-really-a-gem", :gist => "1234") end @@ -514,7 +514,7 @@ The :gist git source is deprecated, and will be removed in the future. Add this bundle :show end - it "prints a deprecation warning recommending `bundle list`", :bundler => "2" do + it "prints a deprecation warning recommending `bundle list`", :bundler => "< 3" do expect(deprecations).to include("use `bundle list` instead of `bundle show`") end @@ -526,7 +526,7 @@ The :gist git source is deprecated, and will be removed in the future. Add this bundle "show --outdated" end - it "prints a deprecation warning informing about its removal", :bundler => "2" do + it "prints a deprecation warning informing about its removal", :bundler => "< 3" do expect(deprecations).to include("the `--outdated` flag to `bundle show` was undocumented and will be removed without replacement") end @@ -538,7 +538,7 @@ The :gist git source is deprecated, and will be removed in the future. Add this bundle "show --verbose" end - it "prints a deprecation warning informing about its removal", :bundler => "2" do + it "prints a deprecation warning informing about its removal", :bundler => "< 3" do expect(deprecations).to include("the `--verbose` flag to `bundle show` was undocumented and will be removed without replacement") end @@ -550,7 +550,7 @@ The :gist git source is deprecated, and will be removed in the future. Add this bundle "show rack" end - it "prints a deprecation warning recommending `bundle info`", :bundler => "2" do + it "prints a deprecation warning recommending `bundle info`", :bundler => "< 3" do expect(deprecations).to include("use `bundle info rack` instead of `bundle show rack`") end @@ -562,7 +562,7 @@ The :gist git source is deprecated, and will be removed in the future. Add this bundle "show --paths" end - it "prints a deprecation warning recommending `bundle list`", :bundler => "2" do + it "prints a deprecation warning recommending `bundle list`", :bundler => "< 3" do expect(deprecations).to include("use `bundle list` instead of `bundle show --paths`") end @@ -574,7 +574,7 @@ The :gist git source is deprecated, and will be removed in the future. Add this bundle "show rack --paths" end - it "prints deprecation warning recommending `bundle info`", :bundler => "2" do + it "prints deprecation warning recommending `bundle info`", :bundler => "< 3" do expect(deprecations).to include("use `bundle info rack --path` instead of `bundle show rack --paths`") end @@ -587,7 +587,7 @@ The :gist git source is deprecated, and will be removed in the future. Add this bundle "console", :raise_on_error => false end - it "prints a deprecation warning", :bundler => "2" do + it "prints a deprecation warning", :bundler => "< 3" do expect(deprecations).to include \ "bundle console will be replaced by `bin/console` generated by `bundle gem `" end @@ -603,7 +603,7 @@ The :gist git source is deprecated, and will be removed in the future. Add this bundle "viz" end - it "prints a deprecation warning", :bundler => "2" do + it "prints a deprecation warning", :bundler => "< 3" do expect(deprecations).to include "The `viz` command has been moved to the `bundle-viz` gem, see https://github.com/bundler/bundler-viz" end diff --git a/spec/bundler/quality_spec.rb b/spec/bundler/quality_spec.rb index 405c786842..2b1e28fa30 100644 --- a/spec/bundler/quality_spec.rb +++ b/spec/bundler/quality_spec.rb @@ -220,7 +220,7 @@ RSpec.describe "The library itself" do gem_list = loaded_gemspec.files - expect(git_list.to_set).to eq(gem_list.to_set) + expect(git_list.sort).to eq(gem_list.sort) end it "does not contain any warnings" do diff --git a/spec/bundler/realworld/double_check_spec.rb b/spec/bundler/realworld/double_check_spec.rb index 0a8f5e4d9e..07697f080e 100644 --- a/spec/bundler/realworld/double_check_spec.rb +++ b/spec/bundler/realworld/double_check_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "double checking sources", :realworld => true, :sometimes => true do +RSpec.describe "double checking sources", :realworld => true do it "finds already-installed gems" do create_file("rails.gemspec", <<-RUBY) Gem::Specification.new do |s| diff --git a/spec/bundler/realworld/edgecases_spec.rb b/spec/bundler/realworld/edgecases_spec.rb index 0f19cc7842..1925f76c06 100644 --- a/spec/bundler/realworld/edgecases_spec.rb +++ b/spec/bundler/realworld/edgecases_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "real world edgecases", :realworld => true, :sometimes => true do +RSpec.describe "real world edgecases", :realworld => true do def rubygems_version(name, requirement) ruby <<-RUBY require "#{spec_dir}/support/artifice/vcr" @@ -64,6 +64,8 @@ RSpec.describe "real world edgecases", :realworld => true, :sometimes => true do it "is able to update a top-level dependency when there is a conflict on a shared transitive child" do # from https://github.com/rubygems/bundler/issues/5031 + system_gems "bundler-2.99.0" + gemfile <<-G source "https://rubygems.org" gem 'rails', '~> 4.2.7.1' @@ -189,7 +191,7 @@ RSpec.describe "real world edgecases", :realworld => true, :sometimes => true do rails (~> 4.2.7.1) L - bundle "lock --update paperclip" + bundle "lock --update paperclip", :env => { "BUNDLER_VERSION" => "2.99.0" } expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0")) end @@ -448,12 +450,219 @@ RSpec.describe "real world edgecases", :realworld => true, :sometimes => true do end G + if Bundler.feature_flag.bundler_3_mode? + # Conflicts on bundler version, so fails earlier + bundle :lock, :env => { "DEBUG_RESOLVER" => "1" }, :raise_on_error => false + expect(out).to display_total_steps_of(435) + else + bundle :lock, :env => { "DEBUG_RESOLVER" => "1" } + expect(out).to display_total_steps_of(1025) + end + end + + it "doesn't hang on tricky gemfile" do + skip "Only for ruby 2.7.2" if RUBY_VERSION != "2.7.2" + + gemfile <<~G + source 'https://rubygems.org' + + group :development do + gem "puppet-module-posix-default-r2.7", '~> 0.3' + gem "puppet-module-posix-dev-r2.7", '~> 0.3' + gem "beaker-rspec" + gem "beaker-puppet" + gem "beaker-docker" + gem "beaker-puppet_install_helper" + gem "beaker-module_install_helper" + end + G + bundle :lock, :env => { "DEBUG_RESOLVER" => "1" } if Bundler.feature_flag.bundler_3_mode? - expect(out).to include("BUNDLER: Finished resolution (2492 steps)") + expect(out).to display_total_steps_of(890) else - expect(out).to include("BUNDLER: Finished resolution (2722 steps)") + expect(out).to display_total_steps_of(891) + end + end + + it "doesn't hang on nix gemfile" do + skip "Only for ruby 3.0.0" if RUBY_VERSION != "3.0.0" + + gemfile <<~G + source "https://rubygems.org" do + gem "addressable" + gem "atk" + gem "awesome_print" + gem "bacon" + gem "byebug" + gem "cairo" + gem "cairo-gobject" + gem "camping" + gem "charlock_holmes" + gem "cld3" + gem "cocoapods" + gem "cocoapods-acknowledgements" + gem "cocoapods-art" + gem "cocoapods-bin" + gem "cocoapods-browser" + gem "cocoapods-bugsnag" + gem "cocoapods-check" + gem "cocoapods-clean" + gem "cocoapods-clean_build_phases_scripts" + gem "cocoapods-core" + gem "cocoapods-coverage" + gem "cocoapods-deintegrate" + gem "cocoapods-dependencies" + gem "cocoapods-deploy" + gem "cocoapods-downloader" + gem "cocoapods-expert-difficulty" + gem "cocoapods-fix-react-native" + gem "cocoapods-generate" + gem "cocoapods-git_url_rewriter" + gem "cocoapods-keys" + gem "cocoapods-no-dev-schemes" + gem "cocoapods-open" + gem "cocoapods-packager" + gem "cocoapods-playgrounds" + gem "cocoapods-plugins" + gem "cocoapods-prune-localizations" + gem "cocoapods-rome" + gem "cocoapods-search" + gem "cocoapods-sorted-search" + gem "cocoapods-static-swift-framework" + gem "cocoapods-stats" + gem "cocoapods-tdfire-binary" + gem "cocoapods-testing" + gem "cocoapods-trunk" + gem "cocoapods-try" + gem "cocoapods-try-release-fix" + gem "cocoapods-update-if-you-dare" + gem "cocoapods-whitelist" + gem "cocoapods-wholemodule" + gem "coderay" + gem "concurrent-ruby" + gem "curb" + gem "curses" + gem "daemons" + gem "dep-selector-libgecode" + gem "digest-sha3" + gem "domain_name" + gem "do_sqlite3" + gem "ethon" + gem "eventmachine" + gem "excon" + gem "faraday" + gem "ffi" + gem "ffi-rzmq-core" + gem "fog-dnsimple" + gem "gdk_pixbuf2" + gem "gio2" + gem "gitlab-markup" + gem "glib2" + gem "gpgme" + gem "gtk2" + gem "hashie" + gem "highline" + gem "hike" + gem "hitimes" + gem "hpricot" + gem "httpclient" + gem "http-cookie" + gem "iconv" + gem "idn-ruby" + gem "jbuilder" + gem "jekyll" + gem "jmespath" + gem "jwt" + gem "libv8" + gem "libxml-ruby" + gem "magic" + gem "markaby" + gem "method_source" + gem "mini_magick" + gem "msgpack" + gem "mysql2" + gem "ncursesw" + gem "netrc" + gem "net-scp" + gem "net-ssh" + gem "nokogiri" + gem "opus-ruby" + gem "ovirt-engine-sdk" + gem "pango" + gem "patron" + gem "pcaprub" + gem "pg" + gem "pry" + gem "pry-byebug" + gem "pry-doc" + gem "public_suffix" + gem "puma" + gem "rails" + gem "rainbow" + gem "rbnacl" + gem "rb-readline" + gem "re2" + gem "redis" + gem "redis-rack" + gem "rest-client" + gem "rmagick" + gem "rpam2" + gem "rspec" + gem "rubocop" + gem "rubocop-performance" + gem "ruby-libvirt" + gem "ruby-lxc" + gem "ruby-progressbar" + gem "ruby-terminfo" + gem "ruby-vips" + gem "rubyzip" + gem "rugged" + gem "sassc" + gem "scrypt" + gem "semian" + gem "sequel" + gem "sequel_pg" + gem "simplecov" + gem "sinatra" + gem "slop" + gem "snappy" + gem "sqlite3" + gem "taglib-ruby" + gem "thrift" + gem "tilt" + gem "tiny_tds" + gem "treetop" + gem "typhoeus" + gem "tzinfo" + gem "unf_ext" + gem "uuid4r" + gem "whois" + gem "zookeeper" + end + G + + bundle :lock, :env => { "DEBUG_RESOLVER" => "1" } + + if Bundler.feature_flag.bundler_3_mode? + expect(out).to display_total_steps_of(1874) + else + expect(out).to display_total_steps_of(1922) + end + end + + private + + RSpec::Matchers.define :display_total_steps_of do |expected_steps| + match do |out| + out.include?("BUNDLER: Finished resolution (#{expected_steps} steps)") + end + + failure_message do |out| + actual_steps = out.scan(/BUNDLER: Finished resolution \((\d+) steps\)/).first.first + + "Expected resolution to finish in #{expected_steps} steps, but took #{actual_steps}" end end end diff --git a/spec/bundler/realworld/parallel_spec.rb b/spec/bundler/realworld/parallel_spec.rb index 2a5154ffbf..97c0e0cab4 100644 --- a/spec/bundler/realworld/parallel_spec.rb +++ b/spec/bundler/realworld/parallel_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "parallel", :realworld => true, :sometimes => true do +RSpec.describe "parallel", :realworld => true do it "installs" do gemfile <<-G source "https://rubygems.org" diff --git a/spec/bundler/realworld/slow_perf_spec.rb b/spec/bundler/realworld/slow_perf_spec.rb new file mode 100644 index 0000000000..518da2800b --- /dev/null +++ b/spec/bundler/realworld/slow_perf_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe "bundle install with complex dependencies", :realworld => true do + it "resolves quickly" do + start_time = Time.now + + install_gemfile <<-G + source 'https://rubygems.org' + + gem "actionmailer" + gem "mongoid", ">= 0.10.2" + G + + duration = Time.now - start_time + + expect(duration.to_f).to be < 120 # seconds + end +end diff --git a/spec/bundler/resolver/basic_spec.rb b/spec/bundler/resolver/basic_spec.rb index 57897f89b4..ee62dc3577 100644 --- a/spec/bundler/resolver/basic_spec.rb +++ b/spec/bundler/resolver/basic_spec.rb @@ -176,7 +176,7 @@ Bundler could not find compatible versions for gem "a": deps = [] @deps.each do |d| - deps << Bundler::DepProxy.new(d, "ruby") + deps << Bundler::DepProxy.get_proxy(d, "ruby") end should_resolve_and_include %w[foo-1.0.0 bar-1.0.0], [[]] diff --git a/spec/bundler/resolver/platform_spec.rb b/spec/bundler/resolver/platform_spec.rb index 415c5458df..bc4081f8b5 100644 --- a/spec/bundler/resolver/platform_spec.rb +++ b/spec/bundler/resolver/platform_spec.rb @@ -28,6 +28,23 @@ RSpec.describe "Resolving platform craziness" do end end + it "resolves multiplatform gems with redundant platforms correctly" do + @index = build_index do + gem "zookeeper", "1.4.11" + gem "zookeeper", "1.4.11", "java" do + dep "slyphon-log4j", "= 1.2.15" + dep "slyphon-zookeeper_jar", "= 3.3.5" + end + gem "slyphon-log4j", "1.2.15" + gem "slyphon-zookeeper_jar", "3.3.5", "java" + end + + dep "zookeeper" + platforms "java", "ruby", "universal-java-11" + + should_resolve_as %w[zookeeper-1.4.11 zookeeper-1.4.11-java slyphon-log4j-1.2.15 slyphon-zookeeper_jar-3.3.5-java] + end + it "takes the latest ruby gem, even if an older platform specific version is available" do @index = build_index do gem "foo", "1.0.0" @@ -120,6 +137,157 @@ RSpec.describe "Resolving platform craziness" do should_resolve_as %w[foo-1.1.0] end + it "doesn't include gems not needed for none of the platforms" do + @index = build_index do + gem "empyrean", "0.1.0" + gem "coderay", "1.1.2" + gem "method_source", "0.9.0" + + gem "spoon", "0.0.6" do + dep "ffi", ">= 0" + end + + gem "pry", "0.11.3", "java" do + dep "coderay", "~> 1.1.0" + dep "method_source", "~> 0.9.0" + dep "spoon", "~> 0.0" + end + + gem "pry", "0.11.3" do + dep "coderay", "~> 1.1.0" + dep "method_source", "~> 0.9.0" + end + + gem "ffi", "1.9.23", "java" + gem "ffi", "1.9.23" + end + + dep "empyrean", "0.1.0" + dep "pry" + + platforms "ruby", "java" + + should_resolve_as %w[coderay-1.1.2 empyrean-0.1.0 ffi-1.9.23-java method_source-0.9.0 pry-0.11.3 pry-0.11.3-java spoon-0.0.6] + end + + it "includes gems needed for at least one platform" do + @index = build_index do + gem "empyrean", "0.1.0" + gem "coderay", "1.1.2" + gem "method_source", "0.9.0" + + gem "spoon", "0.0.6" do + dep "ffi", ">= 0" + end + + gem "pry", "0.11.3", "java" do + dep "coderay", "~> 1.1.0" + dep "method_source", "~> 0.9.0" + dep "spoon", "~> 0.0" + end + + gem "pry", "0.11.3" do + dep "coderay", "~> 1.1.0" + dep "method_source", "~> 0.9.0" + end + + gem "ffi", "1.9.23", "java" + gem "ffi", "1.9.23" + + gem "extra", "1.0.0" do + dep "ffi", ">= 0" + end + end + + dep "empyrean", "0.1.0" + dep "pry" + dep "extra" + + platforms "ruby", "java" + + should_resolve_as %w[coderay-1.1.2 empyrean-0.1.0 extra-1.0.0 ffi-1.9.23 ffi-1.9.23-java method_source-0.9.0 pry-0.11.3 pry-0.11.3-java spoon-0.0.6] + end + + it "includes gems needed for at least one platform even when the platform specific requirement is processed earlier than the generic requirement" do + @index = build_index do + gem "empyrean", "0.1.0" + gem "coderay", "1.1.2" + gem "method_source", "0.9.0" + + gem "spoon", "0.0.6" do + dep "ffi", ">= 0" + end + + gem "pry", "0.11.3", "java" do + dep "coderay", "~> 1.1.0" + dep "method_source", "~> 0.9.0" + dep "spoon", "~> 0.0" + end + + gem "pry", "0.11.3" do + dep "coderay", "~> 1.1.0" + dep "method_source", "~> 0.9.0" + end + + gem "ffi", "1.9.23", "java" + gem "ffi", "1.9.23" + + gem "extra", "1.0.0" do + dep "extra2", ">= 0" + end + + gem "extra2", "1.0.0" do + dep "extra3", ">= 0" + end + + gem "extra3", "1.0.0" do + dep "ffi", ">= 0" + end + end + + dep "empyrean", "0.1.0" + dep "pry" + dep "extra" + + platforms "ruby", "java" + + should_resolve_as %w[coderay-1.1.2 empyrean-0.1.0 extra-1.0.0 extra2-1.0.0 extra3-1.0.0 ffi-1.9.23 ffi-1.9.23-java method_source-0.9.0 pry-0.11.3 pry-0.11.3-java spoon-0.0.6] + end + + it "properly adds platforms when platform requirements come from different dependencies" do + @index = build_index do + gem "ffi", "1.9.14" + gem "ffi", "1.9.14", "universal-mingw32" + + gem "gssapi", "0.1" + gem "gssapi", "0.2" + gem "gssapi", "0.3" + gem "gssapi", "1.2.0" do + dep "ffi", ">= 1.0.1" + end + + gem "mixlib-shellout", "2.2.6" + gem "mixlib-shellout", "2.2.6", "universal-mingw32" do + dep "win32-process", "~> 0.8.2" + end + + # we need all these versions to get the sorting the same as it would be + # pulling from rubygems.org + %w[0.8.3 0.8.2 0.8.1 0.8.0].each do |v| + gem "win32-process", v do + dep "ffi", ">= 1.0.0" + end + end + end + + dep "mixlib-shellout" + dep "gssapi" + + platforms "universal-mingw32", "ruby" + + should_resolve_as %w[ffi-1.9.14 ffi-1.9.14-universal-mingw32 gssapi-1.2.0 mixlib-shellout-2.2.6 mixlib-shellout-2.2.6-universal-mingw32 win32-process-0.8.3] + end + describe "with mingw32" do before :each do @index = build_index do diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb index b46da20096..bd136ddcd7 100644 --- a/spec/bundler/runtime/inline_spec.rb +++ b/spec/bundler/runtime/inline_spec.rb @@ -46,8 +46,6 @@ RSpec.describe "bundler/inline#gemfile" do end it "requires the gems" do - skip "gems not found" if Gem.win_platform? - script <<-RUBY gemfile do path "#{lib_path}" do @@ -94,8 +92,6 @@ RSpec.describe "bundler/inline#gemfile" do end it "lets me use my own ui object" do - skip "prints just one CONFIRMED" if Gem.win_platform? - script <<-RUBY, :artifice => "endpoint" require '#{lib_dir}/bundler' class MyBundlerUI < Bundler::UI::Silent diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb index 215ba8628f..154d967e12 100644 --- a/spec/bundler/runtime/platform_spec.rb +++ b/spec/bundler/runtime/platform_spec.rb @@ -57,6 +57,89 @@ RSpec.describe "Bundler.setup with multi platform stuff" do expect(the_bundle).to include_gems "nokogiri 1.4.2" end + it "will keep both platforms when both ruby and a specific ruby platform are locked and the bundle is unlocked" do + build_repo4 do + build_gem "nokogiri", "1.11.1" do |s| + s.add_dependency "mini_portile2", "~> 2.5.0" + s.add_dependency "racc", "~> 1.4" + end + + build_gem "nokogiri", "1.11.1" do |s| + s.platform = Bundler.local_platform + s.add_dependency "racc", "~> 1.4" + end + + build_gem "mini_portile2", "2.5.0" + build_gem "racc", "1.5.2" + end + + good_lockfile = <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + mini_portile2 (2.5.0) + nokogiri (1.11.1) + mini_portile2 (~> 2.5.0) + racc (~> 1.4) + nokogiri (1.11.1-#{Bundler.local_platform}) + racc (~> 1.4) + racc (1.5.2) + + PLATFORMS + ruby + #{Bundler.local_platform} + + DEPENDENCIES + nokogiri (~> 1.11) + + BUNDLED WITH + #{Bundler::VERSION} + L + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem "nokogiri", "~> 1.11" + G + + lockfile good_lockfile + + bundle "update nokogiri" + + expect(lockfile).to eq(good_lockfile) + end + + it "will use the java platform if both generic java and generic ruby platforms are locked", :jruby do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "nokogiri" + G + + lockfile <<-G + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + nokogiri (1.4.2) + nokogiri (1.4.2-java) + weakling (>= 0.0.3) + weakling (0.0.3) + + PLATFORMS + java + ruby + + DEPENDENCIES + nokogiri + + BUNDLED WITH + 2.1.4 + G + + bundle "install" + + expect(out).to include("Fetching nokogiri 1.4.2 (java)") + expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA" + end + it "will add the resolve for the current platform" do lockfile <<-G GEM @@ -84,9 +167,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 x86-darwin-100" end - it "allows specifying only-ruby-platform" do - simulate_platform "java" - + it "allows specifying only-ruby-platform on jruby", :jruby do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "nokogiri" @@ -100,6 +181,29 @@ RSpec.describe "Bundler.setup with multi platform stuff" do expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 RUBY" end + it "allows specifying only-ruby-platform" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "nokogiri" + gem "platform_specific" + G + + bundle "config set force_ruby_platform true" + + bundle "install" + + expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 RUBY" + end + + it "doesn't pull platform specific gems on truffleruby", :truffleruby do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "platform_specific" + G + + expect(the_bundle).to include_gems "platform_specific 1.0 RUBY" + end + it "allows specifying only-ruby-platform on windows with dependency platforms" do simulate_windows do install_gemfile <<-G diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb index e2bed00a7f..a0036df39e 100644 --- a/spec/bundler/runtime/setup_spec.rb +++ b/spec/bundler/runtime/setup_spec.rb @@ -1252,7 +1252,7 @@ end exempts << "fiddle" if Gem.win_platform? && Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.7") exempts << "uri" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7") exempts << "pathname" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0") - exempts << "set" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0") + exempts << "set" unless Gem::Version.new(Gem::VERSION) >= Gem::Version.new("3.2.6") exempts << "tsort" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0") exempts end @@ -1334,36 +1334,8 @@ end expect(out).to eq("The Gemfile's dependencies are satisfied") end - # bundler respects paths specified directly in RUBYLIB or RUBYOPT, and - # that happens when running ruby from the ruby-core setup. To - # workaround, we manually remove those for these tests when they would - # override the default gem. - def load_path_exclusions_hack_for(name) - if ruby_core? - if ENV.key?("PWD") - # .ext/common is relative from build directory - ext_folder = Pathname(ENV["PWD"]) + ".ext/common" - else - ext_folder = source_root.join(".ext/common") - end - require_name = name.tr("-", "/") - if File.exist?(ext_folder.join("#{require_name}.rb")) - { :exclude_from_load_path => ext_folder.to_s } - else - lib_folder = source_lib_dir - if File.exist?(lib_folder.join("#{require_name}.rb")) - { :exclude_from_load_path => lib_folder.to_s } - else - {} - end - end - else - {} - end - end - Gem::Specification.select(&:default_gem?).map(&:name).each do |g| - it "activates newer versions of #{g}" do + it "activates newer versions of #{g}", :ruby_repo do skip if exemptions.include?(g) build_repo4 do @@ -1375,11 +1347,10 @@ end gem "#{g}", "999999" G - opts = { :env => { "RUBYOPT" => activation_warning_hack_rubyopt } } - expect(the_bundle).to include_gem("#{g} 999999", opts.merge(load_path_exclusions_hack_for(g))) + expect(the_bundle).to include_gem("#{g} 999999", :env => { "RUBYOPT" => activation_warning_hack_rubyopt }) end - it "activates older versions of #{g}" do + it "activates older versions of #{g}", :ruby_repo do skip if exemptions.include?(g) build_repo4 do @@ -1391,8 +1362,7 @@ end gem "#{g}", "0.0.0.a" G - opts = { :env => { "RUBYOPT" => activation_warning_hack_rubyopt } } - expect(the_bundle).to include_gem("#{g} 0.0.0.a", opts.merge(load_path_exclusions_hack_for(g))) + expect(the_bundle).to include_gem("#{g} 0.0.0.a", :env => { "RUBYOPT" => activation_warning_hack_rubyopt }) end end end diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb index 7723c85ce6..a72a1f5255 100644 --- a/spec/bundler/spec_helper.rb +++ b/spec/bundler/spec_helper.rb @@ -23,7 +23,6 @@ require_relative "support/indexes" require_relative "support/matchers" require_relative "support/permissions" require_relative "support/platforms" -require_relative "support/sometimes" require_relative "support/sudo" $debug = false @@ -105,7 +104,7 @@ RSpec.configure do |config| all_output = all_commands_output if example.exception && !all_output.empty? - message = example.exception.message + all_output + message = all_output + "\n" + example.exception.message (class << example.exception; self; end).send(:define_method, :message) do message end diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb index 02a2c0f659..c76c3f505e 100644 --- a/spec/bundler/support/builders.rb +++ b/spec/bundler/support/builders.rb @@ -547,9 +547,11 @@ module Spec class GitBuilder < LibBuilder def _build(options) + default_branch = options[:default_branch] || "master" path = options[:path] || _default_path source = options[:source] || "git@#{path}" super(options.merge(:path => path, :source => source)) + @context.git("config --global init.defaultBranch #{default_branch}", path) @context.git("init", path) @context.git("add *", path) @context.git("config user.email lol@wut.com", path) diff --git a/spec/bundler/support/filters.rb b/spec/bundler/support/filters.rb index b1978e44e6..0c1f27e470 100644 --- a/spec/bundler/support/filters.rb +++ b/spec/bundler/support/filters.rb @@ -32,7 +32,8 @@ RSpec.configure do |config| config.filter_run_excluding :no_color_tty => Gem.win_platform? || !ENV["GITHUB_ACTION"].nil? config.filter_run_excluding :permissions => Gem.win_platform? config.filter_run_excluding :readline => Gem.win_platform? - config.filter_run_excluding :jruby => RUBY_PLATFORM != "java" + config.filter_run_excluding :jruby => RUBY_ENGINE != "jruby" + config.filter_run_excluding :truffleruby => RUBY_ENGINE != "truffleruby" config.filter_run_when_matching :focus unless ENV["CI"] end diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb index c4018eb818..a7cc1ce810 100644 --- a/spec/bundler/support/helpers.rb +++ b/spec/bundler/support/helpers.rb @@ -275,14 +275,12 @@ module Spec def install_gemfile(*args) gemfile(*args) opts = args.last.is_a?(Hash) ? args.last : {} - opts[:retry] ||= 0 bundle :install, opts end def lock_gemfile(*args) gemfile(*args) opts = args.last.is_a?(Hash) ? args.last : {} - opts[:retry] ||= 0 bundle :lock, opts end diff --git a/spec/bundler/support/indexes.rb b/spec/bundler/support/indexes.rb index 7440523fc9..1f3c4ddaa6 100644 --- a/spec/bundler/support/indexes.rb +++ b/spec/bundler/support/indexes.rb @@ -22,7 +22,7 @@ module Spec @deps.each do |d| @platforms.each do |p| source_requirements[d.name] = d.source = default_source - deps << Bundler::DepProxy.new(d, p) + deps << Bundler::DepProxy.get_proxy(d, p) end end source_requirements ||= {} @@ -30,7 +30,7 @@ module Spec args[1] ||= Bundler::GemVersionPromoter.new # gem_version_promoter args[2] ||= [] # additional_base_requirements args[3] ||= @platforms # platforms - Bundler::Resolver.resolve(deps, @index, source_requirements, *args) + Bundler::Resolver.resolve(deps, source_requirements, *args) end def should_resolve_as(specs) diff --git a/spec/bundler/support/matchers.rb b/spec/bundler/support/matchers.rb index 6c9ab0d410..5d129ed849 100644 --- a/spec/bundler/support/matchers.rb +++ b/spec/bundler/support/matchers.rb @@ -115,7 +115,6 @@ module Spec opts = names.last.is_a?(Hash) ? names.pop : {} source = opts.delete(:source) groups = Array(opts[:groups]) - exclude_from_load_path = opts.delete(:exclude_from_load_path) opts[:raise_on_error] = false groups << opts @errors = names.map do |name| @@ -123,11 +122,6 @@ module Spec require_path = name == "bundler" ? "#{lib_dir}/bundler" : name.tr("-", "/") version_const = name == "bundler" ? "Bundler::VERSION" : Spec::Builders.constantize(name) code = [] - if exclude_from_load_path - code << "exclude_from_load_path = File.expand_path('#{exclude_from_load_path}')" - code << "$LOAD_PATH.delete_if { |path| File.expand_path(path) == exclude_from_load_path }" - end - code << "$LOADED_FEATURES.delete_if { |feature| File.basename(feature, '.*') == '#{require_path}' }" code << "require '#{require_path}.rb'" code << "puts #{version_const}" run code.join("; "), *groups @@ -151,23 +145,34 @@ module Spec match_when_negated do opts = names.last.is_a?(Hash) ? names.pop : {} - groups = Array(opts[:groups]) || [] + groups = Array(opts.delete(:groups)).map(&:inspect).join(", ") opts[:raise_on_error] = false @errors = names.map do |name| name, version = name.split(/\s+/, 2) - run <<-R, *(groups + [opts]) + ruby <<-R, opts + begin + require '#{lib_dir}/bundler' + Bundler.setup(#{groups}) + rescue Bundler::GemNotFound, Bundler::GitError + exit 0 + end + begin require '#{name}' - puts #{Spec::Builders.constantize(name)} + name_constant = '#{Spec::Builders.constantize(name)}' + if #{version.nil?} || name_constant == '#{version}' + exit 64 + else + exit 0 + end rescue LoadError, NameError - puts "WIN" + exit 0 end R - next if out == "WIN" + next if exitstatus == 0 + next "command to check version of #{name} installed failed" unless exitstatus == 64 next "expected #{name} to not be installed, but it was" if version.nil? - if Gem::Version.new(out) == Gem::Version.new(version) - next "expected #{name} (#{version}) not to be installed, but it was" - end + next "expected #{name} (#{version}) not to be installed, but it was" end.compact @errors.empty? diff --git a/spec/bundler/support/rubygems_version_manager.rb b/spec/bundler/support/rubygems_version_manager.rb index ac02021cff..c2e5a5f484 100644 --- a/spec/bundler/support/rubygems_version_manager.rb +++ b/spec/bundler/support/rubygems_version_manager.rb @@ -78,6 +78,7 @@ class RubygemsVersionManager end def rubygems_unrequire_needed? + require "rubygems" !$LOADED_FEATURES.include?(local_copy_path.join("lib/rubygems.rb").to_s) end diff --git a/spec/bundler/support/sometimes.rb b/spec/bundler/support/sometimes.rb deleted file mode 100644 index 65a95ed59c..0000000000 --- a/spec/bundler/support/sometimes.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -module Sometimes - def run_with_retries(example_to_run, retries) - example = RSpec.current_example - example.metadata[:retries] ||= retries - - retries.times do |t| - example.metadata[:retried] = t + 1 - example.instance_variable_set(:@exception, nil) - example_to_run.run - break unless example.exception - end - - if e = example.exception - new_exception = e.exception(e.message + "[Retried #{retries} times]") - new_exception.set_backtrace e.backtrace - example.instance_variable_set(:@exception, new_exception) - end - end -end - -RSpec.configure do |config| - config.include Sometimes - config.alias_example_to :sometimes, :sometimes => true - config.add_setting :sometimes_retry_count, :default => 5 - - config.around(:each, :sometimes => true) do |example| - retries = example.metadata[:retries] || RSpec.configuration.sometimes_retry_count - run_with_retries(example, retries) - end - - config.after(:suite) do - message = proc do |color, text| - colored = RSpec::Core::Formatters::ConsoleCodes.wrap(text, color) - notification = RSpec::Core::Notifications::MessageNotification.new(colored) - formatter = RSpec.configuration.formatters.first - formatter.message(notification) if formatter.respond_to?(:message) - end - - retried_examples = RSpec.world.example_groups.map do |g| - g.descendants.map do |d| - d.filtered_examples.select do |e| - e.metadata[:sometimes] && e.metadata.fetch(:retried, 1) > 1 - end - end - end.flatten - - message.call(retried_examples.empty? ? :green : :yellow, "\n\nRetried examples: #{retried_examples.count}") - - retried_examples.each do |e| - message.call(:cyan, " #{e.full_description}") - path = RSpec::Core::Metadata.relative_path(e.location) - message.call(:cyan, " [#{e.metadata[:retried]}/#{e.metadata[:retries]}] " + path) - end - end -end diff --git a/test/rubygems/data/null-required-ruby-version.gemspec.rz b/test/rubygems/data/null-required-ruby-version.gemspec.rz new file mode 100644 index 0000000000000000000000000000000000000000..f4ec1a9620a411067ebcdd0e91ad60792d4bf290 GIT binary patch literal 403 zcmZvX%TB{E5Jdy3Oj?NrNIV2WSpEPvNGy;mP$eF1w{5~EV&NukGiqW7KN^LvEdAEiza^#w{p$*dFx4^pXrB`atgikyD;o*b3w7T4|fGlbz>X zXyer>B7-{`(P-vaKZZ&i2a~Jszz<^mFD1o{gh~927tN z20J5iylk;81fFv!4PI$piAL!SjS_aY`#Kk&4z#F*v?=m?M9@<*3OWkUKqJ!EZrP^6 zj09*D#^gHZTgb= 0") + + assert_equal %w[a-1 b-2 c-2], loaded_spec_names + end + + def test_activate_bin_path_raises_a_meaningful_error_if_a_gem_thats_finally_activated_has_orphaned_dependencies + a1 = util_spec 'a', '1' do |s| + s.executables = ['exec'] + s.add_dependency 'b' + end + + b1 = util_spec 'b', '1' do |s| + s.add_dependency 'c', '1' + end + + b2 = util_spec 'b', '2' do |s| + s.add_dependency 'c', '2' + end + + c1 = util_spec 'c', '1' + + install_specs c1, b1, b2, a1 + + # c2 is missing, and b2 which has it as a dependency will be activated, so we should get an error about the orphaned dependency + + e = assert_raises Gem::UnsatisfiableDependencyError do + load Gem.activate_bin_path("a", "exec", ">= 0") + end + + assert_equal "Unable to resolve dependency: 'b (>= 0)' requires 'c (= 2)'", e.message + end + def test_activate_bin_path_in_debug_mode a1 = util_spec 'a', '1' do |s| s.executables = ['exec'] @@ -416,6 +468,32 @@ class TestGem < Gem::TestCase assert_equal %w[bundler-1.17.3], loaded_spec_names end + def test_activate_bin_path_gives_proper_error_for_bundler_when_underscore_selection_given + File.open("Gemfile.lock", "w") do |f| + f.write <<-L.gsub(/ {8}/, "") + GEM + remote: https://rubygems.org/ + specs: + + PLATFORMS + ruby + + DEPENDENCIES + + BUNDLED WITH + 2.1.4 + L + end + + File.open("Gemfile", "w") {|f| f.puts('source "https://rubygems.org"') } + + e = assert_raises Gem::GemNotFoundException do + load Gem.activate_bin_path("bundler", "bundle", "= 2.2.8") + end + + assert_equal "can't find gem bundler (= 2.2.8) with executable bundle", e.message + end + def test_self_bin_path_no_exec_name e = assert_raises ArgumentError do Gem.bin_path 'a' @@ -692,6 +770,11 @@ class TestGem < Gem::TestCase ensure FileUtils.chmod 0600, parent end + + def test_self_ensure_gem_directories_non_existent_paths + Gem.ensure_gem_subdirectories '/proc/0123456789/bogus' # should not raise + Gem.ensure_gem_subdirectories 'classpath:/bogus/x' # JRuby embed scenario + end end def test_self_extension_dir_shared @@ -1875,15 +1958,9 @@ class TestGem < Gem::TestCase io.write 'gem "a"' end - platform = Bundler::GemHelpers.generic_local_platform - if platform == Gem::Platform::RUBY - platform = '' - else - platform = " #{platform}" - end - expected = <<-EXPECTED -Could not find gem 'a#{platform}' in any of the gem sources listed in your Gemfile. +Could not find gem 'a' in locally installed gems. +The source does not contain any versions of 'a' You may need to `gem install -g` to install missing gems EXPECTED diff --git a/test/rubygems/test_gem_commands_outdated_command.rb b/test/rubygems/test_gem_commands_outdated_command.rb index 57939b8088..c4af421f5d 100644 --- a/test/rubygems/test_gem_commands_outdated_command.rb +++ b/test/rubygems/test_gem_commands_outdated_command.rb @@ -28,4 +28,22 @@ class TestGemCommandsOutdatedCommand < Gem::TestCase assert_equal "foo (0.2 < 2.0)\n", @ui.output assert_equal "", @ui.error end + + def test_execute_with_up_to_date_platform_specific_gem + spec_fetcher do |fetcher| + fetcher.download 'foo', '2.0' + + fetcher.gem 'foo', '1.0' + fetcher.gem 'foo', '2.0' do |s| + s.platform = Gem::Platform.local + end + end + + use_ui @ui do + @cmd.execute + end + + assert_equal "", @ui.output + assert_equal "", @ui.error + end end diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb index ddc35a9594..7db31013aa 100644 --- a/test/rubygems/test_gem_config_file.rb +++ b/test/rubygems/test_gem_config_file.rb @@ -41,6 +41,7 @@ class TestGemConfigFile < Gem::TestCase assert_equal true, @cfg.verbose assert_equal [@gem_repo], Gem.sources assert_equal 365, @cfg.cert_expiration_length_days + assert_equal false, @cfg.ipv4_fallback_enabled File.open @temp_conf, 'w' do |fp| fp.puts ":backtrace: true" @@ -56,6 +57,7 @@ class TestGemConfigFile < Gem::TestCase fp.puts ":ssl_verify_mode: 0" fp.puts ":ssl_ca_cert: /etc/ssl/certs" fp.puts ":cert_expiration_length_days: 28" + fp.puts ":ipv4_fallback_enabled: true" end util_config_file @@ -70,6 +72,14 @@ class TestGemConfigFile < Gem::TestCase assert_equal 0, @cfg.ssl_verify_mode assert_equal '/etc/ssl/certs', @cfg.ssl_ca_cert assert_equal 28, @cfg.cert_expiration_length_days + assert_equal true, @cfg.ipv4_fallback_enabled + end + + def test_initialize_ipv4_fallback_enabled_env + ENV['IPV4_FALLBACK_ENABLED'] = 'true' + util_config_file %W[--config-file #{@temp_conf}] + + assert_equal true, @cfg.ipv4_fallback_enabled end def test_initialize_handle_arguments_config_file diff --git a/test/rubygems/test_gem_dependency_installer.rb b/test/rubygems/test_gem_dependency_installer.rb index 85dba54675..c62a3f355a 100644 --- a/test/rubygems/test_gem_dependency_installer.rb +++ b/test/rubygems/test_gem_dependency_installer.rb @@ -44,7 +44,7 @@ class TestGemDependencyInstaller < Gem::TestCase s.add_development_dependency 'c' end - util_reset_gems + util_setup_spec_fetcher(@a1, @a1_pre, @b1, @d1) end def test_install @@ -287,8 +287,6 @@ class TestGemDependencyInstaller < Gem::TestCase @aa1, @aa1_gem = util_gem 'aa', '1' - util_reset_gems - FileUtils.mv @a1_gem, @tempdir FileUtils.mv @aa1_gem, @tempdir FileUtils.mv @b1_gem, @tempdir @@ -307,8 +305,6 @@ class TestGemDependencyInstaller < Gem::TestCase @aa1, @aa1_gem = util_gem 'aa', '1' - util_reset_gems - FileUtils.mv @a1_gem, @tempdir FileUtils.mv @aa1_gem, @tempdir FileUtils.mv @b1_gem, @tempdir @@ -329,8 +325,6 @@ class TestGemDependencyInstaller < Gem::TestCase @aa1, @aa1_gem = util_gem 'aa', '1' - util_reset_gems - FileUtils.mv @a1_gem, @tempdir FileUtils.mv @aa1_gem, @tempdir FileUtils.mv @b1_gem, @tempdir @@ -946,6 +940,31 @@ class TestGemDependencyInstaller < Gem::TestCase assert_equal %w[d-2], inst.installed_gems.map {|s| s.full_name } end + def test_install_legacy_spec_with_nil_required_ruby_version + path = File.expand_path "../data/null-required-ruby-version.gemspec.rz", __FILE__ + spec = Marshal.load Gem.read_binary(path) + def spec.validate(*args); end + + util_build_gem spec + + cache_file = File.join @tempdir, 'gems', "#{spec.original_name}.gem" + FileUtils.mkdir_p File.dirname cache_file + FileUtils.mv spec.cache_file, cache_file + + util_setup_spec_fetcher spec + + data = Gem.read_binary(cache_file) + + @fetcher.data['http://gems.example.com/gems/activesupport-1.0.0.gem'] = data + + dep = Gem::Dependency.new 'activesupport' + + inst = Gem::DependencyInstaller.new + inst.install dep + + assert_equal %w[activesupport-1.0.0], Gem::Specification.map(&:full_name) + end + def test_install_legacy_spec_with_nil_required_rubygems_version path = File.expand_path "../data/null-required-rubygems-version.gemspec.rz", __FILE__ spec = Marshal.load Gem.read_binary(path) @@ -1131,16 +1150,6 @@ class TestGemDependencyInstaller < Gem::TestCase @d1, @d1_gem = util_gem 'd', '1' @d2, @d2_gem = util_gem 'd', '2' - util_reset_gems - end - - def util_reset_gems - @a1 ||= nil - @b1 ||= nil - @a1_pre ||= nil - @d1 ||= nil - @d2 ||= nil - - util_setup_spec_fetcher(*[@a1, @a1_pre, @b1, @d1, @d2].compact) + util_setup_spec_fetcher(@d1, @d2) end end diff --git a/test/rubygems/test_gem_ext_builder.rb b/test/rubygems/test_gem_ext_builder.rb index 0fe650b8a5..6bebfa7a03 100644 --- a/test/rubygems/test_gem_ext_builder.rb +++ b/test/rubygems/test_gem_ext_builder.rb @@ -14,6 +14,7 @@ class TestGemExtBuilder < Gem::TestCase FileUtils.mkdir_p @dest_path @orig_DESTDIR = ENV['DESTDIR'] + @orig_make = ENV['make'] @spec = util_spec 'a' @@ -22,6 +23,7 @@ class TestGemExtBuilder < Gem::TestCase def teardown ENV['DESTDIR'] = @orig_DESTDIR + ENV['make'] = @orig_make super end @@ -81,6 +83,28 @@ install: assert_match %r{DESTDIR\\=#{ENV['DESTDIR']} install$}, results end + def test_custom_make_with_options + ENV['make'] = 'make V=1' + results = [] + File.open File.join(@ext, 'Makefile'), 'w' do |io| + io.puts <<-MAKEFILE +all: +\t@#{Gem.ruby} -e "puts 'all: OK'" + +clean: +\t@#{Gem.ruby} -e "puts 'clean: OK'" + +install: +\t@#{Gem.ruby} -e "puts 'install: OK'" + MAKEFILE + end + Gem::Ext::Builder.make @dest_path, results, @ext + results = results.join("\n").b + assert_match %r{clean: OK}, results + assert_match %r{all: OK}, results + assert_match %r{install: OK}, results + end + def test_build_extensions @spec.extensions << 'ext/extconf.rb' diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb index 4ce7e92442..5652d86331 100644 --- a/test/rubygems/test_gem_installer.rb +++ b/test/rubygems/test_gem_installer.rb @@ -821,6 +821,8 @@ gem 'other', version assert !File.exist?(system_path), 'plugin written incorrect written to system plugins_dir' assert File.exist?(build_root_path), 'plugin not written to build_root' + + refute_includes File.read(build_root_path), build_root end def test_keeps_plugins_up_to_date diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb index 83a6f24de4..ad7285c082 100644 --- a/test/rubygems/test_gem_platform.rb +++ b/test/rubygems/test_gem_platform.rb @@ -134,7 +134,9 @@ class TestGemPlatform < Gem::TestCase 'i386-solaris2.8' => ['x86', 'solaris', '2.8'], 'mswin32' => ['x86', 'mswin32', nil], 'x86_64-linux' => ['x86_64', 'linux', nil], + 'x86_64-linux-gnu' => ['x86_64', 'linux', nil], 'x86_64-linux-musl' => ['x86_64', 'linux', 'musl'], + 'x86_64-linux-uclibc' => ['x86_64', 'linux', 'uclibc'], 'x86_64-openbsd3.9' => ['x86_64', 'openbsd', '3.9'], 'x86_64-openbsd4.0' => ['x86_64', 'openbsd', '4.0'], 'x86_64-openbsd' => ['x86_64', 'openbsd', nil], @@ -143,6 +145,7 @@ class TestGemPlatform < Gem::TestCase test_cases.each do |arch, expected| platform = Gem::Platform.new arch assert_equal expected, platform.to_a, arch.inspect + assert_equal expected, Gem::Platform.new(platform.to_s).to_a, arch.inspect end end @@ -261,6 +264,32 @@ class TestGemPlatform < Gem::TestCase assert((with_x86_arch === with_nil_arch), 'x86 =~ nil') end + def test_nil_version_is_treated_as_any_version + x86_darwin_8 = Gem::Platform.new 'i686-darwin8.0' + x86_darwin_nil = Gem::Platform.new 'i686-darwin' + + assert((x86_darwin_8 === x86_darwin_nil), '8.0 =~ nil') + assert((x86_darwin_nil === x86_darwin_8), 'nil =~ 8.0') + end + + def test_nil_version_is_stricter_for_linux_os + x86_linux = Gem::Platform.new 'i686-linux' + x86_linux_gnu = Gem::Platform.new 'i686-linux-gnu' + x86_linux_musl = Gem::Platform.new 'i686-linux-musl' + x86_linux_uclibc = Gem::Platform.new 'i686-linux-uclibc' + + assert((x86_linux === x86_linux_gnu), 'linux =~ linux-gnu') + assert((x86_linux_gnu === x86_linux), 'linux-gnu =~ linux') + assert(!(x86_linux_gnu === x86_linux_musl), 'linux-gnu =~ linux-musl') + assert(!(x86_linux_musl === x86_linux_gnu), 'linux-musl =~ linux-gnu') + assert(!(x86_linux_uclibc === x86_linux_musl), 'linux-uclibc =~ linux-musl') + assert(!(x86_linux_musl === x86_linux_uclibc), 'linux-musl =~ linux-uclibc') + assert(!(x86_linux === x86_linux_musl), 'linux =~ linux-musl') + assert(!(x86_linux_musl === x86_linux), 'linux-musl =~ linux') + assert(!(x86_linux === x86_linux_uclibc), 'linux =~ linux-uclibc') + assert(!(x86_linux_uclibc === x86_linux), 'linux-uclibc =~ linux') + end + def test_equals3_cpu_arm arm = Gem::Platform.new 'arm-linux' armv5 = Gem::Platform.new 'armv5-linux' @@ -356,6 +385,14 @@ class TestGemPlatform < Gem::TestCase assert_local_match 'sparc-solaris2.8-mq5.3' end + def test_inspect + result = Gem::Platform.new("universal-java11").inspect + + assert_equal 1, result.scan(/@cpu=/).size + assert_equal 1, result.scan(/@os=/).size + assert_equal 1, result.scan(/@version=/).size + end + def assert_local_match(name) assert_match Gem::Platform.local, name end diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb index 1c88e8d3e8..32600deee2 100644 --- a/test/rubygems/test_gem_remote_fetcher.rb +++ b/test/rubygems/test_gem_remote_fetcher.rb @@ -962,6 +962,12 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== end end + def test_tcpsocketext_require + with_configured_fetcher(":ipv4_fallback_enabled: true") do |fetcher| + refute require('rubygems/core_ext/tcpsocket_init') + end + end + def with_configured_fetcher(config_str = nil, &block) if config_str temp_conf = File.join @tempdir, '.gemrc' diff --git a/test/rubygems/test_gem_requirement.rb b/test/rubygems/test_gem_requirement.rb index 670defe330..13897eaefb 100644 --- a/test/rubygems/test_gem_requirement.rb +++ b/test/rubygems/test_gem_requirement.rb @@ -83,7 +83,7 @@ class TestGemRequirement < Gem::TestCase Gem::Requirement.parse(Gem::Version.new('2')) end - if RUBY_VERSION >= '2.5' + if RUBY_VERSION >= '2.5' && !(Gem.java_platform? && ENV["JRUBY_OPTS"] =~ /--debug/) def test_parse_deduplication assert_same '~>', Gem::Requirement.parse('~> 1').first end @@ -401,6 +401,27 @@ class TestGemRequirement < Gem::TestCase assert_equal r1.hash, r2.hash end + def test_hash_returns_equal_hashes_for_equivalent_requirements + refute_requirement_hash_equal "= 1.2", "= 1.3" + refute_requirement_hash_equal "= 1.3", "= 1.2" + + refute_requirement_hash_equal "~> 1.3", "~> 1.3.0" + refute_requirement_hash_equal "~> 1.3.0", "~> 1.3" + + assert_requirement_hash_equal ["> 2", "~> 1.3", "~> 1.3.1"], ["~> 1.3.1", "~> 1.3", "> 2"] + + assert_requirement_hash_equal ["> 2", "~> 1.3"], ["> 2.0", "~> 1.3"] + assert_requirement_hash_equal ["> 2.0", "~> 1.3"], ["> 2", "~> 1.3"] + + assert_requirement_hash_equal "= 1.0", "= 1.0.0" + assert_requirement_hash_equal "= 1.1", "= 1.1.0" + assert_requirement_hash_equal "= 1", "= 1.0.0" + + assert_requirement_hash_equal "1.0", "1.0.0" + assert_requirement_hash_equal "1.1", "1.1.0" + assert_requirement_hash_equal "1", "1.0.0" + end + # Assert that two requirements are equal. Handles Gem::Requirements, # strings, arrays, numbers, and versions. @@ -415,6 +436,13 @@ class TestGemRequirement < Gem::TestCase "#{requirement} is satisfied by #{version}" end + # Assert that two requirement hashes are equal. Handles Gem::Requirements, + # strings, arrays, numbers, and versions. + + def assert_requirement_hash_equal(expected, actual) + assert_equal req(expected).hash, req(actual).hash + end + # Refute the assumption that two requirements are equal. def refute_requirement_equal(unexpected, actual) @@ -427,4 +455,10 @@ class TestGemRequirement < Gem::TestCase refute req(requirement).satisfied_by?(v(version)), "#{requirement} is not satisfied by #{version}" end + + # Refute the assumption that two requirements hashes are equal. + + def refute_requirement_hash_equal(unexpected, actual) + refute_equal req(unexpected).hash, req(actual).hash + end end diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index 374d58d38f..1389b495f2 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -1161,6 +1161,14 @@ dependencies: [] Gem::Specification.class_variable_set(:@@stubs, nil) end + def test_self_stubs_for_no_lazy_loading_after_all_specs_setup + Gem::Specification.all = [util_spec('a', '1')] + + save_gemspec('b-1', '1', File.join(Gem.dir, 'specifications')){|s| s.name = 'b' } + + assert_equal [], Gem::Specification.stubs_for('b').map {|s| s.full_name } + end + def test_self_stubs_for_mult_platforms # gems for two different platforms are installed with --user-install # the correct one should be returned in the array @@ -1200,6 +1208,16 @@ dependencies: [] Gem.platforms = orig_platform end + def test_self_stubs_returns_only_specified_named_specs + dir_standard_specs = File.join Gem.dir, 'specifications' + + save_gemspec('a-1', '1', dir_standard_specs){|s| s.name = 'a' } + save_gemspec('a-2', '2', dir_standard_specs){|s| s.name = 'a' } + save_gemspec('a-a', '3', dir_standard_specs){|s| s.name = 'a-a' } + + assert_equal ['a-1', 'a-2'], Gem::Specification.stubs_for('a').map(&:full_name).sort + end + def test_handles_private_null_type path = File.expand_path "../data/null-type.gemspec.rz", __FILE__ @@ -1612,20 +1630,6 @@ dependencies: [] refute_path_exists @a1.extension_dir end - def test_build_extensions_old - skip "extensions don't quite work on jruby" if Gem.java_platform? - ext_spec - - refute_empty @ext.extensions, 'sanity check' - - @ext.installed_by_version = v(0) - - @ext.build_extensions - - gem_make_out = File.join @ext.extension_dir, 'gem_make.out' - refute_path_exists gem_make_out - end - def test_build_extensions_preview skip "extensions don't quite work on jruby" if Gem.java_platform? ext_spec @@ -3522,19 +3526,6 @@ Did you mean 'Ruby'? specfile.delete end - ## - # KEEP p-1-x86-darwin-8 - # KEEP p-1 - # KEEP c-1.2 - # KEEP a_evil-9 - # a-1 - # a-1-x86-my_platform-1 - # KEEP a-2 - # a-2-x86-other_platform-1 - # KEEP a-2-x86-my_platform-1 - # a-3.a - # KEEP a-3-x86-other_platform-1 - def test_latest_specs spec_fetcher do |fetcher| fetcher.spec 'a', 1 do |s| @@ -3557,8 +3548,6 @@ Did you mean 'Ruby'? end expected = %W[ - a-2 - a-2-x86-my_platform-1 a-3-x86-other_platform-1 ] @@ -3734,18 +3723,6 @@ end refute spec.missing_extensions? end - def test_missing_extensions_eh_legacy - ext_spec - - @ext.installed_by_version = v '2.2.0.preview.2' - - assert @ext.missing_extensions? - - @ext.installed_by_version = v '2.2.0.preview.1' - - refute @ext.missing_extensions? - end - def test_missing_extensions_eh_none refute @a1.missing_extensions? end diff --git a/test/rubygems/test_gem_stream_ui.rb b/test/rubygems/test_gem_stream_ui.rb index e154a723c9..a62e9ea0cf 100644 --- a/test/rubygems/test_gem_stream_ui.rb +++ b/test/rubygems/test_gem_stream_ui.rb @@ -5,7 +5,7 @@ require 'timeout' class TestGemStreamUI < Gem::TestCase # increase timeout with MJIT for --jit-wait testing - mjit_enabled = defined?(RubyVM::JIT) && RubyVM::JIT.enabled? + mjit_enabled = defined?(RubyVM::JIT) ? RubyVM::JIT.enabled? : defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? SHORT_TIMEOUT = (RUBY_ENGINE == "ruby" && !mjit_enabled) ? 0.1 : 1.0 module IsTty diff --git a/test/rubygems/test_gem_util.rb b/test/rubygems/test_gem_util.rb index acf7ac8962..7197f664e2 100644 --- a/test/rubygems/test_gem_util.rb +++ b/test/rubygems/test_gem_util.rb @@ -46,8 +46,8 @@ class TestGemUtil < Gem::TestCase assert_equal File.join(@tempdir, 'd'), paths[0] assert_equal @tempdir, paths[1] - assert_equal File.realpath(Dir.tmpdir), paths[2] - assert_equal File.realpath("..", Dir.tmpdir), paths[3] + assert_equal File.realpath("..", @tempdir), paths[2] + assert_equal File.realpath("../..", @tempdir), paths[3] ensure # restore default permissions, allow the directory to be removed FileUtils.chmod(0775, 'd/e') unless win_platform? || java_platform? @@ -72,10 +72,10 @@ class TestGemUtil < Gem::TestCase ] files_with_absolute_base = Gem::Util.glob_files_in_dir('*.rb', File.join(@tempdir, 'g')) - assert_equal expected_paths.to_set, files_with_absolute_base.to_set + assert_equal expected_paths.sort, files_with_absolute_base.sort files_with_relative_base = Gem::Util.glob_files_in_dir('*.rb', 'g') - assert_equal expected_paths.to_set, files_with_relative_base.to_set + assert_equal expected_paths.sort, files_with_relative_base.sort end def test_correct_for_windows_path