git-fastclone 1.4.0 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed5f3f84cbab65351479f22b659b912521699f542c898010527f25b7c3786c84
4
- data.tar.gz: 5f02716656bf0962d9a808ae368078f0e151e6bdfd3abba5b25503504f64194b
3
+ metadata.gz: da2eb17be407c42dbabdb002d8cee1fb56820f6543df47758b0376e84ed2344e
4
+ data.tar.gz: 0c1ca35bca06f608f8087670eba49cb5cc7d6c1af7c308e38159477455bd33c3
5
5
  SHA512:
6
- metadata.gz: a9219567c52c31d5219027adb6227c88970dd839fa8dd2f50258fc205adc3e4f9372d44d6f734242849865d7885f6b8f5a3fadfef05f77c88e24d8d85016a849
7
- data.tar.gz: c136b5240dfc87480457c924772944282f518f8c77d49473f41724449ae0566d8cc5476e6f698aa86de2b18f1f77a8995d0f5f3e8778546630e390d6ed2d2530
6
+ metadata.gz: 4a3d27522c916ac8883ad8abceb5d19529bb406409b3f7904652a54640b2edc91184420baad3118d8379b7c26ce2331018367c994375843c9130ae9511f1108f
7
+ data.tar.gz: 413c42026762c4bf1a80d693d2920c0a519969a7d5c4f215fb810b88f6dabb5e449a15d331214c0751e0daa92c4cbc706915e7ca4d9691ec2aaefb292b8cbef6
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Version string for git-fastclone
4
4
  module GitFastCloneVersion
5
- VERSION = '1.4.0'
5
+ VERSION = '1.4.2'
6
6
  end
data/lib/git-fastclone.rb CHANGED
@@ -74,7 +74,7 @@ module GitFastClone
74
74
  DEFAULT_GIT_ALLOW_PROTOCOL = 'file:git:http:https:ssh'
75
75
 
76
76
  attr_accessor :reference_dir, :prefetch_submodules, :reference_updated, :reference_mutex,
77
- :options, :abs_clone_path, :using_local_repo, :verbose, :color,
77
+ :options, :abs_clone_path, :using_local_repo, :verbose, :print_git_errors, :color,
78
78
  :flock_timeout_secs
79
79
 
80
80
  def initialize
@@ -100,6 +100,8 @@ module GitFastClone
100
100
 
101
101
  self.verbose = false
102
102
 
103
+ self.print_git_errors = false
104
+
103
105
  self.color = false
104
106
 
105
107
  self.flock_timeout_secs = 0
@@ -133,9 +135,15 @@ module GitFastClone
133
135
  end
134
136
 
135
137
  opts.on('-v', '--verbose', 'Verbose mode') do
138
+ puts '--print_git_errors is redundant when using --verbose' if print_git_errors
136
139
  self.verbose = true
137
140
  end
138
141
 
142
+ opts.on('--print_git_errors', 'Print git output if a command fails') do
143
+ puts '--print_git_errors is redundant when using --verbose' if verbose
144
+ self.print_git_errors = true
145
+ end
146
+
139
147
  opts.on('-c', '--color', 'Display colored output') do
140
148
  self.color = true
141
149
  end
@@ -212,13 +220,14 @@ module GitFastClone
212
220
  clone_commands = ['git', 'clone', verbose ? '--verbose' : '--quiet']
213
221
  clone_commands << '--reference' << mirror.to_s << url.to_s << clone_dest
214
222
  clone_commands << '--config' << config.to_s unless config.nil?
215
- fail_pipe_on_error(clone_commands, quiet: !verbose)
223
+ fail_on_error(*clone_commands, quiet: !verbose, print_on_failure: print_git_errors)
216
224
  end
217
225
 
218
226
  # Only checkout if we're changing branches to a non-default branch
219
227
  if rev
220
228
  Dir.chdir(File.join(abs_clone_path, src_dir)) do
