git-fastclone 1.2.7 → 1.3.1

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: 22f5537cbc1297198ea1aa106a305ef5ad9526dc24fc9a39048c6889e6d441b4
4
- data.tar.gz: 0f1f38ba2756185c3c02b7a5ec3cf40c01e4498e743388f41991ca1d817793de
3
+ metadata.gz: d83fead53c699b625667b994e06ab9cfe0c25ed62c01239c054ec1523109f986
4
+ data.tar.gz: 9e4513fe51d1e1deb357a1781fadd1df723997df527402c12beb436103ce7b2a
5
5
  SHA512:
6
- metadata.gz: 22634dabfc0c4d91682dc7db8d9e18e6cdc4a6d6db079a66c5e498ccc6ed70c8bcb7ccd24657b91e5142d0fe8be0f34169223d1d70cb1262ed3c6d88e3ec0e83
7
- data.tar.gz: 8dd73fac0eb7eec9b8e8ead616a48549860722982b756b950ad020cd295da4dffeaf4b89b7dad3c88f2dc4b82119a40172a499441924bbcf2885e280313850eb
6
+ metadata.gz: ecc286e3180698eab52e06e61d3bf62c27af6524a3ab398d86cf1bbbe336dd9c547609ec6744264be506ea9f41920cf68210b08d6fad752f5d8fc589b069e2be
7
+ data.tar.gz: 653a21b17f4a033ee23bd77f1953a72df9176006ebb320a33ed179e4f7f2a957133a4569f1d91722c65d2d638ee727eae15d41b6b73f5b133d138823365e6937
data/README.md CHANGED
@@ -44,8 +44,10 @@ Usage
44
44
  git fastclone [options] <git-repo-url>
45
45
 
46
46
  -b, --branch <branch> Clone a specific branch
47
- -v, --verbose Shows more info
48
- -c, --color Pretty colors!
47
+ -v, --verbose Verbose mode
48
+ -c, --color Display colored output
49
+ --config CONFIG Git config applied to the cloned repo
50
+ --lock-timeout N Timeout in seconds to acquire a lock on any reference repo.
49
51
 
50
52
  Change the default `REFERENCE_REPO_DIR` environment variable if necessary.
51
53
 
data/bin/git-fastclone CHANGED
@@ -15,7 +15,7 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
- $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
18
+ $LOAD_PATH.unshift(File.expand_path("#{File.dirname(__FILE__)}/../lib"))
19
19
 
20
20
  require 'git-fastclone'
21
21
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Version string for git-fastclone
4
4
  module GitFastCloneVersion
5
- VERSION = '1.2.7'.freeze
5
+ VERSION = '1.3.1'
6
6
  end
data/lib/git-fastclone.rb CHANGED
@@ -41,7 +41,7 @@ module GitFastClone
41
41
 
42
42
  def reference_repo_dir(url, reference_dir, using_local_repo)
43
43
  if using_local_repo
44
- File.join(reference_dir, 'local' + reference_repo_name(url))
44
+ File.join(reference_dir, "local#{reference_repo_name(url)}")
45
45
  else
46
46
  File.join(reference_dir, reference_repo_name(url))
47
47
  end
@@ -69,9 +69,9 @@ module GitFastClone
69
69
 
70
70
  include GitFastClone::UrlHelper
71
71
 
72
- DEFAULT_REFERENCE_REPO_DIR = '/var/tmp/git-fastclone/reference'.freeze
72
+ DEFAULT_REFERENCE_REPO_DIR = '/var/tmp/git-fastclone/reference'
73
73
 
74
- DEFAULT_GIT_ALLOW_PROTOCOL = 'file:git:http:https:ssh'.freeze
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
77
  :options, :logger, :abs_clone_path, :using_local_repo, :verbose, :color,
@@ -121,7 +121,7 @@ module GitFastClone
121
121
  puts "Cloning #{path_from_git_url(url)} to #{File.join(abs_clone_path, path)}"
122
122
  Terrapin::CommandLine.environment['GIT_ALLOW_PROTOCOL'] =
