file-digests 0.0.30 → 0.0.35

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/bin/file-digests +14 -0
  3. data/lib/file-digests.rb +162 -92
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99b58aede8267994cc69da0ac1fd2e35d6661fd666257e9f3dfcde5054b3b6f8
4
- data.tar.gz: 4b733dcf4be1f14b4a08931d15d0531c18e161f3d6d3af3e3025b36266f67e9d
3
+ metadata.gz: 42e870b2a0e4e711e84a2748d09d8a378fc6964d78706d573ab22737d0f2519e
4
+ data.tar.gz: e14bef4ccd63fdc07950a257654c6594b793eabf44bbdf26cae59e621838e5c3
5
5
  SHA512:
6
- metadata.gz: 9cf4c3df2b8f206b54689f2506de474ca3b021c572869765caf6e1424dac73a7c6dca3b67918ba5cb88f712353febe9aebe5fc40f7da24f6dd4019300b052436
7
- data.tar.gz: cb0904f141e8861c9923647214c92bb8ce022952c53e8a96cee9819a43f4192bf550b47c927cc0ea8f5a16f2c58c3013561555c2afde9c093dd6cd7997f25c94
6
+ metadata.gz: b6b9eb636258ba06b29bd21736b477c0d225ba119ad3b3ccb82612c941bf892109876aa4e8da7402f580f4167f84000a1b2b4ae91e568ac50f223325fab856ef
7
+ data.tar.gz: afcdffefe0dcb346afcdf37ce6f3f523d4862a45a455838d8b7cbf603cead2c8fadbac185640088c79bd7e8c412b5da932bf301d90dd4337171b675a65f34388
@@ -1,5 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ # Copyright 2020 Stanislav Senotrusov <stan@senotrusov.com>
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
+
3
17
  require 'file-digests'
4
18
 
5
19
  FileDigests.run_cli_utility
@@ -1,3 +1,17 @@
1
+ # Copyright 2020 Stanislav Senotrusov <stan@senotrusov.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
1
15
  require "date"
2
16
  require "digest"
3
17
  require "fileutils"
@@ -8,7 +22,8 @@ require "set"
8
22
  require "sqlite3"
9
23
 
10
24
  class FileDigests
11
- DIGEST_ALGORITHMS=["BLAKE2b512", "SHA3-256", "SHA512-256"]
25
+ VERSION = Gem.loaded_specs["file-digests"]&.version&.to_s
26
+ DIGEST_ALGORITHMS = ["BLAKE2b512", "SHA3-256", "SHA512-256"]
12
27
  LEGACY_DIGEST_ALGORITHMS = ["SHA512", "SHA256"]
13
28
 
14
29
  def self.canonical_digest_algorithm_name(string)
@@ -75,7 +90,12 @@ class FileDigests
75
90
  options[:quiet] = true
76
91
  end
77
92
 
78
- opts.on("-t", "--test", "Perform only the test, do not modify the digest database.") do
93
+ opts.on(
94
+ "-t", "--test",
95
+ "Perform a test to verify directory contents.",
96
+ "Compare actual files with the stored digests, check if any files are missing.",
97
+ "Digest database will not be modified."
98
+ ) do
79
99
  options[:test_only] = true
80
100
  end
81
101
 
@@ -96,6 +116,7 @@ class FileDigests
96
116
 
97
117
  def initialize files_path, digest_database_path, options = {}
98
118
  @options = options
119
+ @user_input_wait_time = 0
99
120
 
100
121
  initialize_paths files_path, digest_database_path
101
122
  initialize_database
@@ -139,8 +160,6 @@ class FileDigests
139
160
  @db.results_as_hash = true
140
161
  @db.busy_timeout = 5000
141
162
 
142
- file_digests_gem_version = Gem.loaded_specs["file-digests"]&.version&.to_s
143
-
144
163
  execute "PRAGMA encoding = 'UTF-8'"
145
164
  execute "PRAGMA locking_mode = 'EXCLUSIVE'"
146
165
  execute "PRAGMA journal_mode = 'WAL'"