221
- fail_pipe_on_error(['git', 'checkout', '--quiet', rev.to_s], quiet: !verbose)
229
+ fail_on_error('git', 'checkout', '--quiet', rev.to_s, quiet: !verbose,
230
+ print_on_failure: print_git_errors)
222
231
  end
223
232
  end
224
233
 
@@ -243,7 +252,8 @@ module GitFastClone
243
252
  submodule_url_list = []
244
253
  output = ''
245
254
  Dir.chdir(File.join(abs_clone_path, pwd).to_s) do
246
- output = fail_on_error('git', 'submodule', 'init', quiet: !verbose)
255
+ output = fail_on_error('git', 'submodule', 'init', quiet: !verbose,
256
+ print_on_failure: print_git_errors)
247
257
  end
248
258
 
249
259
  output.split("\n").each do |line|
@@ -261,10 +271,9 @@ module GitFastClone
261
271
  threads << Thread.new do
262
272
  with_git_mirror(submodule_url) do |mirror, _|
263
273
  Dir.chdir(File.join(abs_clone_path, pwd).to_s) do
264
- fail_pipe_on_error(
265
- ['git', 'submodule', verbose ? nil : '--quiet', 'update', '--reference', mirror.to_s,
266
- submodule_path.to_s].compact, quiet: !verbose
267
- )
274
+ cmd = ['git', 'submodule', verbose ? nil : '--quiet', 'update', '--reference', mirror.to_s,
275
+ submodule_path.to_s].compact
276
+ fail_on_error(*cmd, quiet: !verbose, print_on_failure: print_git_errors)
268
277
  end
269
278
  end
270
279
 
@@ -337,21 +346,13 @@ module GitFastClone
337
346
  # that this repo has been updated on this run of fastclone
338
347
  def store_updated_repo(url, mirror, repo_name, fail_hard)
339
348
  unless Dir.exist?(mirror)
340
- fail_pipe_on_error(
341
- ['git', 'clone', verbose ? '--verbose' : '--quiet', '--mirror', url.to_s,
342
- mirror.to_s], quiet: !verbose
343
- )
349
+ fail_on_error('git', 'clone', verbose ? '--verbose' : '--quiet', '--mirror', url.to_s, mirror.to_s,
350
+ quiet: !verbose, print_on_failure: print_git_errors)
344
351
  end
345
352
 
346
353
  Dir.chdir(mirror) do
347
354
  cmd = ['git', 'remote', verbose ? '--verbose' : nil, 'update', '--prune'].compact
348
- if verbose
349
- fail_pipe_on_error(cmd, quiet: !verbose)
350
- else
351
- # Because above operation might spit out a lot to stderr, we use this to swallow them
352
- # and only display if the operation return non 0 exit code
353
- fail_on_error(*cmd, quiet: !verbose)
354
- end
355
+ fail_on_error(*cmd, quiet: !verbose, print_on_failure: print_git_errors)
355
356
  end
356
357
  reference_updated[repo_name] = true
357
358
  rescue RunnerExecutionRuntimeError => e
@@ -20,37 +20,9 @@ module RunnerExecution
20
20
  end
21
21
  end
22
22
 
23
- # Wrapper around open3.pipeline_r which fails on error.
24
- # and stops users from invoking the shell by accident.
25
- def fail_pipe_on_error(*cmd_list, quiet: false, **opts)
26
- print_command('Running Pipeline:', cmd_list) unless quiet
27
-
28
- env = opts.delete(:env) { {} }
29
- raise ArgumentError, "The :env option must be a hash, not #{env.inspect}" unless env.is_a?(Hash)
30
-
31
- cmd_list.map! { |cmd| shell_safe(cmd).unshift(env) }
32
-
33
- output, *status_list = Open3.pipeline_r(*cmd_list, opts) do |out, wait_threads|
34
- out_reader = Thread.new do
35
- if quiet
36
- output = out.read
37
- else
38
- # Output from pipeline should go to stdout and also get returned for
39
- # processing if necessary.
40
- output = tee(out, STDOUT)
41
- end
42
- out.close
43
- output
44
- end
45
- [out_reader.value] + wait_threads.map(&:value)
46
- end
47
- exit_on_status(output, cmd_list, status_list, quiet: quiet)
48
- end
49
- module_function :fail_pipe_on_error
50
-
51
23
  # Runs a command that fails on error.
