ndr_support 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +27 -0
- data/.ruby-version +1 -0
- data/.travis.yml +22 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/Guardfile +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +91 -0
- data/Rakefile +12 -0
- data/code_safety.yml +258 -0
- data/gemfiles/Gemfile.rails32 +6 -0
- data/gemfiles/Gemfile.rails32.lock +108 -0
- data/gemfiles/Gemfile.rails41 +6 -0
- data/gemfiles/Gemfile.rails41.lock +111 -0
- data/gemfiles/Gemfile.rails42 +6 -0
- data/gemfiles/Gemfile.rails42.lock +111 -0
- data/lib/ndr_support.rb +21 -0
- data/lib/ndr_support/array.rb +52 -0
- data/lib/ndr_support/concerns/working_days.rb +94 -0
- data/lib/ndr_support/date_and_time_extensions.rb +103 -0
- data/lib/ndr_support/daterange.rb +196 -0
- data/lib/ndr_support/fixnum/calculations.rb +15 -0
- data/lib/ndr_support/fixnum/julian_date_conversions.rb +14 -0
- data/lib/ndr_support/hash.rb +52 -0
- data/lib/ndr_support/integer.rb +12 -0
- data/lib/ndr_support/nil.rb +38 -0
- data/lib/ndr_support/ourdate.rb +97 -0
- data/lib/ndr_support/ourtime.rb +51 -0
- data/lib/ndr_support/regexp_range.rb +65 -0
- data/lib/ndr_support/safe_file.rb +185 -0
- data/lib/ndr_support/safe_path.rb +268 -0
- data/lib/ndr_support/string/cleaning.rb +136 -0
- data/lib/ndr_support/string/conversions.rb +137 -0
- data/lib/ndr_support/tasks.rb +1 -0
- data/lib/ndr_support/time/conversions.rb +13 -0
- data/lib/ndr_support/utf8_encoding.rb +72 -0
- data/lib/ndr_support/utf8_encoding/control_characters.rb +53 -0
- data/lib/ndr_support/utf8_encoding/force_binary.rb +44 -0
- data/lib/ndr_support/utf8_encoding/object_support.rb +31 -0
- data/lib/ndr_support/version.rb +5 -0
- data/lib/ndr_support/yaml/serialization_migration.rb +65 -0
- data/lib/tasks/audit_code.rake +423 -0
- data/ndr_support.gemspec +39 -0
- data/test/array_test.rb +20 -0
- data/test/concerns/working_days_test.rb +122 -0
- data/test/daterange_test.rb +194 -0
- data/test/fixnum/calculations_test.rb +28 -0
- data/test/hash_test.rb +84 -0
- data/test/integer_test.rb +14 -0
- data/test/nil_test.rb +40 -0
- data/test/ourdate_test.rb +27 -0
- data/test/ourtime_test.rb +27 -0
- data/test/regexp_range_test.rb +135 -0
- data/test/resources/filesystem_paths.yml +37 -0
- data/test/safe_file_test.rb +597 -0
- data/test/safe_path_test.rb +168 -0
- data/test/string/cleaning_test.rb +176 -0
- data/test/string/conversions_test.rb +353 -0
- data/test/test_helper.rb +41 -0
- data/test/time/conversions_test.rb +15 -0
- data/test/utf8_encoding/control_characters_test.rb +84 -0
- data/test/utf8_encoding/force_binary_test.rb +64 -0
- data/test/utf8_encoding_test.rb +170 -0
- data/test/yaml/serialization_test.rb +145 -0
- metadata +295 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'ndr_support/utf8_encoding'
|
2
|
+
|
3
|
+
# Allows any object (if supported) to have all related
|
4
|
+
# strings encoded in place to UTF-8.
|
5
|
+
module UTF8Encoding
|
6
|
+
module ObjectSupport
|
7
|
+
# Recursively ensure the correct encoding is being used:
|
8
|
+
def ensure_utf8_object!(object)
|
9
|
+
case object
|
10
|
+
when String
|
11
|
+
ensure_utf8!(object)
|
12
|
+
when Hash
|
13
|
+
ensure_utf8_hash!(object)
|
14
|
+
when Array
|
15
|
+
ensure_utf8_array!(object)
|
16
|
+
else
|
17
|
+
object
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Ensures all values of the given `hash` are UTF-8, where possible.
|
22
|
+
def ensure_utf8_hash!(hash)
|
23
|
+
hash.each_value { |value| ensure_utf8_object!(value) }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Ensures all elements of the given `array` are UTF-8, where possible.
|
27
|
+
def ensure_utf8_array!(array)
|
28
|
+
array.each { |element| ensure_utf8_object!(element) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'ndr_support/utf8_encoding'
|
3
|
+
|
4
|
+
module NdrSupport
|
5
|
+
module YAML
|
6
|
+
# Lightweight wrapper around YAML serialization, to provide any
|
7
|
+
# necessary support for YAML engines and string encodings.
|
8
|
+
module SerializationMigration
|
9
|
+
include UTF8Encoding
|
10
|
+
|
11
|
+
# Wrapper around: YAML.load(string)
|
12
|
+
def load_yaml(string, coerce_invalid_chars = false)
|
13
|
+
fix_encoding!(string, coerce_invalid_chars)
|
14
|
+
|
15
|
+
# Achieve same behaviour using `syck` and `psych`:
|
16
|
+
handle_special_characters!(string, coerce_invalid_chars)
|
17
|
+
fix_encoding!(string, coerce_invalid_chars)
|
18
|
+
|
19
|
+
object = Psych.load(string)
|
20
|
+
|
21
|
+
# Ensure that any string related to the object
|
22
|
+
# we've loaded is also valid UTF-8.
|
23
|
+
ensure_utf8_object!(object)
|
24
|
+
|
25
|
+
# We escape all non-printing control chars:
|
26
|
+
escape_control_chars_in_object!(object)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Wrapper around: YAML.dump(object)
|
30
|
+
def dump_yaml(object)
|
31
|
+
# Psych produces UTF-8 encoded output; we'd rather
|
32
|
+
# have YAML that can be safely stored in stores with
|
33
|
+
# other encodings. If #load_yaml is used, the binary
|
34
|
+
# encoding of the object will be reversed on load.
|
35
|
+
Psych.dump binary_encode_any_high_ascii(object)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Makes `string` valid UTF-8. If `coerce` is true,
|
41
|
+
# any invalid characters will be escaped - if false,
|
42
|
+
# they will trigger an UTF8Encoding::UTF8CoercionError.
|
43
|
+
def fix_encoding!(string, coerce)
|
44
|
+
coerce ? coerce_utf8!(string) : ensure_utf8!(string)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Within double quotes, YAML allows special characters.
|
48
|
+
# While `psych` emits UTF-8 YAML, `syck` double escapes
|
49
|
+
# higher characters. We need to unescape any we find:
|
50
|
+
def handle_special_characters!(string, coerce_invalid_chars)
|
51
|
+
# Replace any encoded hex chars with their actual value:
|
52
|
+
string.gsub!(/((?:\\x[0-9A-F]{2})+)/) do
|
53
|
+
byte_sequence = $1.scan(/[0-9A-F]{2}/)
|
54
|
+
byte_sequence.pack('H2' * byte_sequence.length).tap do |sequence|
|
55
|
+
fix_encoding!(sequence, coerce_invalid_chars)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Re-escape any non-printing control characters,
|
60
|
+
# as they can break the YAML parser:
|
61
|
+
escape_control_chars_in_object!(string)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,423 @@
|
|
1
|
+
SAFETY_FILE =
|
2
|
+
if defined?(Rails)
|
3
|
+
Rails.root.join('config', 'code_safety.yml')
|
4
|
+
else
|
5
|
+
'code_safety.yml'
|
6
|
+
end
|
7
|
+
|
8
|
+
# Temporary overrides to only audit external access files
|
9
|
+
SAFETY_REPOS = [['/svn/era', '/svn/extra/era/external-access']]
|
10
|
+
|
11
|
+
require 'yaml'
|
12
|
+
|
13
|
+
|
14
|
+
# Parameter max_print is number of entries to print before truncating output
|
15
|
+
# (negative value => print all)
|
16
|
+
def audit_code_safety(max_print = 20, ignore_new = false, show_diffs = false, show_in_priority = false, user_name = 'usr')
|
17
|
+
puts 'Running source code safety audit script.'
|
18
|
+
puts
|
19
|
+
|
20
|
+
max_print = 1_000_000 if max_print < 0
|
21
|
+
safety_cfg = File.exist?(SAFETY_FILE) ? YAML.load_file(SAFETY_FILE) : {}
|
22
|
+
file_safety = safety_cfg["file safety"]
|
23
|
+
if file_safety.nil?
|
24
|
+
puts "Creating new 'file safety' block in #{SAFETY_FILE}"
|
25
|
+
safety_cfg["file safety"] = file_safety = {}
|
26
|
+
end
|
27
|
+
file_safety.each do |k, v|
|
28
|
+
rev = v['safe_revision']
|
29
|
+
v['safe_revision'] = rev.to_s if rev.is_a?(Integer)
|
30
|
+
end
|
31
|
+
orig_count = file_safety.size
|
32
|
+
|
33
|
+
safety_repo = trunk_repo = get_trunk_repo
|
34
|
+
|
35
|
+
# TODO: below is broken for git-svn
|
36
|
+
# Is it needed?
|
37
|
+
|
38
|
+
SAFETY_REPOS.each{|suffix, alt|
|
39
|
+
# Temporarily override to only audit a different file list
|
40
|
+
if safety_repo.end_with?(suffix)
|
41
|
+
safety_repo = safety_repo[0...-suffix.length]+alt
|
42
|
+
break
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
46
|
+
if ignore_new
|
47
|
+
puts "Not checking for new files in #{safety_repo}"
|
48
|
+
else
|
49
|
+
puts "Checking for new files in #{safety_repo}"
|
50
|
+
new_files = get_new_files(safety_repo)
|
51
|
+
# Ignore subdirectories, and exclude code_safety.yml by default.
|
52
|
+
new_files.delete_if{|f| f =~ /[\/\\]$/ || f == SAFETY_FILE}
|
53
|
+
new_files.each{|f|
|
54
|
+
unless file_safety.has_key?(f)
|
55
|
+
file_safety[f] = {
|
56
|
+
"comments" => nil,
|
57
|
+
"reviewed_by" => nil,
|
58
|
+
"safe_revision" => nil }
|
59
|
+
end
|
60
|
+
}
|
61
|
+
File.open(SAFETY_FILE, 'w') do |file|
|
62
|
+
# Consistent file diffs, as ruby preserves Hash insertion order since v1.9
|
63
|
+
safety_cfg["file safety"] = Hash[file_safety.sort]
|
64
|
+
YAML.dump(safety_cfg, file) # Save changes before checking latest revisions
|
65
|
+
end
|
66
|
+
end
|
67
|
+
puts "Updating latest revisions for #{file_safety.size} files"
|
68
|
+
set_last_changed_revision(trunk_repo, file_safety, file_safety.keys)
|
69
|
+
puts "\nSummary:"
|
70
|
+
puts "Number of files originally in #{SAFETY_FILE}: #{orig_count}"
|
71
|
+
puts "Number of new files added: #{file_safety.size - orig_count}"
|
72
|
+
|
73
|
+
# Now generate statistics:
|
74
|
+
unknown = file_safety.values.select{|x| x["safe_revision"].nil?}
|
75
|
+
unsafe = file_safety.values.select{|x|
|
76
|
+
!x["safe_revision"].nil? && x["safe_revision"] != -1 &&
|
77
|
+
x["last_changed_rev"] != x['safe_revision'] &&
|
78
|
+
!(x["last_changed_rev"] =~ /^[0-9]+$/ && x['safe_revision'] =~ /^[0-9]+$/ &&
|
79
|
+
x["last_changed_rev"].to_i < x['safe_revision'].to_i)
|
80
|
+
}
|
81
|
+
puts "Number of files with no safe version: #{unknown.size}"
|
82
|
+
puts "Number of files which are no longer safe: #{unsafe.size}"
|
83
|
+
puts
|
84
|
+
printed = []
|
85
|
+
# We also print a third category: ones which are no longer in the repository
|
86
|
+
if show_in_priority
|
87
|
+
file_list = file_safety.sort_by {|k,v| v.nil? ? -100 : v["last_changed_rev"].to_i}.map(&:first)
|
88
|
+
else
|
89
|
+
file_list = file_safety.keys.sort
|
90
|
+
end
|
91
|
+
|
92
|
+
file_list.each{|f|
|
93
|
+
if print_file_safety(file_safety, trunk_repo, f, false, printed.size >= max_print)
|
94
|
+
printed << f
|
95
|
+
end
|
96
|
+
}
|
97
|
+
puts "... and #{printed.size - max_print} others" if printed.size > max_print
|
98
|
+
if show_diffs
|
99
|
+
puts
|
100
|
+
printed.each{|f|
|
101
|
+
print_file_diffs(file_safety, trunk_repo, f, user_name)
|
102
|
+
}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Print summary details of a file's known safety
|
107
|
+
# If not verbose, only prints details for unsafe files
|
108
|
+
# Returns true if anything printed (or would have been printed if silent),
|
109
|
+
# or false otherwise.
|
110
|
+
def print_file_safety(file_safety, repo, fname, verbose = false, silent = false)
|
111
|
+
msg = "#{fname}\n "
|
112
|
+
entry = file_safety[fname]
|
113
|
+
msg += "File not in audit list" if entry.nil?
|
114
|
+
|
115
|
+
if entry["safe_revision"].nil?
|
116
|
+
msg += "No safe revision known"
|
117
|
+
msg += ", last changed #{entry['last_changed_rev']}" unless entry["last_changed_rev"].nil?
|
118
|
+
else
|
119
|
+
repolatest = entry["last_changed_rev"] # May have been prepopulated en mass
|
120
|
+
msg += 'Not in repository: ' if entry['last_changed_rev'] == -1
|
121
|
+
if (repolatest != entry['safe_revision']) &&
|
122
|
+
!(repolatest =~ /^[0-9]+$/ && entry['safe_revision'] =~ /^[0-9]+$/ &&
|
123
|
+
repolatest.to_i < entry['safe_revision'].to_i)
|
124
|
+
# (Allow later revisions to be treated as safe for svn)
|
125
|
+
msg += "No longer safe since revision #{repolatest}: "
|
126
|
+
else
|
127
|
+
return false unless verbose
|
128
|
+
msg += "Safe: "
|
129
|
+
end
|
130
|
+
msg += "revision #{entry['safe_revision']} reviewed by #{entry['reviewed_by']}"
|
131
|
+
end
|
132
|
+
msg += "\n Comments: #{entry['comments']}" if entry['comments']
|
133
|
+
puts msg unless silent
|
134
|
+
return true
|
135
|
+
end
|
136
|
+
|
137
|
+
def flag_file_as_safe(release, reviewed_by, comments, f)
|
138
|
+
safety_cfg = YAML.load_file(SAFETY_FILE)
|
139
|
+
file_safety = safety_cfg["file safety"]
|
140
|
+
|
141
|
+
unless File.exist?(f)
|
142
|
+
abort("Error: Unable to flag non-existent file as safe: #{f}")
|
143
|
+
end
|
144
|
+
unless file_safety.has_key?(f)
|
145
|
+
file_safety[f] = {
|
146
|
+
"comments" => nil,
|
147
|
+
"reviewed_by" => :dummy, # dummy value, will be overwritten
|
148
|
+
"safe_revision" => nil }
|
149
|
+
end
|
150
|
+
entry = file_safety[f]
|
151
|
+
entry_orig = entry.dup
|
152
|
+
if comments.to_s.length > 0 && entry["comments"] != comments
|
153
|
+
entry["comments"] = if entry["comments"].to_s.empty?
|
154
|
+
comments
|
155
|
+
else
|
156
|
+
"#{entry["comments"]}#{'.' unless entry["comments"].end_with?('.')} Revision #{release}: #{comments}"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
if entry["safe_revision"]
|
160
|
+
unless release
|
161
|
+
abort("Error: File already has safe revision #{entry["safe_revision"]}: #{f}")
|
162
|
+
end
|
163
|
+
if release.is_a?(Integer) && release < entry["safe_revision"]
|
164
|
+
puts("Warning: Rolling back safe revision from #{entry['safe_revision']} to #{release} for #{f}")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
entry["safe_revision"] = release
|
168
|
+
entry["reviewed_by"] = reviewed_by
|
169
|
+
if entry == entry_orig
|
170
|
+
puts "No changes when updating safe_revision to #{release || '[none]'} for #{f}"
|
171
|
+
else
|
172
|
+
File.open(SAFETY_FILE, 'w') do |file|
|
173
|
+
# Consistent file diffs, as ruby preserves Hash insertion order since v1.9
|
174
|
+
safety_cfg["file safety"] = Hash[file_safety.sort]
|
175
|
+
YAML.dump(safety_cfg, file) # Save changes before checking latest revisions
|
176
|
+
end
|
177
|
+
puts "Updated safe_revision to #{release || '[none]'} for #{f}"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Determine the type of repository
|
182
|
+
def repository_type
|
183
|
+
return 'svn' if Dir.exists?('.svn')
|
184
|
+
return 'git-svn' if Dir.exists?('.git') && open('.git/config').grep(/svn/).any?
|
185
|
+
return 'git' if Dir.exists?('.git') && open('.git/config').grep(/git/).any?
|
186
|
+
return 'not known'
|
187
|
+
end
|
188
|
+
|
189
|
+
def get_trunk_repo
|
190
|
+
case repository_type
|
191
|
+
when 'svn'
|
192
|
+
repo_info = %x[svn info]
|
193
|
+
puts 'svn case'
|
194
|
+
return repo_info.split("\n").select{|x| x=~ /^URL: /}.collect{|x| x[5..-1]}.first
|
195
|
+
when 'git-svn'
|
196
|
+
puts 'git-svn case'
|
197
|
+
repo_info = %x[git svn info]
|
198
|
+
return repo_info.split("\n").select{|x| x =~ /^URL: /}.collect{|x| x[5..-1]}.first
|
199
|
+
when 'git'
|
200
|
+
puts 'git case'
|
201
|
+
repo_info = %x[git remote -v]
|
202
|
+
return repo_info.split("\n").first[7..-9]
|
203
|
+
else
|
204
|
+
return 'Information not available. Unknown repository type'
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def get_new_files(safety_repo)
|
209
|
+
case repository_type
|
210
|
+
when 'svn', 'git-svn'
|
211
|
+
%x[svn ls -R "#{safety_repo}"].split("\n")
|
212
|
+
when 'git'
|
213
|
+
#%x[git ls-files --modified].split("\n")
|
214
|
+
%x[git ls-files].split("\n")
|
215
|
+
|
216
|
+
# TODO: Below is for remote repository - for testing use local files
|
217
|
+
#new_files = %x[git ls-files --modified #{safety_repo}].split("\n")
|
218
|
+
# TODO: Do we need the --modified option?
|
219
|
+
#new_files = %x[git ls-files --modified].split("\n")
|
220
|
+
else
|
221
|
+
[]
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Fill in the latest changed revisions in a file safety map.
|
226
|
+
# (Don't write this data to the YAML file, as it is intrinsic to the SVN
|
227
|
+
# repository.)
|
228
|
+
def set_last_changed_revision(repo, file_safety, fnames)
|
229
|
+
dot_freq = (file_safety.size / 40.0).ceil # Print up to 40 progress dots
|
230
|
+
case repository_type
|
231
|
+
when 'git'
|
232
|
+
fnames = file_safety.keys if fnames.nil?
|
233
|
+
|
234
|
+
fnames.each_with_index do |f, i|
|
235
|
+
info = %x[git log -n 1 #{f}].split("\n").first[7..-1]
|
236
|
+
if info.nil? || info.empty?
|
237
|
+
file_safety[f]["last_changed_rev"] = -1
|
238
|
+
else
|
239
|
+
file_safety[f]["last_changed_rev"] = info
|
240
|
+
end
|
241
|
+
# Show progress
|
242
|
+
print '.' if (i % dot_freq) == 0
|
243
|
+
end
|
244
|
+
puts
|
245
|
+
when 'git-svn', 'svn'
|
246
|
+
fnames = file_safety.keys if fnames.nil?
|
247
|
+
|
248
|
+
fnames.each_with_index do |f, i|
|
249
|
+
last_revision = get_last_changed_revision(repo, f)
|
250
|
+
if last_revision.nil? || last_revision.empty?
|
251
|
+
file_safety[f]['last_changed_rev'] = -1
|
252
|
+
else
|
253
|
+
file_safety[f]['last_changed_rev'] = last_revision
|
254
|
+
end
|
255
|
+
# Show progress
|
256
|
+
print '.' if (i % dot_freq) == 0
|
257
|
+
end
|
258
|
+
puts
|
259
|
+
# NOTE: Do we need the following for retries?
|
260
|
+
# if retries && result.size != fnames.size && fnames.size > 1
|
261
|
+
# # At least one invalid (deleted file --> subsequent arguments ignored)
|
262
|
+
# # Try each file individually
|
263
|
+
# # (It would probably be safe to continue from the extra_info.size argument)
|
264
|
+
# puts "Retrying (got #{result.size}, expected #{fnames.size})" if debug >= 2
|
265
|
+
# result = []
|
266
|
+
# fnames.each{ |f|
|
267
|
+
# result += svn_info_entries([f], repo, false, debug)
|
268
|
+
# }
|
269
|
+
# end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# Return the last changed revision
|
274
|
+
def get_last_changed_revision(repo, fname)
|
275
|
+
case repository_type
|
276
|
+
when 'git'
|
277
|
+
%x[git log -n 1 "#{fname}"].split("\n").first[7..-1]
|
278
|
+
when 'git-svn', 'svn'
|
279
|
+
begin
|
280
|
+
svn_info = %x[svn info -r head "#{repo}/#{fname}"]
|
281
|
+
rescue
|
282
|
+
puts "we have an error in the svn info line"
|
283
|
+
end
|
284
|
+
begin
|
285
|
+
svn_info.match('Last Changed Rev: ([0-9]*)').to_a[1]
|
286
|
+
rescue
|
287
|
+
puts "We have an error in getting the revision"
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# Get mime type. Note that Git does not have this information
|
293
|
+
def get_mime_type(repo, fname)
|
294
|
+
case repository_type
|
295
|
+
when 'git'
|
296
|
+
'Git does not provide mime types'
|
297
|
+
when 'git-svn', 'svn'
|
298
|
+
%x[svn propget svn:mime-type "#{repo}/#{fname}"].chomp
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
# # Print file diffs, for code review
|
303
|
+
def print_file_diffs(file_safety, repo, fname, user_name)
|
304
|
+
entry = file_safety[fname]
|
305
|
+
repolatest = entry['last_changed_rev']
|
306
|
+
safe_revision = entry['safe_revision']
|
307
|
+
|
308
|
+
if safe_revision.nil?
|
309
|
+
first_revision = set_safe_revision
|
310
|
+
print_repo_file_diffs(repolatest, repo, fname, user_name, first_revision)
|
311
|
+
else
|
312
|
+
|
313
|
+
rev = get_last_changed_revision(repo, fname)
|
314
|
+
if rev
|
315
|
+
mime = get_mime_type(repo, fname)
|
316
|
+
end
|
317
|
+
|
318
|
+
print_repo_file_diffs(repolatest, repo, fname, user_name, safe_revision) if repolatest != safe_revision
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# Returns first commit for git and 0 for svn in order to be used to display
|
323
|
+
# new files. Called from print_file_diffs
|
324
|
+
def set_safe_revision
|
325
|
+
case repository_type
|
326
|
+
when 'git'
|
327
|
+
%x[git rev-list --max-parents=0 HEAD].chomp
|
328
|
+
when 'git-svn', 'svn'
|
329
|
+
0
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
def print_repo_file_diffs(repolatest, repo, fname, user_name, safe_revision)
|
334
|
+
case repository_type
|
335
|
+
when 'git'
|
336
|
+
puts %[git --no-pager diff -b #{safe_revision}..#{repolatest} #{fname}]
|
337
|
+
puts %x[git --no-pager diff -b #{safe_revision}..#{repolatest} #{fname}]
|
338
|
+
when 'git-svn', 'svn'
|
339
|
+
puts %[svn diff -r #{safe_revision.to_i}:#{repolatest.to_i} -x -b #{repo}/#{fname}]
|
340
|
+
puts %x[svn diff -r #{safe_revision.to_i}:#{repolatest.to_i} -x -b #{repo}/#{fname}]
|
341
|
+
else
|
342
|
+
puts 'Unknown repo'
|
343
|
+
end
|
344
|
+
|
345
|
+
puts %(To flag the changes to this file as safe, run:)
|
346
|
+
puts %( rake audit:safe release=#{repolatest} file=#{fname} reviewed_by=#{user_name} comments="")
|
347
|
+
puts
|
348
|
+
end
|
349
|
+
|
350
|
+
def release_valid?
|
351
|
+
case repository_type
|
352
|
+
when 'svn', 'git-svn'
|
353
|
+
ENV['release'] =~ /\A[0-9][0-9]*\Z/
|
354
|
+
when 'git'
|
355
|
+
ENV['release'] =~ /\A[0-9a-f]{40}\Z/
|
356
|
+
else
|
357
|
+
false
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def get_release
|
362
|
+
release = ENV['release']
|
363
|
+
release = nil if release == '0'
|
364
|
+
case repository_type
|
365
|
+
when 'svn', 'git-svn'
|
366
|
+
release.to_i
|
367
|
+
when 'git'
|
368
|
+
release
|
369
|
+
else
|
370
|
+
''
|
371
|
+
end
|
372
|
+
return release
|
373
|
+
end
|
374
|
+
|
375
|
+
namespace :audit do
|
376
|
+
|
377
|
+
desc "Audit safety of source code.
|
378
|
+
Usage: audit:code [max_print=n] [ignore_new=false|true] [show_diffs=false|true] [reviewed_by=usr]
|
379
|
+
|
380
|
+
File #{SAFETY_FILE} lists the safety and revision information
|
381
|
+
of the era source code. This task updates the list, and [TODO] warns about
|
382
|
+
files which have changed since they were last verified as safe."
|
383
|
+
task(:code) do
|
384
|
+
puts "Usage: audit:code [max_print=n] [ignore_new=false|true] [show_diffs=false|true] [show_in_priority=false|true] [reviewed_by=usr]"
|
385
|
+
puts "This is a #{repository_type} repository"
|
386
|
+
|
387
|
+
ignore_new = (ENV['ignore_new'].to_s =~ /\Atrue\Z/i)
|
388
|
+
show_diffs = (ENV['show_diffs'].to_s =~ /\Atrue\Z/i)
|
389
|
+
show_in_priority = (ENV['show_in_priority'].to_s =~ /\Atrue\Z/i)
|
390
|
+
max_print = ENV['max_print'] =~ /\A-?[0-9][0-9]*\Z/ ? ENV['max_print'].to_i : 20
|
391
|
+
|
392
|
+
audit_code_safety(max_print, ignore_new, show_diffs, show_in_priority, ENV['reviewed_by'])
|
393
|
+
|
394
|
+
unless show_diffs
|
395
|
+
puts "To show file diffs, run: rake audit:code max_print=-1 show_diffs=true"
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
desc "Flag a source file as safe.
|
400
|
+
|
401
|
+
Usage:
|
402
|
+
Flag as safe: rake audit:safe release=revision reviewed_by=usr [comments=...] file=f
|
403
|
+
Needs review: rake audit:safe release=0 [comments=...] file=f"
|
404
|
+
task (:safe) do
|
405
|
+
required_fields = ["release", "file"]
|
406
|
+
required_fields << "reviewed_by" unless ENV['release'] == '0'
|
407
|
+
missing = required_fields.collect{|f| (f if ENV[f].to_s.empty? || (f=='reviewed_by' && ENV[f] == 'usr'))}.compact # Avoid accidental missing username
|
408
|
+
if !missing.empty?
|
409
|
+
puts "Usage: rake audit:safe release=revision reviewed_by=usr [comments=...] file=f"
|
410
|
+
puts "or, to flag a file for review: rake audit:safe release=0 [comments=...] file=f"
|
411
|
+
abort("Error: Missing required argument(s): #{missing.join(', ')}")
|
412
|
+
end
|
413
|
+
|
414
|
+
unless release_valid?
|
415
|
+
puts "Usage: rake audit:safe release=revision reviewed_by=usr [comments=...] file=f"
|
416
|
+
puts "or, to flag a file for review: rake audit:safe release=0 [comments=...] file=f"
|
417
|
+
abort("Error: Invalid release: #{ENV['release']}")
|
418
|
+
end
|
419
|
+
|
420
|
+
release = get_release
|
421
|
+
flag_file_as_safe(release, ENV['reviewed_by'], ENV['comments'], ENV['file'])
|
422
|
+
end
|
423
|
+
end
|