git-fastclone 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e6bcf21a8d3b9c1b30b93efc17487fa8dfa0c319b8636efc1f96a430e27c876
4
- data.tar.gz: 7720184507344bc94ad63e45b135e0948aab6776d371867159a4cd84dcfd2a0d
3
+ metadata.gz: 9d5ae1cf32d0c0f13534a9e7d986cb4d1845cec980085b27332cf366829b0ccf
4
+ data.tar.gz: 909d9f98c4d09d9f52b70539749639b3fc57464f6b622ac9620d24509e690476
5
5
  SHA512:
6
- metadata.gz: 13e48d37e5162a8288da830232476fb12ba7b8bb01c0ef0834a8eb372a224c0a2f8430d10002a9c54c1ec7fc7713db384ef32d0da9f1707366663bbe10e9ff04
7
- data.tar.gz: 1f74af758cfbd3f47f98f6d4fc1a47e24bf574f9e7b58116b9aa3f6186ab334e66c66e413416641b7ce6494b3099b45158142fbd88b4c1bb3a0bb48a86c9b912
6
+ metadata.gz: 8f20b952bdfe79605012ae2b365dfb01b3621239f75e2e9e40aef76d807c9a2eb1af5b876dbd34032311efeea0ae1a9990c18295c3256ea8d7a622f99b1e1032
7
+ data.tar.gz: e9fa4c99ce664a1c1780b76a5a952cdef57b1d43ddf92ea870bba0c29a76fce2e2b0281b6698af48c5a60c1446427c66614fb226cb159f3870b614e06b2d897b
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.3.0'.freeze
5
+ VERSION = '1.3.2'
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,
@@ -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
@@ -163,7 +163,7 @@ module GitFastClone
163
163
  parse_options
164
164
 
165
165
  unless ARGV[0]
166
- STDERR.puts usage
166
+ warn usage
167
167
  exit(129)
168
168
  end
169
169
 
@@ -189,18 +189,40 @@ module GitFastClone
189
189
  [url, path, options]
190
190
  end
191
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
+
192
207
  # Checkout to SOURCE_DIR. Update all submodules recursively. Use reference
193
208
  # repos everywhere for speed.
194
209
  def clone(url, rev, src_dir, config)
210
+ clone_dest = File.join(abs_clone_path, src_dir).to_s
195
211
  initial_time = Time.now
196
212
 
197
- with_git_mirror(url) do |mirror|
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
+
198
220
  clone_command = '--quiet --reference :mirror :url :path'
199
221
  clone_command += ' --config :config' unless config.nil?
200
222
  Terrapin::CommandLine.new('git clone', clone_command)
201
223
  .run(mirror: mirror.to_s,
202
224
  url: url.to_s,
203
- path: File.join(abs_clone_path, src_dir).to_s,
225
+ path: clone_dest,
204
226
  config: config.to_s)
205
227
  end
206
228
 
@@ -245,7 +267,7 @@ module GitFastClone
245
267
 
246
268
  def thread_update_submodule(submodule_url, submodule_path, threads, pwd)
247
269
  threads << Thread.new do
248
- with_git_mirror(submodule_url) do |mirror|
270
+ with_git_mirror(submodule_url) do |mirror, _|
249
271
  Terrapin::CommandLine.new('cd', ':dir; git submodule update --quiet --reference :mirror :path')
250
272
  .run(dir: File.join(abs_clone_path, pwd).to_s,
251
273
  mirror: mirror.to_s,
@@ -270,14 +292,12 @@ module GitFastClone
270
292
  lockfile.close
271
293
  end
272
294
 
273
- def with_reference_repo_thread_lock(url)
295
+ def with_reference_repo_thread_lock(url, &block)
274
296
  # We also need thread level locking because pre-fetch means multiple threads can
275
297
  # attempt to update the same repository from a single git-fastclone process
276
298
  # file locks in posix are tracked per process, not per userland thread.
277
299
  # This gives us the equivalent of pthread_mutex around these accesses.
278
- reference_mutex[reference_repo_name(url)].synchronize do
279
- yield
280
- end
300
+ reference_mutex[reference_repo_name(url)].synchronize(&block)
281
301
  end
282
302
 
283
303
  def update_submodule_reference(url, submodule_url_list)
@@ -338,6 +358,32 @@ module GitFastClone
338
358
  raise e if fail_hard
339
359
  end
340
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, url)
383
+ FileUtils.remove_entry_secure(dir, force: true)
384
+ reference_updated.delete(reference_repo_name(url))
385
+ end
386
+
341
387
  # This command will create and bring the mirror up-to-date on-demand,