@@ -155,14 +174,13 @@ class FileDigests
155
174
  execute "CREATE TABLE metadata (
156
175
  key TEXT NOT NULL PRIMARY KEY,
157
176
  value TEXT)"
158
- execute "CREATE UNIQUE INDEX metadata_key ON metadata(key)"
159
177
  metadata_table_was_created = true
160
178
  end
161
179
 
162
180
  prepare_method :set_metadata_query, "INSERT INTO metadata (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value=excluded.value"
163
181
  prepare_method :get_metadata_query, "SELECT value FROM metadata WHERE key = ?"
164
182
 
165
- set_metadata("metadata_table_created_by_gem_version", file_digests_gem_version) if file_digests_gem_version && metadata_table_was_created
183
+ set_metadata("metadata_table_created_by_gem_version", FileDigests::VERSION) if FileDigests::VERSION && metadata_table_was_created
166
184
 
167
185
  # Heuristic to detect database version 1 (metadata was not stored back then)
168
186
  unless get_metadata("database_version")
@@ -179,20 +197,19 @@ class FileDigests
179
197
  digest TEXT NOT NULL,
180
198
  digest_check_time TEXT NOT NULL)"
181
199
  execute "CREATE UNIQUE INDEX digests_filename ON digests(filename)"
182
- set_metadata("digests_table_created_by_gem_version", file_digests_gem_version) if file_digests_gem_version
200
+ execute "CREATE INDEX digests_digest ON digests(digest)"
201
+ set_metadata("digests_table_created_by_gem_version", FileDigests::VERSION) if FileDigests::VERSION
183
202
  end
184
203
 
185
- prepare_method :insert, "INSERT INTO digests (filename, mtime, digest, digest_check_time) VALUES (?, ?, ?, datetime('now'))"
186
- prepare_method :find_by_filename_query, "SELECT id, mtime, digest FROM digests WHERE filename = ?"
187
- prepare_method :touch_digest_check_time, "UPDATE digests SET digest_check_time = datetime('now') WHERE id = ?"
188
- prepare_method :update_mtime_and_digest, "UPDATE digests SET mtime = ?, digest = ?, digest_check_time = datetime('now') WHERE id = ?"
189
- prepare_method :update_mtime, "UPDATE digests SET mtime = ?, digest_check_time = datetime('now') WHERE id = ?"
190
- prepare_method :delete_by_filename, "DELETE FROM digests WHERE filename = ?"
191
- prepare_method :query_duplicates, "SELECT digest, filename FROM digests WHERE digest IN (SELECT digest FROM digests GROUP BY digest HAVING count(*) > 1) ORDER BY digest, filename;"
192
- prepare_method :update_digest_to_new_digest, "UPDATE digests SET digest = ? WHERE digest = ?"
204
+ prepare_method :digests_insert, "INSERT INTO digests (filename, mtime, digest, digest_check_time) VALUES (?, ?, ?, datetime('now'))"
205
+ prepare_method :digests_find_by_filename_query, "SELECT id, mtime, digest FROM digests WHERE filename = ?"
206
+ prepare_method :digests_touch_check_time, "UPDATE digests SET digest_check_time = datetime('now') WHERE id = ?"
207
+ prepare_method :digests_update_mtime_and_digest, "UPDATE digests SET mtime = ?, digest = ?, digest_check_time = datetime('now') WHERE id = ?"
208
+ prepare_method :digests_update_mtime, "UPDATE digests SET mtime = ?, digest_check_time = datetime('now') WHERE id = ?"
209
+ prepare_method :digests_select_duplicates, "SELECT digest, filename FROM digests WHERE digest IN (SELECT digest FROM digests GROUP BY digest HAVING count(*) > 1) ORDER BY digest, filename;"
193
210
 
194
211
  unless get_metadata("database_version")
195
- set_metadata "database_version", "2"
212
+ set_metadata "database_version", "3"
196
213
  end
197
214
 
198
215
  # Convert database from 1st to 2nd version
