From 8c74eaa08d25355560550787e38da079f1929abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 13 Jul 2022 19:10:47 +0200 Subject: [PATCH] [rubygems/rubygems] Fix `gem update --system` crash while regenerating binstubs Since a few commits ago, we no longer call `Gem::Specification.reset` after each invocation of `Gem::Installer#install`. This means we don't call it when the default Bundler is installed during `gem update --system`. This causes no issues until the end of the upgrade process when: * The previous default Bundler spec is removed from disk. * All specification stubs are turned into real specifications by loading them from disk. But the one for Bundler no longer exists so materializes to `nil` and regenerating binstubs crashes like this: ``` Bundler 2.4.0.dev installed RubyGems 3.4.0.dev installed Regenerating binstubs ERROR: While executing gem ... (NoMethodError) undefined method `platform' for nil:NilClass /Users/deivid/Code/rubygems/rubygems/lib/rubygems/commands/pristine_command.rb:116:in `block in execute' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/specification.rb:981:in `block in each' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/specification.rb:980:in `each' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/specification.rb:980:in `each' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/commands/pristine_command.rb:116:in `map' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/commands/pristine_command.rb:116:in `each' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/commands/pristine_command.rb:116:in `select' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/commands/pristine_command.rb:116:in `execute' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/command.rb:323:in `invoke_with_build_args' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/command.rb:301:in `invoke' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/commands/setup_command.rb:604:in `regenerate_binstubs' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/commands/setup_command.rb:183:in `execute' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/command.rb:323:in `invoke_with_build_args' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/command_manager.rb:185:in `process_args' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/command_manager.rb:149:in `run' /Users/deivid/Code/rubygems/rubygems/lib/rubygems/gem_runner.rb:51:in `run' setup.rb:33:in `
' ``` We fix it by more carefully managing the removal of the previous default Bundler gem. https://github.com/rubygems/rubygems/commit/9989f6d5af --- lib/rubygems/commands/setup_command.rb | 9 +++++---- lib/rubygems/specification.rb | 9 +++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb index 84e9210cfb..35b500936d 100644 --- a/lib/rubygems/commands/setup_command.rb +++ b/lib/rubygems/commands/setup_command.rb @@ -365,10 +365,11 @@ By default, this RubyGems will install gem as: bundler_spec = Dir.chdir("bundler") { Gem::Specification.load("bundler.gemspec") } - # Remove bundler-*.gemspec in default specification directory. - Dir.entries(specs_dir). - select {|gs| gs.start_with?("bundler-") }. - each {|gs| File.delete(File.join(specs_dir, gs)) } + current_default_spec = Gem::Specification.default_stubs.find {|s| s.name == "bundler" } + if current_default_spec + File.delete(current_default_spec.loaded_from) + Gem::Specification.remove_spec current_default_spec + end default_spec_path = File.join(specs_dir, "#{bundler_spec.full_name}.gemspec") Gem.write_binary(default_spec_path, bundler_spec.to_ruby) diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index f6704cbc17..edabcf392a 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -897,6 +897,15 @@ class Gem::Specification < Gem::BasicSpecification _resort!(stubs) end + ## + # Removes +spec+ from the known specs. + + def self.remove_spec(spec) + _all.delete spec.to_spec + stubs.delete spec + (@@stubs_by_name[spec.name] || []).delete spec + end + ## # Returns all specifications. This method is discouraged from use. # You probably want to use one of the Enumerable methods instead.