342
388
  # blocking any code passed in while the mirror is brought up-to-date
343
389
  #
@@ -346,42 +392,31 @@ module GitFastClone
346
392
  # moment means we only need to synchronize our own threads in case a single
347
393
  # submodule url is included twice via multiple dependency paths
348
394
  def with_git_mirror(url)
395
+ retries_allowed ||= 1
396
+ attempt_number ||= 0
397
+
349
398
  update_reference_repo(url, true)
399
+ dir = reference_repo_dir(url, reference_dir, using_local_repo)
350
400
 
351
401
  # Sometimes remote updates involve re-packing objects on a different thread
352
402
  # We grab the reference repo lock here just to make sure whatever thread
353
403
  # ended up doing the update is done with its housekeeping.
354
404
  # This makes sure we have control and unlock when the block returns:
355
405
  with_reference_repo_lock(url) do
356
- dir = reference_repo_dir(url, reference_dir, using_local_repo)
357
- retries_left = 1
358
-
359
- begin
360
- yield dir
361
- rescue Terrapin::ExitStatusError => e
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
- if e.to_s =~ /^STDERR:\n.+^#{Regexp.union(error_strings)}/m
372
- # To avoid corruption of the cache, if we failed to update or check out we remove
373
- # the cache directory entirely. This may cause the current clone to fail, but if the
374
- # underlying error from git is transient it will not affect future clones.
375
- FileUtils.remove_entry_secure(dir, force: true)
376
- if retries_left > 0
377
- retries_left -= 1
378
- retry
379
- end
380
- end
406
+ yield dir, attempt_number
407
+ end
408
+ rescue Terrapin::ExitStatusError => e
409
+ if retriable_error?(e)
410
+ print_formatted_error(e)
411
+ clear_cache(dir, url)
381
412
 
382
- raise
413
+ if attempt_number < retries_allowed
414
+ attempt_number += 1
415
+ retry
383
416
  end
384
417
  end
418
+
419
+ raise
385
420
  end
386
421
 
387
422
  def usage
@@ -24,7 +24,7 @@ describe GitFastClone::Runner do
24
24
  let(:test_reference_repo_dir) { '/var/tmp/git-fastclone/reference/test_reference_dir' }
25
25
  let(:placeholder_arg) { 'PH' }
26
26
 
27
- let(:lockfile) do
27
+ def create_lockfile_double
28
28
  lockfile = double
29
29
  expect(lockfile).to receive(:flock).with(File::LOCK_EX).once
30
30
  expect(lockfile).to receive(:flock).with(File::LOCK_UN).once
@@ -32,6 +32,8 @@ describe GitFastClone::Runner do
32
32
  lockfile
33
33
  end
34
34
 
35
+ let(:lockfile) { create_lockfile_double }
36
+
35
37
  before do
36
38
  stub_const('ARGV', ['ssh://git@git.com/git-fastclone.git', 'test_reference_dir'])
37
39
  end
@@ -91,7 +93,8 @@ describe GitFastClone::Runner do
91
93
  expect(Time).to receive(:now).twice { 0 }
92
94
  allow(Dir).to receive(:pwd) { '/pwd' }
93
95
  allow(Dir).to receive(:chdir).and_yield
94
- allow(subject).to receive(:with_git_mirror).and_yield('/cache')
96
+ allow(subject).to receive(:with_git_mirror).and_yield('/cache', 0)
97
+ expect(subject).to receive(:clear_clone_dest_if_needed).once {}
95
98
  end
96
99
 
97
100
  it 'should clone correctly' do
@@ -114,21 +117,39 @@ describe GitFastClone::Runner do
114
117
  subject.clone(placeholder_arg, placeholder_arg, '.', nil)
115
118
  end
116
119
 
