ndr_support 3.3.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Guardfile +18 -10
- data/README.md +1 -22
- data/Rakefile +1 -1
- data/code_safety.yml +6 -10
- data/lib/ndr_support/version.rb +4 -2
- data/ndr_support.gemspec +2 -0
- metadata +36 -3
- data/lib/tasks/audit_code.rake +0 -475
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d551092d3ebfd8087efcbfa8321e1a7ead1837e3
|
4
|
+
data.tar.gz: 9b6649a2ddae13a5f9f0dacb73cdad200a62c51f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18b986bdcb86d9457514752dd6cbb7a4db9b52daa3814eebd312ef99078d3d2a649e9690f77055189ba89097bc6422c571bfdec94f3872d24c1a4876c1f6a49c
|
7
|
+
data.tar.gz: 8acdf5704f9e9b0cdc47fb1b0d31c2051d8eb3ed6a59e23c82ad9975c6d2305b8948443145df8c12e3045885dfcc0dc5e7cee2f3777e57b68a7e3c80046cacbd
|
data/Guardfile
CHANGED
@@ -1,16 +1,24 @@
|
|
1
1
|
# A sample Guardfile
|
2
2
|
# More info at https://github.com/guard/guard#readme
|
3
3
|
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
# This group allows to skip running rubocop when tests fail.
|
5
|
+
group :red_green_refactor, halt_on_fail: true do
|
6
|
+
guard :minitest do
|
7
|
+
watch(%r{^test/.+_test\.rb$})
|
8
|
+
watch('test/test_helper.rb') { 'test' }
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
# Non-rails
|
11
|
+
watch(%r{^lib/ndr_support/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
|
12
|
+
end
|
13
13
|
|
14
|
-
#
|
15
|
-
|
14
|
+
# automatically check Ruby code style with Rubocop when files are modified
|
15
|
+
guard :shell do
|
16
|
+
watch(/.+\.(rb|rake)$/) do |m|
|
17
|
+
unless system("bundle exec rake rubocop:diff #{m[0]}")
|
18
|
+
Notifier.notify "#{File.basename(m[0])} inspected, offenses detected",
|
19
|
+
title: 'RuboCop results (partial)', image: :failed
|
20
|
+
end
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end
|
16
24
|
end
|
data/README.md
CHANGED
@@ -3,8 +3,7 @@
|
|
3
3
|
This is the Public Health England (PHE) National Disease Registers (NDR) Support ruby gem, providing:
|
4
4
|
|
5
5
|
1. core ruby class extensions;
|
6
|
-
2. additional time, regular expression, file security and encoding classes
|
7
|
-
3. rake tasks to manage code auditing of ruby based projects.
|
6
|
+
2. additional time, regular expression, file security and encoding classes.
|
8
7
|
|
9
8
|
## Installation
|
10
9
|
|
@@ -54,26 +53,6 @@ To enable this add the following line to your code:
|
|
54
53
|
include NdrSupport::YAML::SerializationMigration
|
55
54
|
```
|
56
55
|
|
57
|
-
### Code Auditing Rake Tasks
|
58
|
-
|
59
|
-
ndr_support also provides a mechanism to manage the state of routine code quality and security peer reviews. It should be used as part of wider quality and security policies.
|
60
|
-
|
61
|
-
It provides rake tasks to help manage the process of persisting the state of security reviews.
|
62
|
-
|
63
|
-
Once files have been reviewed as secure, the revision number for that file is stored in code_safety.yml. If used within a Rails app, this file is stored in the config/ folder, otherwise it is kept in the project's root folder.
|
64
|
-
|
65
|
-
Note: This feature works with svn and git repositories and svn, git-svn and git working copies.
|
66
|
-
|
67
|
-
To add code auditing to your project add this line to your application's Rakefile:
|
68
|
-
|
69
|
-
```ruby
|
70
|
-
require 'ndr_support/tasks'
|
71
|
-
```
|
72
|
-
|
73
|
-
For more details of the tasks available, execute:
|
74
|
-
|
75
|
-
$ rake -T audit
|
76
|
-
|
77
56
|
## Contributing
|
78
57
|
|
79
58
|
1. Fork it ( https://github.com/PublicHealthEngland/ndr_support/fork )
|
data/Rakefile
CHANGED
data/code_safety.yml
CHANGED
@@ -27,19 +27,19 @@ file safety:
|
|
27
27
|
Guardfile:
|
28
28
|
comments:
|
29
29
|
reviewed_by: timgentry
|
30
|
-
safe_revision:
|
30
|
+
safe_revision: e33e9dae7f38bc449ce9276515a539836dbbbd53
|
31
31
|
LICENSE.txt:
|
32
32
|
comments:
|
33
33
|
reviewed_by: timgentry
|
34
34
|
safe_revision: 90328cca8494539257e192a63b240a91c89f0616
|
35
35
|
README.md:
|
36
36
|
comments:
|
37
|
-
reviewed_by:
|
38
|
-
safe_revision:
|
37
|
+
reviewed_by: timgentry
|
38
|
+
safe_revision: e33e9dae7f38bc449ce9276515a539836dbbbd53
|
39
39
|
Rakefile:
|
40
40
|
comments:
|
41
41
|
reviewed_by: timgentry
|
42
|
-
safe_revision:
|
42
|
+
safe_revision: e33e9dae7f38bc449ce9276515a539836dbbbd53
|
43
43
|
gemfiles/Gemfile.rails32:
|
44
44
|
comments:
|
45
45
|
reviewed_by: pauleves
|
@@ -159,19 +159,15 @@ file safety:
|
|
159
159
|
lib/ndr_support/version.rb:
|
160
160
|
comments:
|
161
161
|
reviewed_by: timgentry
|
162
|
-
safe_revision:
|
162
|
+
safe_revision: 04c7617a6cc63d614e53cd6bc053dec46cc15786
|
163
163
|
lib/ndr_support/yaml/serialization_migration.rb:
|
164
164
|
comments:
|
165
165
|
reviewed_by: timgentry
|
166
166
|
safe_revision: 29595e6431587ff9b7db6e3ad3abbb3577bff99c
|
167
|
-
lib/tasks/audit_code.rake:
|
168
|
-
comments:
|
169
|
-
reviewed_by: josh.pencheon
|
170
|
-
safe_revision: 20cde082ee86a547de7b21a97a695bb307ac9f64
|
171
167
|
ndr_support.gemspec:
|
172
168
|
comments:
|
173
169
|
reviewed_by: timgentry
|
174
|
-
safe_revision:
|
170
|
+
safe_revision: e33e9dae7f38bc449ce9276515a539836dbbbd53
|
175
171
|
test/array_test.rb:
|
176
172
|
comments:
|
177
173
|
reviewed_by: timgentry
|
data/lib/ndr_support/version.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# 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
|
4
|
+
# Use "rake build" to build the gem, see rake -T for all bundler rake tasks.
|
3
5
|
module NdrSupport
|
4
|
-
VERSION = '
|
6
|
+
VERSION = '4.0.0'
|
5
7
|
end
|
data/ndr_support.gemspec
CHANGED
@@ -31,8 +31,10 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_development_dependency 'minitest', '>= 5.0.0'
|
32
32
|
spec.add_development_dependency 'mocha', '~> 1.1'
|
33
33
|
|
34
|
+
spec.add_development_dependency 'ndr_dev_support', '~> 1.1', '>= 1.1.1'
|
34
35
|
spec.add_development_dependency 'guard'
|
35
36
|
spec.add_development_dependency 'guard-rubocop'
|
37
|
+
spec.add_development_dependency 'guard-shell'
|
36
38
|
spec.add_development_dependency 'guard-minitest'
|
37
39
|
spec.add_development_dependency 'terminal-notifier-guard' if RUBY_PLATFORM =~ /darwin/
|
38
40
|
spec.add_development_dependency 'simplecov'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ndr_support
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- NCRS Development Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -106,6 +106,26 @@ dependencies:
|
|
106
106
|
- - ~>
|
107
107
|
- !ruby/object:Gem::Version
|
108
108
|
version: '1.1'
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: ndr_dev_support
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ~>
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '1.1'
|
116
|
+
- - '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 1.1.1
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '1.1'
|
126
|
+
- - '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: 1.1.1
|
109
129
|
- !ruby/object:Gem::Dependency
|
110
130
|
name: guard
|
111
131
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,6 +154,20 @@ dependencies:
|
|
134
154
|
- - '>='
|
135
155
|
- !ruby/object:Gem::Version
|
136
156
|
version: '0'
|
157
|
+
- !ruby/object:Gem::Dependency
|
158
|
+
name: guard-shell
|
159
|
+
requirement: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - '>='
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
type: :development
|
165
|
+
prerelease: false
|
166
|
+
version_requirements: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - '>='
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '0'
|
137
171
|
- !ruby/object:Gem::Dependency
|
138
172
|
name: guard-minitest
|
139
173
|
requirement: !ruby/object:Gem::Requirement
|
@@ -224,7 +258,6 @@ files:
|
|
224
258
|
- lib/ndr_support/utf8_encoding/object_support.rb
|
225
259
|
- lib/ndr_support/version.rb
|
226
260
|
- lib/ndr_support/yaml/serialization_migration.rb
|
227
|
-
- lib/tasks/audit_code.rake
|
228
261
|
- ndr_support.gemspec
|
229
262
|
- test/array_test.rb
|
230
263
|
- test/concerns/working_days_test.rb
|
data/lib/tasks/audit_code.rake
DELETED
@@ -1,475 +0,0 @@
|
|
1
|
-
SAFETY_FILE =
|
2
|
-
if File.exist?('code_safety.yml')
|
3
|
-
'code_safety.yml'
|
4
|
-
elsif defined?(Rails)
|
5
|
-
Rails.root.join('config', 'code_safety.yml')
|
6
|
-
else
|
7
|
-
'code_safety.yml'
|
8
|
-
end
|
9
|
-
|
10
|
-
# Temporary overrides to only audit external access files
|
11
|
-
SAFETY_REPOS = [['/svn/era', '/svn/extra/era/external-access']]
|
12
|
-
|
13
|
-
require 'yaml'
|
14
|
-
|
15
|
-
# Parameter max_print is number of entries to print before truncating output
|
16
|
-
# (negative value => print all)
|
17
|
-
def audit_code_safety(max_print = 20, ignore_new = false, show_diffs = false, show_in_priority = false, user_name = 'usr')
|
18
|
-
puts 'Running source code safety audit script.'
|
19
|
-
puts
|
20
|
-
|
21
|
-
max_print = 1_000_000 if max_print < 0
|
22
|
-
safety_cfg = File.exist?(SAFETY_FILE) ? YAML.load_file(SAFETY_FILE) : {}
|
23
|
-
file_safety = safety_cfg['file safety']
|
24
|
-
if file_safety.nil?
|
25
|
-
puts "Creating new 'file safety' block in #{SAFETY_FILE}"
|
26
|
-
safety_cfg['file safety'] = file_safety = {}
|
27
|
-
end
|
28
|
-
file_safety.each do |_k, v|
|
29
|
-
rev = v['safe_revision']
|
30
|
-
v['safe_revision'] = rev.to_s if rev.is_a?(Integer)
|
31
|
-
end
|
32
|
-
orig_count = file_safety.size
|
33
|
-
|
34
|
-
safety_repo = trunk_repo = get_trunk_repo
|
35
|
-
|
36
|
-
# TODO: below is broken for git-svn
|
37
|
-
# Is it needed?
|
38
|
-
|
39
|
-
SAFETY_REPOS.each do |suffix, alt|
|
40
|
-
# Temporarily override to only audit a different file list
|
41
|
-
if safety_repo.end_with?(suffix)
|
42
|
-
safety_repo = safety_repo[0...-suffix.length] + alt
|
43
|
-
break
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
if ignore_new
|
48
|
-
puts "Not checking for new files in #{safety_repo}"
|
49
|
-
else
|
50
|
-
puts "Checking for new files in #{safety_repo}"
|
51
|
-
new_files = get_new_files(safety_repo)
|
52
|
-
# Ignore subdirectories, and exclude code_safety.yml by default.
|
53
|
-
new_files.delete_if { |f| f =~ /[\/\\]$/ || f == SAFETY_FILE }
|
54
|
-
new_files.each do |f|
|
55
|
-
next if file_safety.key?(f)
|
56
|
-
file_safety[f] = {
|
57
|
-
'comments' => nil,
|
58
|
-
'reviewed_by' => nil,
|
59
|
-
'safe_revision' => nil }
|
60
|
-
end
|
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 do |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
|
-
end
|
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
|
-
file_list =
|
87
|
-
if show_in_priority
|
88
|
-
file_safety.sort_by { |_k, v| v.nil? ? -100 : v['last_changed_rev'].to_i }.map(&:first)
|
89
|
-
else
|
90
|
-
file_safety.keys.sort
|
91
|
-
end
|
92
|
-
|
93
|
-
file_list.each do |f|
|
94
|
-
if print_file_safety(file_safety, trunk_repo, f, false, printed.size >= max_print)
|
95
|
-
printed << f
|
96
|
-
end
|
97
|
-
end
|
98
|
-
puts "... and #{printed.size - max_print} others" if printed.size > max_print
|
99
|
-
if show_diffs
|
100
|
-
puts
|
101
|
-
printed.each do |f|
|
102
|
-
print_file_diffs(file_safety, trunk_repo, f, user_name)
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# Returns `true` unless there are pending reviews:
|
107
|
-
unsafe.length.zero? && unknown.length.zero?
|
108
|
-
end
|
109
|
-
|
110
|
-
# Print summary details of a file's known safety
|
111
|
-
# If not verbose, only prints details for unsafe files
|
112
|
-
# Returns true if anything printed (or would have been printed if silent),
|
113
|
-
# or false otherwise.
|
114
|
-
def print_file_safety(file_safety, repo, fname, verbose = false, silent = false)
|
115
|
-
msg = "#{fname}\n "
|
116
|
-
entry = file_safety[fname]
|
117
|
-
msg += 'File not in audit list' if entry.nil?
|
118
|
-
|
119
|
-
if entry['safe_revision'].nil?
|
120
|
-
msg += 'No safe revision known'
|
121
|
-
msg += ", last changed #{entry['last_changed_rev']}" unless entry['last_changed_rev'].nil?
|
122
|
-
else
|
123
|
-
repolatest = entry['last_changed_rev'] # May have been prepopulated en mass
|
124
|
-
msg += 'Not in repository: ' if entry['last_changed_rev'] == -1
|
125
|
-
if (repolatest != entry['safe_revision']) &&
|
126
|
-
!(repolatest =~ /^[0-9]+$/ && entry['safe_revision'] =~ /^[0-9]+$/ &&
|
127
|
-
repolatest.to_i < entry['safe_revision'].to_i)
|
128
|
-
# (Allow later revisions to be treated as safe for svn)
|
129
|
-
msg += "No longer safe since revision #{repolatest}: "
|
130
|
-
else
|
131
|
-
return false unless verbose
|
132
|
-
msg += 'Safe: '
|
133
|
-
end
|
134
|
-
msg += "revision #{entry['safe_revision']} reviewed by #{entry['reviewed_by']}"
|
135
|
-
end
|
136
|
-
msg += "\n Comments: #{entry['comments']}" if entry['comments']
|
137
|
-
puts msg unless silent
|
138
|
-
true
|
139
|
-
end
|
140
|
-
|
141
|
-
def flag_file_as_safe(release, reviewed_by, comments, f)
|
142
|
-
safety_cfg = YAML.load_file(SAFETY_FILE)
|
143
|
-
file_safety = safety_cfg['file safety']
|
144
|
-
|
145
|
-
unless File.exist?(f)
|
146
|
-
abort("Error: Unable to flag non-existent file as safe: #{f}")
|
147
|
-
end
|
148
|
-
unless file_safety.key?(f)
|
149
|
-
file_safety[f] = {
|
150
|
-
'comments' => nil,
|
151
|
-
'reviewed_by' => :dummy, # dummy value, will be overwritten
|
152
|
-
'safe_revision' => nil }
|
153
|
-
end
|
154
|
-
entry = file_safety[f]
|
155
|
-
entry_orig = entry.dup
|
156
|
-
if comments.to_s.length > 0 && entry['comments'] != comments
|
157
|
-
entry['comments'] = if entry['comments'].to_s.empty?
|
158
|
-
comments
|
159
|
-
else
|
160
|
-
"#{entry['comments']}#{'.' unless entry['comments'].end_with?('.')} Revision #{release}: #{comments}"
|
161
|
-
end
|
162
|
-
end
|
163
|
-
if entry['safe_revision']
|
164
|
-
unless release
|
165
|
-
abort("Error: File already has safe revision #{entry['safe_revision']}: #{f}")
|
166
|
-
end
|
167
|
-
if release.is_a?(Integer) && release < entry['safe_revision']
|
168
|
-
puts("Warning: Rolling back safe revision from #{entry['safe_revision']} to #{release} for #{f}")
|
169
|
-
end
|
170
|
-
end
|
171
|
-
entry['safe_revision'] = release
|
172
|
-
entry['reviewed_by'] = reviewed_by
|
173
|
-
if entry == entry_orig
|
174
|
-
puts "No changes when updating safe_revision to #{release || '[none]'} for #{f}"
|
175
|
-
else
|
176
|
-
File.open(SAFETY_FILE, 'w') do |file|
|
177
|
-
# Consistent file diffs, as ruby preserves Hash insertion order since v1.9
|
178
|
-
safety_cfg['file safety'] = Hash[file_safety.sort]
|
179
|
-
YAML.dump(safety_cfg, file) # Save changes before checking latest revisions
|
180
|
-
end
|
181
|
-
puts "Updated safe_revision to #{release || '[none]'} for #{f}"
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
# Determine the type of repository
|
186
|
-
def repository_type
|
187
|
-
@repository_type ||= if Dir.exist?('.svn') || system("svn info . > /dev/null 2>&1")
|
188
|
-
'svn'
|
189
|
-
elsif Dir.exist?('.git') && open('.git/config').grep(/svn/).any?
|
190
|
-
'git-svn'
|
191
|
-
elsif Dir.exist?('.git') && open('.git/config').grep(/git/).any?
|
192
|
-
'git'
|
193
|
-
else
|
194
|
-
'not known'
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
def get_trunk_repo
|
199
|
-
case repository_type
|
200
|
-
when 'svn'
|
201
|
-
repo_info = %x[svn info]
|
202
|
-
puts 'svn case'
|
203
|
-
return repo_info.split("\n").select { |x| x =~ /^URL: / }.collect { |x| x[5..-1] }.first
|
204
|
-
when 'git-svn'
|
205
|
-
puts 'git-svn case'
|
206
|
-
repo_info = %x[git svn info]
|
207
|
-
return repo_info.split("\n").select { |x| x =~ /^URL: / }.collect { |x| x[5..-1] }.first
|
208
|
-
when 'git'
|
209
|
-
puts 'git case'
|
210
|
-
repo_info = %x[git remote -v]
|
211
|
-
return repo_info.split("\n").first[7..-9]
|
212
|
-
else
|
213
|
-
return 'Information not available. Unknown repository type'
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
def get_new_files(safety_repo)
|
218
|
-
case repository_type
|
219
|
-
when 'svn', 'git-svn'
|
220
|
-
%x[svn ls -R "#{safety_repo}"].split("\n")
|
221
|
-
when 'git'
|
222
|
-
#%x[git ls-files --modified].split("\n")
|
223
|
-
%x[git ls-files].split("\n")
|
224
|
-
|
225
|
-
# TODO: Below is for remote repository - for testing use local files
|
226
|
-
#new_files = %x[git ls-files --modified #{safety_repo}].split("\n")
|
227
|
-
# TODO: Do we need the --modified option?
|
228
|
-
#new_files = %x[git ls-files --modified].split("\n")
|
229
|
-
else
|
230
|
-
[]
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
# Fill in the latest changed revisions in a file safety map.
|
235
|
-
# (Don't write this data to the YAML file, as it is intrinsic to the SVN
|
236
|
-
# repository.)
|
237
|
-
def set_last_changed_revision(repo, file_safety, fnames)
|
238
|
-
dot_freq = (file_safety.size / 40.0).ceil # Print up to 40 progress dots
|
239
|
-
case repository_type
|
240
|
-
when 'git'
|
241
|
-
fnames = file_safety.keys if fnames.nil?
|
242
|
-
|
243
|
-
fnames.each_with_index do |f, i|
|
244
|
-
info = %x[git log -n 1 #{f}].split("\n").first[7..-1]
|
245
|
-
if info.nil? || info.empty?
|
246
|
-
file_safety[f]['last_changed_rev'] = -1
|
247
|
-
else
|
248
|
-
file_safety[f]['last_changed_rev'] = info
|
249
|
-
end
|
250
|
-
# Show progress
|
251
|
-
print '.' if (i % dot_freq) == 0
|
252
|
-
end
|
253
|
-
puts
|
254
|
-
when 'git-svn', 'svn'
|
255
|
-
fnames = file_safety.keys if fnames.nil?
|
256
|
-
|
257
|
-
fnames.each_with_index do |f, i|
|
258
|
-
last_revision = get_last_changed_revision(repo, f)
|
259
|
-
if last_revision.nil? || last_revision.empty?
|
260
|
-
file_safety[f]['last_changed_rev'] = -1
|
261
|
-
else
|
262
|
-
file_safety[f]['last_changed_rev'] = last_revision
|
263
|
-
end
|
264
|
-
# Show progress
|
265
|
-
print '.' if (i % dot_freq) == 0
|
266
|
-
end
|
267
|
-
puts
|
268
|
-
# NOTE: Do we need the following for retries?
|
269
|
-
# if retries && result.size != fnames.size && fnames.size > 1
|
270
|
-
# # At least one invalid (deleted file --> subsequent arguments ignored)
|
271
|
-
# # Try each file individually
|
272
|
-
# # (It would probably be safe to continue from the extra_info.size argument)
|
273
|
-
# puts "Retrying (got #{result.size}, expected #{fnames.size})" if debug >= 2
|
274
|
-
# result = []
|
275
|
-
# fnames.each{ |f|
|
276
|
-
# result += svn_info_entries([f], repo, false, debug)
|
277
|
-
# }
|
278
|
-
# end
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
# Return the last changed revision
|
283
|
-
def get_last_changed_revision(repo, fname)
|
284
|
-
case repository_type
|
285
|
-
when 'git'
|
286
|
-
%x[git log -n 1 "#{fname}"].split("\n").first[7..-1]
|
287
|
-
when 'git-svn', 'svn'
|
288
|
-
begin
|
289
|
-
svn_info = %x[svn info -r head "#{repo}/#{fname}"]
|
290
|
-
rescue
|
291
|
-
puts 'we have an error in the svn info line'
|
292
|
-
end
|
293
|
-
begin
|
294
|
-
svn_info.match('Last Changed Rev: ([0-9]*)').to_a[1]
|
295
|
-
rescue
|
296
|
-
puts 'We have an error in getting the revision'
|
297
|
-
end
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
# Get mime type. Note that Git does not have this information
|
302
|
-
def get_mime_type(repo, fname)
|
303
|
-
case repository_type
|
304
|
-
when 'git'
|
305
|
-
'Git does not provide mime types'
|
306
|
-
when 'git-svn', 'svn'
|
307
|
-
%x[svn propget svn:mime-type "#{repo}/#{fname}"].chomp
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
# # Print file diffs, for code review
|
312
|
-
def print_file_diffs(file_safety, repo, fname, user_name)
|
313
|
-
entry = file_safety[fname]
|
314
|
-
repolatest = entry['last_changed_rev']
|
315
|
-
safe_revision = entry['safe_revision']
|
316
|
-
|
317
|
-
if safe_revision.nil?
|
318
|
-
first_revision = set_safe_revision
|
319
|
-
print_repo_file_diffs(repolatest, repo, fname, user_name, first_revision)
|
320
|
-
else
|
321
|
-
|
322
|
-
rev = get_last_changed_revision(repo, fname)
|
323
|
-
if rev
|
324
|
-
mime = get_mime_type(repo, fname)
|
325
|
-
end
|
326
|
-
|
327
|
-
print_repo_file_diffs(repolatest, repo, fname, user_name, safe_revision) if repolatest != safe_revision
|
328
|
-
end
|
329
|
-
end
|
330
|
-
|
331
|
-
# Returns first commit for git and 0 for svn in order to be used to display
|
332
|
-
# new files. Called from print_file_diffs
|
333
|
-
def set_safe_revision
|
334
|
-
case repository_type
|
335
|
-
when 'git'
|
336
|
-
%x[git rev-list --max-parents=0 HEAD].chomp
|
337
|
-
when 'git-svn', 'svn'
|
338
|
-
0
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
def print_repo_file_diffs(repolatest, repo, fname, user_name, safe_revision)
|
343
|
-
require 'open3'
|
344
|
-
cmd = nil
|
345
|
-
case repository_type
|
346
|
-
when 'git'
|
347
|
-
cmd = ['git', '--no-pager', 'diff', '-b', "#{safe_revision}..#{repolatest}", fname]
|
348
|
-
when 'git-svn', 'svn'
|
349
|
-
cmd = ['svn', 'diff', '-r', "#{safe_revision.to_i}:#{repolatest.to_i}", '-x', '-b', "#{repo}/#{fname}"]
|
350
|
-
end
|
351
|
-
if cmd
|
352
|
-
puts(cmd.join(' '))
|
353
|
-
stdout_and_err_str, status = Open3.capture2e(*cmd)
|
354
|
-
puts(stdout_and_err_str)
|
355
|
-
else
|
356
|
-
puts 'Unknown repo'
|
357
|
-
end
|
358
|
-
|
359
|
-
puts %(To flag the changes to this file as safe, run:)
|
360
|
-
puts %( rake audit:safe release=#{repolatest} file=#{fname} reviewed_by=#{user_name} comments="")
|
361
|
-
puts
|
362
|
-
end
|
363
|
-
|
364
|
-
def release_valid?(release)
|
365
|
-
case repository_type
|
366
|
-
when 'svn', 'git-svn'
|
367
|
-
release =~ /\A[0-9][0-9]*\Z/
|
368
|
-
when 'git'
|
369
|
-
release =~ /\A[0-9a-f]{40}\Z/
|
370
|
-
else
|
371
|
-
false
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
def get_release
|
376
|
-
release = ENV['release']
|
377
|
-
release = nil if release == '0'
|
378
|
-
case repository_type
|
379
|
-
when 'svn', 'git-svn'
|
380
|
-
release.to_i
|
381
|
-
when 'git'
|
382
|
-
release
|
383
|
-
else
|
384
|
-
''
|
385
|
-
end
|
386
|
-
release
|
387
|
-
end
|
388
|
-
|
389
|
-
def clean_working_copy?
|
390
|
-
case repository_type
|
391
|
-
when 'svn'
|
392
|
-
system('svn status | grep -q [^AMCDG]')
|
393
|
-
when 'git', 'git-svn'
|
394
|
-
system('git diff --quiet HEAD')
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
|
-
namespace :audit do
|
399
|
-
desc "Audit safety of source code.
|
400
|
-
Usage: audit:code [max_print=n] [ignore_new=false|true] [show_diffs=false|true] [reviewed_by=usr]
|
401
|
-
|
402
|
-
File #{SAFETY_FILE} lists the safety and revision information
|
403
|
-
of the era source code. This task updates the list, and [TODO] warns about
|
404
|
-
files which have changed since they were last verified as safe."
|
405
|
-
task(:code) do
|
406
|
-
puts 'Usage: audit:code [max_print=n] [ignore_new=false|true] [show_diffs=false|true] [show_in_priority=false|true] [reviewed_by=usr]'
|
407
|
-
puts "This is a #{repository_type} repository"
|
408
|
-
|
409
|
-
ignore_new = (ENV['ignore_new'].to_s =~ /\Atrue\Z/i)
|
410
|
-
show_diffs = (ENV['show_diffs'].to_s =~ /\Atrue\Z/i)
|
411
|
-
show_in_priority = (ENV['show_in_priority'].to_s =~ /\Atrue\Z/i)
|
412
|
-
max_print = ENV['max_print'] =~ /\A-?[0-9][0-9]*\Z/ ? ENV['max_print'].to_i : 20
|
413
|
-
reviewer = ENV['reviewed_by']
|
414
|
-
|
415
|
-
all_safe = audit_code_safety(max_print, ignore_new, show_diffs, show_in_priority, reviewer)
|
416
|
-
|
417
|
-
unless show_diffs
|
418
|
-
puts 'To show file diffs, run: rake audit:code max_print=-1 show_diffs=true'
|
419
|
-
end
|
420
|
-
|
421
|
-
exit(1) unless all_safe
|
422
|
-
end
|
423
|
-
|
424
|
-
desc "Flag a source file as safe.
|
425
|
-
|
426
|
-
Usage:
|
427
|
-
Flag as safe: rake audit:safe release=revision reviewed_by=usr [comments=...] file=f
|
428
|
-
Needs review: rake audit:safe release=0 [comments=...] file=f"
|
429
|
-
task(:safe) do
|
430
|
-
release = get_release
|
431
|
-
|
432
|
-
required_fields = %w(release file)
|
433
|
-
required_fields << 'reviewed_by' if release # 'Needs review' doesn't need a reviewer
|
434
|
-
missing = required_fields.collect { |f| (f if ENV[f].to_s.empty? || (f == 'reviewed_by' && ENV[f] == 'usr')) }.compact # Avoid accidental missing username
|
435
|
-
unless missing.empty?
|
436
|
-
puts 'Usage: rake audit:safe release=revision reviewed_by=usr [comments=...] file=f'
|
437
|
-
puts 'or, to flag a file for review: rake audit:safe release=0 [comments=...] file=f'
|
438
|
-
abort("Error: Missing required argument(s): #{missing.join(', ')}")
|
439
|
-
end
|
440
|
-
|
441
|
-
unless release.nil? || release_valid?(release)
|
442
|
-
puts 'Usage: rake audit:safe release=revision reviewed_by=usr [comments=...] file=f'
|
443
|
-
puts 'or, to flag a file for review: rake audit:safe release=0 [comments=...] file=f'
|
444
|
-
abort("Error: Invalid release: #{ENV['release']}")
|
445
|
-
end
|
446
|
-
|
447
|
-
flag_file_as_safe(release, ENV['reviewed_by'], ENV['comments'], ENV['file'])
|
448
|
-
end
|
449
|
-
|
450
|
-
desc 'Wraps audit:code, and stops if any review is pending/stale.'
|
451
|
-
task(:ensure_safe) do
|
452
|
-
abort('You have local changes, cannot verify code safety!') unless clean_working_copy?
|
453
|
-
|
454
|
-
puts 'Checking code safety...'
|
455
|
-
|
456
|
-
begin
|
457
|
-
begin
|
458
|
-
$stdout = $stderr = StringIO.new
|
459
|
-
Rake::Task['audit:code'].invoke
|
460
|
-
ensure
|
461
|
-
$stdout, $stderr = STDOUT, STDERR
|
462
|
-
end
|
463
|
-
rescue SystemExit => ex
|
464
|
-
puts '=============================================================='
|
465
|
-
puts 'Code safety review of some files are not up-to-date; aborting!'
|
466
|
-
puts ' - to review the files in question, run: rake audit:code'
|
467
|
-
puts '=============================================================='
|
468
|
-
|
469
|
-
raise ex
|
470
|
-
end
|
471
|
-
end
|
472
|
-
end
|
473
|
-
|
474
|
-
# Prevent building of un-reviewed gems:
|
475
|
-
task build: :'audit:ensure_safe'
|