123
123
  ENV['GIT_ALLOW_PROTOCOL'] || DEFAULT_GIT_ALLOW_PROTOCOL
124
- clone(url, options[:branch], path)
124
+ clone(url, options[:branch], path, options[:config])
125
125
  end
126
126
 
127
127
  def parse_options
@@ -137,7 +137,7 @@ module GitFastClone
137
137
 
138
138
  opts.on('-v', '--verbose', 'Verbose mode') do
139
139
  self.verbose = true
140
- self.logger = Logger.new(STDOUT)
140
+ self.logger = Logger.new($stdout)
141
141
  logger.formatter = proc do |_severity, _datetime, _progname, msg|
142
142
  "#{msg}\n"
143
143
  end
@@ -148,6 +148,10 @@ module GitFastClone
148
148
  self.color = true
149
149
  end
150
150
 
151
+ opts.on('--config CONFIG', 'Git config applied to the cloned repo') do |config|
152
+ options[:config] = config
153
+ end
154
+
151
155
  opts.on('--lock-timeout N', 'Timeout in seconds to acquire a lock on any reference repo.
152
156
  Default is 0 which waits indefinitely.') do |timeout_secs|
153
157
  self.flock_timeout_secs = timeout_secs
@@ -159,7 +163,7 @@ module GitFastClone
159
163
  parse_options
160
164
 
161
165
  unless ARGV[0]
162
- STDERR.puts usage
166
+ warn usage
163
167
  exit(129)
164
168
  end
165
169
 
@@ -185,16 +189,41 @@ module GitFastClone
185
189
  [url, path, options]
186
190
  end
187
191
 
192
+ def clear_clone_dest_if_needed(attempt_number, clone_dest)
193
+ return unless attempt_number.positive?
194
+
195
+ dest_with_dotfiles = Dir.glob("#{clone_dest}/*", File::FNM_DOTMATCH)
196
+ dest_files = dest_with_dotfiles.reject { |f| %w[. ..].include?(File.basename(f)) }
197
+ return if dest_files.empty?
198
+
199
+ clear_clone_dest(dest_files)
200
+ end
201
+
202
+ def clear_clone_dest(dest_files)
203
+ puts 'Non-empty clone directory found, clearing its content now.'
204
+ FileUtils.rm_rf(dest_files)
205
+ end
206
+
188
207
  # Checkout to SOURCE_DIR. Update all submodules recursively. Use reference
189
208
  # repos everywhere for speed.
190
- def clone(url, rev, src_dir)
209
+ def clone(url, rev, src_dir, config)
210
+ clone_dest = File.join(abs_clone_path, src_dir).to_s
191
211
  initial_time = Time.now
192
212
 
193
- with_git_mirror(url) do |mirror|
194
- Terrapin::CommandLine.new('git clone', '--quiet --reference :mirror :url :path')
213
+ if Dir.exist?(clone_dest) && !Dir.empty?(clone_dest)
214
+ raise "Can't clone into an existing non-empty path: #{clone_dest}"
215
+ end
216
+
217
+ with_git_mirror(url) do |mirror, attempt_number|
218
+ clear_clone_dest_if_needed(attempt_number, clone_dest)
219
+
220
+ clone_command = '--quiet --reference :mirror :url :path'
221
+ clone_command += ' --config :config' unless config.nil?
222
+ Terrapin::CommandLine.new('git clone', clone_command)
195
223
  .run(mirror: mirror.to_s,
196
224
  url: url.to_s,
197
- path: File.join(abs_clone_path, src_dir).to_s)
225
+ path: clone_dest,
226
+ config: config.to_s)
198
227
  end
199
228
 
200
229
  # Only checkout if we're changing branches to a non-default branch
@@ -238,7 +267,7 @@ module GitFastClone
238
267
 
239
268
  def thread_update_submodule(submodule_url, submodule_path, threads, pwd)
240
269
  threads << Thread.new do
241
- with_git_mirror(submodule_url) do |mirror|
270
+ with_git_mirror(submodule_url) do |mirror, _|
242
271
  Terrapin::CommandLine.new('cd', ':dir; git submodule update --quiet --reference :mirror :path')
