From e223dde3295cc59415c289305fd8d706d46cd2e9 Mon Sep 17 00:00:00 2001 From: Samuel Giddins Date: Wed, 30 Aug 2023 15:21:20 -0700 Subject: [PATCH] [rubygems/rubygems] Allow bundle pristine to run in parallel Also fix running when BUNDLE_NO_INSTALL happens to be set, same as with install/update commands https://github.com/rubygems/rubygems/commit/a555fd6ccd --- lib/bundler/cli.rb | 4 +- lib/bundler/cli/pristine.rb | 68 ++++++++++++--------- lib/bundler/installer/parallel_installer.rb | 11 ++-- 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 022d2c9142a602..da5950917bbed9 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -660,7 +660,9 @@ def issue D def pristine(*gems) require_relative "cli/pristine" - Pristine.new(gems).run + Bundler.settings.temporary(no_install: false) do + Pristine.new(gems).run + end end if Bundler.feature_flag.plugins? diff --git a/lib/bundler/cli/pristine.rb b/lib/bundler/cli/pristine.rb index d6654f8053593f..e0d7452c440b47 100644 --- a/lib/bundler/cli/pristine.rb +++ b/lib/bundler/cli/pristine.rb @@ -12,40 +12,48 @@ def run definition.validate_runtime! installer = Bundler::Installer.new(Bundler.root, definition) - Bundler.load.specs.each do |spec| - next if spec.name == "bundler" # Source::Rubygems doesn't install bundler - next if !@gems.empty? && !@gems.include?(spec.name) - - gem_name = "#{spec.name} (#{spec.version}#{spec.git_version})" - gem_name += " (#{spec.platform})" if !spec.platform.nil? && spec.platform != Gem::Platform::RUBY - - case source = spec.source - when Source::Rubygems - cached_gem = spec.cache_file - unless File.exist?(cached_gem) - Bundler.ui.error("Failed to pristine #{gem_name}. Cached gem #{cached_gem} does not exist.") + ProcessLock.lock do + installed_specs = definition.specs.reject do |spec| + next if spec.name == "bundler" # Source::Rubygems doesn't install bundler + next if !@gems.empty? && !@gems.include?(spec.name) + + gem_name = "#{spec.name} (#{spec.version}#{spec.git_version})" + gem_name += " (#{spec.platform})" if !spec.platform.nil? && spec.platform != Gem::Platform::RUBY + + case source = spec.source + when Source::Rubygems + cached_gem = spec.cache_file + unless File.exist?(cached_gem) + Bundler.ui.error("Failed to pristine #{gem_name}. Cached gem #{cached_gem} does not exist.") + next + end + + FileUtils.rm_rf spec.full_gem_path + when Source::Git + if source.local? + Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is locally overridden.") + next + end + + source.remote! + if extension_cache_path = source.extension_cache_path(spec) + FileUtils.rm_rf extension_cache_path + end + FileUtils.rm_rf spec.extension_dir + FileUtils.rm_rf spec.full_gem_path + else + Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is sourced from local path.") next end - FileUtils.rm_rf spec.full_gem_path - when Source::Git - if source.local? - Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is locally overridden.") - next - end + true + end.map(&:name) - source.remote! - if extension_cache_path = source.extension_cache_path(spec) - FileUtils.rm_rf extension_cache_path - end - FileUtils.rm_rf spec.extension_dir - FileUtils.rm_rf spec.full_gem_path - else - Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is sourced from local path.") - next - end - - Bundler::GemInstaller.new(spec, installer, false, 0, true).install_from_spec + jobs = installer.send(:installation_parallelization, {}) + pristine_count = definition.specs.count - installed_specs.count + # allow a pristining a single gem to skip the parallel worker + jobs = [jobs, pristine_count].min + ParallelInstaller.call(installer, definition.specs, jobs, false, true, skip: installed_specs) end end end diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb index 02ae7d535f8d76..e745088f81ad5d 100644 --- a/lib/bundler/installer/parallel_installer.rb +++ b/lib/bundler/installer/parallel_installer.rb @@ -62,20 +62,23 @@ def to_s end end - def self.call(*args) - new(*args).call + def self.call(*args, **kwargs) + new(*args, **kwargs).call end attr_reader :size - def initialize(installer, all_specs, size, standalone, force) + def initialize(installer, all_specs, size, standalone, force, skip: nil) @installer = installer @size = size @standalone = standalone @force = force @specs = all_specs.map {|s| SpecInstallation.new(s) } + @specs.each do |spec_install| + spec_install.state = :installed if skip.include?(spec_install.name) + end if skip @spec_set = all_specs - @rake = @specs.find {|s| s.name == "rake" } + @rake = @specs.find {|s| s.name == "rake" unless s.installed? } end def call