52
24
  # Uses popen2e wrapper. Handles bad statuses with potential for retries.
53
- def fail_on_error(*cmd, stdin_data: nil, binmode: false, quiet: false, **opts)
25
+ def fail_on_error(*cmd, stdin_data: nil, binmode: false, quiet: false, print_on_failure: false, **opts)
54
26
  print_command('Running Shell Safe Command:', [cmd]) unless quiet
55
27
  shell_safe_cmd = shell_safe(cmd)
56
28
  retry_times = opts[:retry] || 0
@@ -67,7 +39,9 @@ module RunnerExecution
67
39
  end
68
40
 
69
41
  # Get out with the status, good or bad.
70
- exit_on_status(output, [shell_safe_cmd], [status], quiet: quiet)
42
+ # When quiet, we don't need to print the output, as it is already streamed from popen2e_wrapper
43
+ needs_print_on_failure = quiet && print_on_failure
44
+ exit_on_status(output, [shell_safe_cmd], [status], quiet: quiet, print_on_failure: needs_print_on_failure)
71
45
  end
72
46
  module_function :fail_on_error
73
47
 
@@ -183,20 +157,21 @@ module RunnerExecution
183
157
  # return code of the first one.
184
158
  #
185
159
  # Otherwise returns first argument (output)
186
- def exit_on_status(output, cmd_list, status_list, quiet: false)
160
+ def exit_on_status(output, cmd_list, status_list, quiet: false, print_on_failure: false)
187
161
  status_list.each_index do |index|
188
162
  status = status_list[index]
189
163
  cmd = cmd_list[index]
190
- check_status(cmd, status, output: output, quiet: quiet)
164
+ check_status(cmd, status, output: output, quiet: quiet, print_on_failure: print_on_failure)
191
165
  end
192
166
 
193
167
  output
194
168
  end
195
169
  module_function :exit_on_status
196
170
 
197
- def check_status(cmd, status, output: nil, quiet: false)
171
+ def check_status(cmd, status, output: nil, quiet: false, print_on_failure: false)
198
172
  return if status.exited? && status.exitstatus == 0
199
173
 
174
+ logger.info(output) if print_on_failure
200
175
  # If we exited nonzero or abnormally, print debugging info and explode.
201
176
  if status.exited?
202
177
  logger.debug("Process Exited normally. Exit status:#{status.exitstatus}") unless quiet
@@ -89,7 +89,7 @@ describe GitFastClone::Runner do
89
89
  describe '.clone' do
90
90
  let(:runner_execution_double) { double('runner_execution') }
91
91
  before(:each) do
92
- allow(runner_execution_double).to receive(:fail_pipe_on_error) {}
92
+ allow(runner_execution_double).to receive(:fail_on_error) {}
93
93
  allow(Dir).to receive(:pwd) { '/pwd' }
94
94
  allow(Dir).to receive(:chdir).and_yield
95
95
  allow(subject).to receive(:with_git_mirror).and_yield('/cache', 0)
@@ -97,13 +97,13 @@ describe GitFastClone::Runner do
97
97
  end
98
98
 
99
99
  it 'should clone correctly' do
