ndr_support 3.1.1

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.
Files changed (67) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +14 -0
  3. data/.rubocop.yml +27 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +22 -0
  6. data/CODE_OF_CONDUCT.md +13 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +16 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +91 -0
  11. data/Rakefile +12 -0
  12. data/code_safety.yml +258 -0
  13. data/gemfiles/Gemfile.rails32 +6 -0
  14. data/gemfiles/Gemfile.rails32.lock +108 -0
  15. data/gemfiles/Gemfile.rails41 +6 -0
  16. data/gemfiles/Gemfile.rails41.lock +111 -0
  17. data/gemfiles/Gemfile.rails42 +6 -0
  18. data/gemfiles/Gemfile.rails42.lock +111 -0
  19. data/lib/ndr_support.rb +21 -0
  20. data/lib/ndr_support/array.rb +52 -0
  21. data/lib/ndr_support/concerns/working_days.rb +94 -0
  22. data/lib/ndr_support/date_and_time_extensions.rb +103 -0
  23. data/lib/ndr_support/daterange.rb +196 -0
  24. data/lib/ndr_support/fixnum/calculations.rb +15 -0
  25. data/lib/ndr_support/fixnum/julian_date_conversions.rb +14 -0
  26. data/lib/ndr_support/hash.rb +52 -0
  27. data/lib/ndr_support/integer.rb +12 -0
  28. data/lib/ndr_support/nil.rb +38 -0
  29. data/lib/ndr_support/ourdate.rb +97 -0
  30. data/lib/ndr_support/ourtime.rb +51 -0
  31. data/lib/ndr_support/regexp_range.rb +65 -0
  32. data/lib/ndr_support/safe_file.rb +185 -0
  33. data/lib/ndr_support/safe_path.rb +268 -0
  34. data/lib/ndr_support/string/cleaning.rb +136 -0
  35. data/lib/ndr_support/string/conversions.rb +137 -0
  36. data/lib/ndr_support/tasks.rb +1 -0
  37. data/lib/ndr_support/time/conversions.rb +13 -0
  38. data/lib/ndr_support/utf8_encoding.rb +72 -0
  39. data/lib/ndr_support/utf8_encoding/control_characters.rb +53 -0
  40. data/lib/ndr_support/utf8_encoding/force_binary.rb +44 -0
  41. data/lib/ndr_support/utf8_encoding/object_support.rb +31 -0
  42. data/lib/ndr_support/version.rb +5 -0
  43. data/lib/ndr_support/yaml/serialization_migration.rb +65 -0
  44. data/lib/tasks/audit_code.rake +423 -0
  45. data/ndr_support.gemspec +39 -0
  46. data/test/array_test.rb +20 -0
  47. data/test/concerns/working_days_test.rb +122 -0
  48. data/test/daterange_test.rb +194 -0
  49. data/test/fixnum/calculations_test.rb +28 -0
  50. data/test/hash_test.rb +84 -0
  51. data/test/integer_test.rb +14 -0
  52. data/test/nil_test.rb +40 -0
  53. data/test/ourdate_test.rb +27 -0
  54. data/test/ourtime_test.rb +27 -0
  55. data/test/regexp_range_test.rb +135 -0
  56. data/test/resources/filesystem_paths.yml +37 -0
  57. data/test/safe_file_test.rb +597 -0
  58. data/test/safe_path_test.rb +168 -0
  59. data/test/string/cleaning_test.rb +176 -0
  60. data/test/string/conversions_test.rb +353 -0
  61. data/test/test_helper.rb +41 -0
  62. data/test/time/conversions_test.rb +15 -0
  63. data/test/utf8_encoding/control_characters_test.rb +84 -0
  64. data/test/utf8_encoding/force_binary_test.rb +64 -0
  65. data/test/utf8_encoding_test.rb +170 -0
  66. data/test/yaml/serialization_test.rb +145 -0
  67. 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,5 @@
1
+ # This defines the NdrSupport version. If you change it, rebuild and commit the gem.
2
+ # Use "rake build" to build the gem, see rake -T for all bundler rake tasks (and our own).
3
+ module NdrSupport
4
+ VERSION = '3.1.1'
5
+ 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