maximus 0.1.4 → 0.1.5
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/.travis.yml +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +23 -5
- data/lib/maximus/cli.rb +8 -4
- data/lib/maximus/config.rb +161 -149
- data/lib/maximus/config/maximus.yml +60 -2
- data/lib/maximus/config/wraith/nojs.js +3 -1
- data/lib/maximus/config/wraith/snap.js +3 -1
- data/lib/maximus/git_control.rb +224 -119
- data/lib/maximus/helper.rb +7 -25
- data/lib/maximus/lint.rb +64 -88
- data/lib/maximus/lints/brakeman.rb +2 -9
- data/lib/maximus/lints/jshint.rb +1 -5
- data/lib/maximus/lints/railsbp.rb +12 -21
- data/lib/maximus/lints/rubocop.rb +1 -5
- data/lib/maximus/lints/scsslint.rb +1 -4
- data/lib/maximus/reporter/git-lines.sh +8 -22
- data/lib/maximus/statistic.rb +2 -2
- data/lib/maximus/statistics/phantomas.rb +0 -1
- data/lib/maximus/statistics/stylestats.rb +91 -56
- data/lib/maximus/statistics/wraith.rb +79 -21
- data/lib/maximus/version.rb +1 -1
- data/maximus.gemspec +8 -8
- data/roadmap.md +4 -0
- data/spec/maximus/config_spec.rb +98 -5
- data/spec/maximus/git_control_spec.rb +172 -0
- data/spec/maximus/helper_spec.rb +97 -0
- data/spec/maximus/lint_spec.rb +51 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/rubocop.yml +1 -0
- metadata +40 -33
- data/lib/maximus/config/rubocop.yml +0 -1007
data/lib/maximus/helper.rb
CHANGED
@@ -11,7 +11,6 @@ module Maximus
|
|
11
11
|
module Helper
|
12
12
|
|
13
13
|
# See if project linted is a Rails app
|
14
|
-
#
|
15
14
|
# This will usually be stored as a class variable in the inherited class
|
16
15
|
# @return [Boolean]
|
17
16
|
def is_rails?
|
@@ -19,7 +18,6 @@ module Maximus
|
|
19
18
|
end
|
20
19
|
|
21
20
|
# Get root directory of file being called
|
22
|
-
#
|
23
21
|
# @return [String] absolute path to root directory
|
24
22
|
def root_dir
|
25
23
|
is_rails? ? Rails.root.to_s : Dir.pwd.to_s
|
@@ -31,13 +29,9 @@ module Maximus
|
|
31
29
|
# @param install_instructions [String] how to install the missing command
|
32
30
|
# @return [void] aborts the action if command not found
|
33
31
|
def node_module_exists(command, install_instructions = 'npm install -g')
|
34
|
-
cmd = `if hash #{command} 2>/dev/null; then
|
35
|
-
echo "true"
|
36
|
-
else
|
37
|
-
echo "false"
|
38
|
-
fi`
|
32
|
+
cmd = `if hash #{command} 2>/dev/null; then echo "true"; else echo "false"; fi`
|
39
33
|
if cmd.include? "false"
|
40
|
-
command_msg = "Missing command #{command}"
|
34
|
+
command_msg = "Missing command #{command}"
|
41
35
|
abort "#{command_msg}: Please run `#{install_instructions} #{command}` And try again\n"
|
42
36
|
exit 1
|
43
37
|
end
|
@@ -49,11 +43,10 @@ module Maximus
|
|
49
43
|
# @param file [String] filename with extension to search for
|
50
44
|
# @return [String] path to default config file or file in user's directory
|
51
45
|
def check_default_config_path(file)
|
52
|
-
File.exist?(file) ? file : File.join(File.dirname(__FILE__),
|
46
|
+
File.exist?(file) ? file : File.join(File.dirname(__FILE__), file)
|
53
47
|
end
|
54
48
|
|
55
49
|
# Grab the absolute path of the reporter file
|
56
|
-
#
|
57
50
|
# @param filename [String]
|
58
51
|
# @return [String] absolute path to the reporter file
|
59
52
|
def reporter_path(filename)
|
@@ -82,16 +75,14 @@ module Maximus
|
|
82
75
|
end
|
83
76
|
|
84
77
|
# Convert string to boolean
|
85
|
-
#
|
86
78
|
# @param str [String] the string to evaluate
|
87
79
|
# @return [Boolean] whether or not the string is true
|
88
|
-
def truthy(str)
|
80
|
+
def truthy?(str)
|
89
81
|
return true if str == true || str =~ (/^(true|t|yes|y|1)$/i)
|
90
82
|
return false if str == false || str.blank? || str =~ (/^(false|f|no|n|0)$/i)
|
91
83
|
end
|
92
84
|
|
93
85
|
# Edit and save a YAML file
|
94
|
-
#
|
95
86
|
# @param yaml_location [String] YAML absolute file path
|
96
87
|
# @return [void]
|
97
88
|
def edit_yaml(yaml_location, &block)
|
@@ -101,7 +92,6 @@ module Maximus
|
|
101
92
|
end
|
102
93
|
|
103
94
|
# Request user input
|
104
|
-
#
|
105
95
|
# @param args [Array<String>] prompts to request
|
106
96
|
# @return [String] user input to use elsewhere
|
107
97
|
def prompt(*args)
|
@@ -115,17 +105,10 @@ module Maximus
|
|
115
105
|
# @see Lint#relevant_lint
|
116
106
|
#
|
117
107
|
# @example typical output
|
118
|
-
# lines_added = {
|
108
|
+
# lines_added = {changes: ['0..10', '11..14']}
|
119
109
|
# lines_added_to_range(lines_added)
|
120
110
|
# # output
|
121
|
-
#
|
122
|
-
# 'filename': {
|
123
|
-
# {
|
124
|
-
# [0,1,2,3,4,5,6,7,8,9,10],
|
125
|
-
# [11,12,13,14]
|
126
|
-
# }
|
127
|
-
# }
|
128
|
-
# }
|
111
|
+
# [0,1,2,3,4,5,6,7,8,9,10, 11,12,13,14]
|
129
112
|
#
|
130
113
|
# @todo I'm sure there's a better way of doing this
|
131
114
|
# @todo figure out a better place to put this than in Helper
|
@@ -136,10 +119,9 @@ module Maximus
|
|
136
119
|
end
|
137
120
|
|
138
121
|
# Ensure path exists
|
139
|
-
#
|
140
122
|
# @param path [String, Array] path to files can be directory or glob
|
141
123
|
# @return [Boolean]
|
142
|
-
def path_exists(path = @path)
|
124
|
+
def path_exists?(path = @path)
|
143
125
|
path = path.split(' ') if path.is_a?(String) && path.include?(' ')
|
144
126
|
if path.is_a?(Array)
|
145
127
|
path.each do |p|
|
data/lib/maximus/lint.rb
CHANGED
@@ -20,7 +20,7 @@ module Maximus
|
|
20
20
|
# lint_data = JSON.parse(`some-command-line-linter`)
|
21
21
|
# @output[:files_inspected] ||= files_inspected(extension, delimiter, base_path_replacement)
|
22
22
|
# refine data_from_output
|
23
|
-
#
|
23
|
+
# end
|
24
24
|
#
|
25
25
|
# Inherits settings from {Config#initialize}
|
26
26
|
# @see Config#initialize
|
@@ -45,10 +45,10 @@ module Maximus
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# Convert raw data into warnings, errors, conventions or refactors. Use this wisely.
|
48
|
-
#
|
49
48
|
# @param data [Hash] unfiltered lint data
|
50
49
|
# @return [Hash] refined lint data and all the other bells and whistles
|
51
50
|
def refine(data)
|
51
|
+
|
52
52
|
# Prevent abortive empty JSON.parse error
|
53
53
|
data = '{}' if data.blank?
|
54
54
|
return puts "Error from #{@task}: #{data}" if data.is_a?(String) && data.include?('No such')
|
@@ -60,48 +60,19 @@ module Maximus
|
|
60
60
|
data = @output[:relevant_lints]
|
61
61
|
end
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
error_list.each do |message|
|
70
|
-
# so that :raw_data remains unaffected
|
71
|
-
message = message.clone
|
72
|
-
message.delete('length')
|
73
|
-
message['filename'] = filename
|
74
|
-
if message['severity'] == 'warning'
|
75
|
-
message.delete('severity')
|
76
|
-
lint_warnings << message
|
77
|
-
elsif message['severity'] == 'error'
|
78
|
-
message.delete('severity')
|
79
|
-
lint_errors << message
|
80
|
-
elsif message['severity'] == 'convention'
|
81
|
-
message.delete('severity')
|
82
|
-
lint_conventions << message
|
83
|
-
elsif message['severity'] == 'refactor'
|
84
|
-
message.delete('severity')
|
85
|
-
lint_refactors << message
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
@output[:lint_errors] = lint_errors
|
91
|
-
@output[:lint_warnings] = lint_warnings
|
92
|
-
@output[:lint_conventions] = lint_conventions
|
93
|
-
@output[:lint_refactors] = lint_refactors
|
94
|
-
lint_count = (lint_errors.length + lint_warnings.length + lint_conventions.length + lint_refactors.length)
|
63
|
+
evaluate_severities(data)
|
64
|
+
|
65
|
+
lint_count = (@output[:lint_errors].length + @output[:lint_warnings].length + @output[:lint_conventions].length + @output[:lint_refactors].length)
|
66
|
+
|
67
|
+
puts lint_summarize
|
68
|
+
|
95
69
|
if @config.is_dev?
|
96
70
|
puts lint_dev_format(data) unless data.blank?
|
97
|
-
puts lint_summarize
|
98
71
|
lint_ceiling lint_count
|
99
72
|
else
|
100
|
-
@config.log.info lint_summarize
|
101
73
|
# Because this should be returned in the format it was received
|
102
74
|
@output[:raw_data] = data.to_json
|
103
75
|
end
|
104
|
-
@config.destroy_temp(@task)
|
105
76
|
@output
|
106
77
|
end
|
107
78
|
|
@@ -109,7 +80,6 @@ module Maximus
|
|
109
80
|
protected
|
110
81
|
|
111
82
|
# List all files inspected
|
112
|
-
#
|
113
83
|
# @param ext [String] extension to search for
|
114
84
|
# @param delimiter [String] comma or space separated
|
115
85
|
# @param remove [String] remove from all file names
|
@@ -119,7 +89,6 @@ module Maximus
|
|
119
89
|
end
|
120
90
|
|
121
91
|
# Compare lint output with lines changed in commit
|
122
|
-
#
|
123
92
|
# @param lint [Hash] output lint data
|
124
93
|
# @param files [Hash<String: String>] filename: filepath
|
125
94
|
# @return [Array] lints that match the lines in commit
|
@@ -127,29 +96,25 @@ module Maximus
|
|
127
96
|
all_files = {}
|
128
97
|
files.each do |file|
|
129
98
|
|
130
|
-
# sometimes data will be blank but this is good - it means no errors raised in the lint
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
end
|
99
|
+
# sometimes data will be blank but this is good - it means no errors were raised in the lint
|
100
|
+
next if lint.blank?
|
101
|
+
lint_file = lint[file[:filename].to_s]
|
102
|
+
|
103
|
+
expanded = lines_added_to_range(file)
|
104
|
+
revert_name = file[:filename].gsub("#{@settings[:root_dir]}/", '')
|
105
|
+
unless lint_file.blank?
|
106
|
+
all_files[revert_name] = []
|
107
|
+
|
108
|
+
# @todo originally I tried .map and delete_if, but this works,
|
109
|
+
# and the other method didn't cover all bases.
|
110
|
+
# Gotta be a better way to write this though
|
111
|
+
lint_file.each do |l|
|
112
|
+
if expanded.include?(l['line'].to_i)
|
113
|
+
all_files[revert_name] << l
|
146
114
|
end
|
147
|
-
# If there's nothing there, then it definitely isn't a relevant lint
|
148
|
-
all_files.delete(revert_name) if all_files[revert_name].blank?
|
149
115
|
end
|
150
|
-
|
151
|
-
|
152
|
-
# @example all_files[file[:filename].to_s.gsub("#{@settings[:root_dir]}/", '')] = []
|
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?
|
153
118
|
end
|
154
119
|
end
|
155
120
|
@output[:files_linted] = all_files.keys
|
@@ -157,7 +122,6 @@ module Maximus
|
|
157
122
|
end
|
158
123
|
|
159
124
|
# Look for a config defined from Config#initialize
|
160
|
-
#
|
161
125
|
# @since 0.1.2
|
162
126
|
# @param search_for [String]
|
163
127
|
# @return [String, Boolean] path to temp file
|
@@ -166,58 +130,72 @@ module Maximus
|
|
166
130
|
@settings[search_for.to_sym].blank? ? false : @settings[search_for.to_sym]
|
167
131
|
end
|
168
132
|
|
133
|
+
# Add severities to @output
|
134
|
+
# @since 0.1.5
|
135
|
+
# @param data [Hash]
|
136
|
+
def evaluate_severities(data)
|
137
|
+
@output[:lint_warnings] = []
|
138
|
+
@output[:lint_errors] = []
|
139
|
+
@output[:lint_conventions] = []
|
140
|
+
@output[:lint_refactors] = []
|
141
|
+
return if data.blank?
|
142
|
+
data.each do |filename, error_list|
|
143
|
+
error_list.each do |message|
|
144
|
+
# so that :raw_data remains unaffected
|
145
|
+
message = message.clone
|
146
|
+
message.delete('length')
|
147
|
+
message['filename'] = filename.nil? ? '' : filename.gsub("#{@settings[:root_dir]}/", '')
|
148
|
+
severity = message['severity']
|
149
|
+
message.delete('severity')
|
150
|
+
@output["lint_#{severity}s".to_sym] << message
|
151
|
+
end
|
152
|
+
end
|
153
|
+
return @output
|
154
|
+
end
|
155
|
+
|
169
156
|
|
170
157
|
private
|
171
158
|
|
172
159
|
# Send abbreviated results to console or to the log
|
173
|
-
#
|
174
160
|
# @return [String] console message to display
|
175
161
|
def lint_summarize
|
176
|
-
puts "
|
177
|
-
|
178
|
-
puts "#{'Warning'.color(:red)}: #{@output[:lint_errors].length} errors found in #{@task.to_s}" if @output[:lint_errors].length > 0
|
162
|
+
puts "#{'Warning'.color(:red)}: #{@output[:lint_errors].length} errors found in #{@task}" unless @output[:lint_errors].length
|
179
163
|
|
180
164
|
success = @task.to_s.color(:green)
|
181
165
|
success += ": "
|
182
166
|
success += "[#{@output[:lint_warnings].length}]".color(:yellow)
|
183
|
-
success += "
|
167
|
+
success += " [#{@output[:lint_errors].length}]".color(:red)
|
184
168
|
if @task == 'rubocop'
|
185
|
-
success += "
|
186
|
-
success += "
|
169
|
+
success += " [#{@output[:lint_conventions].length}]".color(:cyan)
|
170
|
+
success += " [#{@output[:lint_refactors].length}]".color(:white)
|
187
171
|
end
|
188
172
|
|
189
173
|
success
|
190
174
|
end
|
191
175
|
|
192
176
|
# If there's just too much to handle, through a warning.
|
193
|
-
#
|
194
177
|
# @param lint_length [Integer] count of how many lints
|
195
178
|
# @return [String] console message to display
|
196
179
|
def lint_ceiling(lint_length)
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
abort errormsg unless truthy(go_on)
|
206
|
-
end
|
180
|
+
return unless lint_length > 100
|
181
|
+
failed_task = @task.color(:green)
|
182
|
+
errors = "#{lint_length} failures.".color(:red)
|
183
|
+
errormsg = ["You wouldn't stand a chance in Rome.\nResolve thy errors and train with #{failed_task} again.", "The gods frown upon you, mortal.\n#{failed_task}. Again.", "Do not embarrass the city. Fight another day. Use #{failed_task}.", "You are without honor. Replenish it with another #{failed_task}.", "You will never claim the throne with a performance like that.", "Pompeii has been lost.", "A wise choice. Do not be discouraged from another #{failed_task}."].sample
|
184
|
+
errormsg += "\n\n"
|
185
|
+
|
186
|
+
go_on = prompt "\n#{errors} Continue? (y/n) "
|
187
|
+
abort errormsg unless truthy?(go_on)
|
207
188
|
end
|
208
189
|
|
209
190
|
# Dev display, executed only when called from command line
|
210
|
-
#
|
211
191
|
# @param errors [Hash] data from lint
|
212
192
|
# @return [String] console message to display
|
213
193
|
def lint_dev_format(errors = @output[:raw_data])
|
214
194
|
return if errors.blank?
|
215
195
|
pretty_output = ''
|
216
196
|
errors.each do |filename, error_list|
|
217
|
-
pretty_output += "\n"
|
218
197
|
filename = filename.gsub("#{@settings[:root_dir]}/", '')
|
219
|
-
pretty_output += filename.color(:cyan).underline
|
220
|
-
pretty_output += "\n"
|
198
|
+
pretty_output += "\n#{filename.color(:cyan).underline} \n"
|
221
199
|
error_list.each do |message|
|
222
200
|
pretty_output += case message['severity']
|
223
201
|
when 'warning' then 'W'.color(:yellow)
|
@@ -226,15 +204,13 @@ module Maximus
|
|
226
204
|
when 'refactor' then 'R'.color(:white)
|
227
205
|
else '?'.color(:blue)
|
228
206
|
end
|
229
|
-
pretty_output += ' '
|
230
|
-
pretty_output += message['line'].to_s.color(:blue)
|
231
|
-
pretty_output += " #{message['linter'].color(:green)}: "
|
232
|
-
pretty_output += message['reason']
|
233
|
-
pretty_output += "\n"
|
207
|
+
pretty_output += " #{message['line'].to_s.color(:blue)} #{message['linter'].color(:green)}: #{message['reason']} \n"
|
234
208
|
end
|
235
209
|
end
|
236
210
|
pretty_output
|
237
211
|
end
|
238
212
|
|
213
|
+
|
214
|
+
|
239
215
|
end
|
240
216
|
end
|
@@ -3,19 +3,13 @@ module Maximus
|
|
3
3
|
class Brakeman < Maximus::Lint
|
4
4
|
|
5
5
|
# Brakeman (requires Rails)
|
6
|
-
#
|
7
6
|
# @see Lint#initialize
|
8
7
|
def result
|
9
8
|
|
10
|
-
return unless is_rails?
|
11
|
-
|
12
9
|
@task = 'brakeman'
|
13
|
-
|
14
|
-
return unless temp_config(@task)
|
15
|
-
|
16
10
|
@path = @settings[:root_dir] if @path.blank?
|
17
11
|
|
18
|
-
return unless path_exists(@path)
|
12
|
+
return unless is_rails? && temp_config(@task) && path_exists?(@path)
|
19
13
|
|
20
14
|
tmp = Tempfile.new('brakeman')
|
21
15
|
quietly { `brakeman #{@path} -f json -o #{tmp.path} -q` }
|
@@ -55,12 +49,11 @@ module Maximus
|
|
55
49
|
private
|
56
50
|
|
57
51
|
# Convert to {file:README.md Maximus format}
|
58
|
-
#
|
59
52
|
# @param error [Hash] lint error
|
60
53
|
# @return [Hash]
|
61
54
|
def hash_for_brakeman(error, type)
|
62
55
|
{
|
63
|
-
linter: error['warning_type'],
|
56
|
+
linter: error['warning_type'].delete(' '),
|
64
57
|
severity: type.chomp('s'),
|
65
58
|
reason: error['message'],
|
66
59
|
column: 0,
|
data/lib/maximus/lints/jshint.rb
CHANGED
@@ -3,16 +3,12 @@ module Maximus
|
|
3
3
|
class Jshint < Maximus::Lint
|
4
4
|
|
5
5
|
# JSHint (requires node module)
|
6
|
-
#
|
7
6
|
# @see Lint#initialize
|
8
7
|
def result
|
9
8
|
@task = 'jshint'
|
10
|
-
|
11
|
-
return unless temp_config(@task)
|
12
|
-
|
13
9
|
@path = is_rails? ? "#{@settings[:root_dir]}/app/assets" : "#{@settings[:root_dir]}source/assets" if @path.blank?
|
14
10
|
|
15
|
-
return unless path_exists(@path)
|
11
|
+
return unless temp_config(@task) && path_exists?(@path)
|
16
12
|
|
17
13
|
node_module_exists(@task)
|
18
14
|
|
@@ -3,19 +3,13 @@ module Maximus
|
|
3
3
|
class Railsbp < Maximus::Lint
|
4
4
|
|
5
5
|
# rails_best_practice (requires Rails)
|
6
|
-
#
|
7
6
|
# @see Lint#initialize
|
8
7
|
def result
|
9
8
|
|
10
|
-
return unless is_rails?
|
11
|
-
|
12
9
|
@task = 'railsbp'
|
13
|
-
|
14
|
-
return unless temp_config(@task)
|
15
|
-
|
16
10
|
@path = @settings[:root_dir] if @path.blank?
|
17
11
|
|
18
|
-
return unless path_exists(@path)
|
12
|
+
return unless is_rails? && temp_config(@task) && path_exists?(@path)
|
19
13
|
|
20
14
|
tmp = Tempfile.new('railsbp')
|
21
15
|
`rails_best_practices #{@path} -f json --output-file #{tmp.path}`
|
@@ -27,15 +21,13 @@ module Maximus
|
|
27
21
|
rbj = JSON.parse(railsbp).group_by { |s| s['filename'] }
|
28
22
|
railsbp = {}
|
29
23
|
rbj.each do |file, errors|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
24
|
+
next unless file
|
25
|
+
|
26
|
+
# This crazy gsub scrubs the absolute path from the filename
|
27
|
+
filename = file.gsub(Rails.root.to_s, '')[1..-1]
|
28
|
+
railsbp[filename] = errors.map { |o| hash_for_railsbp(o) }
|
29
|
+
|
34
30
|
end
|
35
|
-
# The output of railsbp is a mix of strings and symbols
|
36
|
-
# but resetting the JSON like this standardizes everything.
|
37
|
-
# @todo Better way to get around this?
|
38
|
-
railsbp = JSON.parse(railsbp.to_json)
|
39
31
|
end
|
40
32
|
|
41
33
|
@output[:files_inspected] ||= files_inspected('rb', ' ')
|
@@ -46,16 +38,15 @@ module Maximus
|
|
46
38
|
private
|
47
39
|
|
48
40
|
# Convert to {file:README.md Maximus format}
|
49
|
-
#
|
50
41
|
# @param error [Hash] lint error
|
51
42
|
# @return [Hash]
|
52
43
|
def hash_for_railsbp(error)
|
53
44
|
{
|
54
|
-
linter
|
55
|
-
severity
|
56
|
-
reason
|
57
|
-
column
|
58
|
-
line
|
45
|
+
'linter' => error['message'].gsub(/\((.*)\)/, '').strip.parameterize('_').camelize,
|
46
|
+
'severity' => 'warning',
|
47
|
+
'reason' => error['message'],
|
48
|
+
'column' => 0,
|
49
|
+
'line' => error['line_number'].to_i
|
59
50
|
}
|
60
51
|
end
|
61
52
|
|