100
- expect(subject).to receive(:fail_pipe_on_error).with(
101
- ['git', 'checkout', '--quiet', 'PH'],
102
- { quiet: true }
100
+ expect(subject).to receive(:fail_on_error).with(
101
+ 'git', 'checkout', '--quiet', 'PH',
102
+ { quiet: true, print_on_failure: false }
103
103
  ) { runner_execution_double }
104
- expect(subject).to receive(:fail_pipe_on_error).with(
105
- ['git', 'clone', '--quiet', '--reference', '/cache', 'PH', '/pwd/.'],
106
- { quiet: true }
104
+ expect(subject).to receive(:fail_on_error).with(
105
+ 'git', 'clone', '--quiet', '--reference', '/cache', 'PH', '/pwd/.',
106
+ { quiet: true, print_on_failure: false }
107
107
  ) { runner_execution_double }
108
108
 
109
109
  subject.clone(placeholder_arg, placeholder_arg, '.', nil)
@@ -111,26 +111,41 @@ describe GitFastClone::Runner do
111
111
 
112
112
  it 'should clone correctly with verbose mode on' do
113
113
  subject.verbose = true
114
- expect(subject).to receive(:fail_pipe_on_error).with(
115
- ['git', 'checkout', '--quiet', 'PH'],
116
- { quiet: false }
114
+ expect(subject).to receive(:fail_on_error).with(
115
+ 'git', 'checkout', '--quiet', 'PH',
116
+ { quiet: false, print_on_failure: false }
117
117
  ) { runner_execution_double }
118
- expect(subject).to receive(:fail_pipe_on_error).with(
119
- ['git', 'clone', '--verbose', '--reference', '/cache', 'PH', '/pwd/.'],
120
- { quiet: false }
118
+ expect(subject).to receive(:fail_on_error).with(
119
+ 'git', 'clone', '--verbose', '--reference', '/cache', 'PH', '/pwd/.',
120
+ { quiet: false, print_on_failure: false }
121
121
  ) { runner_execution_double }
122
122
 
123
123
  subject.clone(placeholder_arg, placeholder_arg, '.', nil)
124
124
  end
125
125
 
126
126
  it 'should clone correctly with custom configs' do
127
- expect(subject).to receive(:fail_pipe_on_error).with(
128
- ['git', 'clone', '--quiet', '--reference', '/cache', 'PH', '/pwd/.', '--config', 'config'],
129
- { quiet: true }
127
+ expect(subject).to receive(:fail_on_error).with(
128
+ 'git', 'clone', '--quiet', '--reference', '/cache', 'PH', '/pwd/.', '--config', 'config',
129
+ { quiet: true, print_on_failure: false }
130
130
  ) { runner_execution_double }
131
131
 
132
132
  subject.clone(placeholder_arg, nil, '.', 'config')
133
133
  end
134
+
135
+ context 'with printing errors' do
136
+ before(:each) do
137
+ subject.print_git_errors = true
138
+ end
139
+
140
+ it 'prints failures' do
141
+ expect(subject).to receive(:fail_on_error).with(
142
+ 'git', 'clone', '--quiet', '--reference', '/cache', 'PH', '/pwd/.', '--config', 'config',
143
+ { quiet: true, print_on_failure: true }
144
+ ) { runner_execution_double }
145
+
146
+ subject.clone(placeholder_arg, nil, '.', 'config')
147
+ end
148
+ end
134
149
  end
135
150
 
136
151
  describe '.clear_clone_dest_if_needed' do
@@ -298,7 +313,7 @@ describe GitFastClone::Runner do
298
313
  status = double('status')
299
314
  allow(status).to receive(:exitstatus).and_return(1)
300
315
  ex = RunnerExecution::RunnerExecutionRuntimeError.new(status, 'cmd')
301
- allow(subject).to receive(:fail_pipe_on_error) { raise ex }
316
+ allow(subject).to receive(:fail_on_error) { raise ex }
302
317
  expect(FileUtils).to receive(:remove_entry_secure).with(placeholder_arg, force: true)
303
318
  expect do
304
319
  subject.store_updated_repo(placeholder_arg, placeholder_arg, placeholder_arg, true)
@@ -311,7 +326,7 @@ describe GitFastClone::Runner do
311
326
  status = double('status')
312
327
  allow(status).to receive(:exitstatus).and_return(1)
313
328
  ex = RunnerExecution::RunnerExecutionRuntimeError.new(status, 'cmd')
314
- allow(subject).to receive(:fail_pipe_on_error) { raise ex }
329
+ allow(subject).to receive(:fail_on_error) { raise ex }
315
330
  expect(FileUtils).to receive(:remove_entry_secure).with(placeholder_arg, force: true)
316
331
  expect do
317
332
  subject.store_updated_repo(placeholder_arg, placeholder_arg, placeholder_arg, false)
@@ -322,7 +337,7 @@ describe GitFastClone::Runner do
322
337
  let(:placeholder_hash) { {} }
323
338
 
324
339
  it 'should correctly update the hash' do
325
- allow(subject).to receive(:fail_pipe_on_error)
340
+ allow(subject).to receive(:fail_on_error)
326
341
  allow(Dir).to receive(:chdir) {}
327
342
 
328
343
  subject.reference_updated = placeholder_hash
@@ -367,12 +382,6 @@ describe GitFastClone::Runner do
367
382
  let(:expected_commands) { [] }
368
383
 
369
384
  before(:each) do
370
- allow(subject).to receive(:fail_pipe_on_error) { |*params|
371
- command = params[0]
372
- expect(expected_commands.length).to be > 0
373
- expected_command = expected_commands.shift
374
- expect(command).to eq(expected_command)
375
- }
376
385
  allow(subject).to receive(:fail_on_error) { |*params|
377
386
  # last one is an argument `quiet:`
378
387
  command = params.first(params.size - 1)
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2023 Square Inc.
4
+
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'spec_helper'
18
+ require 'git-fastclone'
19
+
20
+ # Integration tests use real demo_tool.sh to inspect the E2E behavior
21
+ describe RunnerExecution do
22
+ subject { described_class }
23
+ let(:external_tool) { "#{__dir__}/../script/spec_demo_tool.sh" }
24
+ let(:logger) { double('logger') }
25
+
26
+ before do
27
+ allow($stdout).to receive(:puts)
28
+ allow(logger).to receive(:info)
29
+ allow(logger).to receive(:debug)
30
+ allow(logger).to receive(:warn)
31
+ allow(RunnerExecution).to receive(:logger).and_return(logger)
32
+ end
33
+
34
+ describe '.fail_on_error' do
35
+ it 'should log failure info on command error' do
36
+ expect(logger).to receive(:info).with("My error output\n")
37
+
38
+ expect do
39
+ described_class.fail_on_error(external_tool, '1', 'My error output', quiet: true,
40
+ print_on_failure: true)
41
+ end.to raise_error(RunnerExecution::RunnerExecutionRuntimeError)
42
+ end
43
+
44
+ it 'should not log failure output on command success' do
45
+ expect($stdout).not_to receive(:info)
46
+
47
+ described_class.fail_on_error(external_tool, '0', 'My success output', quiet: true,
48
+ print_on_failure: true)
49
+ end
50
+
51
+ it 'should not log failure output when not in the quiet mode' do
52
+ expect($stdout).not_to receive(:info)
53
+
54
+ described_class.fail_on_error(external_tool, '0', 'My success output', quiet: false,
55
+ print_on_failure: true)
56
+ end
57
+ end
58
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-fastclone
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Tauraso
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-03-13 00:00:00.000000000 Z
12
+ date: 2023-06-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: colorize
@@ -44,6 +44,7 @@ files:
44
44
  - lib/runner_execution.rb
45
45
  - spec/git_fastclone_runner_spec.rb
46
46
  - spec/git_fastclone_url_helper_spec.rb
47
+ - spec/runner_execution_spec.rb
47
48
  - spec/spec_helper.rb
48
49
  homepage: http://square.github.io/git-fastclone/
49
50
  licenses:
@@ -65,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
66
  - !ruby/object:Gem::Version
66
67
  version: '0'
67
68
  requirements: []
68
- rubygems_version: 3.1.6
69
+ rubygems_version: 3.4.13
69
70
  signing_key:
70
71
  specification_version: 4
71
72
  summary: git-clone --recursive on steroids!