summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Edwards-Jones <jedwardsjones@gitlab.com>2017-11-03 14:31:18 +0000
committerJames Edwards-Jones <jedwardsjones@gitlab.com>2017-11-06 10:20:13 +0000
commit95640413e64c4a857bcfcf7a4dcf0ce79189f894 (patch)
tree7367c047c57d1e93229039cbc28f39b4fcc08a43
parentdfe6c5390d73aff7b20876c3864524d49be74940 (diff)
downloadgitlab-ce-95640413e64c4a857bcfcf7a4dcf0ce79189f894.tar.gz
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.
-rw-r--r--lib/gitlab/git/popen.rb9
-rw-r--r--spec/lib/gitlab/git/popen_spec.rb17
2 files changed, 24 insertions, 2 deletions
diff --git a/lib/gitlab/git/popen.rb b/lib/gitlab/git/popen.rb
index b45da6020ee..d41fe78daa1 100644
--- a/lib/gitlab/git/popen.rb
+++ b/lib/gitlab/git/popen.rb
@@ -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
diff --git a/spec/lib/gitlab/git/popen_spec.rb b/spec/lib/gitlab/git/popen_spec.rb
index 2b65bc1cf15..b033ede9062 100644
--- a/spec/lib/gitlab/git/popen_spec.rb
+++ b/spec/lib/gitlab/git/popen_spec.rb
@@ -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