117
- describe 'with custom configs' do
118
- it 'should clone correctly' 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
120
+ it 'should clone correctly with custom configs' do
121
+ expect(Terrapin::CommandLine).to receive(:new).with(
122
+ 'git clone',
123
+ '--quiet --reference :mirror :url :path --config :config'
124
+ ) { terrapin_commandline_double }
125
+ expect(terrapin_commandline_double).to receive(:run).with(
126
+ mirror: '/cache',
127
+ url: placeholder_arg,
128
+ path: '/pwd/.',
129
+ config: 'config'
130
+ )
131
+
132
+ subject.clone(placeholder_arg, nil, '.', 'config')
133
+ end
134
+ end
135
+
136
+ describe '.clear_clone_dest_if_needed' do
137
+ it 'does not clear on first attempt' do
138
+ expect(Dir).not_to receive(:glob)
139
+ expect(subject).not_to receive(:clear_clone_dest)
140
+ subject.clear_clone_dest_if_needed(0, '/some/path')
141
+ end
142
+
143
+ it 'does not clear if the directory is only FNM_DOTMATCH self and parent refs' do
144
+ expect(Dir).to receive(:glob).and_return(%w[. ..])
145
+ expect(subject).not_to receive(:clear_clone_dest)
146
+ subject.clear_clone_dest_if_needed(1, '/some/path')
147
+ end
148
+
149
+ it 'does clear if the directory is not empty' do
150
+ expect(Dir).to receive(:glob).and_return(%w[. .. /some/path/file.txt])
151
+ expect(subject).to receive(:clear_clone_dest) {}
152
+ subject.clear_clone_dest_if_needed(1, '/some/path')
132
153
  end
133
154
  end
134
155
 
@@ -312,209 +333,200 @@ describe GitFastClone::Runner do
312
333
  end
313
334
 
314
335
  describe '.with_git_mirror' do
315
- it 'should yield properly' do
316
- allow(subject).to receive(:update_reference_repo) {}
317
- expect(subject).to receive(:reference_repo_dir)
318
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
336
+ def retriable_error
337
+ %(
338
+ STDOUT:
319
339
 
320
- subject.with_git_mirror(test_url_valid) do
321
- yielded << test_url_valid
322
- end
340
+ STDERR:
323
341
 
324
- expect(yielded).to eq([test_url_valid])
342
+ fatal: bad object ee35b1e14e7c3a53dcc14d82606e5b872f6a05a7
343
+ fatal: remote did not send all necessary objects
344
+ ).strip.split("\n").map(&:strip).join("\n")
325
345
  end
326
346
 
327
- it 'should retry when the cache looks corrupted' do
328
- allow(subject).to receive(:update_reference_repo) {}
329
- expect(subject).to receive(:reference_repo_dir)
330
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
331
-
332
- responses = [
333
- lambda { |_url|
334
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
335
- STDOUT:
347
+ def try_with_git_mirror(responses, results)
348
+ lambdas = responses.map do |response|
349
+ if response == true
350
+ # Simulate successful response
351
+ ->(url) { url }
352
+ else
353
+ # Simulate failed error response
354
+ ->(_url) { raise Terrapin::ExitStatusError, response }
355
+ end
356
+ end
336
357
 
337
- STDERR:
358
+ subject.with_git_mirror(test_url_valid) do |url, attempt|
359
+ raise 'Not enough responses were provided!' if lambdas.empty?
338
360
 
339
- fatal: bad object ee35b1e14e7c3a53dcc14d82606e5b872f6a05a7
340
- fatal: remote did not send all necessary objects
341
- ERROR
342
- },
343
- ->(url) { url }
344
- ]
345
- subject.with_git_mirror(test_url_valid) do
346
- yielded << responses.shift.call(test_url_valid)
361
+ yielded << [lambdas.shift.call(url), attempt]
347
362
  end
348
363
 
349
- expect(responses).to be_empty
350
- expect(yielded).to eq([test_url_valid])
364
+ expect(lambdas).to be_empty
365
+ expect(yielded).to eq(results)
351
366
  end
352
367
 
