diff --git a/bundler/lib/bundler/definition.rb b/bundler/lib/bundler/definition.rb index 3c8c13b13031..21f3760e6d9d 100644 --- a/bundler/lib/bundler/definition.rb +++ b/bundler/lib/bundler/definition.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative "lockfile_parser" +require_relative "worker" module Bundler class Definition @@ -1100,7 +1101,23 @@ def source_requirements @source_requirements ||= find_source_requirements end + def preload_git_source_worker + @preload_git_source_worker ||= Bundler::Worker.new(5, "Git source preloading", ->(source, _) { source.specs }) + end + + def preload_git_sources + sources.git_sources.each {|source| preload_git_source_worker.enq(source) } + ensure + preload_git_source_worker.stop + end + def find_source_requirements + if Gem.ruby_version >= "3.3" + # Ruby 3.2 has a bug that incorrectly triggers a circular dependency warning. This version will continue to + # fetch git repositories one by one. + preload_git_sources + end + # Record the specs available in each gem's source, so that those # specs will be available later when the resolver knows where to # look for that gemspec (or its dependencies) diff --git a/bundler/lib/bundler/ui/shell.rb b/bundler/lib/bundler/ui/shell.rb index 6f080b64598f..b836208da8e3 100644 --- a/bundler/lib/bundler/ui/shell.rb +++ b/bundler/lib/bundler/ui/shell.rb @@ -17,6 +17,7 @@ def initialize(options = {}) @level = ENV["DEBUG"] ? "debug" : "info" @warning_history = [] @output_stream = :stdout + @thread_safe_logger_key = "logger_level_#{object_id}" end def add_color(string, *color) @@ -97,11 +98,13 @@ def level=(level) end def level(name = nil) - return @level unless name + current_level = Thread.current.thread_variable_get(@thread_safe_logger_key) || @level + return current_level unless name + unless index = LEVELS.index(name) raise "#{name.inspect} is not a valid level" end - index <= LEVELS.index(@level) + index <= LEVELS.index(current_level) end def output_stream=(symbol) @@ -167,12 +170,13 @@ def word_wrap(text, line_width = Thor::Terminal.terminal_width) end * "\n" end - def with_level(level) - original = @level - @level = level + def with_level(desired_level) + old_level = level + Thread.current.thread_variable_set(@thread_safe_logger_key, desired_level) + yield ensure - @level = original + Thread.current.thread_variable_set(@thread_safe_logger_key, old_level) end def with_output_stream(symbol) diff --git a/bundler/spec/bundler/ui/shell_spec.rb b/bundler/spec/bundler/ui/shell_spec.rb index 422c850a6536..83f147191ef1 100644 --- a/bundler/spec/bundler/ui/shell_spec.rb +++ b/bundler/spec/bundler/ui/shell_spec.rb @@ -81,4 +81,32 @@ end end end + + describe "threads" do + it "is thread safe when using with_level" do + stop_thr1 = false + stop_thr2 = false + + expect(subject.level).to eq("debug") + + thr1 = Thread.new do + subject.silence do + sleep(0.1) until stop_thr1 + end + + stop_thr2 = true + end + + thr2 = Thread.new do + subject.silence do + stop_thr1 = true + sleep(0.1) until stop_thr2 + end + end + + [thr1, thr2].each(&:join) + + expect(subject.level).to eq("debug") + end + end end diff --git a/bundler/spec/commands/ssl_spec.rb b/bundler/spec/commands/ssl_spec.rb index b4aca55194e2..4220731b6970 100644 --- a/bundler/spec/commands/ssl_spec.rb +++ b/bundler/spec/commands/ssl_spec.rb @@ -16,16 +16,17 @@ end end - @previous_level = Bundler.ui.level - Bundler.ui.instance_variable_get(:@warning_history).clear - @previous_client = Gem::Request::ConnectionPools.client + @previous_ui = Bundler.ui + Bundler.ui = Bundler::UI::Shell.new Bundler.ui.level = "info" + + @previous_client = Gem::Request::ConnectionPools.client Artifice.activate_with(@dummy_endpoint) Gem::Request::ConnectionPools.client = Gem::Net::HTTP end after(:each) do - Bundler.ui.level = @previous_level + Bundler.ui = @previous_ui Artifice.deactivate Gem::Request::ConnectionPools.client = @previous_client end