Gitlab::Git::Popen can lazily hand output to a block

This allows input to start processing immediately without waiting for the process to complete.
This also allows long or infinite inputs to be partially processed,
which will termiate the process when reading stops with SIGPIPE.
This commit is contained in:
James Edwards-Jones 2017-11-03 14:31:18 +00:00
parent dfe6c5390d
commit 95640413e6
2 changed files with 24 additions and 2 deletions

View file

@ -7,7 +7,7 @@ module Gitlab
module Popen
FAST_GIT_PROCESS_TIMEOUT = 15.seconds
def popen(cmd, path, vars = {})
def popen(cmd, path, vars = {}, lazy_block: nil)
unless cmd.is_a?(Array)
raise "System commands must be given as an array of strings"
end
@ -22,7 +22,12 @@ module Gitlab
yield(stdin) if block_given?
stdin.close
@cmd_output << stdout.read
if lazy_block
return lazy_block.call(stdout.lazy)
else
@cmd_output << stdout.read
end
@cmd_output << stderr.read
@cmd_status = wait_thr.value.exitstatus
end

View file

@ -53,6 +53,23 @@ describe 'Gitlab::Git::Popen' do
it { expect(status).to be_zero }
it { expect(output).to eq('hello') }
end
context 'with lazy block' do
it 'yields a lazy io' do
expect_lazy_io = lambda do |io|
expect(io).to be_a Enumerator::Lazy
expect(io.inspect).to include('#<IO:fd')
end
klass.new.popen(%w[ls], path, lazy_block: expect_lazy_io)
end
it "doesn't wait for process exit" do
Timeout.timeout(2) do
klass.new.popen(%w[yes], path, lazy_block: ->(io) {})
end
end
end
end
context 'popen_with_timeout' do