243
272
  .run(dir: File.join(abs_clone_path, pwd).to_s,
244
273
  mirror: mirror.to_s,
@@ -263,14 +292,12 @@ module GitFastClone
263
292
  lockfile.close
264
293
  end
265
294
 
266
- def with_reference_repo_thread_lock(url)
295
+ def with_reference_repo_thread_lock(url, &block)
267
296
  # We also need thread level locking because pre-fetch means multiple threads can
268
297
  # attempt to update the same repository from a single git-fastclone process
269
298
  # file locks in posix are tracked per process, not per userland thread.
270
299
  # This gives us the equivalent of pthread_mutex around these accesses.
271
- reference_mutex[reference_repo_name(url)].synchronize do
272
- yield
273
- end
300
+ reference_mutex[reference_repo_name(url)].synchronize(&block)
274
301
  end
275
302
 
276
303
  def update_submodule_reference(url, submodule_url_list)
@@ -331,6 +358,31 @@ module GitFastClone
331
358
  raise e if fail_hard
332
359
  end
333
360
 
361
+ def retriable_error?(error)
362
+ error_strings = [
363
+ 'fatal: missing blob object',
364
+ 'fatal: remote did not send all necessary objects',
365
+ /fatal: packed object [a-z0-9]+ \(stored in .*?\) is corrupt/,
366
+ /fatal: pack has \d+ unresolved delta/,
367
+ 'error: unable to read sha1 file of ',
368
+ 'fatal: did not receive expected object',
369
+ /^fatal: unable to read tree [a-z0-9]+\n^warning: Clone succeeded, but checkout failed/
370
+ ]
371
+ error.to_s =~ /^STDERR:\n.*^#{Regexp.union(error_strings)}/m
372
+ end
373
+
374
+ def print_formatted_error(error)
375
+ indented_error = error.to_s.split("\n").map { |s| "> #{s}\n" }.join
376
+ puts "Encountered a retriable error:\n#{indented_error}\n\nRemoving the fastclone cache."
377
+ end
378
+
379
+ # To avoid corruption of the cache, if we failed to update or check out we remove
380
+ # the cache directory entirely. This may cause the current clone to fail, but if the
381
+ # underlying error from git is transient it will not affect future clones.
382
+ def clear_cache(dir)
383
+ FileUtils.remove_entry_secure(dir, force: true)
384
+ end
385
+
334
386
  # This command will create and bring the mirror up-to-date on-demand,
335
387
  # blocking any code passed in while the mirror is brought up-to-date
336
388
  #
@@ -347,27 +399,18 @@ module GitFastClone
347
399
  # This makes sure we have control and unlock when the block returns:
348
400
  with_reference_repo_lock(url) do
349
401
  dir = reference_repo_dir(url, reference_dir, using_local_repo)
350
- retries_left = 1
402
+ retries_allowed = 1
403
+ attempt_number = 0
351
404
 
352
405
  begin
353
- yield dir
406
+ yield dir, attempt_number
354
407
  rescue Terrapin::ExitStatusError => e
355
- error_strings = [
356
- 'fatal: missing blob object',
357
- 'fatal: remote did not send all necessary objects',
358
- /fatal: packed object [a-z0-9]+ \(stored in .*?\) is corrupt/,
359
- /fatal: pack has \d+ unresolved delta/,
360
- 'error: unable to read sha1 file of ',
361
- 'fatal: did not receive expected object',
362
- /^fatal: unable to read tree [a-z0-9]+\n^warning: Clone succeeded, but checkout failed/
363
- ]
364
- if e.to_s =~ /^STDERR:\n.+^#{Regexp.union(error_strings)}/m
365
- # To avoid corruption of the cache, if we failed to update or check out we remove
366
- # the cache directory entirely. This may cause the current clone to fail, but if the
367
- # underlying error from git is transient it will not affect future clones.
368
- FileUtils.remove_entry_secure(dir, force: true)
369
- if retries_left > 0
370
- retries_left -= 1
408
+ if retriable_error?(e)
409
+ print_formatted_error(e)
410
+ clear_cache(dir)
411
+
412
+ if attempt_number < retries_allowed
413
+ attempt_number += 1
371
414
  retry
372
415
  end
373
416
  end
@@ -56,11 +56,22 @@ describe GitFastClone::Runner do
56
56
  let(:options) { { branch: placeholder_arg } }
57
57
 
58
58
  it 'should run with the correct args' do
59
- allow(subject).to receive(:parse_inputs) { [placeholder_arg, placeholder_arg, options] }
60
- expect(subject).to receive(:clone).with(placeholder_arg, placeholder_arg, placeholder_arg)
59
+ allow(subject).to receive(:parse_inputs) { [placeholder_arg, placeholder_arg, options, nil] }
60
+ expect(subject).to receive(:clone).with(placeholder_arg, placeholder_arg, placeholder_arg, nil)
61
61
 
62
62
  subject.run
63
63
  end
64
+
65
+ describe 'with custom configs' do
66
+ let(:options) { { branch: placeholder_arg, config: 'conf' } }
67
+
68
+ it 'should clone correctly' do
69
+ allow(subject).to receive(:parse_inputs) { [placeholder_arg, placeholder_arg, options, 'conf'] }
70
+ expect(subject).to receive(:clone).with(placeholder_arg, placeholder_arg, placeholder_arg, 'conf')
71
+
72
+ subject.run
73
+ end
74
+ end
64
75
  end
65
76
 
66
77
  describe '.parse_inputs' do
@@ -74,17 +85,69 @@ describe GitFastClone::Runner do
74
85
  end
75
86
 
76
87
  describe '.clone' do
77
- it 'should clone correctly' do
78
- terrapin_commandline_double = double('new_terrapin_commandline')
79
- allow(subject).to receive(:with_git_mirror) {}
88
+ let(:terrapin_commandline_double) { double('new_terrapin_commandline') }
89
+ before(:each) do
80
90
  allow(terrapin_commandline_double).to receive(:run) {}
81
- allow(Terrapin::CommandLine).to receive(:new) { terrapin_commandline_double }
82
-
83
91
  expect(Time).to receive(:now).twice { 0 }
84
- expect(Terrapin::CommandLine).to receive(:new)
85
- expect(terrapin_commandline_double).to receive(:run)
92
+ allow(Dir).to receive(:pwd) { '/pwd' }
93
+ allow(Dir).to receive(:chdir).and_yield
94
+ allow(subject).to receive(:with_git_mirror).and_yield('/cache', 0)
95
+ expect(subject).to receive(:clear_clone_dest_if_needed).once {}
96
+ end
97
+
98
+ it 'should clone correctly' do
99
+ expect(Terrapin::CommandLine).to receive(:new).with(
100
+ 'git clone',
101
+ '--quiet --reference :mirror :url :path'
102
+ ) { terrapin_commandline_double }
103
+ expect(Terrapin::CommandLine).to receive(:new).with(
104
+ 'git checkout',
105
+ '--quiet :rev'
106
+ ) { terrapin_commandline_double }
107
+ expect(terrapin_commandline_double).to receive(:run).with(
108
+ mirror: '/cache',
109
+ url: placeholder_arg,
110
+ path: '/pwd/.',
111
+ config: ''
112
+ )
113
+ expect(terrapin_commandline_double).to receive(:run).with(rev: placeholder_arg)
114
+
115
+ subject.clone(placeholder_arg, placeholder_arg, '.', nil)
116
+ end
117
+
118
+ it 'should clone correctly with custom configs' do
119
+ expect(Terrapin::CommandLine).to receive(:new).with(
120
+ 'git clone',
121
+ '--quiet --reference :mirror :url :path --config :config'
122
+ ) { terrapin_commandline_double }
123
+ expect(terrapin_commandline_double).to receive(:run).with(
124
+ mirror: '/cache',
125
+ url: placeholder_arg,
126
+ path: '/pwd/.',
127
+ config: 'config'
128
+ )
129
+
130
+ subject.clone(placeholder_arg, nil, '.', 'config')
131
+ end
132
+ end
133
+
134
+ describe '.clear_clone_dest_if_needed' do
135
+ it 'does not clear on first attempt' do
136
+ expect(Dir).not_to receive(:glob)
137
+ expect(subject).not_to receive(:clear_clone_dest)
138
+ subject.clear_clone_dest_if_needed(0, '/some/path')
139
+ end
140
+
141
+ it 'does not clear if the directory is only FNM_DOTMATCH self and parent refs' do
142
+ expect(Dir).to receive(:glob).and_return(%w[. ..])
143
+ expect(subject).not_to receive(:clear_clone_dest)
144
+ subject.clear_clone_dest_if_needed(1, '/some/path')
145
+ end
86
146
 
87
- subject.clone(placeholder_arg, placeholder_arg, '.')
147
+ it 'does clear if the directory is not empty' do
148
+ expect(Dir).to receive(:glob).and_return(%w[. .. /some/path/file.txt])
149
+ expect(subject).to receive(:clear_clone_dest) {}
150
+ subject.clear_clone_dest_if_needed(1, '/some/path')
88
151
  end
89
152
  end
90
153
 
@@ -268,209 +331,154 @@ describe GitFastClone::Runner do
268
331
  end
269
332
 
270
333
  describe '.with_git_mirror' do
271
- it 'should yield properly' do
334
+ before(:each) do
272
335
  allow(subject).to receive(:update_reference_repo) {}
273
- expect(subject).to receive(:reference_repo_dir)
336
+ allow(subject).to receive(:print_formatted_error) {}
337
+ expect(subject).to receive(:reference_repo_dir).and_return(test_reference_repo_dir)
274
338
  expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
339
+ end
275
340
 
276
- subject.with_git_mirror(test_url_valid) do
277
- yielded << test_url_valid
278
- end
341
+ def retriable_error
342
+ %(
343
+ STDOUT:
279
344
 
280
- expect(yielded).to eq([test_url_valid])
345
+ STDERR:
346
+
347
+ fatal: bad object ee35b1e14e7c3a53dcc14d82606e5b872f6a05a7
348
+ fatal: remote did not send all necessary objects
349
+ ).strip.split("\n").map(&:strip).join("\n")
281
350
  end
282
351
 
283
- it 'should retry when the cache looks corrupted' do
284
- allow(subject).to receive(:update_reference_repo) {}
285
- expect(subject).to receive(:reference_repo_dir)
286
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
352
+ def try_with_git_mirror(responses, results)
353
+ lambdas = responses.map do |response|
354
+ if response == true
355
+ # Simulate successful response
356
+ ->(url) { url }
357
+ else
358
+ # Simulate failed error response
359
+ ->(_url) { raise Terrapin::ExitStatusError, response }
360
+ end
361
+ end
287
362
 
288
- responses = [
289
- lambda { |_url|
290
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
291
- STDOUT:
292
-
293
- STDERR:
294
-
295
- fatal: bad object ee35b1e14e7c3a53dcc14d82606e5b872f6a05a7
296
- fatal: remote did not send all necessary objects
297
- ERROR
298
- },
299
- ->(url) { url }
300
- ]
301
- subject.with_git_mirror(test_url_valid) do
302
- yielded << responses.shift.call(test_url_valid)
363
+ subject.with_git_mirror(test_url_valid) do |url, attempt|
364
+ raise 'Not enough responses were provided!' if lambdas.empty?
365
+
366
+ yielded << [lambdas.shift.call(url), attempt]
303
367
  end
304
368
 
305
- expect(responses).to be_empty
306
- expect(yielded).to eq([test_url_valid])
369
+ expect(lambdas).to be_empty
370
+ expect(yielded).to eq(results)
307
371
  end
308
372
 
309
- it 'should retry when the clone succeeds but checkout fails with corrupt packed object' do
310
- allow(subject).to receive(:update_reference_repo) {}
311
- expect(subject).to receive(:reference_repo_dir)
312
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
313
-
314
- responses = [
315
- lambda { |_url|
316
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
317
- STDOUT:
318
-
319
- STDERR:
320
-
321
- fatal: packed object 7c4d79704f8adf701f38a7bfb3e33ec5342542f1 (stored in /private/var/tmp/git-fastclone/reference/some-repo.git/objects/pack/pack-d37d7ed3e88d6e5f0ac141a7b0a2b32baf6e21a0.pack) is corrupt
322
- warning: Clone succeeded, but checkout failed.
323
- You can inspect what was checked out with 'git status' and retry with 'git restore --source=HEAD :/'
324
- ERROR
325
- },
326
- ->(url) { url }
327
- ]
328
- subject.with_git_mirror(test_url_valid) do
329
- yielded << responses.shift.call(test_url_valid)
330
- end
373
+ it 'should yield properly' do
374
+ expect(subject).not_to receive(:clear_cache)
375
+ try_with_git_mirror([true], [[test_reference_repo_dir, 0]])
376
+ end
331
377
 
332
- expect(responses).to be_empty
333
- expect(yielded).to eq([test_url_valid])
378
+ it 'should retry once for retriable errors' do
379
+ expect(subject).to receive(:clear_cache).once {}
380
+ try_with_git_mirror([retriable_error, true], [[test_reference_repo_dir, 1]])
334
381
  end
335
382
 
336
- it 'should retry when the clone succeeds but checkout fails with unable to read tree' do
337
- allow(subject).to receive(:update_reference_repo) {}
338
- expect(subject).to receive(:reference_repo_dir)
339
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
383
+ it 'should only retry twice at most' do
384
+ expect(subject).to receive(:clear_cache).twice {}
385
+ expect do
386
+ try_with_git_mirror([retriable_error, retriable_error], [])
387
+ end.to raise_error(Terrapin::ExitStatusError)
388
+ end
340
389
 
341
- responses = [
342
- lambda { |_url|
343
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
344
- STDOUT:
345
-
346
- STDERR:
347
-
348
- error: Could not read 92cf57b8f07df010ab5f607b109c325e30e46235
349
- fatal: unable to read tree 0c32c0521d3b0bfb4e74e4a39b97a84d1a3bb9a1
350
- warning: Clone succeeded, but checkout failed.
351
- You can inspect what was checked out with 'git status'
352
- and retry with 'git restore --source=HEAD :/'
353
- ERROR
354
- },
355
- ->(url) { url }
356
- ]
357
- subject.with_git_mirror(test_url_valid) do
358
- yielded << responses.shift.call(test_url_valid)
359
- end
390
+ it 'should not retry for non-retriable errors' do
391
+ expect(subject).not_to receive(:clear_cache)
392
+ expect do
393
+ try_with_git_mirror(['Some unexpected error message'], [])
394
+ end.to raise_error(Terrapin::ExitStatusError)
395
+ end
396
+ end
360
397
 
361
- expect(responses).to be_empty
362
- expect(yielded).to eq([test_url_valid])
398
+ describe '.retriable_error?' do
399
+ def format_error(error)
400
+ error_wrapper = "STDOUT:\n\nSTDERR:\n#{error}"
401
+ error_wrapper.strip.lines.map(&:strip).join("\n")
363
402
  end
364
403
 
365
- it 'should retry when one delta is missing' do
366
- allow(subject).to receive(:update_reference_repo) {}
367
- expect(subject).to receive(:reference_repo_dir)
368
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
404
+ it 'not for a random error message' do
405
+ error = format_error 'random error message'
369
406
 
370
- responses = [
371
- lambda { |_url|
372
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
373
- STDOUT:
374
-
375
- STDERR:
376
-
377
- error: Could not read f7fad86d06fee0678f9af7203b6031feabb40c3e
378
- fatal: pack has 1 unresolved delta
379
- fatal: index-pack failed
380
- ERROR
381
- },
382
- ->(url) { url }
383
- ]
384
- subject.with_git_mirror(test_url_valid) do
385
- yielded << responses.shift.call(test_url_valid)
386
- end
407
+ expect(subject.retriable_error?(error)).to be_falsey
408
+ end
387
409
 
388
- expect(responses).to be_empty
389
- expect(yielded).to eq([test_url_valid])
410
+ it 'when the cache looks corrupted' do
411
+ error = format_error <<-ERROR
412
+ fatal: bad object ee35b1e14e7c3a53dcc14d82606e5b872f6a05a7
413
+ fatal: remote did not send all necessary objects
414
+ ERROR
415
+
416
+ expect(subject.retriable_error?(error)).to be_truthy
390
417
  end
391
418
 
392
- it 'should retry when deltas are missing' do
393
- allow(subject).to receive(:update_reference_repo) {}
394
- expect(subject).to receive(:reference_repo_dir)
395
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
419
+ it 'when the clone succeeds but checkout fails with corrupt packed object' do
420
+ error = format_error <<-ERROR
421
+ fatal: packed object 7c4d79704f8adf701f38a7bfb3e33ec5342542f1 (stored in /private/var/tmp/git-fastclone/reference/some-repo.git/objects/pack/pack-d37d7ed3e88d6e5f0ac141a7b0a2b32baf6e21a0.pack) is corrupt
422
+ warning: Clone succeeded, but checkout failed.
423
+ You can inspect what was checked out with 'git status' and retry with 'git restore --source=HEAD :/'
424
+ ERROR
396
425
 
397
- responses = [
398
- lambda { |_url|
399
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
400
- STDOUT:
401
-
402
- STDERR:
403
-
404
- error: Could not read f7fad86d06fee0678f9af7203b6031feabb40c3e
405
- fatal: pack has 138063 unresolved deltas
406
- fatal: index-pack failed
407
- ERROR
408
- },
409
- ->(url) { url }
410
- ]
411
- subject.with_git_mirror(test_url_valid) do
412
- yielded << responses.shift.call(test_url_valid)
413
- end
426
+ expect(subject.retriable_error?(error)).to be_truthy
427
+ end
414
428
 
415
- expect(responses).to be_empty
416
- expect(yielded).to eq([test_url_valid])
429
+ it 'when the clone succeeds but checkout fails with unable to read tree' do
430
+ error = format_error <<-ERROR
431
+ error: Could not read 92cf57b8f07df010ab5f607b109c325e30e46235
432
+ fatal: unable to read tree 0c32c0521d3b0bfb4e74e4a39b97a84d1a3bb9a1
433
+ warning: Clone succeeded, but checkout failed.
434
+ You can inspect what was checked out with 'git status'
435
+ and retry with 'git restore --source=HEAD :/'
436
+ ERROR
437
+
438
+ expect(subject.retriable_error?(error)).to be_truthy
417
439
  end
418
- end
419
440
 
420
- it 'should retry when the cache errors with unable to read sha1 file' do
421
- allow(subject).to receive(:update_reference_repo) {}
422
- expect(subject).to receive(:reference_repo_dir)
423
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
424
-
425
- responses = [
426
- lambda { |_url|
427
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
428
- STDOUT:
429
-
430
- STDERR:
431
-
432
- error: unable to read sha1 file of sqiosbuild/lib/action/action.rb (6113b739af82d8b07731de8a58d6e233301f80ab)
433
- fatal: unable to checkout working tree
434
- warning: Clone succeeded, but checkout failed.
435
- You can inspect what was checked out with 'git status'
436
- and retry with 'git restore --source=HEAD :/'
437
- ERROR
438
- },
439
- ->(url) { url }
440
- ]
441
- subject.with_git_mirror(test_url_valid) do
442
- yielded << responses.shift.call(test_url_valid)
443
- end
444
-
445
- expect(responses).to be_empty
446
- expect(yielded).to eq([test_url_valid])
447
- end
441
+ it 'when one delta is missing' do
442
+ error = format_error <<-ERROR
443
+ error: Could not read f7fad86d06fee0678f9af7203b6031feabb40c3e
444
+ fatal: pack has 1 unresolved delta
445
+ fatal: index-pack failed
446
+ ERROR
447
+
448
+ expect(subject.retriable_error?(error)).to be_truthy
449
+ end
448
450
 
449
- it 'should retry when the cache errors with did not receive expected object' do
450
- allow(subject).to receive(:update_reference_repo) {}
451
- expect(subject).to receive(:reference_repo_dir)
452
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
451
+ it 'when deltas are missing' do
452
+ error = format_error <<-ERROR
453
+ error: Could not read f7fad86d06fee0678f9af7203b6031feabb40c3e
454
+ fatal: pack has 138063 unresolved deltas
455
+ fatal: index-pack failed
456
+ ERROR
453
457
 
454
- responses = [
455
- lambda { |_url|
456
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
457
- STDOUT:
458
+ expect(subject.retriable_error?(error)).to be_truthy
459
+ end
458
460
 
459
- STDERR:
461
+ it 'when the cache errors with unable to read sha1 file' do
462
+ error = format_error <<-ERROR
463
+ error: unable to read sha1 file of sqiosbuild/lib/action/action.rb (6113b739af82d8b07731de8a58d6e233301f80ab)
464
+ fatal: unable to checkout working tree
465
+ warning: Clone succeeded, but checkout failed.
466
+ You can inspect what was checked out with 'git status'
467
+ and retry with 'git restore --source=HEAD :/'
468
+ ERROR
460
469
 
461
- error: Could not read 6682dfe81f66656436e60883dd795e7ec6735153
462
- error: Could not read 0cd3703c23fa44c0043d97fbc26356a23939f31b
463
- fatal: did not receive expected object 3c64c9dd49c79bd09aa13d4b05ac18263ca29ccd
464
- fatal: index-pack failed
465
- ERROR
466
- },
467
- ->(url) { url }
468
- ]
469
- subject.with_git_mirror(test_url_valid) do
470
- yielded << responses.shift.call(test_url_valid)
470
+ expect(subject.retriable_error?(error)).to be_truthy
471
471
  end
472
472
 
473
- expect(responses).to be_empty
474
- expect(yielded).to eq([test_url_valid])
473
+ it 'when the cache errors with did not receive expected object' do
474
+ error = format_error <<-ERROR
475
+ error: Could not read 6682dfe81f66656436e60883dd795e7ec6735153
476
+ error: Could not read 0cd3703c23fa44c0043d97fbc26356a23939f31b
477
+ fatal: did not receive expected object 3c64c9dd49c79bd09aa13d4b05ac18263ca29ccd
478
+ fatal: index-pack failed
479
+ ERROR
480
+
481
+ expect(subject.retriable_error?(error)).to be_truthy
482
+ end
475
483
  end
476
484
  end
@@ -62,7 +62,7 @@ describe GitFastClone::UrlHelper do
62
62
  allow(subject).to receive(:reference_repo_name) { test_reference_dir }
63
63
 
64
64
  expect(subject.reference_repo_dir(test_url_valid, test_reference_dir, false))
65
- .to eq(test_reference_dir + '/' + test_reference_dir)
65
+ .to eq("#{test_reference_dir}/#{test_reference_dir}")
66
66
  end
67
67
  end
68
68
 
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.2.7
4
+ version: 1.3.1
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: 2021-02-26 00:00:00.000000000 Z
12
+ date: 2022-12-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: colorize
@@ -61,7 +61,8 @@ files:
61
61
  homepage: http://square.github.io/git-fastclone/
62
62
  licenses:
63
63
  - Apache
64
- metadata: {}
64
+ metadata:
65
+ rubygems_mfa_required: 'true'
65
66
  post_install_message:
66
67
  rdoc_options: []
67
68
  require_paths:
@@ -70,18 +71,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
70
71
  requirements:
71
72
  - - ">="
72
73
  - !ruby/object:Gem::Version
73
- version: '0'
74
+ version: '2.7'
74
75
  required_rubygems_version: !ruby/object:Gem::Requirement
75
76
  requirements:
76
77
  - - ">="
77
78
  - !ruby/object:Gem::Version
78
79
  version: '0'
79
80
  requirements: []
80
- rubygems_version: 3.0.1
81
+ rubygems_version: 3.3.7
81
82
  signing_key:
82
83
  specification_version: 4
83
84
  summary: git-clone --recursive on steroids!
84
- test_files:
85
- - spec/git_fastclone_runner_spec.rb
86
- - spec/git_fastclone_url_helper_spec.rb
87
- - spec/spec_helper.rb
85
+ test_files: []