fc6aad0b44
* upstream/master: (1723 commits) Resolve "Editor icons" Refactor issuable destroy action Ignore routes matching legacy_*_redirect in route specs Gitlab::Git::RevList and LfsChanges use lazy popen Gitlab::Git::Popen can lazily hand output to a block Merge branch 'master-i18n' into 'master' Remove unique validation from external_url in Environment Expose `duration` in Job API entity Add TimeCop freeze for DST and Regular time Harcode project visibility update a changelog Put a condition to old migration that adds fast_forward column to MRs Expose project visibility as CI variable fix flaky tests by removing unneeded clicks and focus actions fix flaky test in gfm_autocomplete_spec.rb Use Gitlab::Git operations for repository mirroring Encapsulate git operations for mirroring in Gitlab::Git Create a Wiki Repository's raw_repository properly Add `Gitlab::Git::Repository#fetch` command Fix Gitlab::Metrics::System#real_time and #monotonic_time doc ...
100 lines
2.4 KiB
Ruby
100 lines
2.4 KiB
Ruby
# Gitaly note: JV: no RPC's here.
|
|
|
|
require 'open3'
|
|
|
|
module Gitlab
|
|
module Git
|
|
module Popen
|
|
FAST_GIT_PROCESS_TIMEOUT = 15.seconds
|
|
|
|
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
|
|
|
|
path ||= Dir.pwd
|
|
vars['PWD'] = path
|
|
options = { chdir: path }
|
|
|
|
cmd_output = ""
|
|
cmd_status = 0
|
|
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
|
|
yield(stdin) if block_given?
|
|
stdin.close
|
|
|
|
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
|
|
|
|
[cmd_output, cmd_status]
|
|
end
|
|
|
|
def popen_with_timeout(cmd, timeout, path, vars = {})
|
|
unless cmd.is_a?(Array)
|
|
raise "System commands must be given as an array of strings"
|
|
end
|
|
|
|
path ||= Dir.pwd
|
|
vars['PWD'] = path
|
|
|
|
unless File.directory?(path)
|
|
FileUtils.mkdir_p(path)
|
|
end
|
|
|
|
rout, wout = IO.pipe
|
|
rerr, werr = IO.pipe
|
|
|
|
pid = Process.spawn(vars, *cmd, out: wout, err: werr, chdir: path, pgroup: true)
|
|
|
|
begin
|
|
status = process_wait_with_timeout(pid, timeout)
|
|
|
|
# close write ends so we could read them
|
|
wout.close
|
|
werr.close
|
|
|
|
cmd_output = rout.readlines.join
|
|
cmd_output << rerr.readlines.join # Copying the behaviour of `popen` which merges stderr into output
|
|
|
|
[cmd_output, status.exitstatus]
|
|
rescue Timeout::Error => e
|
|
kill_process_group_for_pid(pid)
|
|
|
|
raise e
|
|
ensure
|
|
wout.close unless wout.closed?
|
|
werr.close unless werr.closed?
|
|
|
|
rout.close
|
|
rerr.close
|
|
end
|
|
end
|
|
|
|
def process_wait_with_timeout(pid, timeout)
|
|
deadline = timeout.seconds.from_now
|
|
wait_time = 0.01
|
|
|
|
while deadline > Time.now
|
|
sleep(wait_time)
|
|
_, status = Process.wait2(pid, Process::WNOHANG)
|
|
|
|
return status unless status.nil?
|
|
end
|
|
|
|
raise Timeout::Error, "Timeout waiting for process ##{pid}"
|
|
end
|
|
|
|
def kill_process_group_for_pid(pid)
|
|
Process.kill("KILL", -pid)
|
|
Process.wait(pid)
|
|
rescue Errno::ESRCH
|
|
end
|
|
end
|
|
end
|
|
end
|