file-digests 0.0.29 → 0.0.34
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/file-digests +14 -0
- data/lib/file-digests.rb +156 -88
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd11bc930ad2146b6e9bbecd44479df1817c8262197a184f19d5db03296d16c1
|
4
|
+
data.tar.gz: 60a3741b3b6d7cfa714991bac995176398e4834a53106751b55d537a9e9ac901
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2cfbe4ac1169e5b48c9bb0c1ee3200fa060e38bc770200d14a127f98ab3967f83476f35130f4bd9d8427bbe7cac07c33b43e14c086e5897921c7383f0748cbfe
|
7
|
+
data.tar.gz: 9a5830dc67eb127ff39a0fffda53193d5ad43cfe25405e56d7bd211475af69192f404433c02f2c83d0c3f79dd4a56e4a02f469dbaad52abf6ab2027d8f9894bf
|
data/bin/file-digests
CHANGED
@@ -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
|
data/lib/file-digests.rb
CHANGED
@@ -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
|
-
|
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(
|
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",
|
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
|
-
|
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 :
|
186
|
-
prepare_method :
|
187
|
-
prepare_method :
|
188
|
-
prepare_method :
|
189
|
-
prepare_method :
|
190
|
-
prepare_method :
|
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", "
|
212
|
+
set_metadata "database_version", "3"
|
196
213
|
end
|
197
214
|
|
198
215
|
# Convert database from 1st to 2nd version
|
@@ -207,75 +224,110 @@ class FileDigests
|
|
207
224
|
end
|
208
225
|
end
|
209
226
|
|
210
|
-
if get_metadata("database_version")
|
211
|
-
|
212
|
-
|
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
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
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"
|
222
246
|
|
223
|
-
|
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
|
270
|
+
|
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
|
-
|
232
|
-
|
233
|
-
|
280
|
+
nested_transaction do
|
281
|
+
puts "Tracking renames..." if @options[:verbose]
|
282
|
+
track_renames
|
283
|
+
end
|
234
284
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
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
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
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
|
-
|
261
|
-
|
262
|
-
|
310
|
+
if any_likely_damaged? || any_exceptions?
|
311
|
+
STDERR.puts "PLEASE REVIEW ERRORS THAT WERE OCCURRED!"
|
312
|
+
end
|
263
313
|
|
264
|
-
|
314
|
+
set_metadata(@options[:test_only] ? "latest_test_only_check_time" : "latest_complete_check_time", time_to_database(Time.now))
|
265
315
|
|
316
|
+
print_counters
|
317
|
+
end
|
318
|
+
|
319
|
+
puts "Performing database maintenance..." if @options[:verbose]
|
266
320
|
execute "PRAGMA optimize"
|
267
321
|
execute "VACUUM"
|
268
322
|
execute "PRAGMA wal_checkpoint(TRUNCATE)"
|
269
323
|
|
270
324
|
hide_database_files
|
271
|
-
|
272
|
-
print_counters
|
273
325
|
end
|
274
326
|
end
|
275
327
|
|
276
328
|
def show_duplicates
|
277
329
|
current_digest = nil
|
278
|
-
|
330
|
+
digests_select_duplicates.each do |found|
|
279
331
|
if current_digest != found["digest"]
|
280
332
|
puts "" if current_digest
|
281
333
|
current_digest = found["digest"]
|
@@ -307,9 +359,10 @@ class FileDigests
|
|
307
359
|
|
308
360
|
normalized_filename = filename.delete_prefix("#{@files_path.to_s}/").encode("utf-8", universal_newline: true).unicode_normalize(:nfkc)
|
309
361
|
mtime_string = time_to_database stat.mtime
|
310
|
-
digest = get_file_digest(filename)
|
362
|
+
digest, new_digest = get_file_digest(filename)
|
311
363
|
|
312
364
|
nested_transaction do
|
365
|
+
new_digests_insert(normalized_filename, new_digest) if new_digest
|
313
366
|
process_file_indeed normalized_filename, mtime_string, digest
|
314
367
|
end
|
315
368
|
|
@@ -327,15 +380,15 @@ class FileDigests
|
|
327
380
|
end
|
328
381
|
|
329
382
|
def process_previously_seen_file found, filename, mtime, digest
|
330
|
-
|
383
|
+
missing_files_delete filename
|
331
384
|
if found["digest"] == digest
|
332
385
|
@counters[:good] += 1
|
333
386
|
puts "GOOD: #{filename}" if @options[:verbose]
|
334
387
|
unless @options[:test_only]
|
335
388
|
if found["mtime"] == mtime
|
336
|
-
|
389
|
+
digests_touch_check_time found["id"]
|
337
390
|
else
|
338
|
-
|
391
|
+
digests_update_mtime mtime, found["id"]
|
339
392
|
end
|
340
393
|
end
|
341
394
|
else
|
@@ -346,18 +399,17 @@ class FileDigests
|
|
346
399
|
@counters[:updated] += 1
|
347
400
|
puts "UPDATED#{" (FATE ACCEPTED)" if found["mtime"] == mtime && @options[:accept_fate]}: #{filename}" unless @options[:quiet]
|
348
401
|
unless @options[:test_only]
|
349
|
-
|
402
|
+
digests_update_mtime_and_digest mtime, digest, found["id"]
|
350
403
|
end
|
351
404
|
end
|
352
405
|
end
|
353
406
|
end
|
354
407
|
|
355
408
|
def process_new_file filename, mtime, digest
|
356
|
-
@counters[:new] += 1
|
357
409
|
puts "NEW: #{filename}" unless @options[:quiet]
|
410
|
+
new_files_insert filename, digest
|
358
411
|
unless @options[:test_only]
|
359
|
-
|
360
|
-
insert filename, mtime, digest
|
412
|
+
digests_insert filename, mtime, digest
|
361
413
|
end
|
362
414
|
end
|
363
415
|
|
@@ -365,29 +417,31 @@ class FileDigests
|
|
365
417
|
# Renames and missing files
|
366
418
|
|
367
419
|
def track_renames
|
368
|
-
@
|
369
|
-
|
370
|
-
@counters[:renamed] += 1
|
371
|
-
unless @options[:test_only]
|
372
|
-
delete_by_filename filename
|
373
|
-
end
|
374
|
-
true
|
375
|
-
end
|
420
|
+
unless @options[:test_only]
|
421
|
+
digests_delete_renamed_files
|
376
422
|
end
|
423
|
+
missing_files_delete_renamed_files
|
424
|
+
@counters[:renamed] = @db.changes
|
377
425
|
end
|
378
426
|
|
379
427
|
def print_missing_files
|
380
428
|
puts "\nMISSING FILES:"
|
381
|
-
|
382
|
-
puts filename
|
429
|
+
missing_files_select_all_filenames.each do |record|
|
430
|
+
puts record["filename"]
|
383
431
|
end
|
384
432
|
end
|
385
433
|
|
386
434
|
def remove_missing_files
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
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
|
391
445
|
end
|
392
446
|
|
393
447
|
|
@@ -452,7 +506,7 @@ class FileDigests
|
|
452
506
|
end
|
453
507
|
|
454
508
|
def find_by_filename filename
|
455
|
-
result =
|
509
|
+
result = digests_find_by_filename_query filename
|
456
510
|
found = result.next
|
457
511
|
raise "Multiple records found" if result.next
|
458
512
|
found
|
@@ -472,6 +526,18 @@ class FileDigests
|
|
472
526
|
end
|
473
527
|
end
|
474
528
|
|
529
|
+
def check_if_database_is_at_certain_version target_version
|
530
|
+
current_version = get_metadata("database_version")
|
531
|
+
if current_version != target_version
|
532
|
+
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."
|
533
|
+
raise "Incompatible database version"
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
def new_files_count
|
538
|
+
new_files_count_query!&.first&.first
|
539
|
+
end
|
540
|
+
|
475
541
|
|
476
542
|
# Filesystem-related helpers
|
477
543
|
|
@@ -494,6 +560,7 @@ class FileDigests
|
|
494
560
|
end
|
495
561
|
|
496
562
|
def walk_files
|
563
|
+
puts "Gathering the list of files..." if @options[:verbose]
|
497
564
|
Dir.glob(@files_path + "**" + "*", File::FNM_DOTMATCH) do |filename|
|
498
565
|
yield filename
|
499
566
|
end
|
@@ -509,18 +576,13 @@ class FileDigests
|
|
509
576
|
digest.update(buffer)
|
510
577
|
new_digest.update(buffer) if @new_digest_algorithm
|
511
578
|
end
|
512
|
-
|
513
|
-
return digest.hexdigest
|
579
|
+
return [digest.hexdigest, (new_digest.hexdigest if @new_digest_algorithm)]
|
514
580
|
end
|
515
581
|
end
|
516
582
|
|
517
583
|
|
518
584
|
# Runtime state helpers
|
519
585
|
|
520
|
-
def any_missing_files?
|
521
|
-
@missing_files.length > 0
|
522
|
-
end
|
523
|
-
|
524
586
|
def any_exceptions?
|
525
587
|
@counters[:exceptions] > 0
|
526
588
|
end
|
@@ -534,14 +596,17 @@ class FileDigests
|
|
534
596
|
def confirm text
|
535
597
|
if STDIN.tty? && STDOUT.tty?
|
536
598
|
puts "#{text} (y/n)?"
|
537
|
-
|
599
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
600
|
+
result = (STDIN.gets.strip.downcase == "y")
|
601
|
+
@user_input_wait_time += (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
|
602
|
+
result
|
538
603
|
end
|
539
604
|
end
|
540
605
|
|
541
606
|
def measure_time
|
542
607
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
543
608
|
yield
|
544
|
-
elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
|
609
|
+
elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) - @user_input_wait_time
|
545
610
|
puts "Elapsed time: #{elapsed.to_i / 3600}h #{(elapsed.to_i % 3600) / 60}m #{"%.3f" % (elapsed % 60)}s" unless @options[:quiet]
|
546
611
|
end
|
547
612
|
|
@@ -559,11 +624,14 @@ class FileDigests
|
|
559
624
|
end
|
560
625
|
|
561
626
|
def print_counters
|
627
|
+
missing_files_count_result = missing_files_count
|
628
|
+
new_files_count_result = new_files_count - @counters[:renamed]
|
629
|
+
|
562
630
|
puts "#{@counters[:good]} file(s) passes digest check" if @counters[:good] > 0
|
563
631
|
puts "#{@counters[:updated]} file(s) are updated" if @counters[:updated] > 0
|
564
|
-
puts "#{
|
632
|
+
puts "#{new_files_count_result} file(s) are new" if new_files_count_result > 0
|
565
633
|
puts "#{@counters[:renamed]} file(s) are renamed" if @counters[:renamed] > 0
|
566
|
-
puts "#{
|
634
|
+
puts "#{missing_files_count_result} file(s) are missing" if missing_files_count_result > 0
|
567
635
|
puts "#{@counters[:likely_damaged]} file(s) are likely damaged (!)" if @counters[:likely_damaged] > 0
|
568
636
|
puts "#{@counters[:exceptions]} file(s) had exceptions occured during processing (!)" if @counters[:exceptions] > 0
|
569
637
|
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.
|
4
|
+
version: 0.0.34
|
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-
|
11
|
+
date: 2020-10-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: openssl
|