diff --git a/ruby/lib/gitlab/git/hook.rb b/ruby/lib/gitlab/git/hook.rb index 222c93d3bb34f43ab19ea0bf697f152302c24748..e1c4932d33f9650c9171d1ed2b4872c4585fe584 100644 --- a/ruby/lib/gitlab/git/hook.rb +++ b/ruby/lib/gitlab/git/hook.rb @@ -72,9 +72,16 @@ module Gitlab stdin.close + # Reading from pipes needs to be non-blocking to avoid deadlocks + out_reader = Thread.new { stdout.read } + err_reader = Thread.new { stderr.read } + + stdout_messages = out_reader.value + stderr_messages = err_reader.value + unless wait_thr.value == 0 exit_status = false - exit_message = retrieve_error_message(stderr, stdout) + exit_message = retrieve_error_messages(stderr_messages, stdout_messages) end end @@ -91,13 +98,18 @@ module Gitlab vars = env_base_vars(gl_id, gl_username) stdout, stderr, status = Open3.capture3(vars, path, *args, options) - [status.success?, stderr.presence || stdout] + + exit_message = if status.success? + nil + else + retrieve_error_messages(stderr, stdout) + end + + [status.success?, exit_message] end - def retrieve_error_message(stderr, stdout) - err_message = stderr.read - err_message = err_message.blank? ? stdout.read : err_message - err_message + def retrieve_error_messages(stderr, stdout) + stderr.presence || stdout end def env_base_vars(gl_id, gl_username) diff --git a/ruby/lib/gitlab/git/repository_mirroring.rb b/ruby/lib/gitlab/git/repository_mirroring.rb index 26534051df62eb7ab9c1ad3331a933cc754ba3bd..6b0130a3d0398fe35c00aa439c7e36d076ac1404 100644 --- a/ruby/lib/gitlab/git/repository_mirroring.rb +++ b/ruby/lib/gitlab/git/repository_mirroring.rb @@ -90,16 +90,11 @@ module Gitlab end def list_remote_tags(remote, env:) - tag_list, exit_code, error = nil cmd = %W[#{Gitlab.config.git.bin_path} --git-dir=#{path} ls-remote --tags #{remote}] - Open3.popen3(env, *cmd) do |_stdin, stdout, stderr, wait_thr| - tag_list = stdout.read - error = stderr.read - exit_code = wait_thr.value.exitstatus - end + tag_list, error, status = Open3.capture3(env, *cmd) - raise RemoteError, error unless exit_code.zero? + raise RemoteError, error unless status.success? tag_list.split("\n") end diff --git a/ruby/spec/lib/gitlab/git/hook_spec.rb b/ruby/spec/lib/gitlab/git/hook_spec.rb index 2e1551ba106285571ff4f72dc67d81c5e71d5f15..1678fe7ee1ac729486128480463431e839dc58fe 100644 --- a/ruby/spec/lib/gitlab/git/hook_spec.rb +++ b/ruby/spec/lib/gitlab/git/hook_spec.rb @@ -68,7 +68,14 @@ describe Gitlab::Git::Hook do end context 'when the hooks are successful' do - let(:script) { "#!/bin/sh\nexit 0\n" } + let(:script) do + <<-SCRIPT + #!/bin/sh + echo "message"; + 1>&2 echo "error"; + exit 0; + SCRIPT + end it 'returns true' do hook_names.each do |hook| @@ -78,10 +85,26 @@ describe Gitlab::Git::Hook do expect(trigger_result.first).to be(true) end end + + it 'returns no messages' do + hook_names.each do |hook| + trigger_result = described_class.new(hook, repo) + .trigger('user-456', 'admin', '0' * 40, 'a' * 40, 'master') + + expect(trigger_result.last).to be_blank + end + end end context 'when the hooks fail' do - let(:script) { "#!/bin/sh\nexit 1\n" } + let(:script) do + <<-SCRIPT + #!/bin/sh + echo "message"; + 1>&2 echo "error"; + exit 1; + SCRIPT + end it 'returns false' do hook_names.each do |hook| @@ -91,6 +114,15 @@ describe Gitlab::Git::Hook do expect(trigger_result.first).to be(false) end end + + it 'returns messages' do + hook_names.each do |hook| + trigger_result = described_class.new(hook, repo) + .trigger('user-1', 'admin', '0' * 40, 'a' * 40, 'master') + + expect(trigger_result.last).to eq("error\n") + end + end end end end diff --git a/ruby/spec/lib/gitlab/git/repository_spec.rb b/ruby/spec/lib/gitlab/git/repository_spec.rb index 4364b8818aafe55ef4e6e48a8af73e7c61d4381d..6345fd39736cf837eaf00c4054e8c3d5fa3fc199 100644 --- a/ruby/spec/lib/gitlab/git/repository_spec.rb +++ b/ruby/spec/lib/gitlab/git/repository_spec.rb @@ -131,6 +131,37 @@ describe Gitlab::Git::Repository do # rubocop:disable Metrics/BlockLength it { expect(repository.tags.size).to eq(SeedRepo::Repo::TAGS.size) } end + describe '#remote_tags' do + context 'when listing of remote tags is successful' do + let(:target) { 'dc872e9fa6963f8f03da6c8f6f264d0845d6b092' } + let(:ref) { 'v1.10.0' } + let(:tag_list) { "#{target}\trefs/tags/#{ref}\n" } + let(:ref_name) { 'refs/heads/feature' } + + it 'returns the list of remote tags' do + expect(Open3).to receive(:capture3).and_return( + [tag_list, '', double('success?' => true)] + ) + + response = repository.remote_tags(ref_name) + + expect(response.length).to eq(1) + expect(response.first.target).to eq(target) + expect(response.first.name).to eq(ref) + end + end + + context 'when listing of remote tags fails' do + let(:ref_name) { 'invalid-ref-name' } + + it 'should raise an error' do + expect { repository.remote_tags(ref_name) }.to( + raise_error(Gitlab::Git::RepositoryMirroring::RemoteError) + ) + end + end + end + describe '#empty?' do it { expect(repository).not_to be_empty } end