@@ -207,74 +224,110 @@ class FileDigests
207
224
  end
208
225
  end
209
226
 
210
- if get_metadata("database_version") != "2"
211
- STDERR.puts "This version of file-digests (#{file_digests_gem_version || "unknown"}) is only compartible with the database version 2. Current database version is #{get_metadata("database_version")}. To use this database, please install appropriate version if file-digest."
212
- raise "Incompatible database version"
227
+ if get_metadata("database_version") == "2"
228
+ execute "CREATE INDEX digests_digest ON digests(digest)"
229
+ set_metadata "database_version", "3"
213
230
  end
231
+
232
+ check_if_database_is_at_certain_version "3"
233
+
234
+ create_temporary_tables
214
235
  end
215
236
  end
216
237
 
217
- def perform_check
218
- perhaps_transaction(@new_digest_algorithm, :exclusive) do
219
- @counters = {good: 0, updated: 0, new: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
220
- @new_files = {}
221
- @new_digests = {}
238
+ def create_temporary_tables
239
+ execute "CREATE TEMPORARY TABLE new_files (
240
+ filename TEXT NOT NULL PRIMARY KEY,
241
+ digest TEXT NOT NULL)"
242
+ execute "CREATE INDEX new_files_digest ON new_files(digest)"
243
+
244
+ prepare_method :new_files_insert, "INSERT INTO new_files (filename, digest) VALUES (?, ?)"
245
+ prepare_method :new_files_count_query, "SELECT count(*) FROM new_files"
246
+
247
+ execute "CREATE TEMPORARY TABLE missing_files (
248
+ filename TEXT NOT NULL PRIMARY KEY,
249
+ digest TEXT NOT NULL)"
250
+ execute "CREATE INDEX missing_files_digest ON missing_files(digest)"
251
+
252
+ execute "INSERT INTO missing_files (filename, digest) SELECT filename, digest FROM digests"
253
+
254
+ prepare_method :missing_files_delete, "DELETE FROM missing_files WHERE filename = ?"
255
+ prepare_method :missing_files_delete_renamed_files, "DELETE FROM missing_files WHERE digest IN (SELECT digest FROM new_files)"
256
+ prepare_method :missing_files_select_all_filenames, "SELECT filename FROM missing_files ORDER BY filename"
257
+ prepare_method :missing_files_delete_all, "DELETE FROM missing_files"
258
+ prepare_method :missing_files_count_query, "SELECT count(*) FROM missing_files"
259
+
260
+ prepare_method :digests_delete_renamed_files, "DELETE FROM digests WHERE filename IN (SELECT filename FROM missing_files WHERE digest IN (SELECT digest FROM new_files))"
261
+ prepare_method :digests_delete_all_missing_files, "DELETE FROM digests WHERE filename IN (SELECT filename FROM missing_files)"
262
+
263
+ execute "CREATE TEMPORARY TABLE new_digests (
264
+ filename TEXT NOT NULL PRIMARY KEY,
265
+ digest TEXT NOT NULL)"
266
+
267
+ prepare_method :new_digests_insert, "INSERT INTO new_digests (filename, digest) VALUES (?, ?)"
268
+ prepare_method :digests_update_digests_to_new_digests, "INSERT INTO digests (filename, digest, digest_check_time) SELECT filename, digest, false FROM new_digests WHERE true ON CONFLICT (filename) DO UPDATE SET digest=excluded.digest"
269
+ end
222
270
 
