maximus 0.1.5.1 → 0.1.6.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +72 -0
- data/LICENSE.txt +1 -1
- data/README.md +1 -61
- data/lib/maximus/cli.rb +23 -8
- data/lib/maximus/config.rb +37 -35
- data/lib/maximus/config/maximus.yml +11 -1
- data/lib/maximus/git_control.rb +90 -56
- data/lib/maximus/helper.rb +26 -29
- data/lib/maximus/lint.rb +96 -47
- data/lib/maximus/lints/brakeman.rb +25 -22
- data/lib/maximus/lints/jshint.rb +3 -2
- data/lib/maximus/lints/railsbp.rb +2 -2
- data/lib/maximus/lints/rubocop.rb +2 -1
- data/lib/maximus/lints/scsslint.rb +2 -2
- data/lib/maximus/reporter/git-lines.sh +0 -1
- data/lib/maximus/statistic.rb +0 -2
- data/lib/maximus/statistics/phantomas.rb +3 -3
- data/lib/maximus/statistics/stylestats.rb +37 -33
- data/lib/maximus/statistics/wraith.rb +25 -16
- data/lib/maximus/version.rb +1 -1
- data/maximus.gemspec +4 -3
- data/roadmap.md +3 -8
- data/spec/maximus/config_spec.rb +95 -16
- data/spec/maximus/git_control_spec.rb +5 -5
- data/spec/maximus/helper_spec.rb +39 -5
- data/spec/maximus/lint_spec.rb +18 -3
- data/spec/spec_helper.rb +3 -0
- data/spec/support/config_helper.rb +14 -0
- metadata +55 -38
data/lib/maximus/helper.rb
CHANGED
@@ -17,6 +17,13 @@ module Maximus
|
|
17
17
|
defined?(Rails)
|
18
18
|
end
|
19
19
|
|
20
|
+
# See if project is a Middleman app
|
21
|
+
# @since 0.1.7
|
22
|
+
# @return [Boolean]
|
23
|
+
def is_middleman?
|
24
|
+
Gem::Specification::find_all_by_name('middleman').any?
|
25
|
+
end
|
26
|
+
|
20
27
|
# Get root directory of file being called
|
21
28
|
# @return [String] absolute path to root directory
|
22
29
|
def root_dir
|
@@ -37,20 +44,11 @@ module Maximus
|
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
40
|
-
# Look for a file in the config directory
|
41
|
-
#
|
42
|
-
# @since 0.1.0
|
43
|
-
# @param file [String] filename with extension to search for
|
44
|
-
# @return [String] path to default config file or file in user's directory
|
45
|
-
def check_default_config_path(file)
|
46
|
-
File.exist?(file) ? file : File.join(File.dirname(__FILE__), file)
|
47
|
-
end
|
48
|
-
|
49
47
|
# Grab the absolute path of the reporter file
|
50
48
|
# @param filename [String]
|
51
49
|
# @return [String] absolute path to the reporter file
|
52
50
|
def reporter_path(filename)
|
53
|
-
File.join(File.dirname(__FILE__),
|
51
|
+
File.join(File.dirname(__FILE__), 'reporter', filename)
|
54
52
|
end
|
55
53
|
|
56
54
|
# Find all files that were linted by extension
|
@@ -99,25 +97,6 @@ module Maximus
|
|
99
97
|
STDIN.gets
|
100
98
|
end
|
101
99
|
|
102
|
-
# Convert the array from lines_added into spelled-out ranges
|
103
|
-
# This is a GitControl helper but it's used in Lint
|
104
|
-
# @see GitControl#lines_added
|
105
|
-
# @see Lint#relevant_lint
|
106
|
-
#
|
107
|
-
# @example typical output
|
108
|
-
# lines_added = {changes: ['0..10', '11..14']}
|
109
|
-
# lines_added_to_range(lines_added)
|
110
|
-
# # output
|
111
|
-
# [0,1,2,3,4,5,6,7,8,9,10, 11,12,13,14]
|
112
|
-
#
|
113
|
-
# @todo I'm sure there's a better way of doing this
|
114
|
-
# @todo figure out a better place to put this than in Helper
|
115
|
-
# @return [Hash] changes_array of spelled-out arrays of integers
|
116
|
-
def lines_added_to_range(file)
|
117
|
-
changes_array = file[:changes].map { |ch| ch.split("..").map(&:to_i) }
|
118
|
-
changes_array.map { |e| (e[0]..e[1]).to_a }.flatten!
|
119
|
-
end
|
120
|
-
|
121
100
|
# Ensure path exists
|
122
101
|
# @param path [String, Array] path to files can be directory or glob
|
123
102
|
# @return [Boolean]
|
@@ -131,6 +110,7 @@ module Maximus
|
|
131
110
|
end
|
132
111
|
end
|
133
112
|
else
|
113
|
+
path = path.gsub('/**', '').gsub('/*', '').split('.')[0..1].first if path.include?('*')
|
134
114
|
if File.exist?(path)
|
135
115
|
return true
|
136
116
|
else
|
@@ -140,5 +120,22 @@ module Maximus
|
|
140
120
|
end
|
141
121
|
end
|
142
122
|
|
123
|
+
# Default paths to check for lints and some stats
|
124
|
+
# @since 0.1.7
|
125
|
+
# @param root [String] base directory
|
126
|
+
# @param folder [String] nested folder to search for for Rails or Middleman
|
127
|
+
# @param extension [String] file glob type to search for if neither
|
128
|
+
# @return [String] path to desired files
|
129
|
+
def discover_path(root = @config.working_dir, folder = '', extension = '')
|
130
|
+
return @path unless @path.blank?
|
131
|
+
if is_rails?
|
132
|
+
File.join(root, 'app', 'assets', folder)
|
133
|
+
elsif is_middleman?
|
134
|
+
File.join(root, 'source', folder)
|
135
|
+
else
|
136
|
+
extension.blank? ? File.join(root) : File.join(root, '/**', "/*.#{extension}")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
143
140
|
end
|
144
141
|
end
|
data/lib/maximus/lint.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'rainbow'
|
2
3
|
|
3
4
|
module Maximus
|
4
5
|
|
@@ -48,27 +49,18 @@ module Maximus
|
|
48
49
|
# @param data [Hash] unfiltered lint data
|
49
50
|
# @return [Hash] refined lint data and all the other bells and whistles
|
50
51
|
def refine(data)
|
52
|
+
@task ||= ''
|
51
53
|
|
52
|
-
|
53
|
-
|
54
|
-
return puts "Error from #{@task}: #{data}" if data.is_a?(String) && data.include?('No such')
|
55
|
-
|
56
|
-
data = data.is_a?(String) ? JSON.parse(data) : data
|
57
|
-
|
58
|
-
@output[:relevant_lints] = relevant_lints( data, @git_files ) unless @git_files.blank?
|
59
|
-
unless @settings[:commit].blank?
|
60
|
-
data = @output[:relevant_lints]
|
61
|
-
end
|
54
|
+
data = parse_data(data)
|
55
|
+
return puts data if data.is_a?(String)
|
62
56
|
|
63
57
|
evaluate_severities(data)
|
64
58
|
|
65
|
-
lint_count = (@output[:lint_errors].length + @output[:lint_warnings].length + @output[:lint_conventions].length + @output[:lint_refactors].length)
|
66
|
-
|
67
59
|
puts lint_summarize
|
68
60
|
|
69
61
|
if @config.is_dev?
|
70
|
-
puts lint_dev_format(data)
|
71
|
-
lint_ceiling
|
62
|
+
puts lint_dev_format(data)
|
63
|
+
lint_ceiling
|
72
64
|
else
|
73
65
|
# Because this should be returned in the format it was received
|
74
66
|
@output[:raw_data] = data.to_json
|
@@ -84,7 +76,7 @@ module Maximus
|
|
84
76
|
# @param delimiter [String] comma or space separated
|
85
77
|
# @param remove [String] remove from all file names
|
86
78
|
# @return all_files [Array<string>] list of file names
|
87
|
-
def files_inspected(ext, delimiter = ',', remove = @
|
79
|
+
def files_inspected(ext, delimiter = ',', remove = @config.working_dir)
|
88
80
|
@path.is_a?(Array) ? @path.split(delimiter) : file_list(@path, ext, remove)
|
89
81
|
end
|
90
82
|
|
@@ -98,24 +90,26 @@ module Maximus
|
|
98
90
|
|
99
91
|
# sometimes data will be blank but this is good - it means no errors were raised in the lint
|
100
92
|
next if lint.blank?
|
101
|
-
lint_file = lint[file[:filename]
|
93
|
+
lint_file = lint[file[:filename]]
|
94
|
+
|
95
|
+
next if lint_file.blank?
|
102
96
|
|
103
97
|
expanded = lines_added_to_range(file)
|
104
|
-
revert_name = file[:filename]
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
end
|
98
|
+
revert_name = strip_working_dir(file[:filename])
|
99
|
+
|
100
|
+
all_files[revert_name] = []
|
101
|
+
|
102
|
+
# @todo originally I tried .map and delete_if, but this works,
|
103
|
+
# and the other method didn't cover all bases.
|
104
|
+
# Gotta be a better way to write this though
|
105
|
+
lint_file.each do |l|
|
106
|
+
if expanded.include?(l['line'].to_i)
|
107
|
+
all_files[revert_name] << l
|
115
108
|
end
|
116
|
-
# If there's nothing there, then it definitely isn't a relevant lint
|
117
|
-
all_files.delete(revert_name) if all_files[revert_name].blank?
|
118
109
|
end
|
110
|
+
|
111
|
+
# If there's nothing there, then it definitely isn't a relevant lint
|
112
|
+
all_files.delete(revert_name) if all_files[revert_name].blank?
|
119
113
|
end
|
120
114
|
@output[:files_linted] = all_files.keys
|
121
115
|
all_files
|
@@ -138,36 +132,56 @@ module Maximus
|
|
138
132
|
@output[:lint_errors] = []
|
139
133
|
@output[:lint_conventions] = []
|
140
134
|
@output[:lint_refactors] = []
|
135
|
+
@output[:lint_fatals] = []
|
136
|
+
|
141
137
|
return if data.blank?
|
138
|
+
|
142
139
|
data.each do |filename, error_list|
|
143
140
|
error_list.each do |message|
|
144
141
|
# so that :raw_data remains unaffected
|
145
142
|
message = message.clone
|
146
143
|
message.delete('length')
|
147
|
-
message['filename'] = filename.nil? ? '' : filename
|
148
|
-
severity = message['severity']
|
144
|
+
message['filename'] = filename.nil? ? '' : strip_working_dir(filename)
|
145
|
+
severity = "lint_#{message['severity']}s".to_sym
|
149
146
|
message.delete('severity')
|
150
|
-
@output[
|
147
|
+
@output[severity] << message if @output.key?(severity)
|
151
148
|
end
|
152
149
|
end
|
153
|
-
|
150
|
+
@output
|
154
151
|
end
|
155
152
|
|
153
|
+
# Convert the array from lines_added into spelled-out ranges
|
154
|
+
# This is a GitControl helper but it's used in Lint
|
155
|
+
# @see GitControl#lines_added
|
156
|
+
# @see Lint#relevant_lint
|
157
|
+
#
|
158
|
+
# @example typical output
|
159
|
+
# lines_added = {changes: ['0..10', '11..14']}
|
160
|
+
# lines_added_to_range(lines_added)
|
161
|
+
# # output
|
162
|
+
# [0,1,2,3,4,5,6,7,8,9,10, 11,12,13,14]
|
163
|
+
#
|
164
|
+
# @return [Hash] changes_array of spelled-out arrays of integers
|
165
|
+
def lines_added_to_range(file)
|
166
|
+
changes_array = file[:changes].map { |ch| ch.split("..").map(&:to_i) }
|
167
|
+
changes_array.map { |e| (e[0]..e[1]).to_a }.flatten!
|
168
|
+
end
|
156
169
|
|
157
170
|
private
|
158
171
|
|
159
172
|
# Send abbreviated results to console or to the log
|
160
173
|
# @return [String] console message to display
|
161
174
|
def lint_summarize
|
162
|
-
puts "#{'Warning'.color(:red)}: #{@output[:lint_errors].length} errors found in #{@task}"
|
175
|
+
puts "#{'Warning'.color(:red)}: #{@output[:lint_errors].length} errors found in #{@task}" if @output[:lint_errors].length > 0
|
163
176
|
|
164
|
-
success = @task.
|
165
|
-
success
|
166
|
-
success
|
167
|
-
success
|
177
|
+
success = @task.color(:green)
|
178
|
+
success << ": "
|
179
|
+
success << "[#{@output[:lint_warnings].length}]".color(:yellow)
|
180
|
+
success << " [#{@output[:lint_errors].length}]".color(:red)
|
168
181
|
if @task == 'rubocop'
|
169
|
-
success
|
170
|
-
success
|
182
|
+
success << " [#{@output[:lint_conventions].length}]".color(:cyan)
|
183
|
+
success << " [#{@output[:lint_refactors].length}]".color(:white)
|
184
|
+
success << " [#{@output[:lint_fatals].length}]".color(:magenta)
|
171
185
|
end
|
172
186
|
|
173
187
|
success
|
@@ -176,12 +190,22 @@ module Maximus
|
|
176
190
|
# If there's just too much to handle, through a warning.
|
177
191
|
# @param lint_length [Integer] count of how many lints
|
178
192
|
# @return [String] console message to display
|
179
|
-
def lint_ceiling
|
193
|
+
def lint_ceiling
|
194
|
+
lint_length = (@output[:lint_errors].length + @output[:lint_warnings].length + @output[:lint_conventions].length + @output[:lint_refactors].length + @output[:lint_fatals].length)
|
195
|
+
|
180
196
|
return unless lint_length > 100
|
181
197
|
failed_task = @task.color(:green)
|
182
198
|
errors = "#{lint_length} failures.".color(:red)
|
183
|
-
errormsg = [
|
184
|
-
|
199
|
+
errormsg = [
|
200
|
+
"You wouldn't stand a chance in Rome.\nResolve thy errors and train with #{failed_task} again.",
|
201
|
+
"The gods frown upon you, mortal.\n#{failed_task}. Again.",
|
202
|
+
"Do not embarrass the city. Fight another day. Use #{failed_task}.",
|
203
|
+
"You are without honor. Replenish it with another #{failed_task}.",
|
204
|
+
"You will never claim the throne with a performance like that.",
|
205
|
+
"Pompeii has been lost.",
|
206
|
+
"A wise choice. Do not be discouraged from another #{failed_task}."
|
207
|
+
].sample
|
208
|
+
errormsg << "\n\n"
|
185
209
|
|
186
210
|
go_on = prompt "\n#{errors} Continue? (y/n) "
|
187
211
|
abort errormsg unless truthy?(go_on)
|
@@ -194,23 +218,48 @@ module Maximus
|
|
194
218
|
return if errors.blank?
|
195
219
|
pretty_output = ''
|
196
220
|
errors.each do |filename, error_list|
|
197
|
-
filename = filename
|
198
|
-
pretty_output
|
221
|
+
filename = strip_working_dir(filename)
|
222
|
+
pretty_output << "\n#{filename.color(:cyan).underline} \n"
|
199
223
|
error_list.each do |message|
|
200
|
-
pretty_output
|
224
|
+
pretty_output << case message['severity']
|
201
225
|
when 'warning' then 'W'.color(:yellow)
|
202
226
|
when 'error' then 'E'.color(:red)
|
203
227
|
when 'convention' then 'C'.color(:cyan)
|
204
228
|
when 'refactor' then 'R'.color(:white)
|
229
|
+
when 'fatal' then 'F'.color(:magenta)
|
205
230
|
else '?'.color(:blue)
|
206
231
|
end
|
207
|
-
pretty_output
|
232
|
+
pretty_output << " #{message['line'].to_s.color(:blue)} #{message['linter'].color(:green)}: #{message['reason']} \n"
|
208
233
|
end
|
209
234
|
end
|
210
235
|
pretty_output
|
211
236
|
end
|
212
237
|
|
238
|
+
# String working directory
|
239
|
+
# @since 0.1.6
|
240
|
+
# @param path [String]
|
241
|
+
# @return [String]
|
242
|
+
def strip_working_dir(path)
|
243
|
+
path.gsub("#{@config.working_dir}/", '')
|
244
|
+
end
|
245
|
+
|
246
|
+
# Handle data and generate relevant_lints if appropriate
|
247
|
+
# @since 0.1.6
|
248
|
+
# @see #refine
|
249
|
+
# @param data [String, Hash]
|
250
|
+
# @return [String, Hash] String if error, Hash if success
|
251
|
+
def parse_data(data)
|
252
|
+
# Prevent abortive empty JSON.parse error
|
253
|
+
data = '{}' if data.blank?
|
254
|
+
|
255
|
+
return "Error from #{@task}: #{data}" if data.is_a?(String) && data.include?('No such')
|
213
256
|
|
257
|
+
data = JSON.parse(data) if data.is_a?(String)
|
258
|
+
|
259
|
+
@output[:relevant_lints] = relevant_lints( data, @git_files ) unless @git_files.blank?
|
260
|
+
data = @output[:relevant_lints] unless @settings[:commit].blank?
|
261
|
+
data
|
262
|
+
end
|
214
263
|
|
215
264
|
end
|
216
265
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module Maximus
|
2
|
+
# Evaluates quality of security on a Rails site
|
2
3
|
# @since 0.1.0
|
3
4
|
class Brakeman < Maximus::Lint
|
4
5
|
|
5
6
|
# Brakeman (requires Rails)
|
6
7
|
# @see Lint#initialize
|
7
8
|
def result
|
8
|
-
|
9
9
|
@task = 'brakeman'
|
10
|
-
@path =
|
10
|
+
@path = discover_path
|
11
11
|
|
12
12
|
return unless is_rails? && temp_config(@task) && path_exists?(@path)
|
13
13
|
|
@@ -19,26 +19,15 @@ module Maximus
|
|
19
19
|
|
20
20
|
unless brakeman.blank?
|
21
21
|
bjson = JSON.parse(brakeman)
|
22
|
-
|
23
|
-
@output[:checks_performed] = bjson['scan_info']['checks_performed']
|
24
|
-
@output[:number_of_controllers] = bjson['scan_info']['number_of_controllers']
|
25
|
-
@output[:number_of_models] = bjson['scan_info']['number_of_models']
|
26
|
-
@output[:number_of_templates] = bjson['scan_info']['number_of_templates']
|
27
|
-
@output[:ruby_version] = bjson['scan_info']['ruby_version']
|
28
|
-
@output[:rails_version] = bjson['scan_info']['rails_version']
|
22
|
+
basics(bjson)
|
29
23
|
brakeman = {}
|
30
24
|
['warnings', 'errors'].each do |type|
|
31
25
|
new_brakeman = bjson[type].group_by { |s| s['file'] }
|
32
26
|
new_brakeman.each do |file, errors|
|
33
|
-
|
34
|
-
|
35
|
-
end
|
27
|
+
next unless file
|
28
|
+
brakeman[file] = errors.map { |e| hash_for_brakeman(e, type) }
|
36
29
|
end
|
37
30
|
end
|
38
|
-
# The output of brakeman is a mix of strings and symbols
|
39
|
-
# but resetting the JSON like this standardizes everything.
|
40
|
-
# @todo Better way to get around this?
|
41
|
-
brakeman = JSON.parse(brakeman.to_json)
|
42
31
|
end
|
43
32
|
|
44
33
|
@output[:files_inspected] ||= files_inspected('rb', ' ')
|
@@ -53,14 +42,28 @@ module Maximus
|
|
53
42
|
# @return [Hash]
|
54
43
|
def hash_for_brakeman(error, type)
|
55
44
|
{
|
56
|
-
linter
|
57
|
-
severity
|
58
|
-
reason
|
59
|
-
column
|
60
|
-
line
|
61
|
-
confidence
|
45
|
+
'linter' => error['warning_type'].delete(' '),
|
46
|
+
'severity' => type.chomp('s'),
|
47
|
+
'reason' => error['message'],
|
48
|
+
'column' => 0,
|
49
|
+
'line' => error['line'].to_i,
|
50
|
+
'confidence' => error['confidence']
|
62
51
|
}
|
63
52
|
end
|
64
53
|
|
54
|
+
# Pull out the general data brakeman provides
|
55
|
+
# @since 0.1.6
|
56
|
+
# @see #result
|
57
|
+
# @param brakeman_data [Hash]
|
58
|
+
def basics(brakeman_data)
|
59
|
+
@output[:ignored_warnings] = brakeman_data['scan_info']['ignored_warnings']
|
60
|
+
@output[:checks_performed] = brakeman_data['scan_info']['checks_performed']
|
61
|
+
@output[:number_of_controllers] = brakeman_data['scan_info']['number_of_controllers']
|
62
|
+
@output[:number_of_models] = brakeman_data['scan_info']['number_of_models']
|
63
|
+
@output[:number_of_templates] = brakeman_data['scan_info']['number_of_templates']
|
64
|
+
@output[:ruby_version] = brakeman_data['scan_info']['ruby_version']
|
65
|
+
@output[:rails_version] = brakeman_data['scan_info']['rails_version']
|
66
|
+
end
|
67
|
+
|
65
68
|
end
|
66
69
|
end
|
data/lib/maximus/lints/jshint.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Maximus
|
2
|
+
# Evaluates quality of JavaScript
|
2
3
|
# @since 0.1.0
|
3
4
|
class Jshint < Maximus::Lint
|
4
5
|
|
@@ -6,14 +7,14 @@ module Maximus
|
|
6
7
|
# @see Lint#initialize
|
7
8
|
def result
|
8
9
|
@task = 'jshint'
|
9
|
-
@path =
|
10
|
+
@path = discover_path(@config.working_dir, 'javascripts', 'js')
|
10
11
|
|
11
12
|
return unless temp_config(@task) && path_exists?(@path)
|
12
13
|
|
13
14
|
node_module_exists(@task)
|
14
15
|
|
15
16
|
jshint_cli = "jshint #{@path} --config=#{temp_config(@task)} --reporter=#{reporter_path('jshint.js')}"
|
16
|
-
jshint_cli
|
17
|
+
jshint_cli << " --exclude-path=#{temp_config(@settings[:jshintignore])}" if @settings.key?(:jshintignore)
|
17
18
|
jshint = `#{jshint_cli}`
|
18
19
|
|
19
20
|
@output[:files_inspected] ||= files_inspected('js')
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module Maximus
|
2
|
+
# Evaluates quality of Rails methods
|
2
3
|
# @since 0.1.0
|
3
4
|
class Railsbp < Maximus::Lint
|
4
5
|
|
5
6
|
# rails_best_practice (requires Rails)
|
6
7
|
# @see Lint#initialize
|
7
8
|
def result
|
8
|
-
|
9
9
|
@task = 'railsbp'
|
10
|
-
@path =
|
10
|
+
@path = discover_path
|
11
11
|
|
12
12
|
return unless is_rails? && temp_config(@task) && path_exists?(@path)
|
13
13
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module Maximus
|
2
|
+
# Evaluates quality of ruby
|
2
3
|
# @since 0.1.0
|
3
4
|
class Rubocop < Maximus::Lint
|
4
5
|
|
@@ -6,7 +7,7 @@ module Maximus
|
|
6
7
|
# @see Lint#initialize
|
7
8
|
def result
|
8
9
|
@task = 'rubocop'
|
9
|
-
@path =
|
10
|
+
@path = discover_path
|
10
11
|
|
11
12
|
return unless temp_config(@task) && path_exists?(@path)
|
12
13
|
|