353
- it 'should retry when the clone succeeds but checkout fails with corrupt packed object' do
354
- allow(subject).to receive(:update_reference_repo) {}
355
- expect(subject).to receive(:reference_repo_dir)
356
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
357
-
358
- responses = [
359
- lambda { |_url|
360
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
361
- STDOUT:
368
+ let(:expected_commands) { [] }
369
+ let(:expected_commands_args) { [] }
362
370
 
363
- STDERR:
364
-
365
- fatal: packed object 7c4d79704f8adf701f38a7bfb3e33ec5342542f1 (stored in /private/var/tmp/git-fastclone/reference/some-repo.git/objects/pack/pack-d37d7ed3e88d6e5f0ac141a7b0a2b32baf6e21a0.pack) is corrupt
366
- warning: Clone succeeded, but checkout failed.
367
- You can inspect what was checked out with 'git status' and retry with 'git restore --source=HEAD :/'
368
- ERROR
369
- },
370
- ->(url) { url }
371
- ]
372
- subject.with_git_mirror(test_url_valid) do
373
- yielded << responses.shift.call(test_url_valid)
371
+ before(:each) do
372
+ expect(expected_commands.length).to eq(expected_commands_args.length)
373
+ allow(Terrapin::CommandLine).to receive(:new) do |*command|
374
+ expect(expected_commands.length).to be > 0
375
+ expected_command = expected_commands.shift
376
+ expected_args = expected_commands_args.shift
377
+ expect(command).to eq(expected_command)
378
+ stub = double(Terrapin::CommandLine)
379
+ expect(stub).to receive(:run).with(expected_args)
380
+ stub
374
381
  end
375
382
 
376
- expect(responses).to be_empty
377
- expect(yielded).to eq([test_url_valid])
383
+ allow(subject).to receive(:print_formatted_error) {}
384
+ allow(subject).to receive(:reference_repo_dir).and_return(test_reference_repo_dir)
385
+ allow(subject).to receive(:reference_repo_lock_file) { create_lockfile_double }
378
386
  end
379
387
 