223
- @missing_files = Hash[@db.prepare("SELECT filename, digest FROM digests").execute!]
271
+ def perform_check
272
+ measure_time do
273
+ perhaps_transaction(@new_digest_algorithm, :exclusive) do
274
+ @counters = {good: 0, updated: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
224
275
 
225
- measure_time do
226
276
  walk_files do |filename|
227
277
  process_file filename
228
278
  end
229
- end
230
279
 
231
- nested_transaction do
232
- track_renames
233
- end
280
+ nested_transaction do
281
+ puts "Tracking renames..." if @options[:verbose]
282
+ track_renames
283
+ end
234
284
 
235
- if any_missing_files?
236
- if any_exceptions?
237
- STDERR.puts "Due to previously occurred errors, database cleanup from missing files will be skipped this time."
238
- else
239
- print_missing_files
240
- if !@options[:test_only] && (@options[:auto] || confirm("Remove missing files from the database"))
241
- nested_transaction do
242
- remove_missing_files
285
+ if any_missing_files?
286
+ if any_exceptions?
287
+ STDERR.puts "Due to previously occurred errors, missing files will not removed from the database."
288
+ else
289
+ print_missing_files
290
+ if !@options[:test_only] && (@options[:auto] || confirm("Remove missing files from the database"))
291
+ nested_transaction do
292
+ puts "Removing missing files..." if @options[:verbose]
293
+ remove_missing_files
294
+ end
243
295
  end
244
296
  end
245
297
  end
246
- end
247
298
 
248
- if @new_digest_algorithm && !@options[:test_only]
249
- if any_missing_files? || any_likely_damaged? || any_exceptions?
250
- STDERR.puts "ERROR: New digest algorithm will not be in effect until there are files that are missing, likely damaged, or processed with an exception."
251
- else
252
- @new_digests.each do |old_digest, new_digest|
253
- update_digest_to_new_digest new_digest, old_digest
299
+ if @new_digest_algorithm && !@options[:test_only]
300
+ if any_missing_files? || any_likely_damaged? || any_exceptions?
301
+ STDERR.puts "ERROR: New digest algorithm will not be in effect until there are files that are missing, likely damaged, or processed with an exception."
302
+ else
303
+ puts "Updating database to a new digest algorithm..." if @options[:verbose]
304
+ digests_update_digests_to_new_digests
305
+ set_metadata "digest_algorithm", @new_digest_algorithm
306
+ puts "Transition to a new digest algorithm complete: #{@new_digest_algorithm}"
254
307
  end
255
- set_metadata "digest_algorithm", @new_digest_algorithm
256
- puts "Transition to a new digest algorithm complete: #{@new_digest_algorithm}"
257
308
  end
258
- end
259
309
 
260
- if any_likely_damaged? || any_exceptions?
261
- STDERR.puts "PLEASE REVIEW ERRORS THAT WERE OCCURRED!"
262
- end
310
+ if any_likely_damaged? || any_exceptions?
311
+ STDERR.puts "PLEASE REVIEW ERRORS THAT WERE OCCURRED!"
312
+ end
263
313
 
264
- set_metadata(@options[:test_only] ? "latest_test_only_check_time" : "latest_complete_check_time", time_to_database(Time.now))
314
+ set_metadata(@options[:test_only] ? "latest_test_only_check_time" : "latest_complete_check_time", time_to_database(Time.now))
265
315
 
266
- print_counters
267
- end
268
- execute "PRAGMA optimize"
269
- execute "VACUUM"
270
- execute "PRAGMA wal_checkpoint(TRUNCATE)"
316
+ print_counters
317
+ end
318
+
319
+ puts "Performing database maintenance..." if @options[:verbose]
320
+ execute "PRAGMA optimize"
321
+ execute "VACUUM"
322
+ execute "PRAGMA wal_checkpoint(TRUNCATE)"
271
323
 
272
- hide_database_files
324
+ hide_database_files
325
+ end
273
326
  end
274
327
 
275
328
  def show_duplicates
276
329
  current_digest = nil
277
- query_duplicates.each do |found|
330
+ digests_select_duplicates.each do |found|
278
331
  if current_digest != found["digest"]
279
332
  puts "" if current_digest
280
333
  current_digest = found["digest"]
@@ -306,9 +359,10 @@ class FileDigests
306
359
 
307
360
  normalized_filename = filename.delete_prefix("#{@files_path.to_s}/").encode("utf-8", universal_newline: true).unicode_normalize(:nfkc)
308
361
  mtime_string = time_to_database stat.mtime
309
- digest = get_file_digest(filename)
362
+ digest, new_digest = get_file_digest(filename)
310
363
 
311
364
  nested_transaction do
365
+ new_digests_insert(normalized_filename, new_digest) if new_digest
312
366
  process_file_indeed normalized_filename, mtime_string, digest
313
367
  end
314
368
 
@@ -326,15 +380,15 @@ class FileDigests
326
380
  end
327
381
 
328
382
  def process_previously_seen_file found, filename, mtime, digest
329
- @missing_files.delete(filename)
383
+ missing_files_delete filename
330
384
  if found["digest"] == digest
331
385
  @counters[:good] += 1
332
386
  puts "GOOD: #{filename}" if @options[:verbose]
333
387
  unless @options[:test_only]
334
388
  if found["mtime"] == mtime
335
- touch_digest_check_time found["id"]
389
+ digests_touch_check_time found["id"]
336
390
  else
337
- update_mtime mtime, found["id"]
391
+ digests_update_mtime mtime, found["id"]
338
392
  end
339
393
  end
340
394
  else
@@ -345,18 +399,17 @@ class FileDigests
345
399
  @counters[:updated] += 1
346
400
  puts "UPDATED#{" (FATE ACCEPTED)" if found["mtime"] == mtime && @options[:accept_fate]}: #{filename}" unless @options[:quiet]
347
401
  unless @options[:test_only]
348
- update_mtime_and_digest mtime, digest, found["id"]
402
+ digests_update_mtime_and_digest mtime, digest, found["id"]
349
403
  end
350
404
  end
351
405
  end
352
406
  end
353
407
 
354
408
  def process_new_file filename, mtime, digest
355
- @counters[:new] += 1
356
409
  puts "NEW: #{filename}" unless @options[:quiet]
410
+ new_files_insert filename, digest
357
411
  unless @options[:test_only]
358
- @new_files[filename] = digest
359
- insert filename, mtime, digest
412
+ digests_insert filename, mtime, digest
360
413
  end
361
414
  end
362
415
 
@@ -364,29 +417,31 @@ class FileDigests
364
417
  # Renames and missing files
365
418
 
366
419
  def track_renames
367
- @missing_files.delete_if do |filename, digest|
368
- if @new_files.value?(digest)
369
- @counters[:renamed] += 1
370
- unless @options[:test_only]
371
- delete_by_filename filename
372
- end
373
- true
374
- end
420
+ unless @options[:test_only]
421
+ digests_delete_renamed_files
375
422
  end
423
+ missing_files_delete_renamed_files
424
+ @counters[:renamed] = @db.changes
376
425
  end
377
426
 
378
427
  def print_missing_files
379
428
  puts "\nMISSING FILES:"
380
- @missing_files.sort.to_h.each do |filename, digest|
381
- puts filename
429
+ missing_files_select_all_filenames.each do |record|
430
+ puts record["filename"]
382
431
  end
383
432
  end
384
433
 
385
434
  def remove_missing_files
386
- @missing_files.each do |filename, digest|
387
- delete_by_filename filename
388
- end
389
- @missing_files = {}
435
+ digests_delete_all_missing_files
436
+ missing_files_delete_all
437
+ end
438
+
439
+ def missing_files_count
440
+ missing_files_count_query!&.first&.first
441
+ end
442
+
443
+ def any_missing_files?
444
+ missing_files_count > 0
390
445
  end
391
446
 
392
447
 
@@ -397,6 +452,7 @@ class FileDigests
397
452
  end
398
453
 
399
454
  def integrity_check
455
+ puts "Checking database integrity..." if @options[:verbose]
400
456
  if execute("PRAGMA integrity_check")&.first&.fetch("integrity_check") != "ok"
401
457
  raise "Database integrity check failed"
402
458
  end
@@ -451,7 +507,7 @@ class FileDigests
451
507
  end
452
508
 
453
509
  def find_by_filename filename
454
- result = find_by_filename_query filename
510
+ result = digests_find_by_filename_query filename
455
511
  found = result.next
456
512
  raise "Multiple records found" if result.next
457
513
  found
@@ -471,6 +527,18 @@ class FileDigests
471
527
  end
472
528
  end
473
529
 
530
+ def check_if_database_is_at_certain_version target_version
531
+ current_version = get_metadata("database_version")
532
+ if current_version != target_version
533
+ STDERR.puts "This version of file-digests (#{FileDigests::VERSION || "unknown"}) is only compartible with the database version #{target_version}. Current database version is #{current_version}. To use this database, please install appropriate version if file-digest."
534
+ raise "Incompatible database version"
535
+ end
536
+ end
537
+
538
+ def new_files_count
539
+ new_files_count_query!&.first&.first
540
+ end
541
+
474
542
 
475
543
  # Filesystem-related helpers
476
544
 
@@ -493,6 +561,7 @@ class FileDigests
493
561
  end
494
562
 
495
563
  def walk_files
564
+ puts "Gathering the list of files..." if @options[:verbose]
496
565
  Dir.glob(@files_path + "**" + "*", File::FNM_DOTMATCH) do |filename|
497
566
  yield filename
498
567
  end
@@ -508,18 +577,13 @@ class FileDigests
508
577
  digest.update(buffer)
509
578
  new_digest.update(buffer) if @new_digest_algorithm
510
579
  end
511
- @new_digests[digest.hexdigest] = new_digest.hexdigest if @new_digest_algorithm
512
- return digest.hexdigest
580
+ return [digest.hexdigest, (new_digest.hexdigest if @new_digest_algorithm)]
513
581
  end
514
582
  end
515
583
 
516
584
 
517
585
  # Runtime state helpers
518
586
 
519
- def any_missing_files?
520
- @missing_files.length > 0
521
- end
522
-
523
587
  def any_exceptions?
524
588
  @counters[:exceptions] > 0
525
589
  end
@@ -533,14 +597,17 @@ class FileDigests
533
597
  def confirm text
534
598
  if STDIN.tty? && STDOUT.tty?
535
599
  puts "#{text} (y/n)?"
536
- STDIN.gets.strip.downcase == "y"
600
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
601
+ result = (STDIN.gets.strip.downcase == "y")
602
+ @user_input_wait_time += (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
603
+ result
537
604
  end
538
605
  end
539
606
 
540
607
  def measure_time
541
608
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
542
609
  yield
543
- elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
610
+ elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) - @user_input_wait_time
544
611
  puts "Elapsed time: #{elapsed.to_i / 3600}h #{(elapsed.to_i % 3600) / 60}m #{"%.3f" % (elapsed % 60)}s" unless @options[:quiet]
545
612
  end
546
613
 
@@ -558,11 +625,14 @@ class FileDigests
558
625
  end
559
626
 
560
627
  def print_counters
628
+ missing_files_count_result = missing_files_count
629
+ new_files_count_result = new_files_count - @counters[:renamed]
630
+
561
631
  puts "#{@counters[:good]} file(s) passes digest check" if @counters[:good] > 0
562
632
  puts "#{@counters[:updated]} file(s) are updated" if @counters[:updated] > 0
563
- puts "#{@counters[:new]} file(s) are new" if @counters[:new] > 0
633
+ puts "#{new_files_count_result} file(s) are new" if new_files_count_result > 0
564
634
  puts "#{@counters[:renamed]} file(s) are renamed" if @counters[:renamed] > 0
565
- puts "#{@missing_files.length} file(s) are missing" if @missing_files.length > 0
635
+ puts "#{missing_files_count_result} file(s) are missing" if missing_files_count_result > 0
566
636
  puts "#{@counters[:likely_damaged]} file(s) are likely damaged (!)" if @counters[:likely_damaged] > 0
567
637
  puts "#{@counters[:exceptions]} file(s) had exceptions occured during processing (!)" if @counters[:exceptions] > 0
568
638
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: file-digests
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.30
4
+ version: 0.0.35
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stanislav Senotrusov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-15 00:00:00.000000000 Z
11
+ date: 2020-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: openssl