file-digests 0.0.12 → 0.0.17
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-test +1 -2
- data/lib/file-digests.rb +93 -57
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 27ef5c07b544bb7e63a8b9ca9d8b99d0b46588b45311ef940d7431691178b99c
|
4
|
+
data.tar.gz: b1febf1fbdabab014eca65a86e3beee4cfaf4478ead18a5da071b045ae0ab56a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4d0db5b5ca2a29adaac1fa9aaefd276977f39d0c6e7e17e5432586d63a91a5bb20bd4af8c955e4e5c625460102ae1431b33947e5c40cc9351c5bb858f28974b
|
7
|
+
data.tar.gz: 3dc1ec4ac2224a84d1cc914b81314ccfba5580a11ca179ac9b4ca9201a599f57a52edb997af8adc369861124a733d1030f5dcb2a234dbf3c10a68b03690a5b3a
|
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,40 +139,53 @@ 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
|
165
154
|
|
155
|
+
if File.directory?(@digest_database_path)
|
156
|
+
@digest_database_path = @digest_database_path + '.file-digests.sqlite'
|
157
|
+
end
|
158
|
+
|
166
159
|
if @files_path == @digest_database_path.dirname
|
167
160
|
@skip_file_digests_sqlite = true
|
168
161
|
end
|
169
162
|
|
170
|
-
|
163
|
+
ensure_dir_exists @digest_database_path.dirname
|
171
164
|
|
165
|
+
# Please do not use this flag, support for sha512 is here for backward compatibility, and one day it will be removed.
|
172
166
|
if File.exist?(@digest_database_path.dirname + '.file-digests.sha512')
|
173
167
|
@use_sha512 = true
|
174
168
|
end
|
175
169
|
|
176
|
-
@digest_database = DigestDatabase.new @digest_database_path
|
170
|
+
@digest_database = DigestDatabase.new @digest_database_path, @options
|
177
171
|
@counters = {good: 0, updated: 0, new: 0, missing: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
|
178
172
|
end
|
179
173
|
|
180
174
|
def perform_check
|
181
|
-
|
175
|
+
measure_time do
|
182
176
|
walk_files do |filename|
|
183
177
|
process_file filename
|
184
178
|
end
|
185
179
|
end
|
186
180
|
|
187
|
-
@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
|
188
189
|
|
189
190
|
if @counters[:likely_damaged] > 0 || @counters[:exceptions] > 0
|
190
191
|
STDERR.puts "ERRORS WERE OCCURRED"
|
@@ -193,9 +194,13 @@ module FileDigests
|
|
193
194
|
puts @counters.inspect
|
194
195
|
end
|
195
196
|
|
196
|
-
|
197
|
-
|
198
|
-
|
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"
|
199
204
|
end
|
200
205
|
end
|
201
206
|
|
@@ -229,7 +234,31 @@ module FileDigests
|
|
229
234
|
)
|
230
235
|
rescue => exception
|
231
236
|
@counters[:exceptions] += 1
|
232
|
-
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
|
233
262
|
end
|
234
263
|
|
235
264
|
def get_file_digest filename
|
@@ -243,5 +272,12 @@ module FileDigests
|
|
243
272
|
end
|
244
273
|
end
|
245
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
|
+
|
246
282
|
end
|
247
283
|
end
|