diff --git a/Library/Homebrew/context.rb b/Library/Homebrew/context.rb index 5a9c410bcc..d1f01c48de 100644 --- a/Library/Homebrew/context.rb +++ b/Library/Homebrew/context.rb @@ -33,27 +33,33 @@ module Context @verbose = verbose end + sig { returns(T::Boolean) } def debug? @debug == true end + sig { returns(T::Boolean) } def quiet? @quiet == true end + sig { returns(T::Boolean) } def verbose? @verbose == true end end + sig { returns(T::Boolean) } def debug? Context.current.debug? end + sig { returns(T::Boolean) } def quiet? Context.current.quiet? end + sig { returns(T::Boolean) } def verbose? Context.current.verbose? end @@ -69,8 +75,10 @@ module Context Thread.current[:context] = new_context - yield - ensure - Thread.current[:context] = old_context + begin + yield + ensure + Thread.current[:context] = old_context + end end end diff --git a/Library/Homebrew/system_command.rb b/Library/Homebrew/system_command.rb index 61d93f8bd7..d598e923e0 100644 --- a/Library/Homebrew/system_command.rb +++ b/Library/Homebrew/system_command.rb @@ -244,9 +244,13 @@ class SystemCommand write_input_to(raw_stdin) raw_stdin.close_write + thread_context = Context.current thread_ready_queue = Queue.new thread_done_queue = Queue.new line_thread = Thread.new do + # Ensure the new thread inherits the current context. + Context.current = thread_context + Thread.handle_interrupt(ProcessTerminatedInterrupt => :never) do thread_ready_queue << true each_line_from [raw_stdout, raw_stderr], &block diff --git a/Library/Homebrew/test/spec_helper.rb b/Library/Homebrew/test/spec_helper.rb index 658a5c7040..3b17651570 100644 --- a/Library/Homebrew/test/spec_helper.rb +++ b/Library/Homebrew/test/spec_helper.rb @@ -109,6 +109,8 @@ RSpec.configure do |config| config.include(FileUtils) + config.include(Context) + config.include(RuboCop::RSpec::ExpectOffense) config.include(Test::Helper::Cask) @@ -236,6 +238,7 @@ RSpec.configure do |config| example.example.set_exception(e) ensure ENV.replace(@__env) + Context.current = Context::ContextStruct.new $stdout.reopen(@__stdout) $stderr.reopen(@__stderr) diff --git a/Library/Homebrew/test/system_command_spec.rb b/Library/Homebrew/test/system_command_spec.rb index d06c5430be..4a800c9184 100644 --- a/Library/Homebrew/test/system_command_spec.rb +++ b/Library/Homebrew/test/system_command_spec.rb @@ -166,7 +166,7 @@ describe SystemCommand do include_examples("it returns '1 2 3 4 5 6'") end - context "with print_stdout" do + context "with `print_stdout: true`" do before do options.merge!(print_stdout: true) end @@ -180,7 +180,38 @@ describe SystemCommand do include_examples("it returns '1 2 3 4 5 6'") end - context "without print_stderr" do + context "with `print_stdout: :debug`" do + before do + options.merge!(print_stdout: :debug) + end + + it "echoes only STDERR output" do + expect { described_class.run(command, **options) } + .to output("2\n4\n6\n").to_stderr + .and not_to_output.to_stdout + end + + context "when `debug?` is true" do + let(:options) do + { args: [ + "-c", + "for i in $(seq 1 2 5); do echo $i; sleep 1; echo $(($i + 1)) >&2; done", + ] } + end + + it "echoes the command and all output to STDERR when `debug?` is true" do + with_context debug: true do + expect { described_class.run(command, **options) } + .to output(/\A.*#{Regexp.escape(command)}.*\n1\n2\n3\n4\n5\n6\n\Z/).to_stderr + .and not_to_output.to_stdout + end + end + end + + include_examples("it returns '1 2 3 4 5 6'") + end + + context "with `print_stderr: false`" do before do options.merge!(print_stderr: false) end @@ -188,13 +219,13 @@ describe SystemCommand do it "echoes nothing" do expect do described_class.run(command, **options) - end.to output("").to_stdout + end.not_to output.to_stdout end include_examples("it returns '1 2 3 4 5 6'") end - context "with print_stdout but without print_stderr" do + context "with `print_stdout: true` and `print_stderr: false`" do before do options.merge!(print_stdout: true, print_stderr: false) end