file-digests 0.0.13 → 0.0.18
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 +4 -4
- data/bin/file-digests +0 -3
- data/bin/file-digests-auto +7 -0
- data/bin/file-digests-test +1 -2
- data/lib/file-digests.rb +89 -57
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d69e75a52c05cbc2caf912491be9fbaaadb5136a2dfc723920a9010d3e4c2592
|
4
|
+
data.tar.gz: f90deae82f2581d301d1cb7ba7c730e684b24f5c9582d6deb2581b9f2e6fa557
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ad20e936d21d42f56ed20250728eb0b7c2b9a034877e764f528c6e297853bb7724d6780a56b7c0f11a3df975c1d51fed04852ddb3f3ff9848f4c546167902e8
|
7
|
+
data.tar.gz: 27f86166310f420ac5858fa86c0233af666bbb2af40f53501a9f8e02205a3753151db5cfd6777558bbf0040e2472f9bc296d9382afebb8dd45f65fb981fc173b
|
data/bin/file-digests
CHANGED
data/bin/file-digests-test
CHANGED
data/lib/file-digests.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
require 'date'
|
3
2
|
require 'set'
|
4
3
|
require 'digest'
|
@@ -8,34 +7,20 @@ require 'sqlite3'
|
|
8
7
|
|
9
8
|
module FileDigests
|
10
9
|
|
11
|
-
def self.ensure_dir_exists path
|
12
|
-
if File.exist?(path)
|
13
|
-
unless File.directory?(path)
|
14
|
-
raise "#{path} is not a directory"
|
15
|
-
end
|
16
|
-
else
|
17
|
-
FileUtils.mkdir_p path
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.measure_time
|
22
|
-
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
23
|
-
yield
|
24
|
-
elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).to_i
|
25
|
-
puts "Elapsed time: #{elapsed / 3600}h #{(elapsed % 3600) / 60}m #{elapsed % 60}s" unless QUIET
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.patch_path_string path
|
29
|
-
Gem.win_platform? ? path.gsub(/\\/, '/') : path
|
30
|
-
end
|
31
|
-
|
32
10
|
def self.perform_check
|
33
|
-
|
11
|
+
options = {
|
12
|
+
auto: (ENV["AUTO"] == "true"),
|
13
|
+
quiet: (ENV["QUIET"] == "true"),
|
14
|
+
test_only: (ENV["TEST_ONLY"] == "true")
|
15
|
+
}
|
16
|
+
checker = Checker.new ARGV[0], ARGV[1], options
|
34
17
|
checker.perform_check
|
35
18
|
end
|
36
19
|
|
37
20
|
class DigestDatabase
|
38
|
-
def initialize path
|
21
|
+
def initialize path, options = {}
|
22
|
+
@options = options
|
23
|
+
|
39
24
|
@db = SQLite3::Database.new path.to_s
|
40
25
|
@db.results_as_hash = true
|
41
26
|
|
@@ -76,8 +61,8 @@ module FileDigests
|
|
76
61
|
|
77
62
|
if found['digest'] == digest
|
78
63
|
counters[:good] += 1
|
79
|
-
# puts "GOOD: #{file_path}" unless
|
80
|
-
unless
|
64
|
+
# puts "GOOD: #{file_path}" unless @options[:quiet]
|
65
|
+
unless @options[:test_only]
|
81
66
|
if found['mtime'] == mtime
|
82
67
|
touch_digest_check_time found['id']
|
83
68
|
else
|
@@ -90,47 +75,50 @@ module FileDigests
|
|
90
75
|
STDERR.puts "LIKELY DAMAGED: #{file_path}"
|
91
76
|
else
|
92
77
|
counters[:updated] += 1
|
93
|
-
puts "UPDATED: #{file_path}" unless
|
94
|
-
unless
|
78
|
+
puts "UPDATED: #{file_path}" unless @options[:quiet]
|
79
|
+
unless @options[:test_only]
|
95
80
|
update_mtime_and_digest mtime, digest, found['id']
|
96
81
|
end
|
97
82
|
end
|
98
83
|
end
|
99
84
|
else
|
100
85
|
counters[:new] += 1
|
101
|
-
puts "NEW: #{file_path}" unless
|
102
|
-
unless
|
86
|
+
puts "NEW: #{file_path}" unless @options[:quiet]
|
87
|
+
unless @options[:test_only]
|
103
88
|
@new_files[file_path] = digest
|
104
89
|
insert file_path, mtime, digest
|
105
90
|
end
|
106
91
|
end
|
107
92
|
end
|
108
93
|
|
109
|
-
def
|
94
|
+
def track_renames counters
|
110
95
|
@missing_files.delete_if do |filename, digest|
|
111
96
|
if @new_files.value?(digest)
|
112
97
|
counters[:renamed] += 1
|
113
|
-
unless
|
98
|
+
unless @options[:test_only]
|
114
99
|
delete_by_filename filename
|
115
100
|
end
|
116
101
|
true
|
117
102
|
end
|
118
103
|
end
|
104
|
+
counters[:missing] = @missing_files.length
|
105
|
+
end
|
119
106
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
107
|
+
def any_missing_files?
|
108
|
+
@missing_files.length > 0
|
109
|
+
end
|
110
|
+
|
111
|
+
def print_missing_files
|
112
|
+
puts "\nMISSING FILES:"
|
113
|
+
@missing_files.sort.to_h.each do |filename, digest|
|
114
|
+
puts filename
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def remove_missing_files
|
119
|
+
@db.transaction do
|
120
|
+
@missing_files.each do |filename, digest|
|
121
|
+
delete_by_filename filename
|
134
122
|
end
|
135
123
|
end
|
136
124
|
end
|
@@ -151,14 +139,15 @@ module FileDigests
|
|
151
139
|
end
|
152
140
|
|
153
141
|
class Checker
|
154
|
-
def initialize files_path, digest_database_path
|
155
|
-
@
|
142
|
+
def initialize files_path, digest_database_path, options = {}
|
143
|
+
@options = options
|
144
|
+
@files_path = cleanup_path(files_path || ".")
|
156
145
|
@prefix_to_remove = @files_path.to_s + '/'
|
157
146
|
|
158
147
|
raise "Files path must be a readable directory" unless (File.directory?(@files_path) && File.readable?(@files_path))
|
159
148
|
|
160
149
|
@digest_database_path = if digest_database_path
|
161
|
-
|
150
|
+
cleanup_path(digest_database_path)
|
162
151
|
else
|
163
152
|
@files_path + '.file-digests.sqlite'
|
164
153
|
end
|
@@ -171,24 +160,32 @@ module FileDigests
|
|
171
160
|
@skip_file_digests_sqlite = true
|
172
161
|
end
|
173
162
|
|
174
|
-
|
163
|
+
ensure_dir_exists @digest_database_path.dirname
|
175
164
|
|
165
|
+
# Please do not use this flag, support for sha512 is here for backward compatibility, and one day it will be removed.
|
176
166
|
if File.exist?(@digest_database_path.dirname + '.file-digests.sha512')
|
177
167
|
@use_sha512 = true
|
178
168
|
end
|
179
169
|
|
180
|
-
@digest_database = DigestDatabase.new @digest_database_path
|
170
|
+
@digest_database = DigestDatabase.new @digest_database_path, @options
|
181
171
|
@counters = {good: 0, updated: 0, new: 0, missing: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
|
182
172
|
end
|
183
173
|
|
184
174
|
def perform_check
|
185
|
-
|
175
|
+
measure_time do
|
186
176
|
walk_files do |filename|
|
187
177
|
process_file filename
|
188
178
|
end
|
189
179
|
end
|
190
180
|
|
191
|
-
@digest_database.
|
181
|
+
@digest_database.track_renames @counters
|
182
|
+
|
183
|
+
if @digest_database.any_missing_files?
|
184
|
+
@digest_database.print_missing_files
|
185
|
+
if !@options[:test_only] && (@options[:auto] || confirm("Remove missing files from the database"))
|
186
|
+
@digest_database.remove_missing_files
|
187
|
+
end
|
188
|
+
end
|
192
189
|
|
193
190
|
if @counters[:likely_damaged] > 0 || @counters[:exceptions] > 0
|
194
191
|
STDERR.puts "ERRORS WERE OCCURRED"
|
@@ -197,9 +194,13 @@ module FileDigests
|
|
197
194
|
puts @counters.inspect
|
198
195
|
end
|
199
196
|
|
200
|
-
|
201
|
-
|
202
|
-
|
197
|
+
private
|
198
|
+
|
199
|
+
|
200
|
+
def confirm text
|
201
|
+
if STDIN.tty? && STDOUT.tty?
|
202
|
+
puts "#{text} (y/n)?"
|
203
|
+
STDIN.gets.strip.downcase == "y"
|
203
204
|
end
|
204
205
|
end
|
205
206
|
|
@@ -233,7 +234,31 @@ module FileDigests
|
|
233
234
|
)
|
234
235
|
rescue => exception
|
235
236
|
@counters[:exceptions] += 1
|
236
|
-
STDERR.puts "EXCEPTION: #{filename}: #{exception.message}"
|
237
|
+
STDERR.puts "EXCEPTION: #{filename.encode('utf-8', universal_newline: true)}: #{exception.message}"
|
238
|
+
end
|
239
|
+
|
240
|
+
def patch_path_string path
|
241
|
+
Gem.win_platform? ? path.gsub(/\\/, '/') : path
|
242
|
+
end
|
243
|
+
|
244
|
+
def cleanup_path path
|
245
|
+
Pathname.new(patch_path_string(path)).cleanpath
|
246
|
+
end
|
247
|
+
|
248
|
+
def ensure_dir_exists path
|
249
|
+
if File.exist?(path)
|
250
|
+
unless File.directory?(path)
|
251
|
+
raise "#{path} is not a directory"
|
252
|
+
end
|
253
|
+
else
|
254
|
+
FileUtils.mkdir_p path
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def walk_files
|
259
|
+
Dir.glob(@files_path + '**' + '*', File::FNM_DOTMATCH) do |filename|
|
260
|
+
yield filename
|
261
|
+
end
|
237
262
|
end
|
238
263
|
|
239
264
|
def get_file_digest filename
|
@@ -247,5 +272,12 @@ module FileDigests
|
|
247
272
|
end
|
248
273
|
end
|
249
274
|
|
275
|
+
def measure_time
|
276
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
277
|
+
yield
|
278
|
+
elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).to_i
|
279
|
+
puts "Elapsed time: #{elapsed / 3600}h #{(elapsed % 3600) / 60}m #{elapsed % 60}s" unless @options[:quiet]
|
280
|
+
end
|
281
|
+
|
250
282
|
end
|
251
283
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.18
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stanislav Senotrusov
|
@@ -28,11 +28,13 @@ description: Calculate file digests and check for the possible file corruption
|
|
28
28
|
email: stan@senotrusov.com
|
29
29
|
executables:
|
30
30
|
- file-digests
|
31
|
+
- file-digests-auto
|
31
32
|
- file-digests-test
|
32
33
|
extensions: []
|
33
34
|
extra_rdoc_files: []
|
34
35
|
files:
|
35
36
|
- bin/file-digests
|
37
|
+
- bin/file-digests-auto
|
36
38
|
- bin/file-digests-test
|
37
39
|
- lib/file-digests.rb
|
38
40
|
homepage: https://github.com/senotrusov/file-digests
|