diff --git a/lib/bundler.rb b/lib/bundler.rb index c6a225977f6070..88f25b523a4188 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 @@ def which(executable) 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 @@ def safe_load_marshal(data) 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 @@ def self_manager 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 ee73bdb50626c6..a3eb494db2a2e3 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -156,6 +156,7 @@ def self.handle_no_command_error(command, has_namespace = $thor_runner) 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 bc96507c2966e2..246b9d64604460 100644 --- a/lib/bundler/cli/init.rb +++ b/lib/bundler/cli/init.rb @@ -32,7 +32,7 @@ def run 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 @@ def run 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 f9987c4da8713f..f009b07ad7457c 100644 --- a/lib/bundler/current_ruby.rb +++ b/lib/bundler/current_ruby.rb @@ -22,6 +22,8 @@ class CurrentRuby 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 e27374f35dd35e..bef86cdd60687b 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -726,6 +726,8 @@ def converge_dependencies dep.source = sources.get(dep.source) end + next if unlocking? + unless locked_dep = @locked_deps[dep.name] changes = true next @@ -886,8 +888,9 @@ def lockfiles_equal?(current, proposed, preserve_unknown_sections) 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 @@ def remove_ruby_from_platforms_if_necessary!(dependencies) 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 1f8b9da2eb6a70..5f179436296e10 100644 --- a/lib/bundler/dependency.rb +++ b/lib/bundler/dependency.rb @@ -9,7 +9,7 @@ class Dependency < Gem::Dependency 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 d315d1cc68de9b..863544b1f926db 100644 --- a/lib/bundler/endpoint_specification.rb +++ b/lib/bundler/endpoint_specification.rb @@ -26,10 +26,6 @@ def fetch_platform @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 70967522af29c2..57013f5d507d8d 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 @@ class EnvironmentPreserver 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 a073bae2789508..e12c15af8a8a24 100644 --- a/lib/bundler/fetcher.rb +++ b/lib/bundler/fetcher.rb @@ -102,11 +102,11 @@ def fetch_spec(spec) 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 332f86139df30c..18b606abb66192 100644 --- a/lib/bundler/fetcher/dependency.rb +++ b/lib/bundler/fetcher/dependency.rb @@ -34,14 +34,10 @@ def specs(gem_names, full_dependency_list = [], last_spec_list = []) 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 0301986ca91154..b8c599f63af999 100644 --- a/lib/bundler/index.rb +++ b/lib/bundler/index.rb @@ -13,8 +13,8 @@ def self.build 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 c2edc9b43a699b..cb644a7f69ac97 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 dce71337690fd9..58c5aafa1cb3c8 100644 --- a/lib/bundler/installer/parallel_installer.rb +++ b/lib/bundler/installer/parallel_installer.rb @@ -110,12 +110,13 @@ def check_for_unmet_dependencies 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 2efef616489e28..2a8c9a432d301b 100644 --- a/lib/bundler/installer/standalone.rb +++ b/lib/bundler/installer/standalone.rb @@ -52,7 +52,7 @@ def bundler_path 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 67498929301527..ad8191c55fe556 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -20,7 +20,7 @@ def initialize(name, version, platform, source = nil) 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 @@ def full_name 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 @@ def to_s 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 23413dbdd6db21..a7ee026f679ab7 100644 --- a/lib/bundler/lockfile_generator.rb +++ b/lib/bundler/lockfile_generator.rb @@ -45,7 +45,7 @@ def add_specs(specs) # 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 0ec5b7de8a206a..97cbf211ba7320 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 @@ def initialize(lockfile) 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 @@ def parse_spec(line) @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 805bd5450d5251..0e21c755065624 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 f5efe77e1070d1..2774e9d28a913b 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 a17f1a6b066e38..f24b63c6fc94ea 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 f1bf0b182150b0..7679945c4890e4 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 b05a8eb0eb08ca..2eb745698cf784 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 fdb06c321db142..493b57e1de7a15 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 6cb0f0f810d1cb..ff239004bfe8d5 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 bfe928c9a1cee2..e463b67477b2f2 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 281f3faedeb422..9e9efe8b6dd7a4 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 4bac1bf44234cf..ea64871fb6c9cd 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 47a42a497edd01..a3b059ea07f323 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 e9f5583e8515d0..5af60c6a779f8d 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 9e8b059f1f2164..e93b4fd5e9c28f 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 9d3d97deea6762..7d3cede1f61945 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 0b0766a40c203f..657d5ef4a7134b 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 fcf6a6a66c9ac6..8f144692f3a0db 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 37d2c837e47173..f24f62dd38ee37 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 422da46d007c97..55d1035d77f420 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 c831bf9ce9a3e0..ff44d1224fe1cf 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 40fe8a4def33ac..8455b71b45f5a8 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 d0015a80ef9b2c..27948787196d72 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 a231bb1e3d3100..39d3dfa04e2bd4 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 8f4bad3db67c1e..f42c7ce1567e86 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 97a65016aca9f6..b18d80554d74ea 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 82706d45f34e8d..efd9ccb0e09690 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 65448f480611d5..c67c44ff86ff60 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 bb03e8b5d67fd0..9a3820f3e6f5d0 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 23fb95b7384d8f..3a070103092279 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 39f3807f304b80..873ba566b1ed1e 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 740a01e0cc2413..a9124d26eacf34 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 26458bd596fc57..f3caff8963c720 100644 --- a/lib/bundler/plugin.rb +++ b/lib/bundler/plugin.rb @@ -15,7 +15,7 @@ class UndefinedCommandError < PluginError; end 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 81ecafa470b10f..c9ff12ce4b718c 100644 --- a/lib/bundler/plugin/installer.rb +++ b/lib/bundler/plugin/installer.rb @@ -83,8 +83,11 @@ def install_all_sources(names, version, git_source_options, rubygems_source) 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 34d7fd116c576f..f626a3218e0503 100644 --- a/lib/bundler/remote_specification.rb +++ b/lib/bundler/remote_specification.rb @@ -29,12 +29,8 @@ def fetch_platform @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 @@ def to_ary 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 8237ff53fe3db0..c8cc88a3eecb73 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -37,7 +37,9 @@ def setup_solver 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 @@ def setup_solver { 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 @@ def incompatibilities_for(package, version) 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 @@ def all_versions_for(package) 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 @@ def filter_matching_specs(specs, requirements) 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 6921c047a73ce5..c6afa820568a66 100644 --- a/lib/bundler/resolver/base.rb +++ b/lib/bundler/resolver/base.rb @@ -49,10 +49,18 @@ def base_requirements 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 @@ def include_prereleases(names) 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 3857948511aac6..72016e848baeb5 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -453,7 +453,7 @@ def fetch_specs(remote, name) 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 a76a7927439b9d..1139eab5037975 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -495,7 +495,7 @@ def self.normalize_uri(uri) 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 32e9b2d7c0fa96..801fd5312aade7 100644 --- a/lib/bundler/setup.rb +++ b/lib/bundler/setup.rb @@ -12,7 +12,10 @@ 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 794a03e62db993..d1d4e1d07a5bf0 100644 --- a/lib/bundler/shared_helpers.rb +++ b/lib/bundler/shared_helpers.rb @@ -160,7 +160,7 @@ def ensure_same_dependencies(spec, old_deps, new_deps) " (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 b8ee4029b443d9..42897813b47742 100644 --- a/lib/bundler/source/git.rb +++ b/lib/bundler/source/git.rb @@ -19,7 +19,7 @@ def initialize(options) # 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 @@ def specs(*) 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 2a7c8473f58c2e..926c9b8eadacc7 100644 --- a/lib/bundler/source/git/git_proxy.rb +++ b/lib/bundler/source/git/git_proxy.rb @@ -28,8 +28,9 @@ class GitCommandError < GitError 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 @@ def git_remote_fetch(args) 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 @@ def clone_needs_extra_fetch? 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 @@ def depth 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 @@ def refspec "#{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 @@ def extra_clone_args 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 20975e20ca41ff..bdfcf8274af9db 100644 --- a/lib/bundler/source/path.rb +++ b/lib/bundler/source/path.rb @@ -11,7 +11,7 @@ class Path < Source 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 c39071705a2e48..8d0c78bd610d40 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -337,8 +337,7 @@ def package_path(cache_path, spec) 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 7478bd9ca2a666..cf63c16a70419b 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -24,6 +24,7 @@ def for(dependencies, check = false, platforms = [nil]) name = dep[0].name platform = dep[1] + incomplete = false key = [name, platform] next if handled.key?(key) @@ -36,14 +37,19 @@ def for(dependencies, check = false, platforms = [nil]) 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 @@ def materialized_for_all_platforms 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 6dab1688f78674..e290fe91eba100 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 d2403f18b2dfe4..00000000000000 --- 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 41c95677a332d3..a0d2ac28267972 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 ac14545126011e..b5a5c4e3925b17 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 d4021980b4f52c..be58dd81566dfb 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 e35a1212452c64..da81f046d469eb 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 00000000000000..ad08593256b68e --- /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 dab58ecdf7ed2c..239eaf3401c064 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 @@ class Incompatibility InvalidDependency = Struct.new(:package, :constraint) do end - CircularDependency = Struct.new(:package, :constraint) do - end - NoVersions = Struct.new(:constraint) do end @@ -66,8 +63,6 @@ def to_s "#{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 @@ def to_s 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 e895812beda64c..4bf61461b269f9 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 @@ def add(name, version, deps: {}) 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 9133332d015632..b71f3eaf53b44f 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 @@ def hash 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 506b447b36a634..8d73c3f7b5d58b 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 @@ def empty? true end - def eql? + def eql?(other) other.empty? end @@ -65,6 +65,7 @@ def select_versions(_) end EMPTY = Empty.new + Empty.singleton_class.undef_method(:new) def self.empty EMPTY @@ -88,7 +89,8 @@ def hash 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 ea5e455968bb1f..2cb8412cf328f0 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 @@ def choose_package_version 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 @@ def choose_package_version 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 d3bd162e4829ee..bc9f8d22cd25e4 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 26b0b1da7eb2df..ef5f079a806c4a 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 @@ def self.ruby_api_version 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 @@ def default_gem_load_paths ## # 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 f6fad0bd83fd4c..2cbc4ff6242141 100644 --- a/lib/rubygems/bundler_version_finder.rb +++ b/lib/rubygems/bundler_version_finder.rb @@ -21,7 +21,7 @@ def self.prioritize!(specs) 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 f4688d793b8288..1e15f612de2a6b 100644 --- a/lib/rubygems/command.rb +++ b/lib/rubygems/command.rb @@ -201,11 +201,15 @@ def get_all_gem_names # 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 @@ def wrap(text, width) # :doc: # :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 1bdbd50530ae0d..0a4b53abe52745 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 00000000000000..1d7e836ed02359 --- /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 8bfb4458ff1c23..bf4ffefbb78eba 100644 --- a/lib/rubygems/commands/help_command.rb +++ b/lib/rubygems/commands/help_command.rb @@ -3,7 +3,7 @@ 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 @@ class Gem::Commands::HelpCommand < Gem::Command 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 @@ class Gem::Commands::HelpCommand < Gem::Command 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 72db53ef378cc3..d7c78ba13d6ede 100644 --- a/lib/rubygems/commands/pristine_command.rb +++ b/lib/rubygems/commands/pristine_command.rb @@ -34,6 +34,11 @@ def initialize 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 @@ def execute 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 3c520826e5bf97..29f6013c5969e3 100644 --- a/lib/rubygems/commands/uninstall_command.rb +++ b/lib/rubygems/commands/uninstall_command.rb @@ -125,6 +125,9 @@ def check_version # :nodoc: 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 6dcc4a06e4b114..568ac347b4ab33 100644 --- a/lib/rubygems/core_ext/kernel_require.rb +++ b/lib/rubygems/core_ext/kernel_require.rb @@ -37,9 +37,6 @@ def require(path) # :doc: 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 @@ def require(path) # :doc: 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 8daff0bc30a723..b186375f693c45 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 @@ def self.data_home # 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 7fa0f91bd4624c..cd03e7e299bd4b 100644 --- a/lib/rubygems/dependency.rb +++ b/lib/rubygems/dependency.rb @@ -299,7 +299,7 @@ def specific? 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 5fe0afb6b07cf2..56505512c30e2a 100644 --- a/lib/rubygems/deprecate.rb +++ b/lib/rubygems/deprecate.rb @@ -143,7 +143,7 @@ def rubygems_deprecate(name, replacement=:none) 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 @@ def rubygems_deprecate_command 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 7fb8958d7f264b..43ea207b237efd 100644 --- a/lib/rubygems/ext/builder.rb +++ b/lib/rubygems/ext/builder.rb @@ -55,6 +55,23 @@ def self.make(dest_path, results, make_dir = Dir.pwd, sitedir = nil, targets = [ 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 27ebd8c62b6aa9..2f4193d6977bd5 100644 --- a/lib/rubygems/ext/ext_conf_builder.rb +++ b/lib/rubygems/ext/ext_conf_builder.rb @@ -21,8 +21,7 @@ def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_di 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 9f2e099d40a357..e74ec8750fb0f9 100644 --- a/lib/rubygems/ext/rake_builder.rb +++ b/lib/rubygems/ext/rake_builder.rb @@ -18,7 +18,7 @@ def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_di 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 590a2f03155f8d..31566e5865e43f 100644 --- a/lib/rubygems/package/tar_header.rb +++ b/lib/rubygems/package/tar_header.rb @@ -208,7 +208,7 @@ def update_checksum 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 cdc3fdc0151520..9f1d49035b3057 100644 --- a/lib/rubygems/package/tar_reader.rb +++ b/lib/rubygems/package/tar_reader.rb @@ -53,39 +53,11 @@ def close 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 8634381c18f0cb..9e7b327431dabc 100644 --- a/lib/rubygems/package/tar_reader/entry.rb +++ b/lib/rubygems/package/tar_reader/entry.rb @@ -8,6 +8,20 @@ # Class for reading entries out of a tar file 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 @@ -21,6 +35,7 @@ def initialize(header, io) @header = header @io = io @orig_pos = @io.pos + @end_pos = @orig_pos + @header.size @read = 0 end @@ -39,7 +54,14 @@ def bytes_read # 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 @@ def pos 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 @@ def size 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 @@ def read(len = nil) 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) @@ -155,13 +187,62 @@ def readpartial(maxlen = nil, outbuf = "".b) outbuf 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 128871f2bffcc2..f4983c11537652 100644 --- a/lib/rubygems/platform.rb +++ b/lib/rubygems/platform.rb @@ -235,11 +235,11 @@ def =~(other) # 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 ad6e45005bc44a..0681f8802ad225 100644 --- a/lib/rubygems/request_set/gem_dependency_api.rb +++ b/lib/rubygems/request_set/gem_dependency_api.rb @@ -435,7 +435,6 @@ def gem_git_reference(options) # :nodoc: 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 64f9ac3465a8a9..bc2fd9af55b861 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 64b458f50407f1..3b95efebf72790 100644 --- a/lib/rubygems/resolver/stats.rb +++ b/lib/rubygems/resolver/stats.rb @@ -32,7 +32,7 @@ def iteration! @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 2609a309e818cb..7ac685f97811fa 100644 --- a/lib/rubygems/source/git.rb +++ b/lib/rubygems/source/git.rb @@ -53,7 +53,7 @@ def initialize(name, repository, reference, submodules = false) @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 5175db2d84403a..b8aa43bf817339 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -338,7 +338,7 @@ def authors=(value) # 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/. @@ -1021,6 +1021,12 @@ def self.find_by_name(name, *requirements) Gem::Dependency.new(name, *requirements).to_spec 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+. @@ -1606,6 +1612,8 @@ def build_args 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 6f4d79cdcf854b..c7e2edc2bfbee6 100644 --- a/lib/rubygems/specification_policy.rb +++ b/lib/rubygems/specification_policy.rb @@ -173,6 +173,7 @@ def validate_duplicate_dependencies # :nodoc: 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 @@ def validate_duplicate_dependencies # :nodoc: 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 512ca9143d92d2..d87abdd9933fb6 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 StubLine # :nodoc: all # 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 StubLine # :nodoc: all }.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 @@ def initialize(data, extensions) 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 d6b891380e368c..be811525f2b860 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 96f47781c089e0..a823521d10bf4c 100644 --- a/lib/rubygems/util/licenses.rb +++ b/lib/rubygems/util/licenses.rb @@ -4,8 +4,8 @@ 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 f67889ef1a315b..c319e1f82027dd 100644 --- a/lib/rubygems/version.rb +++ b/lib/rubygems/version.rb @@ -155,7 +155,7 @@ 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 @@ def inspect # :nodoc: # 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 5894b4aa122dec..862a2c3e07d80f 100644 --- a/spec/bundler/bundler/bundler_spec.rb +++ b/spec/bundler/bundler/bundler_spec.rb @@ -5,9 +5,14 @@ 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 @@ 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 372fa2d5e2cae1..59b958ae4248c9 100644 --- a/spec/bundler/bundler/definition_spec.rb +++ b/spec/bundler/bundler/definition_spec.rb @@ -154,7 +154,7 @@ 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 9b9e2ddf0a8bd3..6e346c36c1e1ef 100644 --- a/spec/bundler/bundler/dependency_spec.rb +++ b/spec/bundler/bundler/dependency_spec.rb @@ -53,6 +53,8 @@ :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 @@ :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 @@ :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 @@ :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 @@ :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 @@ :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 @@ :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 8cb51b8a52c9c8..8b5bf930f2c3f4 100644 --- a/spec/bundler/bundler/dsl_spec.rb +++ b/spec/bundler/bundler/dsl_spec.rb @@ -139,14 +139,19 @@ 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 7cfc86ef76ac99..20307f9c99d090 100644 --- a/spec/bundler/bundler/fetcher/dependency_spec.rb +++ b/spec/bundler/bundler/fetcher/dependency_spec.rb @@ -155,9 +155,9 @@ 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 @@ 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 a1047600753c83..27a63c476d321f 100644 --- a/spec/bundler/bundler/fetcher_spec.rb +++ b/spec/bundler/bundler/fetcher_spec.rb @@ -159,4 +159,34 @@ 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 f67d03356dd40f..c8403a2e38922b 100644 --- a/spec/bundler/bundler/installer/parallel_installer_spec.rb +++ b/spec/bundler/bundler/installer/parallel_installer_spec.rb @@ -22,9 +22,9 @@ 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 369f28fcef7ca2..182aa3646a7dab 100644 --- a/spec/bundler/bundler/rubygems_integration_spec.rb +++ b/spec/bundler/bundler/rubygems_integration_spec.rb @@ -89,5 +89,16 @@ 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 e1c51225649d6f..3c6536c4ebe39c 100644 --- a/spec/bundler/bundler/shared_helpers_spec.rb +++ b/spec/bundler/bundler/shared_helpers_spec.rb @@ -290,7 +290,7 @@ 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 841b8651e43fbd..98d54015e72b16 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 @@ 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 @@ 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 @@ 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 @@ 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 d2671b5f150477..890ba88605af09 100644 --- a/spec/bundler/cache/git_spec.rb +++ b/spec/bundler/cache/git_spec.rb @@ -220,4 +220,57 @@ 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 72f4cc8e5577a5..bfbef58181305e 100644 --- a/spec/bundler/commands/binstubs_spec.rb +++ b/spec/bundler/commands/binstubs_spec.rb @@ -166,6 +166,21 @@ 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 @@ 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 b4996977c1850f..99a858e9e94d5a 100644 --- a/spec/bundler/commands/check_spec.rb +++ b/spec/bundler/commands/check_spec.rb @@ -172,7 +172,7 @@ rack (1.0.0) PLATFORMS - #{local} + #{generic_local_platform} #{not_local} DEPENDENCIES @@ -203,7 +203,7 @@ rack (1.0.0) PLATFORMS - #{local} + #{generic_local_platform} #{not_local} DEPENDENCIES @@ -351,7 +351,7 @@ 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 5ca11dcba267d1..099cdb39fa23f5 100644 --- a/spec/bundler/commands/exec_spec.rb +++ b/spec/bundler/commands/exec_spec.rb @@ -916,6 +916,30 @@ def bin_path(a,b,c) 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 9c499b99a1248e..6aa3e9edd19a3c 100644 --- a/spec/bundler/commands/init_spec.rb +++ b/spec/bundler/commands/init_spec.rb @@ -191,4 +191,17 @@ 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 08ac3336647c7f..126853e35f0706 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -140,6 +140,62 @@ def read_lockfile(file = "Gemfile.lock") 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 @@ def read_lockfile(file = "Gemfile.lock") 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 @@ def read_lockfile(file = "Gemfile.lock") 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 @@ def read_lockfile(file = "Gemfile.lock") 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 @@ def read_lockfile(file = "Gemfile.lock") 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 @@ def read_lockfile(file = "Gemfile.lock") 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 @@ def read_lockfile(file = "Gemfile.lock") 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 @@ def read_lockfile(file = "Gemfile.lock") 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 @@ def read_lockfile(file = "Gemfile.lock") 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 @@ def read_lockfile(file = "Gemfile.lock") 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 @@ def read_lockfile(file = "Gemfile.lock") 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 5827f7380c5534..970e51b8ef7fa0 100644 --- a/spec/bundler/commands/newgem_spec.rb +++ b/spec/bundler/commands/newgem_spec.rb @@ -633,7 +633,7 @@ def create_temporary_dir(dir) 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 @@ def create_temporary_dir(dir) # 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 e084af85d791bb..bbc3cb54ae8094 100644 --- a/spec/bundler/commands/outdated_spec.rb +++ b/spec/bundler/commands/outdated_spec.rb @@ -192,7 +192,7 @@ 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 4e8e3946fe14cc..688bf7b6df9d07 100644 --- a/spec/bundler/commands/platform_spec.rb +++ b/spec/bundler/commands/platform_spec.rb @@ -16,7 +16,7 @@ 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 @@ 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 @@ 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 @@ 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 8c7b9c13c84b85..7016c3e19fc371 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -284,7 +284,7 @@ countries (~> 3.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES countries @@ -871,7 +871,7 @@ vcr (6.0.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES dotenv @@ -1602,7 +1602,7 @@ shared_dep (~> 5.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES isolated_owner @@ -1655,7 +1655,7 @@ 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 3bcb6a703ebead..484664b433e58d 100644 --- a/spec/bundler/install/deploy_spec.rb +++ b/spec/bundler/install/deploy_spec.rb @@ -141,7 +141,7 @@ 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 7e2e7c345a8248..2aa4214818d325 100644 --- a/spec/bundler/install/gemfile/gemspec_spec.rb +++ b/spec/bundler/install/gemfile/gemspec_spec.rb @@ -674,7 +674,7 @@ 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 b6ea32819b38f2..e3be680d899fd8 100644 --- a/spec/bundler/install/gemfile/git_spec.rb +++ b/spec/bundler/install/gemfile/git_spec.rb @@ -1144,6 +1144,17 @@ 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 e9cf0cb636c762..219ae6c2f47371 100644 --- a/spec/bundler/install/gemfile/platform_spec.rb +++ b/spec/bundler/install/gemfile/platform_spec.rb @@ -459,7 +459,7 @@ 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 2199d8bedc82ea..dd86187a6bf1f0 100644 --- a/spec/bundler/install/gemfile/sources_spec.rb +++ b/spec/bundler/install/gemfile/sources_spec.rb @@ -262,7 +262,7 @@ rack PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES depends_on_rack! @@ -376,7 +376,7 @@ 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 @@ 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 @@ zeitwerk (2.4.2) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES activesupport @@ -696,7 +696,7 @@ sidekiq (>= 6.1.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES activesupport @@ -780,7 +780,7 @@ sidekiq (>= 6.1.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES activesupport @@ -836,7 +836,7 @@ sidekiq (>= 6.1.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES activesupport @@ -924,7 +924,7 @@ nokogiri (>= 1.2.3) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES handsoap! @@ -984,7 +984,7 @@ rack (0.9.1) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES rack! @@ -1014,7 +1014,7 @@ rack (0.9.1) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES rack! @@ -1036,7 +1036,7 @@ rack (0.9.1) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES rack! @@ -1448,7 +1448,7 @@ mime-types (3.3.1) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES capybara (~> 2.5.0) @@ -1472,7 +1472,7 @@ mime-types (3.0.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES capybara (~> 2.5.0) @@ -1526,7 +1526,7 @@ pdf-writer (= 1.1.8) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES ruport (= 1.7.0.3)! @@ -1579,7 +1579,7 @@ pdf-writer (= 1.1.8) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES ruport (= 1.7.0.3)! @@ -1620,7 +1620,7 @@ 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 2f82a9f9f3aa96..6974af3270830a 100644 --- a/spec/bundler/install/gemfile/specific_platform_spec.rb +++ b/spec/bundler/install/gemfile/specific_platform_spec.rb @@ -432,7 +432,7 @@ 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 @@ 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 @@ nokogiri (1.13.8-#{Gem::Platform.local}) PLATFORMS - #{lockfile_platforms_for([specific_local_platform, "ruby"])} + #{lockfile_platforms("ruby")} DEPENDENCIES nokogiri @@ -786,6 +786,56 @@ 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 b6c361186ac368..4d5776e2b80925 100644 --- a/spec/bundler/install/gems/compact_index_spec.rb +++ b/spec/bundler/install/gems/compact_index_spec.rb @@ -930,7 +930,7 @@ def start 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 4bbb2ea038f55d..d5fa55be48d6d7 100644 --- a/spec/bundler/install/gems/flex_spec.rb +++ b/spec/bundler/install/gems/flex_spec.rb @@ -198,7 +198,7 @@ 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 279d3422c131ab..fb6dda2f886acf 100644 --- a/spec/bundler/install/gems/resolving_spec.rb +++ b/spec/bundler/install/gems/resolving_spec.rb @@ -190,7 +190,7 @@ 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 ab10670fdf89d5..4d087522562261 100644 --- a/spec/bundler/install/gems/standalone_spec.rb +++ b/spec/bundler/install/gems/standalone_spec.rb @@ -170,7 +170,7 @@ 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 @@ 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 3021d6ae178a75..be34d8b5bc9363 100644 --- a/spec/bundler/install/global_cache_spec.rb +++ b/spec/bundler/install/global_cache_spec.rb @@ -220,9 +220,9 @@ def source2_global_cache(*segments) 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 78b701e4887c49..dc054b50bbe85b 100644 --- a/spec/bundler/install/yanked_spec.rb +++ b/spec/bundler/install/yanked_spec.rb @@ -192,6 +192,12 @@ 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 a633bd546e4eb1..1c1f6fa93da47a 100644 --- a/spec/bundler/lock/git_spec.rb +++ b/spec/bundler/lock/git_spec.rb @@ -33,6 +33,33 @@ 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 cd603e336a2276..8d9554c68d1ff8 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -982,7 +982,7 @@ rack (1.0.0) PLATFORMS - #{lockfile_platforms_for(["java", specific_local_platform])} + #{lockfile_platforms("java")} DEPENDENCIES rack @@ -1218,7 +1218,125 @@ 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 efee5fdd233e2b..aec131f2eeb2bb 100644 --- a/spec/bundler/plugins/install_spec.rb +++ b/spec/bundler/plugins/install_spec.rb @@ -22,6 +22,13 @@ 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 1f4391d0e4272c..a98815158eef82 100644 --- a/spec/bundler/quality_spec.rb +++ b/spec/bundler/quality_spec.rb @@ -206,7 +206,6 @@ def check_for_specific_pronouns(filename) 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 41c6d9972cd0e7..f739f8c02bb2fd 100644 --- a/spec/bundler/resolver/basic_spec.rb +++ b/spec/bundler/resolver/basic_spec.rb @@ -301,4 +301,50 @@ 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 caf69bf085c871..a9933f90e91a60 100644 --- a/spec/bundler/runtime/platform_spec.rb +++ b/spec/bundler/runtime/platform_spec.rb @@ -86,7 +86,7 @@ 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 703c98eca31f69..9bfa1458c6816a 100644 --- a/spec/bundler/support/helpers.rb +++ b/spec/bundler/support/helpers.rb @@ -414,14 +414,14 @@ def realworld_system_gems(*gems) 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 @@ def current_ruby_minor 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 3776901ce3c0fd..eca1b2e60d4371 100644 --- a/spec/bundler/support/platforms.rb +++ b/spec/bundler/support/platforms.rb @@ -21,7 +21,7 @@ def java end def linux - Gem::Platform.new(["x86", "linux", nil]) + Gem::Platform.new("x86_64-linux") end def x86_mswin32 @@ -52,14 +52,6 @@ def all_platforms [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 @@ def not_local_patchlevel 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 45a5a14251fa8e..70b200d7a0d7e8 100644 --- a/test/rubygems/helper.rb +++ b/test/rubygems/helper.rb @@ -563,14 +563,13 @@ def git_gem(name = "a", version = 1) 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 @@ def self.key_path(key_name) # :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 6cee7f86dcf0d1..1353f0fcdcdadf 100644 --- a/test/rubygems/package/tar_test_case.rb +++ b/test/rubygems/package/tar_test_case.rb @@ -67,7 +67,7 @@ def assert_headers_equal(expected, actual) 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 @@ def header(type, fname, dname, length, mode, mtime, checksum = nil, linkname = " 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 @@ def to_oct(n, pad_size) 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 @@ def util_dir_entry 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 0f2ea48198a932..1650910aaf4fb9 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 b572208e6b7817..eaa9fbad11342c 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -1464,6 +1464,9 @@ def test_load_plugins 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 @@ def test_load_user_installed_plugins 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 @@ def test_default_source_date_epoch_doesnt_change 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 fd61000b8aee05..2f5094c526f612 100644 --- a/test/rubygems/test_gem_bundler_version_finder.rb +++ b/test/rubygems/test_gem_bundler_version_finder.rb @@ -4,10 +4,10 @@ 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 6b341561870217..8108dc08705816 100644 --- a/test/rubygems/test_gem_command_manager.rb +++ b/test/rubygems/test_gem_command_manager.rb @@ -371,4 +371,29 @@ def execute 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 00000000000000..8be349742bd1e1 --- /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 14bddec485eb77..686be17c7ce703 100644 --- a/test/rubygems/test_gem_commands_install_command.rb +++ b/test/rubygems/test_gem_commands_install_command.rb @@ -7,6 +7,7 @@ class TestGemCommandsInstallCommand < Gem::TestCase def setup + @orig_args = Gem::Command.build_args super common_installer_setup @@ -14,7 +15,6 @@ def setup @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 5bf1d27eb9af82..db1583bcd3e05c 100644 --- a/test/rubygems/test_gem_commands_pristine_command.rb +++ b/test/rubygems/test_gem_commands_pristine_command.rb @@ -202,6 +202,54 @@ def test_execute_extensions_explicit 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 dd688a44216209..02c6a1b9577c66 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 083b831c98c122..e5090c852f4a50 100644 --- a/test/rubygems/test_gem_commands_uninstall_command.rb +++ b/test/rubygems/test_gem_commands_uninstall_command.rb @@ -22,12 +22,7 @@ def test_execute_all_named 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 @@ def test_execute_all 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 @@ def test_execute_outside_gem_home 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 @@ def test_execute_outside_gem_home 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 @@ def initial_install 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 34d89035952ea6..dcf02156df1905 100644 --- a/test/rubygems/test_gem_ext_builder.rb +++ b/test/rubygems/test_gem_ext_builder.rb @@ -5,6 +5,8 @@ class TestGemExtBuilder < Gem::TestCase def setup + @orig_DESTDIR = ENV["DESTDIR"] + @orig_make = ENV["make"] super @ext = File.join @tempdir, "ext" @@ -13,19 +15,15 @@ def setup 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 df50150d505853..3230ff3f21a276 100644 --- a/test/rubygems/test_gem_ext_cargo_builder.rb +++ b/test/rubygems/test_gem_ext_cargo_builder.rb @@ -87,7 +87,7 @@ def test_build_fail 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 6162043fa15dd7..4e2719fcbab173 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]] @@ -204,6 +207,17 @@ version = "1.1.0" 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" 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 b2f56a91977ef4..91627ada0fca9d 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 f0128febc82853..30b67280a9cb8c 100644 --- a/test/rubygems/test_gem_gem_runner.rb +++ b/test/rubygems/test_gem_gem_runner.rb @@ -3,13 +3,16 @@ 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 @@ def teardown 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 eebe4d86d0a14a..9d6215838d634a 100644 --- a/test/rubygems/test_gem_package.rb +++ b/test/rubygems/test_gem_package.rb @@ -1187,29 +1187,4 @@ def test_contents_from_io 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 19860eb7e82090..62e60489a6f333 100644 --- a/test/rubygems/test_gem_package_tar_reader.rb +++ b/test/rubygems/test_gem_package_tar_reader.rb @@ -56,12 +56,14 @@ def test_seek 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 @@ def test_seek_missing 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 ffb45421781acd..04a4579f9ad727 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 @@ def setup @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 @@ def teardown 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 @@ def test_read 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 @@ def test_read_small 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 @@ def test_rewind 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 5f54230c77f901..098df5a538f029 100644 --- a/test/rubygems/test_gem_remote_fetcher.rb +++ b/test/rubygems/test_gem_remote_fetcher.rb @@ -28,7 +28,7 @@ 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 5fd2bbb9c2f399..6474e500e2ccd2 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 @@ def test_gem_git 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 @@ def test_gem_bitbucket 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 @@ def test_gem_bitbucket_expand_path 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 @@ def test_gem_git_gist 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 @@ def test_gem_git_submodules 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 @@ def test_gem_github 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 @@ def test_gem_github_expand_path 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 @@ def test_git 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 @@ def test_git_source @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 ecb145e6974b71..69a3a328aa355b 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 @@ def test_parse_GIT write_lockfile <<-LOCKFILE GIT remote: git://example/a.git - revision: master + revision: abranch specs: a (2) b (>= 3) @@ -289,7 +289,7 @@ def test_parse_GIT 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 f7063b3d454fdc..6005b1e40fe5bd 100644 --- a/test/rubygems/test_gem_resolver_git_set.rb +++ b/test/rubygems/test_gem_resolver_git_set.rb @@ -13,7 +13,7 @@ def setup 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 @@ def test_add_git_gem 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 @@ def test_add_git_spec 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 @@ def test_find_all 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 @@ def test_find_all_local 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 @@ def test_root_dir 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 @@ def test_prefetch 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 @@ def test_prefetch_cache 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 @@ def test_prefetch_filter 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 454fd9c6e49535..615949bc2aaaca 100644 --- a/test/rubygems/test_gem_resolver_git_specification.rb +++ b/test/rubygems/test_gem_resolver_git_specification.rb @@ -84,7 +84,7 @@ def test_install_extension 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 e164215b57a84c..5a6c9ffc945268 100644 --- a/test/rubygems/test_gem_source.rb +++ b/test/rubygems/test_gem_source.rb @@ -30,7 +30,7 @@ def test_initialize_invalid_uri 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 147c6df1d27e09..c21dd88f8f6ded 100644 --- a/test/rubygems/test_gem_source_git.rb +++ b/test/rubygems/test_gem_source_git.rb @@ -10,7 +10,7 @@ def setup @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 @@ def test_checkout 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 @@ def test_checkout_submodules # 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 @@ def test_cache 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 @@ def test_rev_parse system @git, "checkout", "--quiet", "-b", "other" end - master_head = @head + default_head = @head git_gem "a", 2 @@ -186,7 +187,7 @@ def test_rev_parse 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 @@ def test_root_dir 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 @@ def test_spaceship 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 @@ def test_specs 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 @@ def test_uri_hash 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 7fb5017c59703a..5638e4b0b88422 100644 --- a/test/rubygems/test_gem_source_installed.rb +++ b/test/rubygems/test_gem_source_installed.rb @@ -11,7 +11,7 @@ def test_spaceship 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 ff9465d0ad3342..6344feec597767 100644 --- a/test/rubygems/test_gem_source_lock.rb +++ b/test/rubygems/test_gem_source_lock.rb @@ -18,7 +18,7 @@ def test_fetch_spec 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 @@ def test_equals2 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 @@ def test_spaceship 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 29846e5c136082..50a446c90f5d4a 100644 --- a/test/rubygems/test_gem_source_vendor.rb +++ b/test/rubygems/test_gem_source_vendor.rb @@ -12,7 +12,7 @@ def test_initialize 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 dda47e6bdfbd1f..56ca7acaf272cf 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -10,7 +10,7 @@ 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 @@ class TestGemSpecification < Gem::TestCase 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 @@ def test_validate_prerelease_dependencies_with_prerelease_version 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 @@ def test_find_by_name_prerelease 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 9237608d4a8ca9..f45c72b29cbff0 100644 --- a/test/rubygems/test_gem_version.rb +++ b/test/rubygems/test_gem_version.rb @@ -43,7 +43,7 @@ def test_class_create 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 @@ def test_hash 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 ce38d92d8df0a3..959907444c9eb3 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 dd98ddc729bde9..d78d63797b0f19 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 fbb8d919560e4f..176221d626fb03 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 54307cd0380c27..5917a682077802 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 75880b67616a0e..3affb58f74b053 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