380
- it 'should retry when the clone succeeds but checkout fails with unable to read tree' do
381
- allow(subject).to receive(:update_reference_repo) {}
382
- expect(subject).to receive(:reference_repo_dir)
383
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
384
-
385
- responses = [
386
- lambda { |_url|
387
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
388
- STDOUT:
388
+ after(:each) do
389
+ expect(expected_commands).to be_empty
390
+ end
389
391
 
390
- STDERR:
392
+ def clone_cmds
393
+ [
394
+ ['git clone', '--mirror :url :mirror'],
395
+ ['cd', ':path; git remote update --prune']
396
+ ]
397
+ end
391
398
 
392
- error: Could not read 92cf57b8f07df010ab5f607b109c325e30e46235
393
- fatal: unable to read tree 0c32c0521d3b0bfb4e74e4a39b97a84d1a3bb9a1
394
- warning: Clone succeeded, but checkout failed.
395
- You can inspect what was checked out with 'git status'
396
- and retry with 'git restore --source=HEAD :/'
397
- ERROR
399
+ def clone_args
400
+ [
401
+ {
402
+ mirror: test_reference_repo_dir,
403
+ url: test_url_valid
398
404
  },
399
- ->(url) { url }
405
+ {
406
+ path: test_reference_repo_dir
407
+ }
400
408
  ]
401
- subject.with_git_mirror(test_url_valid) do
402
- yielded << responses.shift.call(test_url_valid)
409
+ end
410
+
411
+ context 'expecting 1 clone attempt' do
412
+ let(:expected_commands) { clone_cmds }
413
+ let(:expected_commands_args) { clone_args }
414
+
415
+ it 'should succeed with a successful clone' do
416
+ expect(subject).not_to receive(:clear_cache)
417
+ try_with_git_mirror([true], [[test_reference_repo_dir, 0]])
403
418
  end
404
419
 
405
- expect(responses).to be_empty
406
- expect(yielded).to eq([test_url_valid])
420
+ it 'should fail after a non-retryable clone error' do
421
+ expect(subject).not_to receive(:clear_cache)
422
+ expect do
423
+ try_with_git_mirror(['Some unexpected error message'], [])
424
+ end.to raise_error(Terrapin::ExitStatusError)
425
+ end
407
426
  end
408
427
 
409
- it 'should retry when one delta is missing' do
410
- allow(subject).to receive(:update_reference_repo) {}
411
- expect(subject).to receive(:reference_repo_dir)
412
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
413
-
414
- responses = [
415
- lambda { |_url|
416
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
417
- STDOUT:
428
+ context 'expecting 2 clone attempts' do
429
+ let(:expected_commands) { clone_cmds + clone_cmds }
430
+ let(:expected_commands_args) { clone_args + clone_args }
418
431
 
419
- STDERR:
432
+ it 'should succeed after a single retryable clone failure' do
433
+ expect(subject).to receive(:clear_cache).and_call_original
434
+ try_with_git_mirror([retriable_error, true], [[test_reference_repo_dir, 1]])
435
+ end
420
436
 
421
- error: Could not read f7fad86d06fee0678f9af7203b6031feabb40c3e
422
- fatal: pack has 1 unresolved delta
423
- fatal: index-pack failed
424
- ERROR
425
- },
426
- ->(url) { url }
427
- ]
428
- subject.with_git_mirror(test_url_valid) do
429
- yielded << responses.shift.call(test_url_valid)
437
+ it 'should fail after two retryable clone failures' do
438
+ expect(subject).to receive(:clear_cache).twice.and_call_original
439
+ expect do
440
+ try_with_git_mirror([retriable_error, retriable_error], [])
441
+ end.to raise_error(Terrapin::ExitStatusError)
430
442
  end
443
+ end
444
+ end
431
445
 
432
- expect(responses).to be_empty
433
- expect(yielded).to eq([test_url_valid])
446
+ describe '.retriable_error?' do
447
+ def format_error(error)
448
+ error_wrapper = "STDOUT:\n\nSTDERR:\n#{error}"
449
+ error_wrapper.strip.lines.map(&:strip).join("\n")
434
450
  end
435
451
 
436
- it 'should retry when deltas are missing' do
437
- allow(subject).to receive(:update_reference_repo) {}
438
- expect(subject).to receive(:reference_repo_dir)
439
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
452
+ it 'not for a random error message' do
453
+ error = format_error 'random error message'
440
454
 
441
- responses = [
442
- lambda { |_url|
443
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
444
- STDOUT:
455
+ expect(subject.retriable_error?(error)).to be_falsey
456
+ end
445
457
 
446
- STDERR:
458
+ it 'when the cache looks corrupted' do
459
+ error = format_error <<-ERROR
460
+ fatal: bad object ee35b1e14e7c3a53dcc14d82606e5b872f6a05a7
461
+ fatal: remote did not send all necessary objects
462
+ ERROR
447
463
 
448
- error: Could not read f7fad86d06fee0678f9af7203b6031feabb40c3e
449
- fatal: pack has 138063 unresolved deltas
450
- fatal: index-pack failed
451
- ERROR
452
- },
453
- ->(url) { url }
454
- ]
455
- subject.with_git_mirror(test_url_valid) do
456
- yielded << responses.shift.call(test_url_valid)
457
- end
464
+ expect(subject.retriable_error?(error)).to be_truthy
465
+ end
458
466
 
459
- expect(responses).to be_empty
460
- expect(yielded).to eq([test_url_valid])
467
+ it 'when the clone succeeds but checkout fails with corrupt packed object' do
468
+ error = format_error <<-ERROR
469
+ fatal: packed object 7c4d79704f8adf701f38a7bfb3e33ec5342542f1 (stored in /private/var/tmp/git-fastclone/reference/some-repo.git/objects/pack/pack-d37d7ed3e88d6e5f0ac141a7b0a2b32baf6e21a0.pack) is corrupt
470
+ warning: Clone succeeded, but checkout failed.
471
+ You can inspect what was checked out with 'git status' and retry with 'git restore --source=HEAD :/'
472
+ ERROR
473
+
474
+ expect(subject.retriable_error?(error)).to be_truthy
461
475
  end
462
- end
463
476
 
464
- it 'should retry when the cache errors with unable to read sha1 file' do
465
- allow(subject).to receive(:update_reference_repo) {}
466
- expect(subject).to receive(:reference_repo_dir)
467
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
468
-
469
- responses = [
470
- lambda { |_url|
471
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
472
- STDOUT:
473
-
474
- STDERR:
475
-
476
- error: unable to read sha1 file of sqiosbuild/lib/action/action.rb (6113b739af82d8b07731de8a58d6e233301f80ab)
477
- fatal: unable to checkout working tree
478
- warning: Clone succeeded, but checkout failed.
479
- You can inspect what was checked out with 'git status'
480
- and retry with 'git restore --source=HEAD :/'
481
- ERROR
482
- },
483
- ->(url) { url }
484
- ]
485
- subject.with_git_mirror(test_url_valid) do
486
- yielded << responses.shift.call(test_url_valid)
487
- end
488
-
489
- expect(responses).to be_empty
490
- expect(yielded).to eq([test_url_valid])
491
- end
477
+ it 'when the clone succeeds but checkout fails with unable to read tree' do
478
+ error = format_error <<-ERROR
479
+ error: Could not read 92cf57b8f07df010ab5f607b109c325e30e46235
480
+ fatal: unable to read tree 0c32c0521d3b0bfb4e74e4a39b97a84d1a3bb9a1
481
+ warning: Clone succeeded, but checkout failed.
482
+ You can inspect what was checked out with 'git status'
483
+ and retry with 'git restore --source=HEAD :/'
484
+ ERROR
492
485
 
493
- it 'should retry when the cache errors with did not receive expected object' do
494
- allow(subject).to receive(:update_reference_repo) {}
495
- expect(subject).to receive(:reference_repo_dir)
496
- expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
486
+ expect(subject.retriable_error?(error)).to be_truthy
487
+ end
497
488
 
498
- responses = [
499
- lambda { |_url|
500
- raise Terrapin::ExitStatusError, <<-ERROR.gsub(/^ {12}/, '')
501
- STDOUT:
489
+ it 'when one delta is missing' do
490
+ error = format_error <<-ERROR
491
+ error: Could not read f7fad86d06fee0678f9af7203b6031feabb40c3e
492
+ fatal: pack has 1 unresolved delta
493
+ fatal: index-pack failed
494
+ ERROR
502
495
 
503
- STDERR:
496
+ expect(subject.retriable_error?(error)).to be_truthy
497
+ end
504
498
 
505
- error: Could not read 6682dfe81f66656436e60883dd795e7ec6735153
506
- error: Could not read 0cd3703c23fa44c0043d97fbc26356a23939f31b
507
- fatal: did not receive expected object 3c64c9dd49c79bd09aa13d4b05ac18263ca29ccd
508
- fatal: index-pack failed
509
- ERROR
510
- },
511
- ->(url) { url }
512
- ]
513
- subject.with_git_mirror(test_url_valid) do
514
- yielded << responses.shift.call(test_url_valid)
499
+ it 'when deltas are missing' do
500
+ error = format_error <<-ERROR
501
+ error: Could not read f7fad86d06fee0678f9af7203b6031feabb40c3e
502
+ fatal: pack has 138063 unresolved deltas
503
+ fatal: index-pack failed
504
+ ERROR
505
+
506
+ expect(subject.retriable_error?(error)).to be_truthy
515
507
  end
516
508
 
517
- expect(responses).to be_empty
518
- expect(yielded).to eq([test_url_valid])
509
+ it 'when the cache errors with unable to read sha1 file' do
510
+ error = format_error <<-ERROR
511
+ error: unable to read sha1 file of sqiosbuild/lib/action/action.rb (6113b739af82d8b07731de8a58d6e233301f80ab)
512
+ fatal: unable to checkout working tree
513
+ warning: Clone succeeded, but checkout failed.
514
+ You can inspect what was checked out with 'git status'
515
+ and retry with 'git restore --source=HEAD :/'
516
+ ERROR
517
+
518
+ expect(subject.retriable_error?(error)).to be_truthy
519
+ end
520
+
521
+ it 'when the cache errors with did not receive expected object' do
522
+ error = format_error <<-ERROR
523
+ error: Could not read 6682dfe81f66656436e60883dd795e7ec6735153
524
+ error: Could not read 0cd3703c23fa44c0043d97fbc26356a23939f31b
525
+ fatal: did not receive expected object 3c64c9dd49c79bd09aa13d4b05ac18263ca29ccd
526
+ fatal: index-pack failed
527
+ ERROR
528
+
529
+ expect(subject.retriable_error?(error)).to be_truthy
530
+ end
519
531
  end
520
532
  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.3.0
4
+ version: 1.3.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: 2022-12-03 00:00:00.000000000 Z
12
+ date: 2023-01-07 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,7 +71,7 @@ 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
  - - ">="
@@ -81,7 +82,4 @@ rubygems_version: 3.1.6
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: []