diff options
author | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2023-03-28 20:36:47 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-28 11:36:47 +0000 |
commit | c3c461c4ff1b998ac49ed1a3ad411d2f0a9123b8 (patch) | |
tree | 70c8438d980e19d718dc50e04b6221b023e26387 | |
parent | f8c775cb413724a45bb5641de94f4f9210044116 (diff) | |
download | ruby-c3c461c4ff1b998ac49ed1a3ad411d2f0a9123b8.tar.gz |
Merge RubyGems-3.4.10 and Bundler-2.4.10 (#7479)
* Merge RubyGems-3.4.7 and Bundler-2.4.7
* Merge RubyGems-3.4.8 and Bundler-2.4.8
* Skip failing test on MSWin
* Merge RubyGems-3.4.9 and Bundler-2.4.9
* Merge RubyGems-3.4.10 and Bundler-2.4.10
---------
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
179 files changed, 2883 insertions, 470 deletions
diff --git a/lib/bundler.rb b/lib/bundler.rb index c6a225977f..88f25b523a 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -39,8 +39,8 @@ module Bundler environment_preserver.replace_with_backup SUDO_MUTEX = Thread::Mutex.new - SAFE_MARSHAL_CLASSES = [Symbol, TrueClass, String, Array, Hash].freeze - SAFE_MARSHAL_ERROR = "Unexpected class %s present in marshaled data. Only %s are allowed.".freeze + SAFE_MARSHAL_CLASSES = [Symbol, TrueClass, String, Array, Hash, Gem::Version, Gem::Specification].freeze + SAFE_MARSHAL_ERROR = "Unexpected class %s present in marshaled data. Only %s are allowed." SAFE_MARSHAL_PROC = proc do |object| object.tap do unless SAFE_MARSHAL_CLASSES.include?(object.class) @@ -85,6 +85,7 @@ module Bundler autoload :StubSpecification, File.expand_path("bundler/stub_specification", __dir__) autoload :UI, File.expand_path("bundler/ui", __dir__) autoload :URICredentialsFilter, File.expand_path("bundler/uri_credentials_filter", __dir__) + autoload :URINormalizer, File.expand_path("bundler/uri_normalizer", __dir__) class << self def configure @@ -506,7 +507,7 @@ EOF if File.file?(executable) && File.executable?(executable) executable elsif paths = ENV["PATH"] - quote = '"'.freeze + quote = '"' paths.split(File::PATH_SEPARATOR).find do |path| path = path[1..-2] if path.start_with?(quote) && path.end_with?(quote) executable_path = File.expand_path(executable, path) @@ -525,12 +526,6 @@ EOF load_marshal(data, :marshal_proc => SAFE_MARSHAL_PROC) end - def load_marshal(data, marshal_proc: nil) - Marshal.load(data, marshal_proc) - rescue TypeError => e - raise MarshalError, "#{e.class}: #{e.message}" - end - def load_gemspec(file, validate = false) @gemspec_cache ||= {} key = File.expand_path(file) @@ -619,6 +614,12 @@ EOF private + def load_marshal(data, marshal_proc: nil) + Marshal.load(data, marshal_proc) + rescue TypeError => e + raise MarshalError, "#{e.class}: #{e.message}" + end + def eval_yaml_gemspec(path, contents) Kernel.require "psych" diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index ee73bdb506..a3eb494db2 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -156,6 +156,7 @@ module Bundler dependency listed in the gemspec file to the newly created Gemfile. D method_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile" + method_option "gemfile", :type => :string, :banner => "Use the specified name for the gemfile instead of 'Gemfile'" def init require_relative "cli/init" Init.new(options.dup).run diff --git a/lib/bundler/cli/init.rb b/lib/bundler/cli/init.rb index bc96507c29..246b9d6460 100644 --- a/lib/bundler/cli/init.rb +++ b/lib/bundler/cli/init.rb @@ -32,7 +32,7 @@ module Bundler file << spec.to_gemfile end else - File.open(File.expand_path("../templates/#{gemfile}", __dir__), "r") do |template| + File.open(File.expand_path("../templates/Gemfile", __dir__), "r") do |template| File.open(gemfile, "wb") do |destination| IO.copy_stream(template, destination) end @@ -45,7 +45,7 @@ module Bundler private def gemfile - @gemfile ||= Bundler.preferred_gemfile_name + @gemfile ||= options[:gemfile] || Bundler.preferred_gemfile_name end end end diff --git a/lib/bundler/current_ruby.rb b/lib/bundler/current_ruby.rb index f9987c4da8..f009b07ad7 100644 --- a/lib/bundler/current_ruby.rb +++ b/lib/bundler/current_ruby.rb @@ -22,6 +22,8 @@ module Bundler 2.7 3.0 3.1 + 3.2 + 3.3 ].freeze KNOWN_MAJOR_VERSIONS = KNOWN_MINOR_VERSIONS.map {|v| v.split(".", 2).first }.uniq.freeze diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index e27374f35d..bef86cdd60 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -726,6 +726,8 @@ module Bundler dep.source = sources.get(dep.source) end + next if unlocking? + unless locked_dep = @locked_deps[dep.name] changes = true next @@ -886,8 +888,9 @@ module Bundler end def additional_base_requirements_for_resolve(resolution_packages, last_resolve) - return resolution_packages unless @locked_gems && unlocking? && !sources.expired_sources?(@locked_gems.sources) + return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources) converge_specs(@originally_locked_specs - last_resolve).each do |locked_spec| + next if locked_spec.source.is_a?(Source::Path) resolution_packages.base_requirements[locked_spec.name] = Gem::Requirement.new(">= #{locked_spec.version}") end resolution_packages @@ -898,6 +901,7 @@ module Bundler Bundler.local_platform == Gem::Platform::RUBY || !platforms.include?(Gem::Platform::RUBY) || (@new_platform && platforms.last == Gem::Platform::RUBY) || + @dependency_changes || !@originally_locked_specs.incomplete_ruby_specs?(dependencies) remove_platform(Gem::Platform::RUBY) diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb index 1f8b9da2eb..5f17943629 100644 --- a/lib/bundler/dependency.rb +++ b/lib/bundler/dependency.rb @@ -9,7 +9,7 @@ module Bundler attr_reader :autorequire attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref - ALL_RUBY_VERSIONS = ((18..27).to_a + (30..31).to_a).freeze + ALL_RUBY_VERSIONS = ((18..27).to_a + (30..33).to_a).freeze PLATFORM_MAP = { :ruby => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS], :mri => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS], diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb index d315d1cc68..863544b1f9 100644 --- a/lib/bundler/endpoint_specification.rb +++ b/lib/bundler/endpoint_specification.rb @@ -26,10 +26,6 @@ module Bundler @platform end - def identifier - @__identifier ||= [name, version, platform.to_s] - end - # needed for standalone, load required_paths from local gemspec # after the gem is installed def require_paths diff --git a/lib/bundler/environment_preserver.rb b/lib/bundler/environment_preserver.rb index 70967522af..57013f5d50 100644 --- a/lib/bundler/environment_preserver.rb +++ b/lib/bundler/environment_preserver.rb @@ -2,7 +2,7 @@ module Bundler class EnvironmentPreserver - INTENTIONALLY_NIL = "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL".freeze + INTENTIONALLY_NIL = "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL" BUNDLER_KEYS = %w[ BUNDLE_BIN_PATH BUNDLE_GEMFILE @@ -16,7 +16,7 @@ module Bundler RUBYLIB RUBYOPT ].map(&:freeze).freeze - BUNDLER_PREFIX = "BUNDLER_ORIG_".freeze + BUNDLER_PREFIX = "BUNDLER_ORIG_" def self.from_env new(env_to_hash(ENV), BUNDLER_KEYS) diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb index a073bae278..e12c15af8a 100644 --- a/lib/bundler/fetcher.rb +++ b/lib/bundler/fetcher.rb @@ -102,11 +102,11 @@ module Bundler uri = Bundler::URI.parse("#{remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz") if uri.scheme == "file" path = Bundler.rubygems.correct_for_windows_path(uri.path) - Bundler.load_marshal Bundler.rubygems.inflate(Gem.read_binary(path)) + Bundler.safe_load_marshal Bundler.rubygems.inflate(Gem.read_binary(path)) elsif cached_spec_path = gemspec_cached_path(spec_file_name) Bundler.load_gemspec(cached_spec_path) else - Bundler.load_marshal Bundler.rubygems.inflate(downloader.fetch(uri).body) + Bundler.safe_load_marshal Bundler.rubygems.inflate(downloader.fetch(uri).body) end rescue MarshalError raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \ diff --git a/lib/bundler/fetcher/dependency.rb b/lib/bundler/fetcher/dependency.rb index 332f86139d..18b606abb6 100644 --- a/lib/bundler/fetcher/dependency.rb +++ b/lib/bundler/fetcher/dependency.rb @@ -34,14 +34,10 @@ module Bundler returned_gems = spec_list.map(&:first).uniq specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list) - rescue MarshalError + rescue MarshalError, HTTPError, GemspecError Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over Bundler.ui.debug "could not fetch from the dependency API, trying the full index" nil - rescue HTTPError, GemspecError - Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over - Bundler.ui.debug "could not fetch from the dependency API\nit's suggested to retry using the full index via `bundle install --full-index`" - nil end def dependency_specs(gem_names) diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb index 0301986ca9..b8c599f63a 100644 --- a/lib/bundler/index.rb +++ b/lib/bundler/index.rb @@ -13,8 +13,8 @@ module Bundler attr_reader :specs, :all_specs, :sources protected :specs, :all_specs - RUBY = "ruby".freeze - NULL = "\0".freeze + RUBY = "ruby" + NULL = "\0" def initialize @sources = [] diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb index c2edc9b43a..cb644a7f69 100644 --- a/lib/bundler/injector.rb +++ b/lib/bundler/injector.rb @@ -2,7 +2,7 @@ module Bundler class Injector - INJECTED_GEMS = "injected gems".freeze + INJECTED_GEMS = "injected gems" def self.inject(new_deps, options = {}) injector = new(new_deps, options) diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb index dce7133769..58c5aafa1c 100644 --- a/lib/bundler/installer/parallel_installer.rb +++ b/lib/bundler/installer/parallel_installer.rb @@ -110,12 +110,13 @@ module Bundler warning = [] warning << "Your lockfile doesn't include a valid resolution." - warning << "You can fix this by regenerating your lockfile or trying to manually editing the bad locked gems to a version that satisfies all dependencies." + warning << "You can fix this by regenerating your lockfile or manually editing the bad locked gems to a version that satisfies all dependencies." warning << "The unmet dependencies are:" unmet_dependencies.each do |spec, unmet_spec_dependencies| unmet_spec_dependencies.each do |unmet_spec_dependency| - warning << "* #{unmet_spec_dependency}, depended upon #{spec.full_name}, unsatisfied by #{@specs.find {|s| s.name == unmet_spec_dependency.name && !unmet_spec_dependency.matches_spec?(s.spec) }.full_name}" + found = @specs.find {|s| s.name == unmet_spec_dependency.name && !unmet_spec_dependency.matches_spec?(s.spec) } + warning << "* #{unmet_spec_dependency}, dependency of #{spec.full_name}, unsatisfied by #{found.full_name}" end end diff --git a/lib/bundler/installer/standalone.rb b/lib/bundler/installer/standalone.rb index 2efef61648..2a8c9a432d 100644 --- a/lib/bundler/installer/standalone.rb +++ b/lib/bundler/installer/standalone.rb @@ -52,7 +52,7 @@ module Bundler def gem_path(path, spec) full_path = Pathname.new(path).absolute? ? path : File.join(spec.full_gem_path, path) - if spec.source.instance_of?(Source::Path) + if spec.source.instance_of?(Source::Path) && spec.source.path.absolute? full_path else Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path)).to_s diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index 6749892930..ad8191c55f 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -20,7 +20,7 @@ module Bundler end def full_name - if platform == Gem::Platform::RUBY + @full_name ||= if platform == Gem::Platform::RUBY "#{@name}-#{@version}" else "#{@name}-#{@version}-#{platform}" @@ -28,15 +28,15 @@ module Bundler end def ==(other) - identifier == other.identifier + full_name == other.full_name end def eql?(other) - identifier.eql?(other.identifier) + full_name.eql?(other.full_name) end def hash - identifier.hash + full_name.hash end ## @@ -129,10 +129,6 @@ module Bundler end end - def identifier - @__identifier ||= [name, version, platform.to_s] - end - def git_version return unless source.is_a?(Bundler::Source::Git) " #{source.revision[0..6]}" diff --git a/lib/bundler/lockfile_generator.rb b/lib/bundler/lockfile_generator.rb index 23413dbdd6..a7ee026f67 100644 --- a/lib/bundler/lockfile_generator.rb +++ b/lib/bundler/lockfile_generator.rb @@ -45,7 +45,7 @@ module Bundler # gems with the same name, but different platform # are ordered consistently specs.sort_by(&:full_name).each do |spec| - next if spec.name == "bundler".freeze + next if spec.name == "bundler" out << spec.to_lock end end diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb index 0ec5b7de8a..97cbf211ba 100644 --- a/lib/bundler/lockfile_parser.rb +++ b/lib/bundler/lockfile_parser.rb @@ -4,15 +4,15 @@ module Bundler class LockfileParser attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version - BUNDLED = "BUNDLED WITH".freeze - DEPENDENCIES = "DEPENDENCIES".freeze - PLATFORMS = "PLATFORMS".freeze - RUBY = "RUBY VERSION".freeze - GIT = "GIT".freeze - GEM = "GEM".freeze - PATH = "PATH".freeze - PLUGIN = "PLUGIN SOURCE".freeze - SPECS = " specs:".freeze + BUNDLED = "BUNDLED WITH" + DEPENDENCIES = "DEPENDENCIES" + PLATFORMS = "PLATFORMS" + RUBY = "RUBY VERSION" + GIT = "GIT" + GEM = "GEM" + PATH = "PATH" + PLUGIN = "PLUGIN SOURCE" + SPECS = " specs:" OPTIONS = /^ ([a-z]+): (.*)$/i.freeze SOURCE = [GIT, GEM, PATH, PLUGIN].freeze @@ -86,7 +86,7 @@ module Bundler send("parse_#{@state}", line) end end - @specs = @specs.values.sort_by(&:identifier) + @specs = @specs.values.sort_by(&:full_name) rescue ArgumentError => e Bundler.ui.debug(e) raise LockfileError, "Your lockfile is unreadable. Run `rm #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` " \ @@ -199,7 +199,7 @@ module Bundler @current_spec.source = @current_source @current_source.add_dependency_names(name) - @specs[@current_spec.identifier] = @current_spec + @specs[@current_spec.full_name] = @current_spec elsif spaces.size == 6 version = version.split(",").map(&:strip) if version dep = Gem::Dependency.new(name, version) diff --git a/lib/bundler/man/bundle-add.1 b/lib/bundler/man/bundle-add.1 index 805bd5450d..0e21c75506 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" "January 2023" "" "" +.TH "BUNDLE\-ADD" "1" "February 2023" "" "" . .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 f5efe77e10..2774e9d28a 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" "January 2023" "" "" +.TH "BUNDLE\-BINSTUBS" "1" "February 2023" "" "" . .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 a17f1a6b06..f24b63c6fc 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" "January 2023" "" "" +.TH "BUNDLE\-CACHE" "1" "February 2023" "" "" . .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 f1bf0b1821..7679945c48 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" "January 2023" "" "" +.TH "BUNDLE\-CHECK" "1" "February 2023" "" "" . .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 b05a8eb0eb..2eb745698c 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" "January 2023" "" "" +.TH "BUNDLE\-CLEAN" "1" "February 2023" "" "" . .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 fdb06c321d..493b57e1de 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" "January 2023" "" "" +.TH "BUNDLE\-CONFIG" "1" "February 2023" "" "" . .SH "NAME" \fBbundle\-config\fR \- Set bundler configuration options diff --git a/lib/bundler/man/bundle-console.1 b/lib/bundler/man/bundle-console.1 index 6cb0f0f810..ff239004bf 100644 --- a/lib/bundler/man/bundle-console.1 +++ b/lib/bundler/man/bundle-console.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-CONSOLE" "1" "January 2023" "" "" +.TH "BUNDLE\-CONSOLE" "1" "February 2023" "" "" . .SH "NAME" \fBbundle\-console\fR \- Deprecated way to open an IRB session with the bundle pre\-loaded diff --git a/lib/bundler/man/bundle-doctor.1 b/lib/bundler/man/bundle-doctor.1 index bfe928c9a1..e463b67477 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" "January 2023" "" "" +.TH "BUNDLE\-DOCTOR" "1" "February 2023" "" "" . .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 281f3faede..9e9efe8b6d 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" "January 2023" "" "" +.TH "BUNDLE\-EXEC" "1" "February 2023" "" "" . .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 4bac1bf442..ea64871fb6 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" "January 2023" "" "" +.TH "BUNDLE\-GEM" "1" "February 2023" "" "" . .SH "NAME" \fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem diff --git a/lib/bundler/man/bundle-help.1 b/lib/bundler/man/bundle-help.1 index 47a42a497e..a3b059ea07 100644 --- a/lib/bundler/man/bundle-help.1 +++ b/lib/bundler/man/bundle-help.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-HELP" "1" "January 2023" "" "" +.TH "BUNDLE\-HELP" "1" "February 2023" "" "" . .SH "NAME" \fBbundle\-help\fR \- Displays detailed help for each subcommand diff --git a/lib/bundler/man/bundle-info.1 b/lib/bundler/man/bundle-info.1 index e9f5583e85..5af60c6a77 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" "January 2023" "" "" +.TH "BUNDLE\-INFO" "1" "February 2023" "" "" . .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 9e8b059f1f..e93b4fd5e9 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" "January 2023" "" "" +.TH "BUNDLE\-INIT" "1" "February 2023" "" "" . .SH "NAME" \fBbundle\-init\fR \- Generates a Gemfile into the current working directory @@ -18,6 +18,10 @@ Init generates a default [\fBGemfile(5)\fR][Gemfile(5)] in the current working d \fB\-\-gemspec\fR Use the specified \.gemspec to create the [\fBGemfile(5)\fR][Gemfile(5)] . +.TP +\fB\-\-gemfile\fR +Use the specified name for the gemfile instead of \fBGemfile\fR +. .SH "FILES" Included in the default [\fBGemfile(5)\fR][Gemfile(5)] generated is the line \fB# frozen_string_literal: true\fR\. This is a magic comment supported for the first time in Ruby 2\.3\. The presence of this line results in all string literals in the file being implicitly frozen\. . diff --git a/lib/bundler/man/bundle-init.1.ronn b/lib/bundler/man/bundle-init.1.ronn index 9d3d97deea..7d3cede1f6 100644 --- a/lib/bundler/man/bundle-init.1.ronn +++ b/lib/bundler/man/bundle-init.1.ronn @@ -16,6 +16,8 @@ created [`Gemfile(5)`][Gemfile(5)]. * `--gemspec`: Use the specified .gemspec to create the [`Gemfile(5)`][Gemfile(5)] +* `--gemfile`: + Use the specified name for the gemfile instead of `Gemfile` ## FILES diff --git a/lib/bundler/man/bundle-inject.1 b/lib/bundler/man/bundle-inject.1 index 0b0766a40c..657d5ef4a7 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" "January 2023" "" "" +.TH "BUNDLE\-INJECT" "1" "February 2023" "" "" . .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 fcf6a6a66c..8f144692f3 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" "January 2023" "" "" +.TH "BUNDLE\-INSTALL" "1" "February 2023" "" "" . .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 37d2c837e4..f24f62dd38 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" "January 2023" "" "" +.TH "BUNDLE\-LIST" "1" "February 2023" "" "" . .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 422da46d00..55d1035d77 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" "January 2023" "" "" +.TH "BUNDLE\-LOCK" "1" "February 2023" "" "" . .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 c831bf9ce9..ff44d1224f 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" "January 2023" "" "" +.TH "BUNDLE\-OPEN" "1" "February 2023" "" "" . .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 40fe8a4def..8455b71b45 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" "January 2023" "" "" +.TH "BUNDLE\-OUTDATED" "1" "February 2023" "" "" . .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 d0015a80ef..2794878719 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" "January 2023" "" "" +.TH "BUNDLE\-PLATFORM" "1" "February 2023" "" "" . .SH "NAME" \fBbundle\-platform\fR \- Displays platform compatibility information diff --git a/lib/bundler/man/bundle-plugin.1 b/lib/bundler/man/bundle-plugin.1 index a231bb1e3d..39d3dfa04e 100644 --- a/lib/bundler/man/bundle-plugin.1 +++ b/lib/bundler/man/bundle-plugin.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-PLUGIN" "1" "January 2023" "" "" +.TH "BUNDLE\-PLUGIN" "1" "February 2023" "" "" . .SH "NAME" \fBbundle\-plugin\fR \- Manage Bundler plugins diff --git a/lib/bundler/man/bundle-pristine.1 b/lib/bundler/man/bundle-pristine.1 index 8f4bad3db6..f42c7ce156 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" "January 2023" "" "" +.TH "BUNDLE\-PRISTINE" "1" "February 2023" "" "" . .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 97a65016ac..b18d80554d 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" "January 2023" "" "" +.TH "BUNDLE\-REMOVE" "1" "February 2023" "" "" . .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 82706d45f3..efd9ccb0e0 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" "January 2023" "" "" +.TH "BUNDLE\-SHOW" "1" "February 2023" "" "" . .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 65448f4806..c67c44ff86 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" "January 2023" "" "" +.TH "BUNDLE\-UPDATE" "1" "February 2023" "" "" . .SH "NAME" \fBbundle\-update\fR \- Update your gems to the latest available versions diff --git a/lib/bundler/man/bundle-version.1 b/lib/bundler/man/bundle-version.1 index bb03e8b5d6..9a3820f3e6 100644 --- a/lib/bundler/man/bundle-version.1 +++ b/lib/bundler/man/bundle-version.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BUNDLE\-VERSION" "1" "January 2023" "" "" +.TH "BUNDLE\-VERSION" "1" "February 2023" "" "" . .SH "NAME" \fBbundle\-version\fR \- Prints Bundler version information diff --git a/lib/bundler/man/bundle-viz.1 b/lib/bundler/man/bundle-viz.1 index 23fb95b738..3a07010309 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" "January 2023" "" "" +.TH "BUNDLE\-VIZ" "1" "February 2023" "" "" . .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 39f3807f30..873ba566b1 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" "January 2023" "" "" +.TH "BUNDLE" "1" "February 2023" "" "" . .SH "NAME" \fBbundle\fR \- Ruby Dependency Management diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5 index 740a01e0cc..a9124d26ea 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" "January 2023" "" "" +.TH "GEMFILE" "5" "February 2023" "" "" . .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 26458bd596..f3caff8963 100644 --- a/lib/bundler/plugin.rb +++ b/lib/bundler/plugin.rb @@ -15,7 +15,7 @@ module Bundler class UnknownSourceError < PluginError; end class PluginInstallError < PluginError; end - PLUGIN_FILE_NAME = "plugins.rb".freeze + PLUGIN_FILE_NAME = "plugins.rb" module_function diff --git a/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb index 81ecafa470..c9ff12ce4b 100644 --- a/lib/bundler/plugin/installer.rb +++ b/lib/bundler/plugin/installer.rb @@ -83,8 +83,11 @@ module Bundler Bundler.configure_gem_home_and_path(Plugin.root) - definition = Definition.new(nil, deps, source_list, true) - install_definition(definition) + Bundler.settings.temporary(:deployment => false, :frozen => false) do + definition = Definition.new(nil, deps, source_list, true) + + install_definition(definition) + end end # Installs the plugins and deps from the provided specs and returns map of diff --git a/lib/bundler/remote_specification.rb b/lib/bundler/remote_specification.rb index 34d7fd116c..f626a3218e 100644 --- a/lib/bundler/remote_specification.rb +++ b/lib/bundler/remote_specification.rb @@ -29,12 +29,8 @@ module Bundler @platform = _remote_specification.platform end - def identifier - @__identifier ||= [name, version, @platform.to_s] - end - def full_name - if @platform == Gem::Platform::RUBY + @full_name ||= if @platform == Gem::Platform::RUBY "#{@name}-#{@version}" else "#{@name}-#{@version}-#{@platform}" @@ -106,7 +102,7 @@ module Bundler def _remote_specification @_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, @original_platform]) @_remote_specification || raise(GemspecError, "Gemspec data for #{full_name} was" \ - " missing from the server! Try installing with `--full-index` as a workaround.") + " missing from the server!") end def method_missing(method, *args, &blk) diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 8237ff53fe..c8cc88a3ee 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -37,7 +37,9 @@ module Bundler root_version = Resolver::Candidate.new(0) @all_specs = Hash.new do |specs, name| - specs[name] = source_for(name).specs.search(name).sort_by {|s| [s.version, s.platform.to_s] } + specs[name] = source_for(name).specs.search(name).reject do |s| + s.dependencies.any? {|d| d.name == name && !d.requirement.satisfied_by?(s.version) } # ignore versions that depend on themselves incorrectly + end.sort_by {|s| [s.version, s.platform.to_s] } end @sorted_versions = Hash.new do |candidates, package| @@ -55,7 +57,7 @@ module Bundler { root_version => root_dependencies } else Hash.new do |versions, version| - versions[version] = to_dependency_hash(version.dependencies, @packages) + versions[version] = to_dependency_hash(version.dependencies.reject {|d| d.name == package.name }, @packages) end end end @@ -186,11 +188,6 @@ module Bundler package_deps = @cached_dependencies[package] sorted_versions = @sorted_versions[package] package_deps[version].map do |dep_package, dep_constraint| - if package == dep_package - cause = PubGrub::Incompatibility::CircularDependency.new(dep_package, dep_constraint.constraint_string) - return [PubGrub::Incompatibility.new([PubGrub::Term.new(dep_constraint, true)], :cause => cause)] - end - low = high = sorted_versions.index(version) # find version low such that all >= low share the same dep @@ -243,7 +240,7 @@ module Bundler ruby_specs = select_best_platform_match(specs, Gem::Platform::RUBY) groups << Resolver::Candidate.new(version, :specs => ruby_specs) if ruby_specs.any? - next groups if platform_specs == ruby_specs + next groups if platform_specs == ruby_specs || package.force_ruby_platform? groups << Resolver::Candidate.new(version, :specs => platform_specs) @@ -302,7 +299,7 @@ module Bundler end def filter_prereleases(specs, package) - return specs unless package.ignores_prereleases? + return specs unless package.ignores_prereleases? && specs.size > 1 specs.reject {|s| s.version.prerelease? } end diff --git a/lib/bundler/resolver/base.rb b/lib/bundler/resolver/base.rb index 6921c047a7..c6afa82056 100644 --- a/lib/bundler/resolver/base.rb +++ b/lib/bundler/resolver/base.rb @@ -49,10 +49,18 @@ module Bundler end def unlock_names(names) - names.each do |name| - @base.delete_by_name(name) - - @base_requirements.delete(name) + indirect_pins = indirect_pins(names) + + if indirect_pins.any? + loosen_names(indirect_pins) + else + pins = pins(names) + + if pins.any? + loosen_names(pins) + else + unrestrict_names(names) + end end end @@ -64,6 +72,30 @@ module Bundler private + def indirect_pins(names) + names.select {|name| @base_requirements[name].exact? && @requirements.none? {|dep| dep.name == name } } + end + + def pins(names) + names.select {|name| @base_requirements[name].exact? } + end + + def loosen_names(names) + names.each do |name| + version = @base_requirements[name].requirements.first[1] + + @base_requirements[name] = Gem::Requirement.new(">= #{version}") + + @base.delete_by_name(name) + end + end + + def unrestrict_names(names) + names.each do |name| + @base_requirements.delete(name) + end + end + def build_base_requirements base_requirements = {} @base.each do |ls| diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index 3857948511..72016e848b 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -453,7 +453,7 @@ module Bundler fetcher = gem_remote_fetcher fetcher.headers = { "X-Gemfile-Source" => remote.original_uri.to_s } if remote.original_uri string = fetcher.fetch_path(path) - Bundler.load_marshal(string) + Bundler.safe_load_marshal(string) rescue Gem::RemoteFetcher::FetchError # it's okay for prerelease to fail raise unless name == "prerelease_specs" diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index a76a792743..1139eab503 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -495,7 +495,7 @@ module Bundler uri = $2 suffix = $3 end - uri = "#{uri}/" unless uri.end_with?("/") + uri = URINormalizer.normalize_suffix(uri) require_relative "vendored_uri" uri = Bundler::URI(uri) unless uri.absolute? diff --git a/lib/bundler/setup.rb b/lib/bundler/setup.rb index 32e9b2d7c0..801fd5312a 100644 --- a/lib/bundler/setup.rb +++ b/lib/bundler/setup.rb @@ -12,7 +12,10 @@ if Bundler::SharedHelpers.in_bundle? Bundler.ui.error e.message Bundler.ui.warn e.backtrace.join("\n") if ENV["DEBUG"] if e.is_a?(Bundler::GemNotFound) - Bundler.ui.warn "Run `bundle install` to install missing gems." + suggested_cmd = "bundle install" + original_gemfile = Bundler.original_env["BUNDLE_GEMFILE"] + suggested_cmd += " --gemfile #{original_gemfile}" if original_gemfile + Bundler.ui.warn "Run `#{suggested_cmd}` to install missing gems." end exit e.status_code end diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb index 794a03e62d..d1d4e1d07a 100644 --- a/lib/bundler/shared_helpers.rb +++ b/lib/bundler/shared_helpers.rb @@ -160,7 +160,7 @@ module Bundler " (was expecting #{old_deps.map(&:to_s)}, but the real spec has #{new_deps.map(&:to_s)})" raise APIResponseMismatchError, "Downloading #{spec.full_name} revealed dependencies not in the API or the lockfile (#{extra_deps.join(", ")})." \ - "\nEither installing with `--full-index` or running `bundle update #{spec.name}` should fix the problem." + "\nRunning `bundle update #{spec.name}` should fix the problem." end def pretty_dependency(dep) diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb index b8ee4029b4..42897813b4 100644 --- a/lib/bundler/source/git.rb +++ b/lib/bundler/source/git.rb @@ -19,7 +19,7 @@ module Bundler # Stringify options that could be set as symbols %w[ref branch tag revision].each {|k| options[k] = options[k].to_s if options[k] } - @uri = options["uri"] || "" + @uri = URINormalizer.normalize_suffix(options["uri"] || "", :trailing_slash => false) @safe_uri = URICredentialsFilter.credential_filtered_uri(@uri) @branch = options["branch"] @ref = options["ref"] || options["branch"] || options["tag"] @@ -173,6 +173,7 @@ module Bundler end def install(spec, options = {}) + return if Bundler.settings[:no_install] force = options[:force] print_using_message "Using #{version_message(spec, options[:previous_spec])} from #{self}" diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb index 2a7c8473f5..926c9b8ead 100644 --- a/lib/bundler/source/git/git_proxy.rb +++ b/lib/bundler/source/git/git_proxy.rb @@ -28,8 +28,9 @@ module Bundler def initialize(command, path, extra_info = nil) @command = command - msg = String.new - msg << "Git error: command `#{command}` in directory #{path} has failed." + msg = String.new("Git error: command `#{command}`") + msg << " in directory #{path}" if path + msg << " has failed." msg << "\n#{extra_info}" if extra_info super msg end @@ -139,8 +140,8 @@ module Bundler out, err, status = capture(command, path) return out if status.success? - if err.include?("couldn't find remote ref") - raise MissingGitRevisionError.new(command_with_no_credentials, path, explicit_ref, credential_filtered_uri) + if err.include?("couldn't find remote ref") || err.include?("not our ref") + raise MissingGitRevisionError.new(command_with_no_credentials, path, commit || explicit_ref, credential_filtered_uri) else raise GitCommandError.new(command_with_no_credentials, path, err) end @@ -153,9 +154,20 @@ module Bundler SharedHelpers.filesystem_access(path.dirname) do |p| FileUtils.mkdir_p(p) end - git_retry "clone", "--bare", "--no-hardlinks", "--quiet", *extra_clone_args, "--", configured_uri, path.to_s - extra_ref + command = ["clone", "--bare", "--no-hardlinks", "--quiet", *extra_clone_args, "--", configured_uri, path.to_s] + command_with_no_credentials = check_allowed(command) + + Bundler::Retry.new("`#{command_with_no_credentials}`", [MissingGitRevisionError]).attempts do + _, err, status = capture(command, nil) + return extra_ref if status.success? + + if err.include?("Could not find remote branch") + raise MissingGitRevisionError.new(command_with_no_credentials, nil, explicit_ref, credential_filtered_uri) + else + raise GitCommandError.new(command_with_no_credentials, path, err) + end + end end def clone_needs_unshallow? @@ -186,8 +198,6 @@ module Bundler end def refspec - commit = pinned_to_full_sha? ? ref : @revision - if commit @commit_ref = "refs/#{commit}-sha" return "#{commit}:#{@commit_ref}" @@ -206,6 +216,10 @@ module Bundler "#{reference}:#{reference}" end + def commit + @commit ||= pinned_to_full_sha? ? ref : @revision + end + def fully_qualified_ref if branch "refs/heads/#{branch}" @@ -352,6 +366,11 @@ module Bundler args += ["--single-branch"] args.unshift("--no-tags") if supports_cloning_with_no_tags? + # If there's a locked revision, no need to clone any specific branch + # or tag, since we will end up checking out that locked revision + # anyways. + return args if @revision + args += ["--branch", branch || tag] if branch || tag args end diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb index 20975e20ca..bdfcf8274a 100644 --- a/lib/bundler/source/path.rb +++ b/lib/bundler/source/path.rb @@ -11,7 +11,7 @@ module Bundler protected :original_path - DEFAULT_GLOB = "{,*,*/*}.gemspec".freeze + DEFAULT_GLOB = "{,*,*/*}.gemspec" def initialize(options) @options = options.dup diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index c39071705a..8d0c78bd61 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -337,8 +337,7 @@ module Bundler end def normalize_uri(uri) - uri = uri.to_s - uri = "#{uri}/" unless %r{/$}.match?(uri) + uri = URINormalizer.normalize_suffix(uri.to_s) require_relative "../vendored_uri" uri = Bundler::URI(uri) raise ArgumentError, "The source must be an absolute URI. For example:\n" \ diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index 7478bd9ca2..cf63c16a70 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -24,6 +24,7 @@ module Bundler name = dep[0].name platform = dep[1] + incomplete = false key = [name, platform] next if handled.key?(key) @@ -36,14 +37,19 @@ module Bundler specs_for_dep.first.dependencies.each do |d| next if d.type == :development + incomplete = true if d.name != "bundler" && lookup[d.name].empty? deps << [d, dep[1]] end - elsif check - @incomplete_specs += lookup[name] + else + incomplete = true + end + + if incomplete && check + @incomplete_specs += lookup[name].any? ? lookup[name] : [LazySpecification.new(name, nil, nil)] end end - specs + specs.uniq end def [](key) @@ -95,6 +101,10 @@ module Bundler end def incomplete_ruby_specs?(deps) + return false if @specs.empty? + + @incomplete_specs = [] + self.for(deps, true, [Gem::Platform::RUBY]) @incomplete_specs.any? diff --git a/lib/bundler/templates/Executable.bundler b/lib/bundler/templates/Executable.bundler index 6dab1688f7..e290fe91eb 100644 --- a/lib/bundler/templates/Executable.bundler +++ b/lib/bundler/templates/Executable.bundler @@ -47,7 +47,7 @@ m = Module.new do def lockfile lockfile = case File.basename(gemfile) - when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) + when "gems.rb" then gemfile.sub(/\.rb$/, ".locked") else "#{gemfile}.lock" end File.expand_path(lockfile) diff --git a/lib/bundler/templates/gems.rb b/lib/bundler/templates/gems.rb deleted file mode 100644 index d2403f18b2..0000000000 --- a/lib/bundler/templates/gems.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -# gem "rails" diff --git a/lib/bundler/templates/newgem/Gemfile.tt b/lib/bundler/templates/newgem/Gemfile.tt index 41c95677a3..a0d2ac2826 100644 --- a/lib/bundler/templates/newgem/Gemfile.tt +++ b/lib/bundler/templates/newgem/Gemfile.tt @@ -10,7 +10,7 @@ gem "rake", "~> 13.0" gem "rake-compiler" <%- if config[:ext] == 'rust' -%> -gem "rb_sys" +gem "rb_sys", "~> 0.9.63" <%- end -%> <%- end -%> <%- if config[:test] -%> diff --git a/lib/bundler/templates/newgem/Rakefile.tt b/lib/bundler/templates/newgem/Rakefile.tt index ac14545126..b5a5c4e392 100644 --- a/lib/bundler/templates/newgem/Rakefile.tt +++ b/lib/bundler/templates/newgem/Rakefile.tt @@ -41,6 +41,15 @@ require "standard/rake" <% if config[:ext] -%> <% default_task_names.unshift(:compile) -%> <% default_task_names.unshift(:clobber) unless config[:ext] == 'rust' -%> +<% if config[:ext] == 'rust' -%> +require "rb_sys/extensiontask" + +task build: :compile + +RbSys::ExtensionTask.new(<%= config[:name].inspect %>) do |ext| + ext.lib_dir = "lib/<%= config[:namespaced_path] %>" +end +<% else -%> require "rake/extensiontask" task build: :compile @@ -48,6 +57,7 @@ task build: :compile Rake::ExtensionTask.new("<%= config[:underscored_name] %>") do |ext| ext.lib_dir = "lib/<%= config[:namespaced_path] %>" end +<% end -%> <% end -%> <% if default_task_names.size == 1 -%> diff --git a/lib/bundler/templates/newgem/github/workflows/main.yml.tt b/lib/bundler/templates/newgem/github/workflows/main.yml.tt index d4021980b4..be58dd8156 100644 --- a/lib/bundler/templates/newgem/github/workflows/main.yml.tt +++ b/lib/bundler/templates/newgem/github/workflows/main.yml.tt @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v3 <%- if config[:ext] == 'rust' -%> - name: Set up Ruby & Rust - uses: oxidize-rb/actions/setup-ruby-and-rust@main + uses: oxidize-rb/actions/setup-ruby-and-rust@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt index e35a121245..da81f046d4 100644 --- a/lib/bundler/templates/newgem/newgem.gemspec.tt +++ b/lib/bundler/templates/newgem/newgem.gemspec.tt @@ -29,7 +29,7 @@ Gem::Specification.new do |spec| # The `git ls-files -z` loads the files in the RubyGem that have been added into git. spec.files = Dir.chdir(__dir__) do `git ls-files -z`.split("\x0").reject do |f| - (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)}) + (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor]) end end spec.bindir = "exe" diff --git a/lib/bundler/uri_normalizer.rb b/lib/bundler/uri_normalizer.rb new file mode 100644 index 0000000000..ad08593256 --- /dev/null +++ b/lib/bundler/uri_normalizer.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Bundler + module URINormalizer + module_function + + # Normalizes uri to a consistent version, either with or without trailing + # slash. + # + # TODO: Currently gem sources are locked with a trailing slash, while git + # sources are locked without a trailing slash. This should be normalized but + # the inconsistency is there for now to avoid changing all lockfiles + # including GIT sources. We could normalize this on the next major. + # + def normalize_suffix(uri, trailing_slash: true) + if trailing_slash + uri.end_with?("/") ? uri : "#{uri}/" + else + uri.end_with?("/") ? uri.delete_suffix("/") : uri + end + end + end +end diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb index dab58ecdf7..239eaf3401 100644 --- a/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb +++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/incompatibility.rb @@ -8,9 +8,6 @@ module Bundler::PubGrub InvalidDependency = Struct.new(:package, :constraint) do end - CircularDependency = Struct.new(:package, :constraint) do - end - NoVersions = Struct.new(:constraint) do end @@ -66,8 +63,6 @@ module Bundler::PubGrub "#{terms[0].to_s(allow_every: true)} depends on #{terms[1].invert}" when Bundler::PubGrub::Incompatibility::InvalidDependency "#{terms[0].to_s(allow_every: true)} depends on unknown package #{cause.package}" - when Bundler::PubGrub::Incompatibility::CircularDependency - "#{terms[0].to_s(allow_every: true)} depends on itself" when Bundler::PubGrub::Incompatibility::NoVersions "no versions satisfy #{cause.constraint}" when Bundler::PubGrub::Incompatibility::ConflictCause @@ -76,9 +71,13 @@ module Bundler::PubGrub elsif terms.length == 1 term = terms[0] if term.positive? - "#{terms[0].to_s(allow_every: true)} is forbidden" + if term.constraint.any? + "#{term.package} cannot be used" + else + "#{term.to_s(allow_every: true)} cannot be used" + end else - "#{terms[0].invert} is required" + "#{term.invert} is required" end else if terms.all?(&:positive?) diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb index e895812bed..4bf61461b2 100644 --- a/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb +++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/static_package_source.rb @@ -19,7 +19,14 @@ module Bundler::PubGrub version = Gem::Version.new(version) @packages[name] ||= {} raise ArgumentError, "#{name} #{version} declared twice" if @packages[name].key?(version) - @packages[name][version] = deps + @packages[name][version] = clean_deps(name, version, deps) + end + + private + + # Exclude redundant self-referencing dependencies + def clean_deps(name, version, deps) + deps.reject {|dep_name, req| name == dep_name && Bundler::PubGrub::RubyGems.parse_range(req).include?(version) } end end diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb index 9133332d01..b71f3eaf53 100644 --- a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb +++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_constraint.rb @@ -15,15 +15,16 @@ module Bundler::PubGrub package.hash ^ range.hash end + def ==(other) + package == other.package && + range == other.range + end + def eql?(other) package.eql?(other.package) && range.eql?(other.range) end - def ==(other) - package == other.package && range == other.range - end - class << self def exact(package, version) range = VersionRange.new(min: version, max: version, include_min: true, include_max: true) diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb index 506b447b36..8d73c3f7b5 100644 --- a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb +++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb @@ -19,7 +19,7 @@ module Bundler::PubGrub true end - def eql? + def eql?(other) other.empty? end @@ -65,6 +65,7 @@ module Bundler::PubGrub end EMPTY = Empty.new + Empty.singleton_class.undef_method(:new) def self.empty EMPTY @@ -88,7 +89,8 @@ module Bundler::PubGrub def eql?(other) if other.is_a?(VersionRange) - min.eql?(other.min) && + !other.empty? && + min.eql?(other.min) && max.eql?(other.max) && include_min.eql?(other.include_min) && include_max.eql?(other.include_max) diff --git a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb index ea5e455968..2cb8412cf3 100644 --- a/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb +++ b/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb @@ -125,6 +125,7 @@ module Bundler::PubGrub package = next_package_to_try unsatisfied_term = solution.unsatisfied.find { |t| t.package == package } version = source.versions_for(package, unsatisfied_term.constraint.range).first + logger.debug { "attempting #{package} #{version}" } if version.nil? add_incompatibility source.no_versions_incompatibility_for(package, unsatisfied_term) @@ -148,9 +149,11 @@ module Bundler::PubGrub end unless conflict - logger.info { "selecting #{package} #{version}" } + logger.info { "selected #{package} #{version}" } solution.decide(package, version) + else + logger.info { "conflict: #{conflict.inspect}" } end package diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index d3bd162e48..bc9f8d22cd 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "2.4.6".freeze + VERSION = "2.4.10".freeze def self.bundler_major_version @bundler_major_version ||= VERSION.split(".").first.to_i diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 26b0b1da7e..ef5f079a80 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -8,7 +8,7 @@ require "rbconfig" module Gem - VERSION = "3.4.6".freeze + VERSION = "3.4.10" end # Must be first since it unloads the prelude from 1.9.2 @@ -824,7 +824,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} def self.env_requirement(gem_name) @env_requirements_by_name ||= {} @env_requirements_by_name[gem_name] ||= begin - req = ENV["GEM_REQUIREMENT_#{gem_name.upcase}"] || ">= 0".freeze + req = ENV["GEM_REQUIREMENT_#{gem_name.upcase}"] || ">= 0" Gem::Requirement.create(req) end end @@ -1301,7 +1301,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} ## # Location of Marshal quick gemspecs on remote repositories - MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/".freeze + MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/" autoload :ConfigFile, File.expand_path("rubygems/config_file", __dir__) autoload :Dependency, File.expand_path("rubygems/dependency", __dir__) diff --git a/lib/rubygems/bundler_version_finder.rb b/lib/rubygems/bundler_version_finder.rb index f6fad0bd83..2cbc4ff624 100644 --- a/lib/rubygems/bundler_version_finder.rb +++ b/lib/rubygems/bundler_version_finder.rb @@ -21,7 +21,7 @@ module Gem::BundlerVersionFinder end def self.bundle_update_bundler_version - return unless File.basename($0) == "bundle".freeze + return unless File.basename($0) == "bundle" return unless "update".start_with?(ARGV.first || " ") bundler_version = nil update_index = nil diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb index f4688d793b..1e15f612de 100644 --- a/lib/rubygems/command.rb +++ b/lib/rubygems/command.rb @@ -201,11 +201,15 @@ class Gem::Command # respectively. def get_all_gem_names_and_versions get_all_gem_names.map do |name| - if /\A(.*):(#{Gem::Requirement::PATTERN_RAW})\z/ =~ name - [$1, $2] - else - [name] - end + extract_gem_name_and_version(name) + end + end + + def extract_gem_name_and_version(name) # :nodoc: + if /\A(.*):(#{Gem::Requirement::PATTERN_RAW})\z/ =~ name + [$1, $2] + else + [name] end end @@ -624,7 +628,7 @@ class Gem::Command # :stopdoc: - HELP = <<-HELP.freeze + HELP = <<-HELP RubyGems is a package manager for Ruby. Usage: diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb index 1bdbd50530..0a4b53abe5 100644 --- a/lib/rubygems/command_manager.rb +++ b/lib/rubygems/command_manager.rb @@ -43,6 +43,7 @@ class Gem::CommandManager :contents, :dependency, :environment, + :exec, :fetch, :generate_index, :help, diff --git a/lib/rubygems/commands/exec_command.rb b/lib/rubygems/commands/exec_command.rb new file mode 100644 index 0000000000..1d7e836ed0 --- /dev/null +++ b/lib/rubygems/commands/exec_command.rb @@ -0,0 +1,248 @@ +# frozen_string_literal: true +require_relative "../command" +require_relative "../dependency_installer" +require_relative "../gem_runner" +require_relative "../package" +require_relative "../version_option" + +class Gem::Commands::ExecCommand < Gem::Command + include Gem::VersionOption + + def initialize + super "exec", "Run a command from a gem", { + version: Gem::Requirement.default, + } + + add_version_option + add_prerelease_option "to be installed" + + add_option "-g", "--gem GEM", "run the executable from the given gem" do |value, options| + options[:gem_name] = value + end + + add_option(:"Install/Update", "--conservative", + "Prefer the most recent installed version, ", + "rather than the latest version overall") do |value, options| + options[:conservative] = true + end + end + + def arguments # :nodoc: + "COMMAND the executable command to run" + end + + def defaults_str # :nodoc: + "--version '#{Gem::Requirement.default}'" + end + + def description # :nodoc: + <<-EOF +The exec command handles installing (if necessary) and running an executable +from a gem, regardless of whether that gem is currently installed. + +The exec command can be thought of as a shortcut to running `gem install` and +then the executable from the installed gem. + +For example, `gem exec rails new .` will run `rails new .` in the current +directory, without having to manually run `gem install rails`. +Additionally, the exec command ensures the most recent version of the gem +is used (unless run with `--conservative`), and that the gem is not installed +to the same gem path as user-installed gems. + EOF + end + + def usage # :nodoc: + "#{program_name} [options --] COMMAND [args]" + end + + def execute + gem_paths = { "GEM_HOME" => Gem.paths.home, "GEM_PATH" => Gem.paths.path.join(File::PATH_SEPARATOR), "GEM_SPEC_CACHE" => Gem.paths.spec_cache_dir }.compact + + check_executable + + print_command + if options[:gem_name] == "gem" && options[:executable] == "gem" + set_gem_exec_install_paths + Gem::GemRunner.new.run options[:args] + return + elsif options[:conservative] + install_if_needed + else + install + activate! + end + + load! + ensure + ENV.update(gem_paths) if gem_paths + Gem.clear_paths + end + + private + + def handle_options(args) + args = add_extra_args(args) + check_deprecated_options(args) + @options = Marshal.load Marshal.dump @defaults # deep copy + parser.order!(args) do |v| + # put the non-option back at the front of the list of arguments + args.unshift(v) + + # stop parsing once we hit the first non-option, + # so you can call `gem exec rails --version` and it prints the rails + # version rather than rubygem's + break + end + @options[:args] = args + + options[:executable], gem_version = extract_gem_name_and_version(options[:args].shift) + options[:gem_name] ||= options[:executable] + + if gem_version + if options[:version].none? + options[:version] = Gem::Requirement.new(gem_version) + else + options[:version].concat [gem_version] + end + end + + if options[:prerelease] && !options[:version].prerelease? + if options[:version].none? + options[:version] = Gem::Requirement.default_prerelease + else + options[:version].concat [Gem::Requirement.default_prerelease] + end + end + end + + def check_executable + if options[:executable].nil? + raise Gem::CommandLineError, + "Please specify an executable to run (e.g. #{program_name} COMMAND)" + end + end + + def print_command + verbose "running #{program_name} with:\n" + opts = options.reject {|_, v| v.nil? || Array(v).empty? } + max_length = opts.map {|k, _| k.size }.max + opts.each do |k, v| + next if v.nil? + verbose "\t#{k.to_s.rjust(max_length)}: #{v}" + end + verbose "" + end + + def install_if_needed + activate! + rescue Gem::MissingSpecError + verbose "#{Gem::Dependency.new(options[:gem_name], options[:version])} not available locally, installing from remote" + install + activate! + end + + def set_gem_exec_install_paths + home = File.join(Gem.dir, "gem_exec") + + ENV["GEM_PATH"] = ([home] + Gem.path).join(File::PATH_SEPARATOR) + ENV["GEM_HOME"] = home + Gem.clear_paths + end + + def install + set_gem_exec_install_paths + + gem_name = options[:gem_name] + gem_version = options[:version] + + install_options = options.merge( + minimal_deps: false, + wrappers: true + ) + + suppress_always_install do + dep_installer = Gem::DependencyInstaller.new install_options + + request_set = dep_installer.resolve_dependencies gem_name, gem_version + + verbose "Gems to install:" + request_set.sorted_requests.each do |activation_request| + verbose "\t#{activation_request.full_name}" + end + + request_set.install install_options + end + + Gem::Specification.reset + rescue Gem::InstallError => e + alert_error "Error installing #{gem_name}:\n\t#{e.message}" + terminate_interaction 1 + rescue Gem::GemNotFoundException => e + show_lookup_failure e.name, e.version, e.errors, false + + terminate_interaction 2 + rescue Gem::UnsatisfiableDependencyError => e + show_lookup_failure e.name, e.version, e.errors, false, + "'#{gem_name}' (#{gem_version})" + + terminate_interaction 2 + end + + def activate! + gem(options[:gem_name], options[:version]) + Gem.finish_resolve + + verbose "activated #{options[:gem_name]} (#{Gem.loaded_specs[options[:gem_name]].version})" + end + + def load! + argv = ARGV.clone + ARGV.replace options[:args] + + exe = executable = options[:executable] + + contains_executable = Gem.loaded_specs.values.select do |spec| + spec.executables.include?(executable) + end + + if contains_executable.any? {|s| s.name == executable } + contains_executable.select! {|s| s.name == executable } + end + + if contains_executable.empty? + if (spec = Gem.loaded_specs[executable]) && (exe = spec.executable) + contains_executable << spec + else + alert_error "Failed to load executable `#{executable}`," \ + " are you sure the gem `#{options[:gem_name]}` contains it?" + terminate_interaction 1 + end + end + + if contains_executable.size > 1 + alert_error "Ambiguous which gem `#{executable}` should come from: " \ + "the options are #{contains_executable.map(&:name)}, " \ + "specify one via `-g`" + terminate_interaction 1 + end + + load Gem.activate_bin_path(contains_executable.first.name, exe, ">= 0.a") + ensure + ARGV.replace argv + end + + def suppress_always_install + name = :always_install + cls = ::Gem::Resolver::InstallerSet + method = cls.instance_method(name) + cls.remove_method(name) + cls.define_method(name) { [] } + + begin + yield + ensure + cls.remove_method(name) + cls.define_method(name, method) + end + end +end diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb index 8bfb4458ff..bf4ffefbb7 100644 --- a/lib/rubygems/commands/help_command.rb +++ b/lib/rubygems/commands/help_command.rb @@ -3,7 +3,7 @@ require_relative "../command" class Gem::Commands::HelpCommand < Gem::Command # :stopdoc: - EXAMPLES = <<-EOF.freeze + EXAMPLES = <<-EOF Some examples of 'gem' usage. * Install 'rake', either from local directory or remote server: @@ -52,7 +52,7 @@ Some examples of 'gem' usage. gem update --system EOF - GEM_DEPENDENCIES = <<-EOF.freeze + GEM_DEPENDENCIES = <<-EOF A gem dependencies file allows installation of a consistent set of gems across multiple environments. The RubyGems implementation is designed to be compatible with Bundler's Gemfile format. You can see additional @@ -229,7 +229,7 @@ default. This may be overridden with the :development_group option: EOF - PLATFORMS = <<-'EOF'.freeze + PLATFORMS = <<-'EOF' RubyGems platforms are composed of three parts, a CPU, an OS, and a version. These values are taken from values in rbconfig.rb. You can view your current platform by running `gem environment`. diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb index 72db53ef37..d7c78ba13d 100644 --- a/lib/rubygems/commands/pristine_command.rb +++ b/lib/rubygems/commands/pristine_command.rb @@ -34,6 +34,11 @@ class Gem::Commands::PristineCommand < Gem::Command options[:extensions] = value end + add_option("--only-missing-extensions", + "Only restore gems with missing extensions") do |value, options| + options[:only_missing_extensions] = value + end + add_option("--only-executables", "Only restore executables") do |value, options| options[:only_executables] = value @@ -107,6 +112,10 @@ extensions will be restored. Gem::Specification.select do |spec| spec.extensions && !spec.extensions.empty? end + elsif options[:only_missing_extensions] + Gem::Specification.select do |spec| + spec.missing_extensions? + end else get_all_gem_names.sort.map do |gem_name| Gem::Specification.find_all_by_name(gem_name, options[:version]).reverse diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb index 3c520826e5..29f6013c59 100644 --- a/lib/rubygems/commands/uninstall_command.rb +++ b/lib/rubygems/commands/uninstall_command.rb @@ -125,6 +125,9 @@ that is a dependency of an existing gem. You can use the def execute check_version + # Consider only gem specifications installed at `--install-dir` + Gem::Specification.dirs = options[:install_dir] if options[:install_dir] + if options[:all] && !options[:args].empty? uninstall_specific elsif options[:all] diff --git a/lib/rubygems/core_ext/kernel_require.rb b/lib/rubygems/core_ext/kernel_require.rb index 6dcc4a06e4..568ac347b4 100644 --- a/lib/rubygems/core_ext/kernel_require.rb +++ b/lib/rubygems/core_ext/kernel_require.rb @@ -37,9 +37,6 @@ module Kernel return gem_original_require(path) unless Gem.discover_gems_on_require begin - if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?) - monitor_owned = RUBYGEMS_ACTIVATION_MONITOR.mon_owned? - end RUBYGEMS_ACTIVATION_MONITOR.enter path = path.to_path if path.respond_to? :to_path @@ -163,13 +160,6 @@ module Kernel end raise load_error - ensure - if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?) - if monitor_owned != (ow = RUBYGEMS_ACTIVATION_MONITOR.mon_owned?) - STDERR.puts [$$, Thread.current, $!, $!.backtrace].inspect if $! - raise "CRITICAL: RUBYGEMS_ACTIVATION_MONITOR.owned?: before #{monitor_owned} -> after #{ow}" - end - end end end diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb index 8daff0bc30..b186375f69 100644 --- a/lib/rubygems/defaults.rb +++ b/lib/rubygems/defaults.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true module Gem - DEFAULT_HOST = "https://rubygems.org".freeze + DEFAULT_HOST = "https://rubygems.org" @post_install_hooks ||= [] @done_installing_hooks ||= [] @@ -158,7 +158,7 @@ module Gem # The path to standard location of the user's state directory. def self.state_home - @data_home ||= (ENV["XDG_STATE_HOME"] || File.join(Gem.user_home, ".local", "state")) + @state_home ||= (ENV["XDG_STATE_HOME"] || File.join(Gem.user_home, ".local", "state")) end ## diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb index 7fa0f91bd4..cd03e7e299 100644 --- a/lib/rubygems/dependency.rb +++ b/lib/rubygems/dependency.rb @@ -299,7 +299,7 @@ class Gem::Dependency end def prioritizes_bundler? - name == "bundler".freeze && !specific? + name == "bundler" && !specific? end def to_specs diff --git a/lib/rubygems/deprecate.rb b/lib/rubygems/deprecate.rb index 5fe0afb6b0..56505512c3 100644 --- a/lib/rubygems/deprecate.rb +++ b/lib/rubygems/deprecate.rb @@ -143,7 +143,7 @@ module Gem::Deprecate end # Deprecation method to deprecate Rubygems commands - def rubygems_deprecate_command + def rubygems_deprecate_command(version = Gem::Deprecate.next_rubygems_major_version) class_eval do define_method "deprecated?" do true @@ -151,7 +151,7 @@ module Gem::Deprecate define_method "deprecation_warning" do msg = [ "#{self.command} command is deprecated", - ". It will be removed in Rubygems #{Gem::Deprecate.next_rubygems_major_version}.\n", + ". It will be removed in Rubygems #{version}.\n", ] alert_warning "#{msg.join}" unless Gem::Deprecate.skip diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb index 7fb8958d7f..43ea207b23 100644 --- a/lib/rubygems/ext/builder.rb +++ b/lib/rubygems/ext/builder.rb @@ -55,6 +55,23 @@ class Gem::Ext::Builder end end + def self.ruby + require "shellwords" + # Gem.ruby is quoted if it contains whitespace + cmd = Gem.ruby.shellsplit + + # This load_path is only needed when running rubygems test without a proper installation. + # Prepending it in a normal installation will cause problem with order of $LOAD_PATH. + # Therefore only add load_path if it is not present in the default $LOAD_PATH. + load_path = File.expand_path("../..", __dir__) + case load_path + when RbConfig::CONFIG["sitelibdir"], RbConfig::CONFIG["vendorlibdir"], RbConfig::CONFIG["rubylibdir"] + cmd + else + cmd << "-I#{load_path}" + end + end + def self.run(command, results, command_name = nil, dir = Dir.pwd, env = {}) verbose = Gem.configuration.really_verbose diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb index 27ebd8c62b..2f4193d697 100644 --- a/lib/rubygems/ext/ext_conf_builder.rb +++ b/lib/rubygems/ext/ext_conf_builder.rb @@ -21,8 +21,7 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder destdir = ENV["DESTDIR"] begin - require "shellwords" - cmd = Gem.ruby.shellsplit << "-I" << File.expand_path("../..", __dir__) << File.basename(extension) + cmd = ruby << File.basename(extension) cmd.push(*args) run(cmd, results, class_name, extension_dir) do |s, r| diff --git a/lib/rubygems/ext/rake_builder.rb b/lib/rubygems/ext/rake_builder.rb index 9f2e099d40..e74ec8750f 100644 --- a/lib/rubygems/ext/rake_builder.rb +++ b/lib/rubygems/ext/rake_builder.rb @@ -18,7 +18,7 @@ class Gem::Ext::RakeBuilder < Gem::Ext::Builder rake = rake.shellsplit else begin - rake = [Gem.ruby, "-I#{File.expand_path("../..", __dir__)}", "-rrubygems", Gem.bin_path("rake", "rake")] + rake = ruby << "-rrubygems" << Gem.bin_path("rake", "rake") rescue Gem::Exception rake = [Gem.default_exec_format % "rake"] end diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb index 590a2f0315..31566e5865 100644 --- a/lib/rubygems/package/tar_header.rb +++ b/lib/rubygems/package/tar_header.rb @@ -208,7 +208,7 @@ class Gem::Package::TarHeader private def calculate_checksum(header) - header.unpack("C*").inject {|a, b| a + b } + header.sum(0) end def header(checksum = @checksum) diff --git a/lib/rubygems/package/tar_reader.rb b/lib/rubygems/package/tar_reader.rb index cdc3fdc015..9f1d49035b 100644 --- a/lib/rubygems/package/tar_reader.rb +++ b/lib/rubygems/package/tar_reader.rb @@ -53,39 +53,11 @@ class Gem::Package::TarReader def each return enum_for __method__ unless block_given? - use_seek = @io.respond_to?(:seek) - until @io.eof? do header = Gem::Package::TarHeader.from @io return if header.empty? - entry = Gem::Package::TarReader::Entry.new header, @io - size = entry.header.size - yield entry - - skip = (512 - (size % 512)) % 512 - pending = size - entry.bytes_read - - if use_seek - begin - # avoid reading if the @io supports seeking - @io.seek pending, IO::SEEK_CUR - pending = 0 - rescue Errno::EINVAL - end - end - - # if seeking isn't supported or failed - while pending > 0 do - bytes_read = @io.read([pending, 4096].min).size - raise UnexpectedEOF if @io.eof? - pending -= bytes_read - end - - @io.read skip # discard trailing zeros - - # make sure nobody can use #read, #getc or #rewind anymore entry.close end end diff --git a/lib/rubygems/package/tar_reader/entry.rb b/lib/rubygems/package/tar_reader/entry.rb index 8634381c18..9e7b327431 100644 --- a/lib/rubygems/package/tar_reader/entry.rb +++ b/lib/rubygems/package/tar_reader/entry.rb @@ -9,6 +9,20 @@ class Gem::Package::TarReader::Entry ## + # Creates a new tar entry for +header+ that will be read from +io+ + # If a block is given, the entry is yielded and then closed. + + def self.open(header, io, &block) + entry = new header, io + return entry unless block_given? + begin + yield entry + ensure + entry.close + end + end + + ## # Header for this tar entry attr_reader :header @@ -21,6 +35,7 @@ class Gem::Package::TarReader::Entry @header = header @io = io @orig_pos = @io.pos + @end_pos = @orig_pos + @header.size @read = 0 end @@ -39,7 +54,14 @@ class Gem::Package::TarReader::Entry # Closes the tar entry def close + return if closed? + # Seek to the end of the entry if it wasn't fully read + seek(0, IO::SEEK_END) + # discard trailing zeros + skip = (512 - (@header.size % 512)) % 512 + @io.read(skip) @closed = true + nil end ## @@ -117,6 +139,14 @@ class Gem::Package::TarReader::Entry bytes_read end + ## + # Seek to the position in the tar entry + + def pos=(new_pos) + seek(new_pos, IO::SEEK_SET) + new_pos + end + def size @header.size end @@ -130,9 +160,10 @@ class Gem::Package::TarReader::Entry def read(len = nil) check_closed - return nil if @read >= @header.size - len ||= @header.size - @read + + return nil if len > 0 && @read >= @header.size + max_read = [len, @header.size - @read].min ret = @io.read max_read @@ -144,9 +175,10 @@ class Gem::Package::TarReader::Entry def readpartial(maxlen = nil, outbuf = "".b) check_closed - raise EOFError if @read >= @header.size - maxlen ||= @header.size - @read + + raise EOFError if maxlen > 0 && @read >= @header.size + max_read = [maxlen, @header.size - @read].min @io.readpartial(max_read, outbuf) @@ -156,12 +188,61 @@ class Gem::Package::TarReader::Entry end ## + # Seeks to +offset+ bytes into the tar file entry + # +whence+ can be IO::SEEK_SET, IO::SEEK_CUR, or IO::SEEK_END + + def seek(offset, whence = IO::SEEK_SET) + check_closed + + new_pos = + case whence + when IO::SEEK_SET then @orig_pos + offset + when IO::SEEK_CUR then @io.pos + offset + when IO::SEEK_END then @end_pos + offset + else + raise ArgumentError, "invalid whence" + end + + if new_pos < @orig_pos + new_pos = @orig_pos + elsif new_pos > @end_pos + new_pos = @end_pos + end + + pending = new_pos - @io.pos + + if @io.respond_to?(:seek) + begin + # avoid reading if the @io supports seeking + @io.seek new_pos, IO::SEEK_SET + pending = 0 + rescue Errno::EINVAL + end + end + + # if seeking isn't supported or failed + # negative seek requires that we rewind and read + if pending < 0 + @io.rewind + pending = new_pos + end + + while pending > 0 do + size_read = @io.read([pending, 4096].min).size + raise UnexpectedEOF if @io.eof? + pending -= size_read + end + + @read = @io.pos - @orig_pos + + 0 + end + + ## # Rewinds to the beginning of the tar file entry def rewind check_closed - - @io.pos = @orig_pos - @read = 0 + seek(0, IO::SEEK_SET) end end diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb index 128871f2bf..f4983c1153 100644 --- a/lib/rubygems/platform.rb +++ b/lib/rubygems/platform.rb @@ -235,11 +235,11 @@ class Gem::Platform # A pure-Ruby gem that may use Gem::Specification#extensions to build # binary files. - RUBY = "ruby".freeze + RUBY = "ruby" ## # A platform-specific gem that is built for the packaging Ruby's platform. # This will be replaced with Gem::Platform::local. - CURRENT = "current".freeze + CURRENT = "current" end diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb index ad6e45005b..0681f8802a 100644 --- a/lib/rubygems/request_set/gem_dependency_api.rb +++ b/lib/rubygems/request_set/gem_dependency_api.rb @@ -435,7 +435,6 @@ Gem dependencies file #{@path} requires #{name} more than once. reference ||= ref reference ||= branch reference ||= tag - reference ||= "master" if ref && branch warn <<-WARNING diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index 64f9ac3465..bc2fd9af55 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -22,7 +22,7 @@ class Gem::Requirement SOURCE_SET_REQUIREMENT = Struct.new(:for_lockfile).new "!" # :nodoc: quoted = OPS.keys.map {|k| Regexp.quote k }.join "|" - PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*".freeze # :nodoc: + PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*" # :nodoc: ## # A regular expression that matches a requirement diff --git a/lib/rubygems/resolver/stats.rb b/lib/rubygems/resolver/stats.rb index 64b458f504..3b95efebf7 100644 --- a/lib/rubygems/resolver/stats.rb +++ b/lib/rubygems/resolver/stats.rb @@ -32,7 +32,7 @@ class Gem::Resolver::Stats @iterations += 1 end - PATTERN = "%20s: %d\n".freeze + PATTERN = "%20s: %d\n" def display $stdout.puts "=== Resolver Statistics ===" diff --git a/lib/rubygems/source/git.rb b/lib/rubygems/source/git.rb index 2609a309e8..7ac685f978 100644 --- a/lib/rubygems/source/git.rb +++ b/lib/rubygems/source/git.rb @@ -53,7 +53,7 @@ class Gem::Source::Git < Gem::Source @uri = Gem::Uri.parse(repository) @name = name @repository = repository - @reference = reference + @reference = reference || "HEAD" @need_submodules = submodules @remote = true diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 5175db2d84..b8aa43bf81 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -338,7 +338,7 @@ class Gem::Specification < Gem::BasicSpecification # The simplest way is to specify the standard SPDX ID # https://spdx.org/licenses/ for the license. # Ideally, you should pick one that is OSI (Open Source Initiative) - # http://opensource.org/licenses/alphabetical approved. + # https://opensource.org/licenses/ approved. # # The most commonly used OSI-approved licenses are MIT and Apache-2.0. # GitHub also provides a license picker at http://choosealicense.com/. @@ -1022,6 +1022,12 @@ class Gem::Specification < Gem::BasicSpecification end ## + # Find the best specification matching a +full_name+. + def self.find_by_full_name(full_name) + stubs.find {|s| s.full_name == full_name }&.to_spec + end + + ## # Return the best specification that contains the file matching +path+. def self.find_by_path(path) @@ -1606,6 +1612,8 @@ class Gem::Specification < Gem::BasicSpecification def build_extensions # :nodoc: return if extensions.empty? return if default_gem? + # we need to fresh build when same name and version of default gems + return if self.class.find_by_full_name(full_name)&.default_gem? return if File.exist? gem_build_complete_path return if !File.writable?(base_dir) return if !File.exist?(File.join(base_dir, "extensions")) diff --git a/lib/rubygems/specification_policy.rb b/lib/rubygems/specification_policy.rb index 6f4d79cdcf..c7e2edc2bf 100644 --- a/lib/rubygems/specification_policy.rb +++ b/lib/rubygems/specification_policy.rb @@ -173,6 +173,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use: end ## + # Checks that the gem does not depend on itself. # Checks that dependencies use requirements as we recommend. Warnings are # issued when dependencies are open-ended or overly strict for semantic # versioning. @@ -180,6 +181,10 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use: def validate_dependencies # :nodoc: warning_messages = [] @specification.dependencies.each do |dep| + if dep.name == @specification.name # warn on self reference + warning_messages << "Self referencing dependency is unnecessary and strongly discouraged." + end + prerelease_dep = dep.requirements_list.any? do |req| Gem::Requirement.new(req).prerelease? end diff --git a/lib/rubygems/stub_specification.rb b/lib/rubygems/stub_specification.rb index 512ca9143d..d87abdd993 100644 --- a/lib/rubygems/stub_specification.rb +++ b/lib/rubygems/stub_specification.rb @@ -6,10 +6,10 @@ class Gem::StubSpecification < Gem::BasicSpecification # :nodoc: - PREFIX = "# stub: ".freeze + PREFIX = "# stub: " # :nodoc: - OPEN_MODE = "r:UTF-8:-".freeze + OPEN_MODE = "r:UTF-8:-" class StubLine # :nodoc: all attr_reader :name, :version, :platform, :require_paths, :extensions, @@ -19,9 +19,9 @@ class Gem::StubSpecification < Gem::BasicSpecification # These are common require paths. REQUIRE_PATHS = { # :nodoc: - "lib" => "lib".freeze, - "test" => "test".freeze, - "ext" => "ext".freeze, + "lib" => "lib", + "test" => "test", + "ext" => "ext", }.freeze # These are common require path lists. This hash is used to optimize @@ -33,7 +33,7 @@ class Gem::StubSpecification < Gem::BasicSpecification }.freeze def initialize(data, extensions) - parts = data[PREFIX.length..-1].split(" ".freeze, 4) + parts = data[PREFIX.length..-1].split(" ", 4) @name = parts[0].freeze @version = if Gem::Version.correct?(parts[1]) Gem::Version.new(parts[1]) @@ -50,7 +50,7 @@ class Gem::StubSpecification < Gem::BasicSpecification end path_list = parts.last - @require_paths = REQUIRE_PATH_LIST[path_list] || path_list.split("\0".freeze).map! do |x| + @require_paths = REQUIRE_PATH_LIST[path_list] || path_list.split("\0").map! do |x| REQUIRE_PATHS[x] || x end end diff --git a/lib/rubygems/text.rb b/lib/rubygems/text.rb index d6b891380e..be811525f2 100644 --- a/lib/rubygems/text.rb +++ b/lib/rubygems/text.rb @@ -9,7 +9,7 @@ module Gem::Text # Remove any non-printable characters and make the text suitable for # printing. def clean_text(text) - text.gsub(/[\000-\b\v-\f\016-\037\177]/, ".".freeze) + text.gsub(/[\000-\b\v-\f\016-\037\177]/, ".") end def truncate_text(text, description, max_length = 100_000) diff --git a/lib/rubygems/util/licenses.rb b/lib/rubygems/util/licenses.rb index 96f47781c0..a823521d10 100644 --- a/lib/rubygems/util/licenses.rb +++ b/lib/rubygems/util/licenses.rb @@ -4,8 +4,8 @@ require_relative "../text" class Gem::Licenses extend Gem::Text - NONSTANDARD = "Nonstandard".freeze - LICENSE_REF = "LicenseRef-.+".freeze + NONSTANDARD = "Nonstandard" + LICENSE_REF = "LicenseRef-.+" # Software Package Data Exchange (SPDX) standard open-source software # license identifiers diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb index f67889ef1a..c319e1f820 100644 --- a/lib/rubygems/version.rb +++ b/lib/rubygems/version.rb @@ -155,7 +155,7 @@ require_relative "deprecate" class Gem::Version include Comparable - VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?'.freeze # :nodoc: + VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?' # :nodoc: ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/.freeze # :nodoc: ## @@ -272,7 +272,7 @@ class Gem::Version # string for backwards (RubyGems 1.3.5 and earlier) compatibility. def marshal_dump - [version] + [@version] end ## diff --git a/spec/bundler/bundler/bundler_spec.rb b/spec/bundler/bundler/bundler_spec.rb index 5894b4aa12..862a2c3e07 100644 --- a/spec/bundler/bundler/bundler_spec.rb +++ b/spec/bundler/bundler/bundler_spec.rb @@ -5,9 +5,14 @@ require "tmpdir" RSpec.describe Bundler do describe "#load_marshal" do + it "is a private method and raises an error" do + data = Marshal.dump(Bundler) + expect { Bundler.load_marshal(data) }.to raise_error(NoMethodError, /private method `load_marshal' called/) + end + it "loads any data" do data = Marshal.dump(Bundler) - expect(Bundler.load_marshal(data)).to eq(Bundler) + expect(Bundler.send(:load_marshal, data)).to eq(Bundler) end end @@ -22,6 +27,18 @@ RSpec.describe Bundler do data = Marshal.dump(simple_structure) expect(Bundler.safe_load_marshal(data)).to eq(simple_structure) end + + it "loads Gem::Version" do + gem_version = Gem::Version.new("3.7.2") + data = Marshal.dump(gem_version) + expect(Bundler.safe_load_marshal(data)).to eq(gem_version) + end + + it "loads Gem::Specification" do + gem_spec = Gem::Specification.new("name", "3.7.2") + data = Marshal.dump(gem_spec) + expect(Bundler.safe_load_marshal(data)).to eq(gem_spec) + end end describe "#load_gemspec_uncached" do diff --git a/spec/bundler/bundler/definition_spec.rb b/spec/bundler/bundler/definition_spec.rb index 372fa2d5e2..59b958ae42 100644 --- a/spec/bundler/bundler/definition_spec.rb +++ b/spec/bundler/bundler/definition_spec.rb @@ -154,7 +154,7 @@ RSpec.describe Bundler::Definition do only_java (1.1-java) PLATFORMS - #{lockfile_platforms_for(["java", specific_local_platform])} + #{lockfile_platforms("java")} DEPENDENCIES only_java diff --git a/spec/bundler/bundler/dependency_spec.rb b/spec/bundler/bundler/dependency_spec.rb index 9b9e2ddf0a..6e346c36c1 100644 --- a/spec/bundler/bundler/dependency_spec.rb +++ b/spec/bundler/bundler/dependency_spec.rb @@ -53,6 +53,8 @@ RSpec.describe Bundler::Dependency do :ruby_27 => Gem::Platform::RUBY, :ruby_30 => Gem::Platform::RUBY, :ruby_31 => Gem::Platform::RUBY, + :ruby_32 => Gem::Platform::RUBY, + :ruby_33 => Gem::Platform::RUBY, :mri => Gem::Platform::RUBY, :mri_18 => Gem::Platform::RUBY, :mri_19 => Gem::Platform::RUBY, @@ -66,6 +68,8 @@ RSpec.describe Bundler::Dependency do :mri_27 => Gem::Platform::RUBY, :mri_30 => Gem::Platform::RUBY, :mri_31 => Gem::Platform::RUBY, + :mri_32 => Gem::Platform::RUBY, + :mri_33 => Gem::Platform::RUBY, :rbx => Gem::Platform::RUBY, :truffleruby => Gem::Platform::RUBY, :jruby => Gem::Platform::JAVA, @@ -84,6 +88,8 @@ RSpec.describe Bundler::Dependency do :windows_27 => Gem::Platform::WINDOWS, :windows_30 => Gem::Platform::WINDOWS, :windows_31 => Gem::Platform::WINDOWS, + :windows_32 => Gem::Platform::WINDOWS, + :windows_33 => Gem::Platform::WINDOWS, :mswin => Gem::Platform::MSWIN, :mswin_18 => Gem::Platform::MSWIN, :mswin_19 => Gem::Platform::MSWIN, @@ -97,6 +103,8 @@ RSpec.describe Bundler::Dependency do :mswin_27 => Gem::Platform::MSWIN, :mswin_30 => Gem::Platform::MSWIN, :mswin_31 => Gem::Platform::MSWIN, + :mswin_32 => Gem::Platform::MSWIN, + :mswin_33 => Gem::Platform::MSWIN, :mswin64 => Gem::Platform::MSWIN64, :mswin64_19 => Gem::Platform::MSWIN64, :mswin64_20 => Gem::Platform::MSWIN64, @@ -109,6 +117,8 @@ RSpec.describe Bundler::Dependency do :mswin64_27 => Gem::Platform::MSWIN64, :mswin64_30 => Gem::Platform::MSWIN64, :mswin64_31 => Gem::Platform::MSWIN64, + :mswin64_32 => Gem::Platform::MSWIN64, + :mswin64_33 => Gem::Platform::MSWIN64, :mingw => Gem::Platform::MINGW, :mingw_18 => Gem::Platform::MINGW, :mingw_19 => Gem::Platform::MINGW, @@ -122,6 +132,8 @@ RSpec.describe Bundler::Dependency do :mingw_27 => Gem::Platform::MINGW, :mingw_30 => Gem::Platform::MINGW, :mingw_31 => Gem::Platform::MINGW, + :mingw_32 => Gem::Platform::MINGW, + :mingw_33 => Gem::Platform::MINGW, :x64_mingw => Gem::Platform::X64_MINGW, :x64_mingw_20 => Gem::Platform::X64_MINGW, :x64_mingw_21 => Gem::Platform::X64_MINGW, @@ -132,7 +144,9 @@ RSpec.describe Bundler::Dependency do :x64_mingw_26 => Gem::Platform::X64_MINGW, :x64_mingw_27 => Gem::Platform::X64_MINGW, :x64_mingw_30 => Gem::Platform::X64_MINGW, - :x64_mingw_31 => Gem::Platform::X64_MINGW } + :x64_mingw_31 => Gem::Platform::X64_MINGW, + :x64_mingw_32 => Gem::Platform::X64_MINGW, + :x64_mingw_33 => Gem::Platform::X64_MINGW } end # rubocop:enable Naming/VariableNumber diff --git a/spec/bundler/bundler/dsl_spec.rb b/spec/bundler/bundler/dsl_spec.rb index 8cb51b8a52..8b5bf930f2 100644 --- a/spec/bundler/bundler/dsl_spec.rb +++ b/spec/bundler/bundler/dsl_spec.rb @@ -139,14 +139,19 @@ RSpec.describe Bundler::Dsl do describe "#gem" do # rubocop:disable Naming/VariableNumber [:ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24, :ruby_25, :ruby_26, :ruby_27, - :ruby_30, :ruby_31, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24, :mri_25, :mri_26, - :mri_27, :mri_30, :mri_31, :jruby, :rbx, :truffleruby].each do |platform| + :ruby_30, :ruby_31, :ruby_32, :ruby_33, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24, + :mri_25, :mri_26, :mri_27, :mri_30, :mri_31, :mri_32, :mri_33, :jruby, :rbx, :truffleruby].each do |platform| it "allows #{platform} as a valid platform" do subject.gem("foo", :platform => platform) end end # rubocop:enable Naming/VariableNumber + it "allows platforms matching the running Ruby version" do + platform = "ruby_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}" + subject.gem("foo", :platform => platform) + end + it "rejects invalid platforms" do expect { subject.gem("foo", :platform => :bogus) }. to raise_error(Bundler::GemfileError, /is not a valid platform/) diff --git a/spec/bundler/bundler/fetcher/dependency_spec.rb b/spec/bundler/bundler/fetcher/dependency_spec.rb index 7cfc86ef76..20307f9c99 100644 --- a/spec/bundler/bundler/fetcher/dependency_spec.rb +++ b/spec/bundler/bundler/fetcher/dependency_spec.rb @@ -155,9 +155,9 @@ RSpec.describe Bundler::Fetcher::Dependency do end end - shared_examples_for "the error suggests retrying with the full index" do - it "should log the inability to fetch from API at debug level" do - expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API\nit's suggested to retry using the full index via `bundle install --full-index`") + shared_examples_for "the error is logged" do + it "should log the inability to fetch from API at debug level, and mention retrying" do + expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API, trying the full index") subject.specs(gem_names, full_dependency_list, last_spec_list) end end @@ -166,25 +166,21 @@ RSpec.describe Bundler::Fetcher::Dependency do before { allow(subject).to receive(:dependency_specs) { raise Bundler::HTTPError.new } } it_behaves_like "the error is properly handled" - it_behaves_like "the error suggests retrying with the full index" + it_behaves_like "the error is logged" end context "when a GemspecError occurs" do before { allow(subject).to receive(:dependency_specs) { raise Bundler::GemspecError.new } } it_behaves_like "the error is properly handled" - it_behaves_like "the error suggests retrying with the full index" + it_behaves_like "the error is logged" end context "when a MarshalError occurs" do before { allow(subject).to receive(:dependency_specs) { raise Bundler::MarshalError.new } } it_behaves_like "the error is properly handled" - - it "should log the inability to fetch from API and mention retrying" do - expect(Bundler).to receive_message_chain(:ui, :debug).with("could not fetch from the dependency API, trying the full index") - subject.specs(gem_names, full_dependency_list, last_spec_list) - end + it_behaves_like "the error is logged" end end diff --git a/spec/bundler/bundler/fetcher_spec.rb b/spec/bundler/bundler/fetcher_spec.rb index a104760075..27a63c476d 100644 --- a/spec/bundler/bundler/fetcher_spec.rb +++ b/spec/bundler/bundler/fetcher_spec.rb @@ -159,4 +159,34 @@ RSpec.describe Bundler::Fetcher do end end end + + describe "#fetch_spec" do + let(:name) { "name" } + let(:version) { "1.3.17" } + let(:platform) { "platform" } + let(:downloader) { double("downloader") } + let(:body) { double(Net::HTTP::Get, :body => downloaded_data) } + + context "when attempting to load a Gem::Specification" do + let(:spec) { Gem::Specification.new(name, version) } + let(:downloaded_data) { Zlib::Deflate.deflate(Marshal.dump(spec)) } + + it "returns the spec" do + expect(Bundler::Fetcher::Downloader).to receive(:new).and_return(downloader) + expect(downloader).to receive(:fetch).once.and_return(body) + result = fetcher.fetch_spec([name, version, platform]) + expect(result).to eq(spec) + end + end + + context "when attempting to load an unexpected class" do + let(:downloaded_data) { Zlib::Deflate.deflate(Marshal.dump(3)) } + + it "raises a HTTPError error" do + expect(Bundler::Fetcher::Downloader).to receive(:new).and_return(downloader) + expect(downloader).to receive(:fetch).once.and_return(body) + expect { fetcher.fetch_spec([name, version, platform]) }.to raise_error(Bundler::HTTPError, /Gemspec .* contained invalid data/i) + end + end + end end diff --git a/spec/bundler/bundler/installer/parallel_installer_spec.rb b/spec/bundler/bundler/installer/parallel_installer_spec.rb index f67d03356d..c8403a2e38 100644 --- a/spec/bundler/bundler/installer/parallel_installer_spec.rb +++ b/spec/bundler/bundler/installer/parallel_installer_spec.rb @@ -22,9 +22,9 @@ RSpec.describe Bundler::ParallelInstaller do it "prints a warning" do expect(Bundler.ui).to receive(:warn).with(<<-W.strip) Your lockfile doesn't include a valid resolution. -You can fix this by regenerating your lockfile or trying to manually editing the bad locked gems to a version that satisfies all dependencies. +You can fix this by regenerating your lockfile or manually editing the bad locked gems to a version that satisfies all dependencies. The unmet dependencies are: -* diff-lcs (< 1.4), depended upon cucumber-4.1.0, unsatisfied by diff-lcs-1.4.4 +* diff-lcs (< 1.4), dependency of cucumber-4.1.0, unsatisfied by diff-lcs-1.4.4 W subject.check_for_unmet_dependencies end diff --git a/spec/bundler/bundler/rubygems_integration_spec.rb b/spec/bundler/bundler/rubygems_integration_spec.rb index 369f28fcef..182aa3646a 100644 --- a/spec/bundler/bundler/rubygems_integration_spec.rb +++ b/spec/bundler/bundler/rubygems_integration_spec.rb @@ -89,5 +89,16 @@ RSpec.describe Bundler::RubygemsIntegration do expect(result).to eq(%w[specs prerelease_specs]) end end + + context "when loading an unexpected class" do + let(:remote_no_mirror) { double("remote", :uri => uri, :original_uri => nil) } + let(:unexpected_specs_response) { Marshal.dump(3) } + + it "raises a MarshalError error" do + expect(Bundler.rubygems).to receive(:gem_remote_fetcher).once.and_return(fetcher) + expect(fetcher).to receive(:fetch_path).with(uri + "specs.4.8.gz").and_return(unexpected_specs_response) + expect { Bundler.rubygems.fetch_all_remote_specs(remote_no_mirror) }.to raise_error(Bundler::MarshalError, /unexpected class/i) + end + end end end diff --git a/spec/bundler/bundler/shared_helpers_spec.rb b/spec/bundler/bundler/shared_helpers_spec.rb index e1c5122564..3c6536c4eb 100644 --- a/spec/bundler/bundler/shared_helpers_spec.rb +++ b/spec/bundler/bundler/shared_helpers_spec.rb @@ -290,7 +290,7 @@ RSpec.describe Bundler::SharedHelpers do if Gem.respond_to?(:path_separator) allow(Gem).to receive(:path_separator).and_return(":") else - stub_const("File::PATH_SEPARATOR", ":".freeze) + stub_const("File::PATH_SEPARATOR", ":") end allow(Bundler).to receive(:bundle_path) { Pathname.new("so:me/dir/bin") } expect { subject.send(:validate_bundle_path) }.to raise_error( diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb index 841b8651e4..98d54015e7 100644 --- a/spec/bundler/bundler/source/git/git_proxy_spec.rb +++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb @@ -6,13 +6,15 @@ RSpec.describe Bundler::Source::Git::GitProxy do let(:ref) { "HEAD" } let(:revision) { nil } let(:git_source) { nil } + let(:clone_result) { double(Process::Status, :success? => true) } + let(:base_clone_args) { ["clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--depth", "1", "--single-branch"] } subject { described_class.new(path, uri, ref, revision, git_source) } context "with configured credentials" do it "adds username and password to URI" do Bundler.settings.temporary(uri => "u:p") do allow(subject).to receive(:git).with("--version").and_return("git version 2.14.0") - expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--depth", "1", "--single-branch", "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s) + expect(subject).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result]) subject.checkout end end @@ -20,7 +22,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do it "adds username and password to URI for host" do Bundler.settings.temporary("github.com" => "u:p") do allow(subject).to receive(:git).with("--version").and_return("git version 2.14.0") - expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--depth", "1", "--single-branch", "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s) + expect(subject).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result]) subject.checkout end end @@ -28,7 +30,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do it "does not add username and password to mismatched URI" do Bundler.settings.temporary("https://u:p@github.com/rubygems/rubygems-mismatch.git" => "u:p") do allow(subject).to receive(:git).with("--version").and_return("git version 2.14.0") - expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--depth", "1", "--single-branch", "--", uri, path.to_s) + expect(subject).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "", clone_result]) subject.checkout end end @@ -38,7 +40,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do original = "https://orig:info@github.com/rubygems/rubygems.git" subject = described_class.new(Pathname("path"), original, "HEAD") allow(subject).to receive(:git).with("--version").and_return("git version 2.14.0") - expect(subject).to receive(:git_retry).with("clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--depth", "1", "--single-branch", "--", original, path.to_s) + expect(subject).to receive(:capture).with([*base_clone_args, "--", original, path.to_s], nil).and_return(["", "", clone_result]) subject.checkout end end diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb index d2671b5f15..890ba88605 100644 --- a/spec/bundler/cache/git_spec.rb +++ b/spec/bundler/cache/git_spec.rb @@ -220,4 +220,57 @@ RSpec.describe "bundle cache with git" do expect(the_bundle).to include_gem "foo 1.0" end end + + it "respects the --no-install flag" do + git = build_git "foo", &:add_c_extension + ref = git.ref_for("main", 11) + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "foo", :git => '#{lib_path("foo-1.0")}' + G + bundle "config set cache_all true" + + # The algorithm for the cache location for a git checkout is + # in Bundle::Source::Git#cache_path + cache_path_name = "foo-1.0-#{Digest(:SHA1).hexdigest(lib_path("foo-1.0").to_s)}" + + # Run this test twice. This is because materially different codepaths + # will get hit the second time around. + # The first time, Bundler::Sources::Git#install_path is set to the system + # wide cache directory bundler/gems; the second time, it's set to the + # vendor/cache directory. We don't want the native extension to appear in + # either of these places, so run the `bundle cache` command twice. + 2.times do + bundle :cache, "all-platforms" => true, :install => false + + # it did _NOT_ actually install the gem - neither in $GEM_HOME (bundler 2 mode), + # nor in .bundle (bundler 3 mode) + expect(Pathname.new(File.join(default_bundle_path, "gems/foo-1.0-#{ref}"))).to_not exist + # it _did_ cache the gem in vendor/ + expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist + # it did _NOT_ build the gems extensions in the vendor/ dir + expect(Dir[bundled_app("vendor/cache/foo-1.0-#{ref}/lib/foo_c*")]).to be_empty + # it _did_ cache the git checkout + expect(default_cache_path("git", cache_path_name)).to exist + # And the checkout is a bare checkout + expect(default_cache_path("git", cache_path_name, "HEAD")).to exist + end + + # Subsequently installing the gem should compile it. + # _currently_, the gem gets compiled in vendor/cache, and vendor/cache is added + # to the $LOAD_PATH for git extensions, so it all kind of "works". However, in the + # future we would like to stop adding vendor/cache to the $LOAD_PATH for git extensions + # and instead treat them identically to normal gems (where the gem install location, + # not the cache location, is added to $LOAD_PATH). + # Verify that the compilation worked and the result is in $LOAD_PATH by simply attempting + # to require it; that should make sure this spec does not break if the load path behaviour + # is changed. + bundle :install, :local => true + ruby <<~R, :raise_on_error => false + require 'bundler/setup' + require 'foo_c' + R + expect(last_command).to_not be_failure + end end diff --git a/spec/bundler/commands/binstubs_spec.rb b/spec/bundler/commands/binstubs_spec.rb index 72f4cc8e55..bfbef58181 100644 --- a/spec/bundler/commands/binstubs_spec.rb +++ b/spec/bundler/commands/binstubs_spec.rb @@ -166,6 +166,21 @@ RSpec.describe "bundle binstubs <gem>" do end end + context "and the version is newer when given `gems.rb` and `gems.locked`" do + before do + gemfile bundled_app("gems.rb"), gemfile + lockfile bundled_app("gems.locked"), lockfile.gsub(system_bundler_version, "999.999") + end + + it "runs the correct version of bundler" do + sys_exec "bin/bundle install", :env => { "BUNDLE_GEMFILE" => "gems.rb" }, :raise_on_error => false + + expect(exitstatus).to eq(42) + expect(err).to include("Activating bundler (~> 999.999) failed:"). + and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`") + end + end + context "and the version is older and a different major" do let(:system_bundler_version) { "55" } @@ -181,6 +196,22 @@ RSpec.describe "bundle binstubs <gem>" do end end + context "and the version is older and a different major when given `gems.rb` and `gems.locked`" do + let(:system_bundler_version) { "55" } + + before do + gemfile bundled_app("gems.rb"), gemfile + lockfile bundled_app("gems.locked"), lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 44.0") + end + + it "runs the correct version of bundler" do + sys_exec "bin/bundle install", :env => { "BUNDLE_GEMFILE" => "gems.rb" }, :raise_on_error => false + expect(exitstatus).to eq(42) + expect(err).to include("Activating bundler (~> 44.0) failed:"). + and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 44.0'`") + end + end + context "and the version is older and the same major" do let(:system_bundler_version) { "2.999.999" } diff --git a/spec/bundler/commands/check_spec.rb b/spec/bundler/commands/check_spec.rb index b4996977c1..99a858e9e9 100644 --- a/spec/bundler/commands/check_spec.rb +++ b/spec/bundler/commands/check_spec.rb @@ -172,7 +172,7 @@ RSpec.describe "bundle check" do rack (1.0.0) PLATFORMS - #{local} + #{generic_local_platform} #{not_local} DEPENDENCIES @@ -203,7 +203,7 @@ RSpec.describe "bundle check" do rack (1.0.0) PLATFORMS - #{local} + #{generic_local_platform} #{not_local} DEPENDENCIES @@ -351,7 +351,7 @@ RSpec.describe "bundle check" do PLATFORMS ruby - #{specific_local_platform} + #{local_platform} DEPENDENCIES rack diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb index 5ca11dcba2..099cdb39fa 100644 --- a/spec/bundler/commands/exec_spec.rb +++ b/spec/bundler/commands/exec_spec.rb @@ -916,6 +916,30 @@ Run `bundle install` to install missing gems. end end + context "when Bundler.setup fails and Gemfile is not the default" do + before do + create_file "CustomGemfile", <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack', '2' + G + ENV["BUNDLER_FORCE_TTY"] = "true" + ENV["BUNDLE_GEMFILE"] = "CustomGemfile" + ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"] = nil + end + + let(:exit_code) { Bundler::GemNotFound.new.status_code } + let(:expected) { "" } + + it "prints proper suggestion" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to include("Run `bundle install --gemfile CustomGemfile` to install missing gems.") + 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 } diff --git a/spec/bundler/commands/init_spec.rb b/spec/bundler/commands/init_spec.rb index 9c499b99a1..6aa3e9edd1 100644 --- a/spec/bundler/commands/init_spec.rb +++ b/spec/bundler/commands/init_spec.rb @@ -191,4 +191,17 @@ RSpec.describe "bundle init" do end end end + + describe "using the --gemfile" do + it "should use the --gemfile value to name the gemfile" do + custom_gemfile_name = "NiceGemfileName" + + bundle :init, :gemfile => custom_gemfile_name + + expect(out).to include("Writing new #{custom_gemfile_name}") + used_template = File.read("#{source_root}/lib/bundler/templates/Gemfile") + generated_gemfile = bundled_app(custom_gemfile_name).read + expect(generated_gemfile).to eq(used_template) + end + end end diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index 08ac333664..126853e35f 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -140,6 +140,62 @@ RSpec.describe "bundle lock" do expect(read_lockfile).to eq(@lockfile) end + it "does not unlock git sources when only uri shape changes" do + build_git("foo") + + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}" + G + + # Change uri format to end with "/" and reinstall + install_gemfile <<-G, :verbose => true + source "#{file_uri_for(gem_repo1)}" + gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}/" + G + + expect(out).to include("using resolution from the lockfile") + expect(out).not_to include("re-resolving dependencies because the list of sources changed") + end + + it "updates specific gems using --update using the locked revision of unrelated git gems for resolving" do + ref = build_git("foo").ref_for("HEAD") + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rake" + gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :branch => "deadbeef" + G + + lockfile <<~L + GIT + remote: #{file_uri_for(lib_path("foo-1.0"))} + revision: #{ref} + branch: deadbeef + specs: + foo (1.0) + + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + rake (10.0.1) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo! + rake + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "lock --update rake --verbose" + expect(out).to match(/Writing lockfile to.+lock/) + expect(lockfile).to include("rake (13.0.1)") + end + it "errors when updating a missing specific gems using --update" do lockfile @lockfile @@ -266,7 +322,7 @@ RSpec.describe "bundle lock" do allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to match_array([java, x86_mingw32, specific_local_platform].uniq) + expect(lockfile.platforms).to match_array([java, x86_mingw32, local_platform].uniq) end it "supports adding new platforms with force_ruby_platform = true" do @@ -275,11 +331,11 @@ RSpec.describe "bundle lock" do remote: #{file_uri_for(gem_repo1)}/ specs: platform_specific (1.0) - platform_specific (1.0-x86-linux) + platform_specific (1.0-x86-64_linux) PLATFORMS ruby - x86-linux + x86_64-linux DEPENDENCIES platform_specific @@ -298,7 +354,7 @@ RSpec.describe "bundle lock" do allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to match_array(["ruby", specific_local_platform].uniq) + expect(lockfile.platforms).to match_array(["ruby", local_platform].uniq) end it "warns when adding an unknown platform" do @@ -311,12 +367,12 @@ RSpec.describe "bundle lock" do allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to match_array([java, x86_mingw32, specific_local_platform].uniq) + expect(lockfile.platforms).to match_array([java, x86_mingw32, local_platform].uniq) bundle "lock --remove-platform java" lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to match_array([x86_mingw32, specific_local_platform].uniq) + expect(lockfile.platforms).to match_array([x86_mingw32, local_platform].uniq) end it "also cleans up redundant platform gems when removing platforms" do @@ -375,7 +431,7 @@ RSpec.describe "bundle lock" do end it "errors when removing all platforms" do - bundle "lock --remove-platform #{specific_local_platform}", :raise_on_error => false + bundle "lock --remove-platform #{local_platform}", :raise_on_error => false expect(err).to include("Removing all platforms from the bundle is not allowed") end @@ -644,6 +700,62 @@ RSpec.describe "bundle lock" do bundle "lock --add-platform x86_64-linux", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } end + it "does not crash on conflicting ruby requirements between platform versions in two different gems" do + build_repo4 do + build_gem "unf_ext", "0.0.8.2" + + build_gem "unf_ext", "0.0.8.2" do |s| + s.required_ruby_version = [">= 2.4", "< #{previous_ruby_minor}"] + s.platform = "x64-mingw32" + end + + build_gem "unf_ext", "0.0.8.2" do |s| + s.required_ruby_version = [">= #{previous_ruby_minor}", "< #{current_ruby_minor}"] + s.platform = "x64-mingw-ucrt" + end + + build_gem "google-protobuf", "3.21.12" + + build_gem "google-protobuf", "3.21.12" do |s| + s.required_ruby_version = [">= 2.5", "< #{previous_ruby_minor}"] + s.platform = "x64-mingw32" + end + + build_gem "google-protobuf", "3.21.12" do |s| + s.required_ruby_version = [">= #{previous_ruby_minor}", "< #{current_ruby_minor}"] + s.platform = "x64-mingw-ucrt" + end + end + + gemfile <<~G + source "https://gem.repo4" + + gem "google-protobuf" + gem "unf_ext" + G + + lockfile <<~L + GEM + remote: https://gem.repo4/ + specs: + google-protobuf (3.21.12) + unf_ext (0.0.8.2) + + PLATFORMS + x64-mingw-ucrt + x64-mingw32 + + DEPENDENCIES + google-protobuf + unf_ext + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "DEBUG_RESOLVER" => "1" } + end + it "respects lower bound ruby requirements" do build_repo4 do build_gem "our_private_gem", "0.1.0" do |s| @@ -880,7 +992,7 @@ RSpec.describe "bundle lock" do and rails >= 7.0.2.3, < 7.0.3.1 depends on activesupport = 7.0.2.3, every version of activemodel is incompatible with rails >= 7.0.2.3, < 7.0.3.1. And because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3, - rails >= 7.0.2.3, < 7.0.3.1 is forbidden. + rails >= 7.0.2.3, < 7.0.3.1 cannot be used. (1) So, because rails >= 7.0.3.1, < 7.0.4 depends on activemodel = 7.0.3.1 and rails >= 7.0.4 depends on activemodel = 7.0.4, rails >= 7.0.2.3 requires activemodel = 7.0.3.1 OR = 7.0.4. @@ -892,7 +1004,7 @@ RSpec.describe "bundle lock" do and every version of activemodel depends on activesupport = 6.0.4, activemodel != 7.0.2.3 is incompatible with rails >= 7.0.2.3. And because rails >= 7.0.2.3 requires activemodel = 7.0.3.1 OR = 7.0.4 (1), - rails >= 7.0.2.3 is forbidden. + rails >= 7.0.2.3 cannot be used. So, because Gemfile depends on rails >= 7.0.2.3, version solving has failed. ERR @@ -924,6 +1036,31 @@ RSpec.describe "bundle lock" do expect(lockfile).to include("autobuild (1.10.1)") end + # Newer rails depends on Bundler, while ancient Rails does not. Bundler tries + # a first resolution pass that does not consider pre-releases. However, when + # using a pre-release Bundler (like the .dev version), that results in that + # pre-release being ignored and resolving to a version that does not depend on + # Bundler at all. We should avoid that and still consider .dev Bundler. + # + it "does not ignore prereleases with there's only one candidate" do + build_repo4 do + build_gem "rails", "7.4.0.2" do |s| + s.add_dependency "bundler", ">= 1.15.0" + end + + build_gem "rails", "2.3.18" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + gem "rails" + G + + bundle "lock" + expect(lockfile).to_not include("rails (2.3.18)") + expect(lockfile).to include("rails (7.4.0.2)") + end + it "deals with platform specific incompatibilities" do build_repo4 do build_gem "activerecord", "6.0.6" @@ -976,4 +1113,135 @@ RSpec.describe "bundle lock" do expect(lockfile).not_to include("tzinfo-data (1.2022.7)") end end + + context "when resolving platform specific gems as indirect dependencies on truffleruby", :truffleruby_only do + before do + build_lib "foo", :path => bundled_app do |s| + s.add_dependency "nokogiri" + end + + build_repo4 do + build_gem "nokogiri", "1.14.2" + build_gem "nokogiri", "1.14.2" do |s| + s.platform = "x86_64-linux" + end + end + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gemspec + G + end + + it "locks ruby specs" do + simulate_platform "x86_64-linux" do + bundle "lock" + end + + expect(lockfile).to eq <<~L + PATH + remote: . + specs: + foo (1.0) + nokogiri + + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.14.2) + + PLATFORMS + x86_64-linux + + DEPENDENCIES + foo! + + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + context "when adding a new gem that requires unlocking other transitive deps" do + before do + build_repo4 do + build_gem "govuk_app_config", "0.1.0" + + build_gem "govuk_app_config", "4.13.0" do |s| + s.add_dependency "railties", ">= 5.0" + end + + %w[7.0.4.1 7.0.4.3].each do |v| + build_gem "railties", v do |s| + s.add_dependency "actionpack", v + s.add_dependency "activesupport", v + end + + build_gem "activesupport", v + build_gem "actionpack", v + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "govuk_app_config" + gem "activesupport", "7.0.4.3" + G + + # Simulate out of sync lockfile because top level dependency on + # activesuport has just been added to the Gemfile, and locked to a higher + # version + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + actionpack (7.0.4.1) + activesupport (7.0.4.1) + govuk_app_config (4.13.0) + railties (>= 5.0) + railties (7.0.4.1) + actionpack (= 7.0.4.1) + activesupport (= 7.0.4.1) + + PLATFORMS + arm64-darwin-22 + + DEPENDENCIES + govuk_app_config + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "does not downgrade top level dependencies" do + simulate_platform "arm64-darwin-22" do + bundle "lock" + end + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + actionpack (7.0.4.3) + activesupport (7.0.4.3) + govuk_app_config (4.13.0) + railties (>= 5.0) + railties (7.0.4.3) + actionpack (= 7.0.4.3) + activesupport (= 7.0.4.3) + + PLATFORMS + arm64-darwin-22 + + DEPENDENCIES + activesupport (= 7.0.4.3) + govuk_app_config + + BUNDLED WITH + #{Bundler::VERSION} + L + end + end end diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb index 5827f7380c..970e51b8ef 100644 --- a/spec/bundler/commands/newgem_spec.rb +++ b/spec/bundler/commands/newgem_spec.rb @@ -633,7 +633,7 @@ RSpec.describe "bundle gem" do it "does not include the gemspec file in files" do bundle "gem #{gem_name}" - bundler_gemspec = Bundler::GemHelper.new(gemspec_dir).gemspec + bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec expect(bundler_gemspec.files).not_to include("#{gem_name}.gemspec") end @@ -1473,11 +1473,11 @@ RSpec.describe "bundle gem" do # frozen_string_literal: true require "bundler/gem_tasks" - require "rake/extensiontask" + require "rb_sys/extensiontask" task build: :compile - Rake::ExtensionTask.new("#{gem_name}") do |ext| + RbSys::ExtensionTask.new("#{gem_name}") do |ext| ext.lib_dir = "lib/#{gem_name}" end diff --git a/spec/bundler/commands/outdated_spec.rb b/spec/bundler/commands/outdated_spec.rb index e084af85d7..bbc3cb54ae 100644 --- a/spec/bundler/commands/outdated_spec.rb +++ b/spec/bundler/commands/outdated_spec.rb @@ -192,7 +192,7 @@ RSpec.describe "bundle outdated" do vcr (6.0.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES dotenv diff --git a/spec/bundler/commands/platform_spec.rb b/spec/bundler/commands/platform_spec.rb index 4e8e3946fe..688bf7b6df 100644 --- a/spec/bundler/commands/platform_spec.rb +++ b/spec/bundler/commands/platform_spec.rb @@ -16,7 +16,7 @@ RSpec.describe "bundle platform" do Your platform is: #{Gem::Platform.local} Your app has gems that work on these platforms: -* #{specific_local_platform} +* #{local_platform} Your Gemfile specifies a Ruby version requirement: * ruby #{Gem.ruby_version} @@ -39,7 +39,7 @@ G Your platform is: #{Gem::Platform.local} Your app has gems that work on these platforms: -* #{specific_local_platform} +* #{local_platform} Your Gemfile specifies a Ruby version requirement: * #{Bundler::RubyVersion.system.single_version_string} @@ -60,7 +60,7 @@ G Your platform is: #{Gem::Platform.local} Your app has gems that work on these platforms: -* #{specific_local_platform} +* #{local_platform} Your Gemfile does not specify a Ruby version requirement. G @@ -80,7 +80,7 @@ G Your platform is: #{Gem::Platform.local} Your app has gems that work on these platforms: -* #{specific_local_platform} +* #{local_platform} Your Gemfile specifies a Ruby version requirement: * ruby #{not_local_ruby_version} diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb index 8c7b9c13c8..7016c3e19f 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -284,7 +284,7 @@ RSpec.describe "bundle update" do countries (~> 3.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES countries @@ -871,7 +871,7 @@ RSpec.describe "bundle update" do vcr (6.0.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES dotenv @@ -1602,7 +1602,7 @@ RSpec.describe "bundle update conservative" do shared_dep (~> 5.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES isolated_owner @@ -1655,7 +1655,7 @@ RSpec.describe "bundle update conservative" do shared_dep (~> 5.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES isolated_owner diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb index 3bcb6a703e..484664b433 100644 --- a/spec/bundler/install/deploy_spec.rb +++ b/spec/bundler/install/deploy_spec.rb @@ -141,7 +141,7 @@ RSpec.describe "install in deployment or frozen mode" do rack (1.0.0) PLATFORMS - #{local} + #{generic_local_platform} DEPENDENCIES rack diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb index 7e2e7c345a..2aa4214818 100644 --- a/spec/bundler/install/gemfile/gemspec_spec.rb +++ b/spec/bundler/install/gemfile/gemspec_spec.rb @@ -674,7 +674,7 @@ RSpec.describe "bundle install from an existing gemspec" do railties (6.1.4) PLATFORMS - #{lockfile_platforms_for(["java", specific_local_platform])} + #{lockfile_platforms("java")} DEPENDENCIES activeadmin! diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb index b6ea32819b..e3be680d89 100644 --- a/spec/bundler/install/gemfile/git_spec.rb +++ b/spec/bundler/install/gemfile/git_spec.rb @@ -1144,6 +1144,17 @@ RSpec.describe "bundle install with git sources" do G expect(err).to include("Revision deadbeef does not exist in the repository") end + + it "gives a helpful error message when the remote branch no longer exists" do + build_git "foo" + + install_gemfile <<-G, :raise_on_error => false + source "#{file_uri_for(gem_repo1)}" + gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :branch => "deadbeef" + G + + expect(err).to include("Revision deadbeef does not exist in the repository") + end end describe "bundle install with deployment mode configured and git sources" do diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb index e9cf0cb636..219ae6c2f4 100644 --- a/spec/bundler/install/gemfile/platform_spec.rb +++ b/spec/bundler/install/gemfile/platform_spec.rb @@ -459,7 +459,7 @@ RSpec.describe "bundle install with platform conditionals" do tzinfo (2.0.4) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES activesupport diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb index 2199d8bedc..dd86187a6b 100644 --- a/spec/bundler/install/gemfile/sources_spec.rb +++ b/spec/bundler/install/gemfile/sources_spec.rb @@ -262,7 +262,7 @@ RSpec.describe "bundle install with gems on multiple sources" do rack PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES depends_on_rack! @@ -376,7 +376,7 @@ RSpec.describe "bundle install with gems on multiple sources" do Because every version of depends_on_missing depends on missing >= 0 and missing >= 0 could not be found in any of the sources, - every version of depends_on_missing is forbidden. + depends_on_missing cannot be used. So, because Gemfile depends on depends_on_missing >= 0, version solving has failed. E @@ -438,7 +438,7 @@ RSpec.describe "bundle install with gems on multiple sources" do Because every version of depends_on_rack depends on rack >= 0 and rack >= 0 could not be found in rubygems repository https://gem.repo2/ or installed locally, - every version of depends_on_rack is forbidden. + depends_on_rack cannot be used. So, because Gemfile depends on depends_on_rack >= 0, version solving has failed. E @@ -640,7 +640,7 @@ RSpec.describe "bundle install with gems on multiple sources" do zeitwerk (2.4.2) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES activesupport @@ -696,7 +696,7 @@ RSpec.describe "bundle install with gems on multiple sources" do sidekiq (>= 6.1.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES activesupport @@ -780,7 +780,7 @@ RSpec.describe "bundle install with gems on multiple sources" do sidekiq (>= 6.1.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES activesupport @@ -836,7 +836,7 @@ RSpec.describe "bundle install with gems on multiple sources" do sidekiq (>= 6.1.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES activesupport @@ -924,7 +924,7 @@ RSpec.describe "bundle install with gems on multiple sources" do nokogiri (>= 1.2.3) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES handsoap! @@ -984,7 +984,7 @@ RSpec.describe "bundle install with gems on multiple sources" do rack (0.9.1) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES rack! @@ -1014,7 +1014,7 @@ RSpec.describe "bundle install with gems on multiple sources" do rack (0.9.1) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES rack! @@ -1036,7 +1036,7 @@ RSpec.describe "bundle install with gems on multiple sources" do rack (0.9.1) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES rack! @@ -1448,7 +1448,7 @@ RSpec.describe "bundle install with gems on multiple sources" do mime-types (3.3.1) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES capybara (~> 2.5.0) @@ -1472,7 +1472,7 @@ RSpec.describe "bundle install with gems on multiple sources" do mime-types (3.0.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES capybara (~> 2.5.0) @@ -1526,7 +1526,7 @@ RSpec.describe "bundle install with gems on multiple sources" do pdf-writer (= 1.1.8) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES ruport (= 1.7.0.3)! @@ -1579,7 +1579,7 @@ RSpec.describe "bundle install with gems on multiple sources" do pdf-writer (= 1.1.8) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES ruport (= 1.7.0.3)! @@ -1620,7 +1620,7 @@ RSpec.describe "bundle install with gems on multiple sources" do pdf-writer (1.1.8) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES pdf-writer (= 1.1.8) diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb index 2f82a9f9f3..6974af3270 100644 --- a/spec/bundler/install/gemfile/specific_platform_spec.rb +++ b/spec/bundler/install/gemfile/specific_platform_spec.rb @@ -432,7 +432,7 @@ RSpec.describe "bundle install with specific platforms" do Because every version of sorbet depends on sorbet-static = 0.5.6433 and sorbet-static = 0.5.6433 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally for any resolution platforms (arm64-darwin-21), - every version of sorbet is forbidden. + sorbet cannot be used. So, because Gemfile depends on sorbet = 0.5.6433, version solving has failed. @@ -514,7 +514,7 @@ RSpec.describe "bundle install with specific platforms" do sorbet-runtime (= 0.5.10160) PLATFORMS - #{lockfile_platforms_for([specific_local_platform, "ruby"])} + #{lockfile_platforms("ruby")} DEPENDENCIES sorbet-static-and-runtime @@ -769,7 +769,7 @@ RSpec.describe "bundle install with specific platforms" do nokogiri (1.13.8-#{Gem::Platform.local}) PLATFORMS - #{lockfile_platforms_for([specific_local_platform, "ruby"])} + #{lockfile_platforms("ruby")} DEPENDENCIES nokogiri @@ -786,6 +786,56 @@ RSpec.describe "bundle install with specific platforms" do expect(lockfile).to eq(original_lockfile) end + it "does not remove ruby when adding a new gem to the Gemfile" do + build_repo4 do + build_gem "concurrent-ruby", "1.2.2" + build_gem "rack", "3.0.7" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "concurrent-ruby" + gem "rack" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + concurrent-ruby (1.2.2) + + PLATFORMS + ruby + + DEPENDENCIES + concurrent-ruby + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "lock" + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + concurrent-ruby (1.2.2) + rack (3.0.7) + + PLATFORMS + #{formatted_lockfile_platforms(*["ruby", generic_local_platform].uniq)} + + DEPENDENCIES + concurrent-ruby + rack + + BUNDLED WITH + #{Bundler::VERSION} + L + end + it "can fallback to a source gem when platform gems are incompatible with current ruby version" do setup_multiplatform_gem_with_source_gem diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb index b6c361186a..4d5776e2b8 100644 --- a/spec/bundler/install/gems/compact_index_spec.rb +++ b/spec/bundler/install/gems/compact_index_spec.rb @@ -930,7 +930,7 @@ The checksum of /versions does not match the checksum provided by the server! So expect(out).to include("rails-2.3.2 from rubygems remote at #{source_uri}/ has either corrupted API or lockfile dependencies") expect(err).to include(<<-E.strip) Bundler::APIResponseMismatchError: Downloading rails-2.3.2 revealed dependencies not in the API or the lockfile (#{deps.map(&:to_s).join(", ")}). -Either installing with `--full-index` or running `bundle update rails` should fix the problem. +Running `bundle update rails` should fix the problem. E end diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb index 4bbb2ea038..d5fa55be48 100644 --- a/spec/bundler/install/gems/flex_spec.rb +++ b/spec/bundler/install/gems/flex_spec.rb @@ -198,7 +198,7 @@ RSpec.describe "bundle flex_install" do Because rack-obama >= 2.0 depends on rack = 1.2 and rack = 1.2 could not be found in rubygems repository #{file_uri_for(gem_repo2)}/ or installed locally, - rack-obama >= 2.0 is forbidden. + rack-obama >= 2.0 cannot be used. So, because Gemfile depends on rack-obama = 2.0, version solving has failed. E diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb index 279d3422c1..fb6dda2f88 100644 --- a/spec/bundler/install/gems/resolving_spec.rb +++ b/spec/bundler/install/gems/resolving_spec.rb @@ -190,7 +190,7 @@ RSpec.describe "bundle install with install-time dependencies" do expect(out).to include(" net_b"). and include("Resolving dependencies..."). and include("Solution found after 1 attempts:"). - and include("selecting net_b 1.0") + and include("selected net_b 1.0") end end end diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb index ab10670fdf..4d08752256 100644 --- a/spec/bundler/install/gems/standalone_spec.rb +++ b/spec/bundler/install/gems/standalone_spec.rb @@ -170,7 +170,7 @@ RSpec.shared_examples "bundle install --standalone" do end end - describe "with Gemfiles using path sources and resulting bundle moved to a folder hierarchy with different nesting" do + describe "with Gemfiles using absolute path sources and resulting bundle moved to a folder hierarchy with different nesting" do before do build_lib "minitest", "1.0.0", :path => lib_path("minitest") @@ -200,6 +200,35 @@ RSpec.shared_examples "bundle install --standalone" do end end + describe "with Gemfiles using relative path sources and app moved to a different root" do + before do + FileUtils.mkdir_p bundled_app("app/vendor") + + build_lib "minitest", "1.0.0", :path => bundled_app("app/vendor/minitest") + + gemfile bundled_app("app/Gemfile"), <<-G + source "#{file_uri_for(gem_repo1)}" + gem "minitest", :path => "vendor/minitest" + G + + bundle "install", :standalone => true, :dir => bundled_app("app") + + FileUtils.mv(bundled_app("app"), bundled_app2("app")) + end + + it "also works" do + ruby <<-RUBY, :dir => bundled_app2("app") + require "./bundle/bundler/setup" + + require "minitest" + puts MINITEST + RUBY + + expect(out).to eq("1.0.0") + expect(err).to be_empty + end + end + describe "with gems with native extension" do before do bundle "config set --local path #{bundled_app("bundle")}" diff --git a/spec/bundler/install/global_cache_spec.rb b/spec/bundler/install/global_cache_spec.rb index 3021d6ae17..be34d8b5bc 100644 --- a/spec/bundler/install/global_cache_spec.rb +++ b/spec/bundler/install/global_cache_spec.rb @@ -220,9 +220,9 @@ RSpec.describe "global gem caching" do gem "very_simple_path_binary", :path => "#{lib_path("very_simple_path_binary-1.0")}" G - gem_binary_cache = home(".bundle", "cache", "extensions", specific_local_platform.to_s, Bundler.ruby_scope, + gem_binary_cache = home(".bundle", "cache", "extensions", local_platform.to_s, Bundler.ruby_scope, Digest(:MD5).hexdigest("#{gem_repo1}/"), "very_simple_binary-1.0") - git_binary_cache = home(".bundle", "cache", "extensions", specific_local_platform.to_s, Bundler.ruby_scope, + git_binary_cache = home(".bundle", "cache", "extensions", local_platform.to_s, Bundler.ruby_scope, "very_simple_git_binary-1.0-#{revision}", "very_simple_git_binary-1.0") cached_extensions = Pathname.glob(home(".bundle", "cache", "extensions", "*", "*", "*", "*", "*")).sort diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb index 78b701e488..dc054b50bb 100644 --- a/spec/bundler/install/yanked_spec.rb +++ b/spec/bundler/install/yanked_spec.rb @@ -192,6 +192,12 @@ RSpec.context "when using gem before installing" do expect(err).to_not include("Your bundle is locked to rack (0.9.1) from") expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.") expect(err).to_not include("You'll need to update your bundle to a different version of rack (0.9.1) that hasn't been removed in order to install.") + + # Check error message is still correct when multiple platforms are locked + lockfile lockfile.gsub(/PLATFORMS\n #{lockfile_platforms}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}") + + bundle :list, :raise_on_error => false + expect(err).to include("Could not find rack-0.9.1 in locally installed gems") end it "does not suggest the author has yanked the gem when using more than one gem, but shows all gems that couldn't be found in the source" do diff --git a/spec/bundler/lock/git_spec.rb b/spec/bundler/lock/git_spec.rb index a633bd546e..1c1f6fa93d 100644 --- a/spec/bundler/lock/git_spec.rb +++ b/spec/bundler/lock/git_spec.rb @@ -33,6 +33,33 @@ RSpec.describe "bundle lock with git gems" do expect(err).to include("Revision bad does not exist in the repository") end + it "prints a proper error when installing a Gemfile with a locked ref that does not exist" do + lockfile <<~L + GIT + remote: #{lib_path("foo-1.0")} + revision: #{"a" * 40} + specs: + foo (1.0) + + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo! + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install", :raise_on_error => false + + expect(err).to include("Revision #{"a" * 40} does not exist in the repository") + end + it "locks a git source to the current ref" do update_git "foo" bundle :install diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index cd603e336a..8d9554c68d 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -982,7 +982,7 @@ RSpec.describe "the lockfile format" do rack (1.0.0) PLATFORMS - #{lockfile_platforms_for(["java", specific_local_platform])} + #{lockfile_platforms("java")} DEPENDENCIES rack @@ -1218,7 +1218,125 @@ RSpec.describe "the lockfile format" do G expect(err).to include("Downloading rack_middleware-1.0 revealed dependencies not in the API or the lockfile (#{Gem::Dependency.new("rack", "= 0.9.1")})."). - and include("Either installing with `--full-index` or running `bundle update rack_middleware` should fix the problem.") + and include("Running `bundle update rack_middleware` should fix the problem.") + end + + it "regenerates a lockfile with no specs" do + build_repo4 do + build_gem "indirect_dependency", "1.2.3" do |s| + s.metadata["funding_uri"] = "https://example.com/donate" + end + + build_gem "direct_dependency", "4.5.6" do |s| + s.add_dependency "indirect_dependency", ">= 0" + end + end + + lockfile <<-G + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + + PLATFORMS + ruby + + DEPENDENCIES + direct_dependency + + BUNDLED WITH + #{Bundler::VERSION} + G + + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + + gem "direct_dependency" + G + + expect(lockfile).to eq <<~G + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + direct_dependency (4.5.6) + indirect_dependency + indirect_dependency (1.2.3) + + PLATFORMS + #{formatted_lockfile_platforms(*["ruby", generic_local_platform].uniq)} + + DEPENDENCIES + direct_dependency + + BUNDLED WITH + #{Bundler::VERSION} + G + end + + shared_examples_for "a lockfile missing dependent specs" do + it "auto-heals" do + build_repo4 do + build_gem "minitest-bisect", "1.6.0" do |s| + s.add_dependency "path_expander", "~> 1.1" + end + + build_gem "path_expander", "1.1.1" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + gem "minitest-bisect" + G + + # Corrupt lockfile (completely missing path_expander) + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + minitest-bisect (1.6.0) + + PLATFORMS + #{platforms} + + DEPENDENCIES + minitest-bisect + + BUNDLED WITH + #{Bundler::VERSION} + L + + cache_gems "minitest-bisect-1.6.0", "path_expander-1.1.1", :gem_repo => gem_repo4 + bundle :install + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + minitest-bisect (1.6.0) + path_expander (~> 1.1) + path_expander (1.1.1) + + PLATFORMS + #{platforms} + + DEPENDENCIES + minitest-bisect + + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + context "with just specific platform" do + let(:platforms) { lockfile_platforms } + + it_behaves_like "a lockfile missing dependent specs" + end + + context "with both ruby and specific platform" do + let(:platforms) { lockfile_platforms("ruby") } + + it_behaves_like "a lockfile missing dependent specs" end it "auto-heals when the lockfile is missing specs" do diff --git a/spec/bundler/plugins/install_spec.rb b/spec/bundler/plugins/install_spec.rb index efee5fdd23..aec131f2ee 100644 --- a/spec/bundler/plugins/install_spec.rb +++ b/spec/bundler/plugins/install_spec.rb @@ -22,6 +22,13 @@ RSpec.describe "bundler plugin install" do plugin_should_be_installed("foo") end + it "installs from rubygems source in frozen mode" do + bundle "plugin install foo --source #{file_uri_for(gem_repo2)}", :env => { "BUNDLE_DEPLOYMENT" => "true" } + + expect(out).to include("Installed plugin foo") + plugin_should_be_installed("foo") + end + it "installs from sources configured as Gem.sources without any flags" do bundle "plugin install foo", :env => { "BUNDLER_SPEC_GEM_SOURCES" => file_uri_for(gem_repo2).to_s } diff --git a/spec/bundler/quality_spec.rb b/spec/bundler/quality_spec.rb index 1f4391d0e4..a98815158e 100644 --- a/spec/bundler/quality_spec.rb +++ b/spec/bundler/quality_spec.rb @@ -206,7 +206,6 @@ RSpec.describe "The library itself" do lib/bundler/deployment.rb lib/bundler/gem_tasks.rb lib/bundler/vlad.rb - lib/bundler/templates/gems.rb ] files_to_require = lib_tracked_files.grep(/\.rb$/) - exclusions files_to_require.reject! {|f| f.start_with?("lib/bundler/vendor") } diff --git a/spec/bundler/resolver/basic_spec.rb b/spec/bundler/resolver/basic_spec.rb index 41c6d9972c..f739f8c02b 100644 --- a/spec/bundler/resolver/basic_spec.rb +++ b/spec/bundler/resolver/basic_spec.rb @@ -301,4 +301,50 @@ RSpec.describe "Resolving" do end end end + + it "handles versions that redundantly depend on themselves" do + @index = build_index do + gem "rack", "3.0.0" + + gem "standalone_migrations", "7.1.0" do + dep "rack", "~> 2.0" + end + + gem "standalone_migrations", "2.0.4" do + dep "standalone_migrations", ">= 0" + end + + gem "standalone_migrations", "1.0.13" do + dep "rack", ">= 0" + end + end + + dep "rack", "~> 3.0" + dep "standalone_migrations" + + should_resolve_as %w[rack-3.0.0 standalone_migrations-2.0.4] + end + + it "ignores versions that incorrectly depend on themselves" do + @index = build_index do + gem "rack", "3.0.0" + + gem "standalone_migrations", "7.1.0" do + dep "rack", "~> 2.0" + end + + gem "standalone_migrations", "2.0.4" do + dep "standalone_migrations", ">= 2.0.5" + end + + gem "standalone_migrations", "1.0.13" do + dep "rack", ">= 0" + end + end + + dep "rack", "~> 3.0" + dep "standalone_migrations" + + should_resolve_as %w[rack-3.0.0 standalone_migrations-1.0.13] + end end diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb index caf69bf085..a9933f90e9 100644 --- a/spec/bundler/runtime/platform_spec.rb +++ b/spec/bundler/runtime/platform_spec.rb @@ -86,7 +86,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do racc (1.5.2) PLATFORMS - #{lockfile_platforms_for(["ruby", specific_local_platform])} + #{lockfile_platforms("ruby")} DEPENDENCIES nokogiri (~> 1.11) diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb index 703c98eca3..9bfa1458c6 100644 --- a/spec/bundler/support/helpers.rb +++ b/spec/bundler/support/helpers.rb @@ -414,14 +414,14 @@ module Spec end end - def cache_gems(*gems) + def cache_gems(*gems, gem_repo: gem_repo1) gems = gems.flatten FileUtils.rm_rf("#{bundled_app}/vendor/cache") FileUtils.mkdir_p("#{bundled_app}/vendor/cache") gems.each do |g| - path = "#{gem_repo1}/gems/#{g}.gem" + path = "#{gem_repo}/gems/#{g}.gem" raise "OMG `#{path}` does not exist!" unless File.exist?(path) FileUtils.cp(path, "#{bundled_app}/vendor/cache") end @@ -483,7 +483,17 @@ module Spec end def next_ruby_minor - Gem.ruby_version.segments[0..1].map.with_index {|s, i| i == 1 ? s + 1 : s }.join(".") + ruby_major_minor.map.with_index {|s, i| i == 1 ? s + 1 : s }.join(".") + end + + def previous_ruby_minor + return "2.7" if ruby_major_minor == [3, 0] + + ruby_major_minor.map.with_index {|s, i| i == 1 ? s - 1 : s }.join(".") + end + + def ruby_major_minor + Gem.ruby_version.segments[0..1] end # versions not including diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb index 3776901ce3..eca1b2e60d 100644 --- a/spec/bundler/support/platforms.rb +++ b/spec/bundler/support/platforms.rb @@ -21,7 +21,7 @@ module Spec end def linux - Gem::Platform.new(["x86", "linux", nil]) + Gem::Platform.new("x86_64-linux") end def x86_mswin32 @@ -52,14 +52,6 @@ module Spec [rb, java, linux, windows_platforms].flatten end - def local - generic_local_platform - end - - def specific_local_platform - Bundler.local_platform - end - def not_local all_platforms.find {|p| p != generic_local_platform } end @@ -103,11 +95,11 @@ module Spec 9999 end - def lockfile_platforms - lockfile_platforms_for([specific_local_platform]) + def lockfile_platforms(*extra) + formatted_lockfile_platforms(local_platform, *extra) end - def lockfile_platforms_for(platforms) + def formatted_lockfile_platforms(*platforms) platforms.map(&:to_s).sort.join("\n ") end end diff --git a/test/rubygems/helper.rb b/test/rubygems/helper.rb index 45a5a14251..70b200d7a0 100644 --- a/test/rubygems/helper.rb +++ b/test/rubygems/helper.rb @@ -563,14 +563,13 @@ class Gem::TestCase < Test::Unit::TestCase Dir.chdir directory do unless File.exist? ".git" system @git, "init", "--quiet" - system @git, "checkout", "-b", "master", "--quiet" system @git, "config", "user.name", "RubyGems Tests" system @git, "config", "user.email", "rubygems@example" end system @git, "add", gemspec system @git, "commit", "-a", "-m", "a non-empty commit message", "--quiet" - head = Gem::Util.popen(@git, "rev-parse", "master").strip + head = Gem::Util.popen(@git, "rev-parse", "HEAD").strip end return name, git_spec.version, directory, head @@ -1555,7 +1554,7 @@ Also, a list: # :stopdoc: # only available in RubyGems tests - PRIVATE_KEY_PASSPHRASE = "Foo bar".freeze + PRIVATE_KEY_PASSPHRASE = "Foo bar" begin PRIVATE_KEY = load_key "private" diff --git a/test/rubygems/package/tar_test_case.rb b/test/rubygems/package/tar_test_case.rb index 6cee7f86dc..1353f0fcdc 100644 --- a/test/rubygems/package/tar_test_case.rb +++ b/test/rubygems/package/tar_test_case.rb @@ -67,7 +67,7 @@ class Gem::Package::TarTestCase < Gem::TestCase end def calc_checksum(header) - sum = header.unpack("C*").inject {|s,a| s + a } + sum = header.sum(0) SP(Z(to_oct(sum, 6))) end @@ -90,31 +90,36 @@ class Gem::Package::TarTestCase < Gem::TestCase ASCIIZ("wheel", 32), # char gname[32]; ASCIIZ Z(to_oct(0, 7)), # char devmajor[8]; 0 padded, octal, null Z(to_oct(0, 7)), # char devminor[8]; 0 padded, octal, null - ASCIIZ(dname, 155), # char prefix[155]; ASCII + (Z unless filled) + ASCIIZ(dname, 155), # char prefix[155]; ASCII + (Z unless filled) ] h = arr.join - ret = h + "\0" * (512 - h.size) + ret = ASCIIZ(h, 512) assert_equal(512, ret.size) ret end - def tar_dir_header(name, prefix, mode, mtime) - h = header("5", name, prefix, 0, mode, mtime) + def header_with_checksum(type, fname, dname, length, mode, mtime, linkname = "") + h = header(type, fname, dname, length, mode, mtime, nil, linkname) checksum = calc_checksum(h) - header("5", name, prefix, 0, mode, mtime, checksum) + header(type, fname, dname, length, mode, mtime, checksum, linkname) + end + + def tar_dir_header(name, prefix, mode, mtime) + header_with_checksum("5", name, prefix, 0, mode, mtime) end def tar_file_header(fname, dname, mode, length, mtime) - h = header("0", fname, dname, length, mode, mtime) - checksum = calc_checksum(h) - header("0", fname, dname, length, mode, mtime, checksum) + header_with_checksum("0", fname, dname, length, mode, mtime) end - def tar_symlink_header(fname, prefix, mode, mtime, linkname) - h = header("2", fname, prefix, 0, mode, mtime, nil, linkname) - checksum = calc_checksum(h) - header("2", fname, prefix, 0, mode, mtime, checksum, linkname) + def tar_symlink_header(fname, dname, mode, mtime, linkname) + header_with_checksum("2", fname, dname, 0, mode, mtime, linkname) + end + + def tar_file_contents(content) + pad = (512 - (content.size % 512)) % 512 + content + "\0" * pad end def to_oct(n, pad_size) @@ -122,11 +127,15 @@ class Gem::Package::TarTestCase < Gem::TestCase end def util_entry(tar) - io = TempIO.new tar + io = tar.respond_to?(:read) ? tar : TempIO.new(tar) header = Gem::Package::TarHeader.from io - Gem::Package::TarReader::Entry.new header, io + Gem::Package::TarReader::Entry.open header, io + end + + def close_util_entry(entry) + entry.instance_variable_get(:@io).close! end def util_dir_entry @@ -136,4 +145,30 @@ class Gem::Package::TarTestCase < Gem::TestCase def util_symlink_entry util_entry tar_symlink_header("foo", "bar", 0, Time.now, "link") end + + def util_tar(&block) + tar_io = StringIO.new + Gem::Package::TarWriter.new(tar_io, &block) + tar_io.rewind + tar_io + end + + def util_tar_gz(&block) + tar_io = util_tar(&block) + StringIO.new util_gzip(tar_io.string) + end + + def util_gem_data_tar(spec = nil, &block) + data_tgz = util_tar_gz(&block) + util_tar do |tar| + if spec + tar.add_file "metadata.gz", 0444 do |io| + io.write util_gzip(spec.to_yaml) + end + end + tar.add_file "data.tar.gz", 0644 do |io| + io.write data_tgz.string + end + end + end end diff --git a/test/rubygems/simple_gem.rb b/test/rubygems/simple_gem.rb index 0f2ea48198..1650910aaf 100644 --- a/test/rubygems/simple_gem.rb +++ b/test/rubygems/simple_gem.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -SIMPLE_GEM = <<-GEMDATA.freeze +SIMPLE_GEM = <<-GEMDATA MD5SUM = "989bf34a1cbecd52e0ea66b662b3a405" if $0 == __FILE__ require 'optparse' diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb index b572208e6b..eaa9fbad11 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -1464,6 +1464,9 @@ class TestGem < Gem::TestCase end def test_load_user_installed_plugins + @orig_gem_home = ENV["GEM_HOME"] + ENV["GEM_HOME"] = @gemhome + plugin_path = File.join "lib", "rubygems_plugin.rb" Dir.chdir @tempdir do @@ -1486,6 +1489,8 @@ class TestGem < Gem::TestCase Gem.load_plugins assert_equal %w[plugin], PLUGINS_LOADED + ensure + ENV["GEM_HOME"] = @orig_gem_home end def test_load_env_plugins @@ -1654,6 +1659,30 @@ class TestGem < Gem::TestCase ENV["SOURCE_DATE_EPOCH"] = old_epoch end + def test_data_home_default + expected = File.join(@userhome, ".local", "share") + assert_equal expected, Gem.data_home + end + + def test_data_home_from_env + ENV["XDG_DATA_HOME"] = expected = "/test/data/home" + assert_equal expected, Gem.data_home + end + + def test_state_home_default + Gem.instance_variable_set :@state_home, nil + Gem.data_home # memoize @data_home, to demonstrate GH-6418 + expected = File.join(@userhome, ".local", "state") + assert_equal expected, Gem.state_home + end + + def test_state_home_from_env + Gem.instance_variable_set :@state_home, nil + Gem.data_home # memoize @data_home, to demonstrate GH-6418 + ENV["XDG_STATE_HOME"] = expected = "/test/state/home" + assert_equal expected, Gem.state_home + end + private def ruby_install_name(name) diff --git a/test/rubygems/test_gem_bundler_version_finder.rb b/test/rubygems/test_gem_bundler_version_finder.rb index fd61000b8a..2f5094c526 100644 --- a/test/rubygems/test_gem_bundler_version_finder.rb +++ b/test/rubygems/test_gem_bundler_version_finder.rb @@ -4,10 +4,10 @@ require "rubygems/bundler_version_finder" class TestGemBundlerVersionFinder < Gem::TestCase def setup - super - @argv = ARGV.dup @dollar_0 = $0 + super + without_any_upwards_gemfiles end diff --git a/test/rubygems/test_gem_command_manager.rb b/test/rubygems/test_gem_command_manager.rb index 6b34156187..8108dc0870 100644 --- a/test/rubygems/test_gem_command_manager.rb +++ b/test/rubygems/test_gem_command_manager.rb @@ -371,4 +371,29 @@ class TestGemCommandManager < Gem::TestCase ensure Gem::Commands.send(:remove_const, :FooCommand) end + + def test_deprecated_command_with_version + require "rubygems/command" + foo_command = Class.new(Gem::Command) do + extend Gem::Deprecate + + rubygems_deprecate_command("9.9.9") + + def execute + say "pew pew!" + end + end + + Gem::Commands.send(:const_set, :FooCommand, foo_command) + @command_manager.register_command(:foo, foo_command.new("foo")) + + use_ui @ui do + @command_manager.process_args(%w[foo]) + end + + assert_equal "pew pew!\n", @ui.output + assert_match(/WARNING: foo command is deprecated. It will be removed in Rubygems 9.9.9/, @ui.error) + ensure + Gem::Commands.send(:remove_const, :FooCommand) + end end diff --git a/test/rubygems/test_gem_commands_exec_command.rb b/test/rubygems/test_gem_commands_exec_command.rb new file mode 100644 index 0000000000..8be349742b --- /dev/null +++ b/test/rubygems/test_gem_commands_exec_command.rb @@ -0,0 +1,851 @@ +# frozen_string_literal: true +require_relative "helper" +require "rubygems/commands/exec_command" + +class TestGemCommandsExecCommand < Gem::TestCase + def setup + @orig_args = Gem::Command.build_args + @orig_specific_extra_args = Gem::Command.specific_extra_args_hash.dup + @orig_extra_args = Gem::Command.extra_args.dup + + super + common_installer_setup + + @cmd = Gem::Commands::ExecCommand.new + + @gem_home = Gem.dir + @gem_path = Gem.path + @test_arch = RbConfig::CONFIG["arch"] + + @installed_specs = [] + Gem.post_install {|installer| @installed_specs << installer.spec } + end + + def teardown + super + + common_installer_teardown + + Gem::Command.build_args = @orig_args + Gem::Command.specific_extra_args_hash = @orig_specific_extra_args + Gem::Command.extra_args = @orig_extra_args + Gem.configuration = nil + end + + def invoke(*args) + @ui.outs.truncate(0) + @ui.outs.rewind + @ui.errs.truncate(0) + @ui.errs.rewind + @installed_specs.clear + + @cmd.invoke(*args) + ensure + Gem::Specification.unresolved_deps.clear + Gem.loaded_specs.clear + Gem.instance_variable_set(:@activated_gem_paths, 0) + Gem.clear_default_specs + Gem.use_paths(@gem_home, @gem_path) + Gem.refresh + end + + def test_error_with_no_arguments + e = assert_raise Gem::CommandLineError do + @cmd.invoke + end + assert_equal "Please specify an executable to run (e.g. gem exec COMMAND)", + e.message + end + + def test_error_with_no_executable + e = assert_raise Gem::CommandLineError do + @cmd.invoke "--verbose", "--gem", "GEM", "--version", "< 10", "--conservative" + end + assert_equal "Please specify an executable to run (e.g. gem exec COMMAND)", + e.message + end + + def test_full_option_parsing + @cmd.when_invoked do |options| + assert_equal options, { + args: ["install", "--no-color", "--help", "--verbose"], + executable: "pod", + :explicit_prerelease => false, + gem_name: "cocoapods", + prerelease: false, + :version => Gem::Requirement.new(["> 1", "< 1.3"]), + build_args: nil, + } + end + @cmd.invoke "--gem", "cocoapods", "-v", "> 1", "--version", "< 1.3", "--verbose", "--", "pod", "install", "--no-color", "--help", "--verbose" + end + + def test_single_arg_parsing + @cmd.when_invoked do |options| + assert_equal options, { + args: [], + executable: "rails", + gem_name: "rails", + :version => Gem::Requirement.new([">= 0"]), + build_args: nil, + } + end + @cmd.invoke "rails" + end + + def test_single_arg_parsing_with_version + @cmd.when_invoked do |options| + assert_equal options, { + args: [], + executable: "rails", + gem_name: "rails", + :version => Gem::Requirement.new(["= 7.1"]), + build_args: nil, + } + end + @cmd.invoke "rails:7.1" + end + + def test_gem_without_executable + spec_fetcher do |fetcher| + fetcher.gem "a", 2 + end + + util_clear_gems + + use_ui @ui do + e = assert_raise Gem::MockGemUi::TermError, @ui.error do + @cmd.invoke "a:2" + end + assert_equal 1, e.exit_code + assert_equal "ERROR: Failed to load executable `a`, are you sure the gem `a` contains it?\n", @ui.error + end + end + + def test_gem_with_executable + spec_fetcher do |fetcher| + fetcher.gem "a", 2 do |s| + s.executables = %w[a] + s.files = %w[bin/a lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << "Gem.ui.say #{s.original_name.dump}" + end + end + end + + util_clear_gems + + use_ui @ui do + @cmd.invoke "a:2" + assert_equal "a-2\n", @ui.output + end + end + + def test_gem_with_platforms + spec_fetcher do |fetcher| + fetcher.download "a", 2 do |s| + s.executables = %w[a] + s.files = %w[bin/a lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << "Gem.ui.say #{s.original_name.dump}" + end + end + + fetcher.download "a", 2 do |s| + s.executables = %w[a] + s.files = %w[bin/a lib/a.rb] + s.platform = "x86_64-darwin" + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << "Gem.ui.say #{s.original_name.dump}" + end + end + end + + use_ui @ui do + invoke "a:2" + assert_equal "a-2\n", @ui.output + end + + use_ui @ui do + util_set_arch "x86_64-darwin-18" + invoke "a:2" + assert_equal "a-2-x86_64-darwin\n", @ui.output + end + end + + def test_gem_with_platform_dependencies + spec_fetcher do |fetcher| + fetcher.download "a", 2 do |s| + s.executables = %w[a] + s.files = %w[bin/a lib/a.rb] + s.add_runtime_dependency "with_platform" + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << 'require "with_platform"' << "\n" + f << 'Gem.ui.say Gem.loaded_specs.each_value.map(&:original_name).sort.join("\n")' + end + end + + fetcher.download "with_platform", 2 do |s| + s.files = %w[lib/with_platform.rb] + s.platform = Gem::Platform.local + end + + fetcher.download "with_platform", 2 do |s| + s.files = %w[lib/with_platform.rb] + end + end + + use_ui @ui do + util_set_arch "unknown-unknown" + invoke "a" + assert_equal "a-2\nwith_platform-2\n", @ui.output + end + + use_ui @ui do + util_set_arch @test_arch + invoke "a" + assert_empty @ui.error + assert_equal "a-2\nwith_platform-2-#{Gem::Platform.local}\n", @ui.output + end + end + + def test_gem_with_platform_and_platform_dependencies + pend "extensions don't quite work on jruby" if Gem.java_platform? + + spec_fetcher do |fetcher| + fetcher.download "a", 2 do |s| + s.executables = %w[a] + s.files = %w[bin/a lib/a.rb] + s.add_runtime_dependency "with_platform" + s.platform = Gem::Platform.local.to_s + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << 'require "with_platform"' << "\n" + f << 'Gem.ui.say Gem.loaded_specs.each_value.map(&:original_name).sort.join("\n")' + end + end + + fetcher.download "a", 2 do |s| + s.executables = %w[a] + s.files = %w[bin/a lib/a.rb extconf.rb] + s.add_runtime_dependency "with_platform" + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << 'require "with_platform"' << "\n" + f << 'Gem.ui.say Gem.loaded_specs.each_value.map(&:original_name).sort.join("\n")' + end + + s.extensions = %w[extconf.rb] + write_file File.join(*%W[gems #{s.original_name} extconf.rb]) do |f| + f.write <<-RUBY + gem('with_platform', '~> 2.0') + require 'with_platform' + gem 'sometimes_used' + require 'sometimes_used' + require "mkmf" + create_makefile("#{s.name}") + RUBY + end + end + + fetcher.download "with_platform", 2 do |s| + s.files = %w[lib/with_platform.rb] + s.platform = Gem::Platform.local.to_s + end + + fetcher.download "with_platform", 2 do |s| + s.files = %w[lib/with_platform.rb] + s.add_runtime_dependency "sometimes_used" + end + + fetcher.download "sometimes_used", 2 do |s| + s.files = %w[lib/sometimes_used.rb] + end + end + + use_ui @ui do + util_set_arch "unknown-unknown" + invoke "a" + assert_empty @ui.error + assert_equal "Building native extensions. This could take a while...\na-2\nsometimes_used-2\nwith_platform-2\n", @ui.output + end + + use_ui @ui do + util_set_arch @test_arch + invoke "a" + assert_empty @ui.error + assert_equal "a-2-#{Gem::Platform.local}\nwith_platform-2-#{Gem::Platform.local}\n", @ui.output + end + end + + def test_gem_with_other_executable_name + spec_fetcher do |fetcher| + fetcher.gem "a", 2 do |s| + s.executables = %w[foo] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump}" + end + end + end + + util_clear_gems + + use_ui @ui do + @cmd.invoke "a:2" + assert_equal "a-2\n", @ui.output + end + end + + def test_gem_with_executable_error + spec_fetcher do |fetcher| + fetcher.gem "a", 2 do |s| + s.executables = %w[foo] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "raise #{s.original_name.dump}" + end + end + end + + util_clear_gems + + use_ui @ui do + e = assert_raise RuntimeError do + @cmd.invoke "a:2" + end + assert_equal "a-2", e.message + assert_empty @ui.error + end + end + + def test_gem_with_multiple_executables_one_match + spec_fetcher do |fetcher| + fetcher.gem "a", 2 do |s| + s.executables = %w[foo a] + s.files = %w[bin/foo bin/a lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + util_clear_gems + + use_ui @ui do + @cmd.invoke "a:2" + assert_equal "a-2 a\n", @ui.output + end + end + + def test_gem_with_multiple_executables_no_match + spec_fetcher do |fetcher| + fetcher.gem "a", 2 do |s| + s.executables = %w[foo bar] + s.files = %w[bin/foo bin/bar lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + + write_file File.join(*%W[gems #{s.original_name} bin bar]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + util_clear_gems + + use_ui @ui do + @cmd.invoke "a:2" + assert_equal "a-2 foo\n", @ui.output + end + end + + def test_gem_dependency_contains_executable + spec_fetcher do |fetcher| + fetcher.gem "a", 2 do |s| + s.executables = %w[] + s.files = %w[lib/a.rb] + + s.add_dependency "b" + end + + fetcher.gem "b", 2 do |s| + s.executables = %w[a] + s.files = %w[bin/a lib/b.rb] + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + util_clear_gems + + use_ui @ui do + @cmd.invoke "a:2" + assert_equal "b-2 a\n", @ui.output + end + end + + def test_gem_dependency_contains_other_executable + spec_fetcher do |fetcher| + fetcher.gem "a", 2 do |s| + s.executables = %w[] + s.files = %w[lib/a.rb] + + s.add_dependency "b" + end + + fetcher.gem "b", 2 do |s| + s.executables = %w[foo] + s.files = %w[bin/foo lib/b.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + util_clear_gems + + use_ui @ui do + e = assert_raise Gem::MockGemUi::TermError do + @cmd.invoke "a:2" + end + assert_equal 1, e.exit_code + assert_equal <<~ERR, @ui.error + ERROR: Failed to load executable `a`, are you sure the gem `a` contains it? + ERR + end + end + + def test_other_gem_contains_executable + spec_fetcher do |fetcher| + fetcher.gem "a", 2 do |s| + s.executables = %w[] + s.files = %w[lib/a.rb] + end + + fetcher.gem "b", 2 do |s| + s.executables = %w[a] + s.files = %w[bin/a lib/b.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + util_clear_gems + + use_ui @ui do + e = assert_raise Gem::MockGemUi::TermError do + @cmd.invoke "a:2" + end + assert_equal 1, e.exit_code + assert_equal <<~ERR, @ui.error + ERROR: Failed to load executable `a`, are you sure the gem `a` contains it? + ERR + end + end + + def test_missing_gem + spec_fetcher do |fetcher| + end + + use_ui @ui do + e = assert_raise Gem::MockGemUi::TermError do + @cmd.invoke "a" + end + assert_equal 2, e.exit_code + assert_equal <<~ERR, @ui.error + ERROR: Could not find a valid gem 'a' (>= 0) in any repository + ERR + end + end + + def test_version_mismatch + spec_fetcher do |fetcher| + fetcher.gem "a", 1 + end + + util_clear_gems + + use_ui @ui do + e = assert_raise Gem::MockGemUi::TermError do + @cmd.invoke "a:2" + end + assert_equal 2, e.exit_code + assert_equal <<~ERR, @ui.error + ERROR: Could not find a valid gem 'a' (= 2) in any repository + ERROR: Possible alternatives: a + ERR + end + end + + def test_pre_argument + spec_fetcher do |fetcher| + fetcher.gem "a", 1 do |s| + s.executables = %w[foo] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + fetcher.gem "a", "1.1.a" do |s| + s.executables = %w[foo ] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + util_clear_gems + + use_ui @ui do + @cmd.invoke "--pre", "a" + assert_equal "a-1.1.a foo\n", @ui.output + end + end + + def test_pre_version_option + spec_fetcher do |fetcher| + fetcher.download "a", 1 do |s| + s.executables = %w[foo] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + fetcher.download "a", "1.1.a" do |s| + s.executables = %w[foo ] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + use_ui @ui do + @cmd.invoke "-v", ">= 0.a", "a" + assert_equal "a-1.1.a foo\n", @ui.output + end + end + + def test_conservative_missing_gem + spec_fetcher do |fetcher| + fetcher.gem "a", 1 do |s| + s.executables = %w[foo] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + util_clear_gems + + use_ui @ui do + e = assert_raise Gem::MockGemUi::TermError do + @cmd.invoke "--verbose", "--conservative", "a:2" + end + assert_equal 2, e.exit_code + assert_include @ui.output, "a (= 2) not available locally" + assert_equal <<~ERROR, @ui.error + ERROR: Could not find a valid gem 'a' (= 2) in any repository + ERROR: Possible alternatives: a + ERROR + end + end + + def test_conservative + spec_fetcher do |fetcher| + fetcher.download "a", 1 do |s| + s.executables = %w[foo] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + use_ui @ui do + invoke "--verbose", "--conservative", "a" + assert_include @ui.output, "a (>= 0) not available locally" + assert_include @ui.output, "a-1 foo" + assert_equal %w[a-1], @installed_specs.map(&:original_name) + end + + spec_fetcher do |fetcher| + fetcher.gem "a", 1 do |s| + s.executables = %w[foo] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + + fetcher.download "a", 2 do |s| + s.executables = %w[foo] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + use_ui @ui do + invoke "--verbose", "--conservative", "a" + assert_not_include @ui.output, "a (>= 0) not available locally" + assert_include @ui.output, "a-1 foo" + assert_empty @installed_specs.map(&:original_name) + end + end + + def test_uses_newest_version + spec_fetcher do |fetcher| + fetcher.download "a", 1 do |s| + s.executables = %w[foo] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + use_ui @ui do + invoke "a" + assert_include @ui.output, "a-1 foo" + end + + spec_fetcher do |fetcher| + fetcher.download "a", 1 do |s| + s.executables = %w[foo] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + + fetcher.download "a", 2 do |s| + s.executables = %w[foo] + s.files = %w[bin/foo lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin foo]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + use_ui @ui do + invoke "--verbose", "a:2" + refute_predicate @ui, :terminated? + assert_empty @ui.error + assert_include @ui.output, "a-2 foo" + assert_equal %w[a-2], @installed_specs.map(&:original_name) + end + end + + def test_uses_newest_version_of_dependency + spec_fetcher do |fetcher| + fetcher.gem "a", 1 do |s| + s.executables = %w[] + s.files = %w[lib/a.rb] + s.add_runtime_dependency "b" + end + + fetcher.gem "b", 1 do |s| + s.executables = %w[a] + s.files = %w[bin/a lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + + fetcher.download "b", 2 do |s| + s.executables = %w[a] + s.files = %w[bin/a lib/a.rb] + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + use_ui @ui do + invoke "a" + assert_include @ui.output, "b-2 a" + assert_equal %w[b-2], @installed_specs.map(&:original_name) + end + end + + def test_gem_exec_gem_uninstall + spec_fetcher do |fetcher| + fetcher.download "a", 2 do |s| + s.executables = %w[a] + s.files = %w[bin/a lib/a.rb] + s.add_runtime_dependency "b" + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << "Gem.ui.say #{s.original_name.dump}" + end + end + + fetcher.download "b", 2 do |s| + s.files = %w[lib/b.rb] + end + end + + use_ui @ui do + invoke "a:2" + assert_equal "a-2\n", @ui.output + + invoke "gem", "list", "--local" + assert_includes @ui.output, "a (2)\n" + assert_includes @ui.output, "b (2)\n" + + invoke "gem", "uninstall", "--verbose", "-x", "a" rescue nil + refute_includes @ui.output, "running gem exec with" + assert_includes @ui.output, "Successfully uninstalled a-2\n" + + invoke "--verbose", "gem", "uninstall", "b" + assert_includes @ui.output, "Successfully uninstalled b-2\n" + + invoke "gem", "list", "--local" + assert_empty @ui.error + assert_match(/\A\s*\** LOCAL GEMS \**\s*\z/m, @ui.output) + + invoke "gem", "env", "GEM_HOME" + assert_equal "#{@gem_home}/gem_exec\n", @ui.output + end + end + + def test_only_prerelease_available + spec_fetcher do |fetcher| + fetcher.download "a", "1.a" do |s| + s.executables = %w[a] + s.files = %w[lib/a.rb bin/a] + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + use_ui @ui do + assert_raise Gem::MockGemUi::TermError do + invoke "a" + end + assert_equal "ERROR: Could not find a valid gem 'a' (>= 0) in any repository\n" + + "ERROR: Possible alternatives: a\n", @ui.error + assert_empty @ui.output + assert_empty @installed_specs + end + + use_ui @ui do + invoke "a:1.a" + assert_empty @ui.error + assert_equal "a-1.a a\n", @ui.output + assert_equal %w[a-1.a], @installed_specs.map(&:full_name) + end + + FileUtils.rm_rf Gem.dir + + use_ui @ui do + invoke "--version", ">= 1.a", "a" + assert_empty @ui.error + assert_equal "a-1.a a\n", @ui.output + assert_equal %w[a-1.a], @installed_specs.map(&:full_name) + end + + FileUtils.rm_rf Gem.dir + + use_ui @ui do + invoke "--pre", "a" + assert_empty @ui.error + assert_equal "a-1.a a\n", @ui.output + assert_equal %w[a-1.a], @installed_specs.map(&:full_name) + end + end + + def test_newer_prerelease_available + spec_fetcher do |fetcher| + fetcher.download "a", "1" do |s| + s.executables = %w[a] + s.files = %w[lib/a.rb bin/a] + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + + fetcher.download "a", "1.1.a" do |s| + s.executables = %w[a] + s.files = %w[lib/a.rb bin/a] + + write_file File.join(*%W[gems #{s.original_name} bin a]) do |f| + f << "Gem.ui.say #{s.original_name.dump} + ' ' + File.basename(__FILE__)" + end + end + end + + use_ui @ui do + invoke "a" + assert_empty @ui.error + assert_equal "a-1 a\n", @ui.output + assert_equal %w[a-1], @installed_specs.map(&:full_name) + end + + FileUtils.rm_rf Gem.dir + + use_ui @ui do + invoke "a:1.1.a" + assert_empty @ui.error + assert_equal "a-1.1.a a\n", @ui.output + assert_equal %w[a-1.1.a], @installed_specs.map(&:full_name) + end + + FileUtils.rm_rf Gem.dir + + use_ui @ui do + invoke "--version", ">= 1.a", "a" + assert_empty @ui.error + assert_equal "a-1.1.a a\n", @ui.output + assert_equal %w[a-1.1.a], @installed_specs.map(&:full_name) + end + + FileUtils.rm_rf Gem.dir + + use_ui @ui do + invoke "--pre", "a" + assert_empty @ui.error + assert_equal "a-1.1.a a\n", @ui.output + assert_equal %w[a-1.1.a], @installed_specs.map(&:full_name) + end + end +end diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb index 14bddec485..686be17c7c 100644 --- a/test/rubygems/test_gem_commands_install_command.rb +++ b/test/rubygems/test_gem_commands_install_command.rb @@ -7,6 +7,7 @@ require "rubygems/rdoc" class TestGemCommandsInstallCommand < Gem::TestCase def setup + @orig_args = Gem::Command.build_args super common_installer_setup @@ -14,7 +15,6 @@ class TestGemCommandsInstallCommand < Gem::TestCase @cmd.options[:document] = [] @gemdeps = "tmp_install_gemdeps" - @orig_args = Gem::Command.build_args common_installer_setup end diff --git a/test/rubygems/test_gem_commands_pristine_command.rb b/test/rubygems/test_gem_commands_pristine_command.rb index 5bf1d27eb9..db1583bcd3 100644 --- a/test/rubygems/test_gem_commands_pristine_command.rb +++ b/test/rubygems/test_gem_commands_pristine_command.rb @@ -202,6 +202,54 @@ class TestGemCommandsPristineCommand < Gem::TestCase assert_empty out, out.inspect end + def test_execute_extensions_only_missing_extensions + a = util_spec "a" do |s| + s.extensions << "ext/a/extconf.rb" + end + + ext_path = File.join @tempdir, "ext", "a", "extconf.rb" + write_file ext_path do |io| + io.write <<-'RUBY' + File.open "Makefile", "w" do |f| + f.puts "clean:\n\techo cleaned\n" + f.puts "all:\n\techo built\n" + f.puts "install:\n\techo installed\n" + end + RUBY + end + + b = util_spec "b" do |s| + s.extensions << "ext/b/extconf.rb" + end + + ext_path = File.join @tempdir, "ext", "b", "extconf.rb" + write_file ext_path do |io| + io.write <<-'RUBY' + File.open "Makefile", "w" do |f| + f.puts "clean:\n\techo cleaned\n" + f.puts "all:\n\techo built\n" + f.puts "install:\n\techo installed\n" + end + RUBY + end + + install_gem a + install_gem b + + # Remove the extension files for b + FileUtils.rm_rf b.gem_build_complete_path + + @cmd.options[:only_missing_extensions] = true + @cmd.options[:args] = [] + + use_ui @ui do + @cmd.execute + end + + refute_includes @ui.output, "Restored #{a.full_name}" + assert_includes @ui.output, "Restored #{b.full_name}" + end + def test_execute_no_extension a = util_spec "a" do |s| s.extensions << "ext/a/extconf.rb" diff --git a/test/rubygems/test_gem_commands_setup_command.rb b/test/rubygems/test_gem_commands_setup_command.rb index dd688a4421..02c6a1b957 100644 --- a/test/rubygems/test_gem_commands_setup_command.rb +++ b/test/rubygems/test_gem_commands_setup_command.rb @@ -8,7 +8,7 @@ class TestGemCommandsSetupCommand < Gem::TestCase if File.exist?(bundler_gemspec) BUNDLER_VERS = File.read(bundler_gemspec).match(/VERSION = "(#{Gem::Version::VERSION_PATTERN})"/)[1] else - BUNDLER_VERS = "2.0.1".freeze + BUNDLER_VERS = "2.0.1" end def setup diff --git a/test/rubygems/test_gem_commands_uninstall_command.rb b/test/rubygems/test_gem_commands_uninstall_command.rb index 083b831c98..e5090c852f 100644 --- a/test/rubygems/test_gem_commands_uninstall_command.rb +++ b/test/rubygems/test_gem_commands_uninstall_command.rb @@ -22,12 +22,7 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase a_4, = util_gem "a", 4 install_gem a_4, :install_dir => gemhome2 - Gem::Specification.dirs = [@gemhome, gemhome2] - - assert_includes Gem::Specification.all_names, "a-1" - assert_includes Gem::Specification.all_names, "a-4" - assert_includes Gem::Specification.all_names, "b-2" - assert_includes Gem::Specification.all_names, "default-1" + assert_gems_presence "a-1", "a-4", "b-2", "default-1", dirs: [@gemhome, gemhome2] @cmd.options[:all] = true @cmd.options[:args] = %w[a] @@ -346,11 +341,7 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase a_4, = util_gem "a", 4 install_gem a_4 - Gem::Specification.dirs = [@gemhome, gemhome2] - - assert_includes Gem::Specification.all_names, "a-1" - assert_includes Gem::Specification.all_names, "a-4" - assert_includes Gem::Specification.all_names, "default-1" + assert_gems_presence "a-1", "a-4", "default-1", dirs: [@gemhome, gemhome2] @cmd.options[:all] = true @cmd.options[:args] = [] @@ -371,9 +362,7 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase a_4, = util_gem "a", 4 install_gem a_4 , :install_dir => gemhome2 - Gem::Specification.dirs = [@gemhome, gemhome2] - - assert_includes Gem::Specification.all_names, "a-4" + assert_gems_presence "a-4", dirs: [@gemhome, gemhome2] @cmd.options[:args] = ["a:4"] @@ -386,6 +375,26 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase assert_includes e.message, "a is not installed in GEM_HOME" end + def test_execute_outside_gem_home_when_install_dir_given + gemhome2 = "#{@gemhome}2" + + a_4, = util_gem "a", 4 + install_gem a_4 , :install_dir => gemhome2 + + assert_gems_presence "a-4", dirs: [@gemhome, gemhome2] + + Gem::Specification.dirs = [@gemhome] + + @cmd.options[:install_dir] = gemhome2 + @cmd.options[:args] = ["a:4"] + + @cmd.execute + + Gem::Specification.dirs = [gemhome2] + + refute_includes Gem::Specification.all_names.sort, "a-4" + end + def test_handle_options @cmd.handle_options %w[] @@ -501,4 +510,12 @@ WARNING: Use your OS package manager to uninstall vendor gems end end end + + def assert_gems_presence(*gems, dirs:) + Gem::Specification.dirs = dirs + + gems.each do |full_name| + assert_includes Gem::Specification.all_names, full_name + end + end end diff --git a/test/rubygems/test_gem_ext_builder.rb b/test/rubygems/test_gem_ext_builder.rb index 34d8903595..dcf02156df 100644 --- a/test/rubygems/test_gem_ext_builder.rb +++ b/test/rubygems/test_gem_ext_builder.rb @@ -5,6 +5,8 @@ require "rubygems/installer" class TestGemExtBuilder < Gem::TestCase def setup + @orig_DESTDIR = ENV["DESTDIR"] + @orig_make = ENV["make"] super @ext = File.join @tempdir, "ext" @@ -13,19 +15,15 @@ class TestGemExtBuilder < Gem::TestCase FileUtils.mkdir_p @ext FileUtils.mkdir_p @dest_path - @orig_DESTDIR = ENV["DESTDIR"] - @orig_make = ENV["make"] - @spec = util_spec "a" @builder = Gem::Ext::Builder.new @spec, "" end def teardown + super ENV["DESTDIR"] = @orig_DESTDIR ENV["make"] = @orig_make - - super end def test_class_make diff --git a/test/rubygems/test_gem_ext_cargo_builder.rb b/test/rubygems/test_gem_ext_cargo_builder.rb index df50150d50..3230ff3f21 100644 --- a/test/rubygems/test_gem_ext_cargo_builder.rb +++ b/test/rubygems/test_gem_ext_cargo_builder.rb @@ -87,7 +87,7 @@ class TestGemExtCargoBuilder < Gem::TestCase end end - assert_match /cargo\s.*\sfailed/, error.message + assert_match(/cargo\s.*\sfailed/, error.message) end def test_full_integration diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock index 6162043fa1..4e2719fcba 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock @@ -135,31 +135,34 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] [[package]] name = "rb-sys" -version = "0.9.54" +version = "0.9.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3277448b8eee18de8bedb18883ae02dcd60d47922ddfc6ab408def77da0a9b4" +checksum = "e8fe617bad8e88fd7e5d6f432e35f09e5f94144dfb8e8ee4adde82fb920dc59b" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.54" +version = "0.9.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9baae802c93180af02cccb21819589d109070f8e28e14e7070a9ffdeca9b464" +checksum = "007e63597f91c711cbb299e60fecbdb6f5ad4a066d6a20c81943893f1584c895" dependencies = [ "bindgen", + "lazy_static", + "quote", "regex", "shell-words", + "syn", ] [[package]] @@ -205,6 +208,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] name = "unicode-ident" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml index b2f56a9197..91627ada0f 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -rb-sys = "0.9.54" +rb-sys = "0.9.65" diff --git a/test/rubygems/test_gem_gem_runner.rb b/test/rubygems/test_gem_gem_runner.rb index f0128febc8..30b67280a9 100644 --- a/test/rubygems/test_gem_gem_runner.rb +++ b/test/rubygems/test_gem_gem_runner.rb @@ -3,13 +3,16 @@ require_relative "helper" class TestGemGemRunner < Gem::TestCase def setup - super + @orig_gem_home = ENV["GEM_HOME"] + ENV["GEM_HOME"] = @gemhome require "rubygems/command" @orig_args = Gem::Command.build_args @orig_specific_extra_args = Gem::Command.specific_extra_args_hash.dup @orig_extra_args = Gem::Command.extra_args.dup + super + require "rubygems/gem_runner" @runner = Gem::GemRunner.new end @@ -20,6 +23,8 @@ class TestGemGemRunner < Gem::TestCase Gem::Command.build_args = @orig_args Gem::Command.specific_extra_args_hash = @orig_specific_extra_args Gem::Command.extra_args = @orig_extra_args + + ENV["GEM_HOME"] = @orig_gem_home end def test_do_configuration diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb index eebe4d86d0..9d6215838d 100644 --- a/test/rubygems/test_gem_package.rb +++ b/test/rubygems/test_gem_package.rb @@ -1187,29 +1187,4 @@ class TestGemPackage < Gem::Package::TarTestCase assert_equal %w[lib/code.rb], package.contents end - - def util_tar - tar_io = StringIO.new - - Gem::Package::TarWriter.new tar_io do |tar| - yield tar - end - - tar_io.rewind - - tar_io - end - - def util_tar_gz(&block) - tar_io = util_tar(&block) - - tgz_io = StringIO.new - - # can't wrap TarWriter because it seeks - Zlib::GzipWriter.wrap tgz_io do |io| - io.write tar_io.string - end - - StringIO.new tgz_io.string - end end diff --git a/test/rubygems/test_gem_package_tar_reader.rb b/test/rubygems/test_gem_package_tar_reader.rb index 19860eb7e8..62e60489a6 100644 --- a/test/rubygems/test_gem_package_tar_reader.rb +++ b/test/rubygems/test_gem_package_tar_reader.rb @@ -56,12 +56,14 @@ class TestGemPackageTarReader < Gem::Package::TarTestCase io = TempIO.new tar Gem::Package::TarReader.new io do |tar_reader| - tar_reader.seek "baz/bar" do |entry| + retval = tar_reader.seek "baz/bar" do |entry| assert_kind_of Gem::Package::TarReader::Entry, entry assert_equal "baz/bar", entry.full_name + entry.read end + assert_equal "", retval assert_equal 0, io.pos end ensure @@ -84,4 +86,49 @@ class TestGemPackageTarReader < Gem::Package::TarTestCase ensure io.close! end + + def test_read_in_gem_data + gem_tar = util_gem_data_tar do |tar| + tar.add_file "lib/code.rb", 0444 do |io| + io.write "# lib/code.rb" + end + end + + count = 0 + Gem::Package::TarReader.new(gem_tar).each do |entry| + next unless entry.full_name == "data.tar.gz" + + Zlib::GzipReader.wrap entry do |gzio| + Gem::Package::TarReader.new(gzio).each do |contents_entry| + assert_equal "# lib/code.rb", contents_entry.read + count += 1 + end + end + end + + assert_equal 1, count, "should have found one file" + end + + def test_seek_in_gem_data + gem_tar = util_gem_data_tar do |tar| + tar.add_file "lib/code.rb", 0444 do |io| + io.write "# lib/code.rb" + end + tar.add_file "lib/foo.rb", 0444 do |io| + io.write "# lib/foo.rb" + end + end + + count = 0 + Gem::Package::TarReader.new(gem_tar).seek("data.tar.gz") do |entry| + Zlib::GzipReader.wrap entry do |gzio| + Gem::Package::TarReader.new(gzio).seek("lib/foo.rb") do |contents_entry| + assert_equal "# lib/foo.rb", contents_entry.read + count += 1 + end + end + end + + assert_equal 1, count, "should have found one file" + end end diff --git a/test/rubygems/test_gem_package_tar_reader_entry.rb b/test/rubygems/test_gem_package_tar_reader_entry.rb index ffb4542178..04a4579f9a 100644 --- a/test/rubygems/test_gem_package_tar_reader_entry.rb +++ b/test/rubygems/test_gem_package_tar_reader_entry.rb @@ -10,8 +10,7 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase @tar = String.new @tar << tar_file_header("lib/foo", "", 0, @contents.size, Time.now) - @tar << @contents - @tar << "\0" * (512 - (@tar.size % 512)) + @tar << tar_file_contents(@contents) @entry = util_entry @tar end @@ -21,8 +20,35 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase super end - def close_util_entry(entry) - entry.instance_variable_get(:@io).close! + def test_open + io = TempIO.new @tar + header = Gem::Package::TarHeader.from io + retval = Gem::Package::TarReader::Entry.open header, io do |entry| + entry.getc + end + assert_equal "a", retval + assert_equal @tar.size, io.pos, "should have read to end of entry" + end + + def test_open_closes_entry + io = TempIO.new @tar + header = Gem::Package::TarHeader.from io + entry = nil + Gem::Package::TarReader::Entry.open header, io do |e| + entry = e + end + assert entry.closed? + assert_raise(IOError) { entry.getc } + end + + def test_open_returns_entry + io = TempIO.new @tar + header = Gem::Package::TarHeader.from io + entry = Gem::Package::TarReader::Entry.open header, io + refute entry.closed? + assert_equal ?a, entry.getc + assert_nil entry.close + assert entry.closed? end def test_bytes_read @@ -125,6 +151,18 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase assert_equal @contents, @entry.read end + def test_consecutive_read + expected = StringIO.new(@contents) + assert_equal expected.read, @entry.read + assert_equal expected.read, @entry.read + end + + def test_consecutive_read_bytes_past_eof + expected = StringIO.new(@contents) + assert_equal expected.read, @entry.read + assert_equal expected.read(1), @entry.read(1) + end + def test_read_big assert_equal @contents, @entry.read(@contents.size * 2) end @@ -133,9 +171,24 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase assert_equal @contents[0...100], @entry.read(100) end - def test_readpartial + def test_read_remaining + @entry.read(100) + assert_equal @contents[100..-1], @entry.read + end + + def test_read_partial + assert_equal @contents[0...100], @entry.readpartial(100) + end + + def test_read_partial_buffer + buffer = "".b + @entry.readpartial(100, buffer) + assert_equal @contents[0...100], buffer + end + + def test_readpartial_past_eof + @entry.readpartial(@contents.size) assert_raise(EOFError) do - @entry.read(@contents.size) @entry.readpartial(1) end end @@ -149,4 +202,96 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase assert_equal char, @entry.getc end + + def test_seek + @entry.seek(50) + assert_equal 50, @entry.pos + assert_equal @contents[50..-1], @entry.read, "read remaining after seek" + @entry.seek(-50, IO::SEEK_CUR) + assert_equal @contents.size - 50, @entry.pos + assert_equal @contents[-50..-1], @entry.read, "read after stepping back 50 from the end" + @entry.seek(0, IO::SEEK_SET) + assert_equal 0, @entry.pos + assert_equal @contents, @entry.read, "read from beginning" + @entry.seek(-10, IO::SEEK_END) + assert_equal @contents.size - 10, @entry.pos + assert_equal @contents[-10..-1], @entry.read, "read from end" + end + + def test_read_zero + expected = StringIO.new("") + assert_equal expected.read(0), @entry.read(0) + end + + def test_readpartial_zero + expected = StringIO.new("") + assert_equal expected.readpartial(0), @entry.readpartial(0) + end + + def test_zero_byte_file_read + zero_entry = util_entry(tar_file_header("foo", "", 0, 0, Time.now)) + expected = StringIO.new("") + assert_equal expected.read, zero_entry.read + end + + def test_zero_byte_file_readpartial + zero_entry = util_entry(tar_file_header("foo", "", 0, 0, Time.now)) + expected = StringIO.new("") + assert_equal expected.readpartial(0), zero_entry.readpartial(0) + end + + def test_read_from_gzip_io + tgz = util_gzip(@tar) + + Zlib::GzipReader.wrap StringIO.new(tgz) do |gzio| + entry = util_entry(gzio) + assert_equal @contents, entry.read + entry.rewind + assert_equal @contents, entry.read, "second read after rewind should read same contents" + end + end + + def test_read_from_gzip_io_with_non_zero_offset + contents2 = ("0".."9").to_a.join * 100 + @tar << tar_file_header("lib/bar", "", 0, contents2.size, Time.now) + @tar << tar_file_contents(contents2) + + tgz = util_gzip(@tar) + + Zlib::GzipReader.wrap StringIO.new(tgz) do |gzio| + util_entry(gzio).close # skip the first entry so io.pos is not 0, preventing easy rewind + entry = util_entry(gzio) + + assert_equal contents2, entry.read + entry.rewind + assert_equal contents2, entry.read, "second read after rewind should read same contents" + end + end + + def test_seek_in_gzip_io_with_non_zero_offset + contents2 = ("0".."9").to_a.join * 100 + @tar << tar_file_header("lib/bar", "", 0, contents2.size, Time.now) + @tar << tar_file_contents(contents2) + + tgz = util_gzip(@tar) + + Zlib::GzipReader.wrap StringIO.new(tgz) do |gzio| + util_entry(gzio).close # skip the first entry so io.pos is not 0 + entry = util_entry(gzio) + + entry.seek(50) + assert_equal 50, entry.pos + assert_equal contents2[50..-1], entry.read, "read remaining after seek" + entry.seek(-50, IO::SEEK_CUR) + assert_equal contents2.size - 50, entry.pos + assert_equal contents2[-50..-1], entry.read, "read after stepping back 50 from the end" + entry.seek(0, IO::SEEK_SET) + assert_equal 0, entry.pos + assert_equal contents2, entry.read, "read from beginning" + entry.seek(-10, IO::SEEK_END) + assert_equal contents2.size - 10, entry.pos + assert_equal contents2[-10..-1], entry.read, "read from end" + assert_equal contents2.size, entry.pos + end + end end diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb index 5f54230c77..098df5a538 100644 --- a/test/rubygems/test_gem_remote_fetcher.rb +++ b/test/rubygems/test_gem_remote_fetcher.rb @@ -28,7 +28,7 @@ require "rubygems/package" class TestGemRemoteFetcher < Gem::TestCase include Gem::DefaultUserInteraction - SERVER_DATA = <<-EOY.freeze + SERVER_DATA = <<-EOY --- !ruby/object:Gem::Cache gems: rake-0.4.11: !ruby/object:Gem::Specification diff --git a/test/rubygems/test_gem_request_set_gem_dependency_api.rb b/test/rubygems/test_gem_request_set_gem_dependency_api.rb index 5fd2bbb9c2..6474e500e2 100644 --- a/test/rubygems/test_gem_request_set_gem_dependency_api.rb +++ b/test/rubygems/test_gem_request_set_gem_dependency_api.rb @@ -95,7 +95,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase assert_equal [dep("a")], @set.dependencies - assert_equal %w[git/a master], @git_set.repositories["a"] + assert_equal ["git/a", nil], @git_set.repositories["a"] expected = { "a" => Gem::Requirement.create("!") } @@ -107,7 +107,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase assert_equal [dep("a")], @set.dependencies - assert_equal %w[https://example@bitbucket.org/example/repository.git master], + assert_equal ["https://example@bitbucket.org/example/repository.git", nil], @git_set.repositories["a"] expected = { "a" => Gem::Requirement.create("!") } @@ -120,7 +120,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase assert_equal [dep("a")], @set.dependencies - assert_equal %w[https://example@bitbucket.org/example/example.git master], + assert_equal ["https://example@bitbucket.org/example/example.git", nil], @git_set.repositories["a"] expected = { "a" => Gem::Requirement.create("!") } @@ -145,7 +145,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase assert_equal [dep("a")], @set.dependencies - assert_equal %w[https://gist.github.com/a.git master], + assert_equal ["https://gist.github.com/a.git", nil], @git_set.repositories["a"] end @@ -166,7 +166,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase assert_equal [dep("a")], @set.dependencies - assert_equal %w[git/a master], @git_set.repositories["a"] + assert_equal ["git/a", nil], @git_set.repositories["a"] assert_equal %w[git/a], @git_set.need_submodules.keys end @@ -183,7 +183,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase assert_equal [dep("a")], @set.dependencies - assert_equal %w[https://github.com/example/repository.git master], + assert_equal ["https://github.com/example/repository.git", nil], @git_set.repositories["a"] expected = { "a" => Gem::Requirement.create("!") } @@ -196,7 +196,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase assert_equal [dep("a")], @set.dependencies - assert_equal %w[https://github.com/example/example.git master], + assert_equal ["https://github.com/example/example.git", nil], @git_set.repositories["a"] expected = { "a" => Gem::Requirement.create("!") } @@ -609,8 +609,8 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase assert_equal [dep("a"), dep("b")], @set.dependencies - assert_equal %w[git://example/repo.git master], @git_set.repositories["a"] - assert_equal %w[git://example/repo.git master], @git_set.repositories["b"] + assert_equal ["git://example/repo.git", nil], @git_set.repositories["a"] + assert_equal ["git://example/repo.git", nil], @git_set.repositories["b"] end def test_git_source @@ -620,7 +620,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase @gda.gem "a", :example => "repo" - assert_equal %w[git://example/repo.git master], @git_set.repositories["a"] + assert_equal ["git://example/repo.git", nil], @git_set.repositories["a"] end def test_group diff --git a/test/rubygems/test_gem_request_set_lockfile_parser.rb b/test/rubygems/test_gem_request_set_lockfile_parser.rb index ecb145e697..69a3a328aa 100644 --- a/test/rubygems/test_gem_request_set_lockfile_parser.rb +++ b/test/rubygems/test_gem_request_set_lockfile_parser.rb @@ -257,7 +257,7 @@ DEPENDENCIES write_lockfile <<-LOCKFILE GIT remote: git://example/a.git - revision: master + revision: abranch specs: a (2) b (>= 3) @@ -289,7 +289,7 @@ DEPENDENCIES git_set.specs.values.first.dependencies expected = { - "a" => %w[git://example/a.git master], + "a" => %w[git://example/a.git abranch], } assert_equal expected, git_set.repositories diff --git a/test/rubygems/test_gem_resolver_git_set.rb b/test/rubygems/test_gem_resolver_git_set.rb index f7063b3d45..6005b1e40f 100644 --- a/test/rubygems/test_gem_resolver_git_set.rb +++ b/test/rubygems/test_gem_resolver_git_set.rb @@ -13,7 +13,7 @@ class TestGemResolverGitSet < Gem::TestCase def test_add_git_gem name, version, repository, = git_gem - @set.add_git_gem name, repository, "master", false + @set.add_git_gem name, repository, nil, false dependency = dep "a" @@ -27,7 +27,7 @@ class TestGemResolverGitSet < Gem::TestCase def test_add_git_gem_submodules name, _, repository, = git_gem - @set.add_git_gem name, repository, "master", true + @set.add_git_gem name, repository, nil, true dependency = dep "a" @@ -57,7 +57,7 @@ class TestGemResolverGitSet < Gem::TestCase def test_find_all name, _, repository, = git_gem - @set.add_git_gem name, repository, "master", false + @set.add_git_gem name, repository, nil, false dependency = dep "a", "~> 1.0" req = Gem::Resolver::DependencyRequest.new dependency, nil @@ -73,7 +73,7 @@ class TestGemResolverGitSet < Gem::TestCase def test_find_all_local name, _, repository, = git_gem - @set.add_git_gem name, repository, "master", false + @set.add_git_gem name, repository, nil, false @set.remote = false dependency = dep "a", "~> 1.0" @@ -88,7 +88,7 @@ class TestGemResolverGitSet < Gem::TestCase def test_find_all_prerelease name, _, repository, = git_gem "a", "1.a" - @set.add_git_gem name, repository, "master", false + @set.add_git_gem name, repository, nil, false dependency = dep "a", ">= 0" req = Gem::Resolver::DependencyRequest.new dependency, nil @@ -122,7 +122,7 @@ class TestGemResolverGitSet < Gem::TestCase def test_prefetch name, _, repository, = git_gem - @set.add_git_gem name, repository, "master", false + @set.add_git_gem name, repository, nil, false dependency = dep name req = Gem::Resolver::DependencyRequest.new dependency, nil @@ -136,7 +136,7 @@ class TestGemResolverGitSet < Gem::TestCase def test_prefetch_cache name, _, repository, = git_gem - @set.add_git_gem name, repository, "master", false + @set.add_git_gem name, repository, nil, false dependency = dep name req = Gem::Resolver::DependencyRequest.new dependency, nil @@ -154,7 +154,7 @@ class TestGemResolverGitSet < Gem::TestCase def test_prefetch_filter name, _, repository, = git_gem - @set.add_git_gem name, repository, "master", false + @set.add_git_gem name, repository, nil, false dependency = dep "b" req = Gem::Resolver::DependencyRequest.new dependency, nil @@ -168,7 +168,7 @@ class TestGemResolverGitSet < Gem::TestCase def test_prefetch_root_dir name, _, repository, = git_gem - @set.add_git_gem name, repository, "master", false + @set.add_git_gem name, repository, nil, false dependency = dep name req = Gem::Resolver::DependencyRequest.new dependency, nil diff --git a/test/rubygems/test_gem_resolver_git_specification.rb b/test/rubygems/test_gem_resolver_git_specification.rb index 454fd9c6e4..615949bc2a 100644 --- a/test/rubygems/test_gem_resolver_git_specification.rb +++ b/test/rubygems/test_gem_resolver_git_specification.rb @@ -84,7 +84,7 @@ class TestGemResolverGitSpecification < Gem::TestCase system @git, "commit", "--quiet", "-m", "Add extension files" end - source = Gem::Source::Git.new name, repository, "master", true + source = Gem::Source::Git.new name, repository, nil, true spec = source.specs.first diff --git a/test/rubygems/test_gem_source.rb b/test/rubygems/test_gem_source.rb index e164215b57..5a6c9ffc94 100644 --- a/test/rubygems/test_gem_source.rb +++ b/test/rubygems/test_gem_source.rb @@ -30,7 +30,7 @@ class TestGemSource < Gem::TestCase def test_initialize_git repository = "git@example:a.git" - source = Gem::Source::Git.new "a", repository, "master", false + source = Gem::Source::Git.new "a", repository, nil, false assert_equal repository, source.uri end diff --git a/test/rubygems/test_gem_source_git.rb b/test/rubygems/test_gem_source_git.rb index 147c6df1d2..c21dd88f8f 100644 --- a/test/rubygems/test_gem_source_git.rb +++ b/test/rubygems/test_gem_source_git.rb @@ -10,7 +10,7 @@ class TestGemSourceGit < Gem::TestCase @hash = Digest::SHA1.hexdigest @repository - @source = Gem::Source::Git.new @name, @repository, "master", false + @source = Gem::Source::Git.new @name, @repository, nil, false end def test_base_dir @@ -27,12 +27,13 @@ class TestGemSourceGit < Gem::TestCase assert_path_exist File.join @source.install_dir, "a.gemspec" end - def test_checkout_master + def test_checkout_default Dir.chdir @repository do + default_branch = Gem::Util.popen(@git, "branch", "--show-current").strip system @git, "checkout", "-q", "-b", "other" system @git, "mv", "a.gemspec", "b.gemspec" system @git, "commit", "-q", "-a", "-m", "rename gemspec" - system @git, "checkout", "-q", "master" + system @git, "checkout", "-q", default_branch end @source = Gem::Source::Git.new @name, @repository, "other", false @@ -68,7 +69,7 @@ class TestGemSourceGit < Gem::TestCase # https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/ system(@git, *%W[config --global protocol.file.allow always]) - source = Gem::Source::Git.new @name, @repository, "master", true + source = Gem::Source::Git.new @name, @repository, nil, true git_gem "b" @@ -92,7 +93,7 @@ class TestGemSourceGit < Gem::TestCase assert_path_exist @source.repo_cache_dir Dir.chdir @source.repo_cache_dir do - assert_equal @head, Gem::Util.popen(@git, "rev-parse", "master").strip + assert_equal @head, Gem::Util.popen(@git, "rev-parse", "HEAD").strip end end @@ -178,7 +179,7 @@ class TestGemSourceGit < Gem::TestCase system @git, "checkout", "--quiet", "-b", "other" end - master_head = @head + default_head = @head git_gem "a", 2 @@ -186,7 +187,7 @@ class TestGemSourceGit < Gem::TestCase source.cache - refute_equal master_head, source.rev_parse + refute_equal default_head, source.rev_parse source = Gem::Source::Git.new @name, @repository, "nonexistent", false @@ -209,7 +210,7 @@ class TestGemSourceGit < Gem::TestCase end def test_spaceship - git = Gem::Source::Git.new "a", "git/a", "master", false + git = Gem::Source::Git.new "a", "git/a", nil, false remote = Gem::Source.new @gem_repo installed = Gem::Source::Installed.new vendor = Gem::Source::Vendor.new "vendor/foo" @@ -227,7 +228,7 @@ class TestGemSourceGit < Gem::TestCase end def test_specs - source = Gem::Source::Git.new @name, @repository, "master", true + source = Gem::Source::Git.new @name, @repository, nil, true Dir.chdir "git/a" do FileUtils.mkdir "b" @@ -278,7 +279,7 @@ class TestGemSourceGit < Gem::TestCase end def test_specs_local - source = Gem::Source::Git.new @name, @repository, "master", true + source = Gem::Source::Git.new @name, @repository, nil, true source.remote = false capture_output do @@ -294,13 +295,13 @@ class TestGemSourceGit < Gem::TestCase assert_equal @hash, @source.uri_hash source = - Gem::Source::Git.new "a", "http://git@example/repo.git", "master", false + Gem::Source::Git.new "a", "http://git@example/repo.git", nil, false assert_equal "291c4caac7feba8bb64c297987028acb3dde6cfe", source.uri_hash source = - Gem::Source::Git.new "a", "HTTP://git@EXAMPLE/repo.git", "master", false + Gem::Source::Git.new "a", "HTTP://git@EXAMPLE/repo.git", nil, false assert_equal "291c4caac7feba8bb64c297987028acb3dde6cfe", source.uri_hash diff --git a/test/rubygems/test_gem_source_installed.rb b/test/rubygems/test_gem_source_installed.rb index 7fb5017c59..5638e4b0b8 100644 --- a/test/rubygems/test_gem_source_installed.rb +++ b/test/rubygems/test_gem_source_installed.rb @@ -11,7 +11,7 @@ class TestGemSourceInstalled < Gem::TestCase specific = Gem::Source::SpecificFile.new a1.cache_file installed = Gem::Source::Installed.new local = Gem::Source::Local.new - git = Gem::Source::Git.new "a", "a", "master" + git = Gem::Source::Git.new "a", "a", nil vendor = Gem::Source::Vendor.new "a" assert_equal(0, installed.<=>(installed), "installed <=> installed") diff --git a/test/rubygems/test_gem_source_lock.rb b/test/rubygems/test_gem_source_lock.rb index ff9465d0ad..6344feec59 100644 --- a/test/rubygems/test_gem_source_lock.rb +++ b/test/rubygems/test_gem_source_lock.rb @@ -18,7 +18,7 @@ class TestGemSourceLock < Gem::TestCase end def test_equals2 - git = Gem::Source::Git.new "a", "git/a", "master", false + git = Gem::Source::Git.new "a", "git/a", nil, false g_lock = Gem::Source::Lock.new git installed = Gem::Source::Installed.new @@ -30,7 +30,7 @@ class TestGemSourceLock < Gem::TestCase end def test_spaceship - git = Gem::Source::Git.new "a", "git/a", "master", false + git = Gem::Source::Git.new "a", "git/a", nil, false g_lock = Gem::Source::Lock.new git installed = Gem::Source::Installed.new @@ -54,7 +54,7 @@ class TestGemSourceLock < Gem::TestCase end def test_spaceship_git - git = Gem::Source::Git.new "a", "git/a", "master", false + git = Gem::Source::Git.new "a", "git/a", nil, false lock = Gem::Source::Lock.new git assert_equal(1, lock.<=>(git), "lock <=> git") diff --git a/test/rubygems/test_gem_source_vendor.rb b/test/rubygems/test_gem_source_vendor.rb index 29846e5c13..50a446c90f 100644 --- a/test/rubygems/test_gem_source_vendor.rb +++ b/test/rubygems/test_gem_source_vendor.rb @@ -12,7 +12,7 @@ class TestGemSourceVendor < Gem::TestCase def test_spaceship vendor = Gem::Source::Vendor.new "vendor/foo" remote = Gem::Source.new @gem_repo - git = Gem::Source::Git.new "a", "a", "master" + git = Gem::Source::Git.new "a", "a", nil installed = Gem::Source::Installed.new assert_equal(0, vendor.<=>(vendor), "vendor <=> vendor") diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index dda47e6bdf..56ca7acaf2 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -10,7 +10,7 @@ require "rubygems/installer" require "rubygems/platform" class TestGemSpecification < Gem::TestCase - LEGACY_YAML_SPEC = <<-EOF.freeze + LEGACY_YAML_SPEC = <<-EOF --- !ruby/object:Gem::Specification rubygems_version: "1.0" name: keyedlist @@ -29,7 +29,7 @@ email: flgr@ccan.de has_rdoc: true EOF - LEGACY_RUBY_SPEC = <<-EOF.freeze + LEGACY_RUBY_SPEC = <<-EOF Gem::Specification.new do |s| s.name = %q{keyedlist} s.version = %q{0.4.0} @@ -2677,6 +2677,23 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use: end end + def test_validate_self_referencing_dependencies + util_setup_validate + + Dir.chdir @tempdir do + @a1.add_runtime_dependency @a1.name, "1" + + use_ui @ui do + @a1.validate + end + + assert_equal <<-EXPECTED, @ui.error +#{w}: Self referencing dependency is unnecessary and strongly discouraged. +#{w}: See https://guides.rubygems.org/specification-reference/ for help + EXPECTED + end + end + def test_validate_rake_extension_have_rake_dependency_warning util_setup_validate @@ -3709,6 +3726,23 @@ end assert Gem::Specification.find_by_name "b", ">1" end + def test_find_by_full_name + pl = Gem::Platform.new "x86_64-linux" + + a = util_spec "a", "1" + install_specs a + + a_pl = util_spec("a", "1") {|s| s.platform = pl } + install_specs a_pl + + assert_equal a, Gem::Specification.find_by_full_name("a-1") + assert_equal a_pl, Gem::Specification.find_by_full_name("a-1-x86_64-linux") + + assert_nil Gem::Specification.find_by_full_name("a-2") + assert_nil Gem::Specification.find_by_full_name("b-1") + assert_nil Gem::Specification.find_by_full_name("a-1-arm64-linux") + end + def test_find_by_path a = util_spec "foo", "1", nil, "lib/foo.rb" diff --git a/test/rubygems/test_gem_version.rb b/test/rubygems/test_gem_version.rb index 9237608d4a..f45c72b29c 100644 --- a/test/rubygems/test_gem_version.rb +++ b/test/rubygems/test_gem_version.rb @@ -43,7 +43,7 @@ class TestGemVersion < Gem::TestCase assert_equal v("5.1"), Gem::Version.create("5.1") - ver = "1.1".freeze + ver = "1.1" assert_equal v("1.1"), Gem::Version.create(ver) end @@ -88,7 +88,7 @@ class TestGemVersion < Gem::TestCase end def test_initialize - ["1.0", "1.0 ", " 1.0 ", "1.0\n", "\n1.0\n", "1.0".freeze].each do |good| + ["1.0", "1.0 ", " 1.0 ", "1.0\n", "\n1.0\n", "1.0"].each do |good| assert_version_equal "1.0", good end diff --git a/test/rubygems/test_kernel.rb b/test/rubygems/test_kernel.rb index ce38d92d8d..959907444c 100644 --- a/test/rubygems/test_kernel.rb +++ b/test/rubygems/test_kernel.rb @@ -5,19 +5,11 @@ class TestKernel < Gem::TestCase def setup super - @old_path = $:.dup - util_make_gems without_any_upwards_gemfiles end - def teardown - super - - $:.replace @old_path - end - def test_gem assert gem("a", "= 1"), "Should load" assert $:.any? {|p| p.include?("a-1/lib") } diff --git a/tool/bundler/dev_gems.rb.lock b/tool/bundler/dev_gems.rb.lock index dd98ddc729..d78d63797b 100644 --- a/tool/bundler/dev_gems.rb.lock +++ b/tool/bundler/dev_gems.rb.lock @@ -10,7 +10,7 @@ GEM parallel power_assert (2.0.2) rake (13.0.6) - rb_sys (0.9.52) + rb_sys (0.9.63) rdiscount (2.2.7) ronn (0.7.3) hpricot (>= 0.8.2) @@ -54,4 +54,4 @@ DEPENDENCIES webrick (~> 1.6) BUNDLED WITH - 2.4.6 + 2.4.10 diff --git a/tool/bundler/rubocop_gems.rb.lock b/tool/bundler/rubocop_gems.rb.lock index fbb8d91956..176221d626 100644 --- a/tool/bundler/rubocop_gems.rb.lock +++ b/tool/bundler/rubocop_gems.rb.lock @@ -14,7 +14,7 @@ GEM rake (13.0.6) rake-compiler (1.2.0) rake - rb_sys (0.9.52) + rb_sys (0.9.63) regexp_parser (2.6.1) rexml (3.2.5) rspec (3.12.0) @@ -70,4 +70,4 @@ DEPENDENCIES test-unit BUNDLED WITH - 2.4.6 + 2.4.10 diff --git a/tool/bundler/standard_gems.rb.lock b/tool/bundler/standard_gems.rb.lock index 54307cd038..5917a68207 100644 --- a/tool/bundler/standard_gems.rb.lock +++ b/tool/bundler/standard_gems.rb.lock @@ -15,7 +15,7 @@ GEM rake (13.0.6) rake-compiler (1.2.0) rake - rb_sys (0.9.52) + rb_sys (0.9.63) regexp_parser (2.6.1) rexml (3.2.5) rspec (3.12.0) @@ -78,4 +78,4 @@ DEPENDENCIES test-unit BUNDLED WITH - 2.4.6 + 2.4.10 diff --git a/tool/bundler/test_gems.rb.lock b/tool/bundler/test_gems.rb.lock index 75880b6761..3affb58f74 100644 --- a/tool/bundler/test_gems.rb.lock +++ b/tool/bundler/test_gems.rb.lock @@ -11,7 +11,7 @@ GEM rack-test (1.1.0) rack (>= 1.0, < 3) rake (13.0.1) - rb_sys (0.9.52) + rb_sys (0.9.63) ruby2_keywords (0.0.5) sinatra (2.0.8.1) mustermann (~> 1.0) @@ -42,4 +42,4 @@ DEPENDENCIES webrick (= 1.7.0) BUNDLED WITH - 2.4.6 + 2.4.10 |