rubosquad 0.4.1 → 0.5.0
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/lib/rubosquad.rb +117 -16
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bdc45fe50d0678e9dd9f45bc7967aeea4ed6b3ee830593875d9ad8911f277592
|
|
4
|
+
data.tar.gz: 784949c827c7d611bf125f791cf3d5913f93a9b508efc1dcb0814c3d3350a607
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 929f4fea15d4ab2e7396f4a1b7ba159734bad3ba7d9d889f72de7c668bf5b47981028fa24971b263afe5b2655a1a74d4c651fae5d4250cb7c0a06a02eedbe031
|
|
7
|
+
data.tar.gz: c62933394e08b6520233aec0180384ec8367ac9de7c84f42e7c195a05ca888e91fc23881ef75ac4b2f0997a311657e47a4e75aae52ee35a398f6aa0d2892e6e8
|
data/lib/rubosquad.rb
CHANGED
|
@@ -6,6 +6,13 @@ require 'optparse'
|
|
|
6
6
|
|
|
7
7
|
module Rubosquad
|
|
8
8
|
class CLI
|
|
9
|
+
OFFENSE_COLORS = {
|
|
10
|
+
'E' => "\e[31m",
|
|
11
|
+
'F' => "\e[31m",
|
|
12
|
+
'W' => "\e[33m"
|
|
13
|
+
}.freeze
|
|
14
|
+
RESET = "\e[0m"
|
|
15
|
+
|
|
9
16
|
class << self
|
|
10
17
|
def run
|
|
11
18
|
options = parse_options
|
|
@@ -24,6 +31,8 @@ module Rubosquad
|
|
|
24
31
|
exit 0
|
|
25
32
|
end
|
|
26
33
|
|
|
34
|
+
options[:base_branch] = base_branch
|
|
35
|
+
options[:current_branch] = current_branch
|
|
27
36
|
print_file_list(files, current_branch, base_branch)
|
|
28
37
|
run_rubocop(options, files)
|
|
29
38
|
end
|
|
@@ -48,6 +57,12 @@ module Rubosquad
|
|
|
48
57
|
opts.on('-A', '--unsafe-auto-correct', 'Run with rubocop -A (unsafe auto-corrections) instead of -a') do
|
|
49
58
|
options[:unsafe_auto_correct] = true
|
|
50
59
|
end
|
|
60
|
+
opts.on('-v', '--verbose', 'Show full stderr including new-cops notices and extra detail') do
|
|
61
|
+
options[:verbose] = true
|
|
62
|
+
end
|
|
63
|
+
opts.on('--all', 'Report all offenses, including pre-existing ones not in this branch') do
|
|
64
|
+
options[:all] = true
|
|
65
|
+
end
|
|
51
66
|
end.parse!
|
|
52
67
|
options
|
|
53
68
|
end
|
|
@@ -112,20 +127,69 @@ module Rubosquad
|
|
|
112
127
|
cmd = build_rubocop_cmd(options, extensions, files)
|
|
113
128
|
stdout, stderr, status = Open3.capture3(*cmd)
|
|
114
129
|
|
|
130
|
+
changed_lines = options[:all] ? nil : parse_changed_lines(options, files)
|
|
131
|
+
|
|
115
132
|
puts "\nRubocop Output:"
|
|
116
133
|
puts '==Error type: Legend: C = Convention, W = Warning, E = Error, F = Fatal=='
|
|
117
|
-
|
|
134
|
+
formatted = format_output(stdout, changed_lines: changed_lines)
|
|
135
|
+
puts formatted
|
|
136
|
+
|
|
137
|
+
print_rubocop_result(stdout, stderr, status, extensions, options)
|
|
138
|
+
|
|
139
|
+
exit 0 if !status.success? && changed_lines && count_new_offenses(formatted).zero?
|
|
118
140
|
|
|
119
|
-
print_rubocop_result(stdout, stderr, status, extensions)
|
|
120
141
|
exit status.exitstatus unless status.success?
|
|
121
142
|
end
|
|
122
143
|
|
|
144
|
+
def parse_changed_lines(options, files)
|
|
145
|
+
base_branch = options[:base_branch]
|
|
146
|
+
current_branch = options[:current_branch]
|
|
147
|
+
|
|
148
|
+
files.each_with_object({}) do |file, changed|
|
|
149
|
+
diff_text = if current_branch == base_branch
|
|
150
|
+
fetch_base_branch_diff(file)
|
|
151
|
+
else
|
|
152
|
+
fetch_feature_branch_diff(base_branch, file)
|
|
153
|
+
end
|
|
154
|
+
changed[file] = parse_hunk_lines(diff_text)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def fetch_feature_branch_diff(base_branch, file)
|
|
159
|
+
stdout, = Open3.capture3('git', 'diff', '-U0', "#{base_branch}...HEAD", '--', file)
|
|
160
|
+
stdout
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def fetch_base_branch_diff(file)
|
|
164
|
+
unstaged, = Open3.capture3('git', 'diff', '-U0', 'HEAD', '--', file)
|
|
165
|
+
staged, = Open3.capture3('git', 'diff', '-U0', '--cached', '--', file)
|
|
166
|
+
"#{unstaged}#{staged}"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def parse_hunk_lines(diff_text)
|
|
170
|
+
lines = Set.new
|
|
171
|
+
diff_text.scan(/@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/) do |start, count|
|
|
172
|
+
start = start.to_i
|
|
173
|
+
count = count.nil? ? 1 : count.to_i
|
|
174
|
+
count.times { |i| lines << (start + i) } if count.positive?
|
|
175
|
+
end
|
|
176
|
+
lines
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def count_new_offenses(formatted_output)
|
|
180
|
+
formatted_output.lines.count { |l| l.match?(/^\[.+:\d+:\d+\] -/) }
|
|
181
|
+
end
|
|
182
|
+
|
|
123
183
|
def detect_extensions
|
|
124
184
|
%w[rubocop-factory_bot rubocop-rails rubocop-rspec rubocop-rspec_rails].select do |ext|
|
|
125
185
|
Gem::Specification.find_by_name(ext)
|
|
186
|
+
require ext
|
|
126
187
|
true
|
|
127
188
|
rescue Gem::LoadError
|
|
128
189
|
false
|
|
190
|
+
rescue LoadError => e
|
|
191
|
+
warn "Warning: #{ext} is installed but could not be loaded (#{e.message}). Skipping."
|
|
192
|
+
false
|
|
129
193
|
end
|
|
130
194
|
end
|
|
131
195
|
|
|
@@ -144,7 +208,7 @@ module Rubosquad
|
|
|
144
208
|
false
|
|
145
209
|
end
|
|
146
210
|
|
|
147
|
-
def format_output(stdout)
|
|
211
|
+
def format_output(stdout, changed_lines: nil)
|
|
148
212
|
lines = []
|
|
149
213
|
seen_files = Set.new
|
|
150
214
|
current_file = nil
|
|
@@ -153,13 +217,10 @@ module Rubosquad
|
|
|
153
217
|
if line.start_with?('==')
|
|
154
218
|
file_path = line.strip.gsub(/^==\s*|\s*==$/, '')
|
|
155
219
|
current_file = file_path
|
|
156
|
-
lines << (seen_files.add?(file_path) ? "\n\e[36m#{line}\e[0m" : "\n#{line}")
|
|
220
|
+
lines << (seen_files.add?(file_path) ? "\n\e[36m#{line.chomp}\e[0m\n" : "\n#{line}")
|
|
157
221
|
elsif line.match?(/^[A-Z]:/)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
line_col = parts.shift(2).map(&:strip).join(':')
|
|
161
|
-
message = parts.join(':').strip
|
|
162
|
-
lines << "[#{current_file || 'unknown'}:#{line_col}] - #{offense_type}: #{message}\n"
|
|
222
|
+
offense = parse_offense_line(line, current_file, changed_lines)
|
|
223
|
+
lines << offense if offense
|
|
163
224
|
else
|
|
164
225
|
lines << line
|
|
165
226
|
end
|
|
@@ -168,21 +229,61 @@ module Rubosquad
|
|
|
168
229
|
lines.join
|
|
169
230
|
end
|
|
170
231
|
|
|
171
|
-
def
|
|
232
|
+
def parse_offense_line(line, current_file, changed_lines)
|
|
233
|
+
parts = line.split(':')
|
|
234
|
+
offense_type = parts.shift
|
|
235
|
+
line_num = parts.first.strip.to_i
|
|
236
|
+
line_col = parts.shift(2).map(&:strip).join(':')
|
|
237
|
+
message = parts.join(':').strip
|
|
238
|
+
|
|
239
|
+
return nil if changed_lines && !changed_lines[current_file]&.include?(line_num)
|
|
240
|
+
|
|
241
|
+
type_str = if (color = OFFENSE_COLORS[offense_type])
|
|
242
|
+
"#{color}#{offense_type}#{RESET}"
|
|
243
|
+
else
|
|
244
|
+
offense_type
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
"[#{current_file || 'unknown'}:#{line_col}] - #{type_str}: #{message}\n"
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def print_rubocop_result(stdout, stderr, status, extensions, options = {})
|
|
251
|
+
total = stdout.scan(/(\d+) offenses? detected/).flatten.map(&:to_i).sum
|
|
252
|
+
corrected = stdout.scan(/(\d+) offenses? corrected/).flatten.map(&:to_i).sum
|
|
253
|
+
remaining = total - corrected
|
|
254
|
+
|
|
172
255
|
if status.success?
|
|
173
256
|
puts "\nRubocop auto-corrections complete."
|
|
174
257
|
else
|
|
175
258
|
puts "\nRubocop completed with offenses:"
|
|
176
|
-
total = stdout.scan(/(\d+) offense.* detected/).flatten.map(&:to_i).sum
|
|
177
259
|
puts "Files inspected: #{stdout[/(\d+) files? inspected/, 1] || 'Unknown'}"
|
|
178
|
-
puts "Total offenses detected: #{total}"
|
|
179
|
-
unless stderr.strip.empty?
|
|
180
|
-
puts "\nStandard error:"
|
|
181
|
-
puts stderr
|
|
182
|
-
end
|
|
183
260
|
end
|
|
261
|
+
|
|
262
|
+
puts "#{corrected} auto-corrected | #{remaining} remaining" if total.positive?
|
|
263
|
+
display_stderr(stderr, options[:verbose])
|
|
184
264
|
puts "\nRuboCop extensions used: #{extensions.join(', ')}" unless extensions.empty?
|
|
185
265
|
end
|
|
266
|
+
|
|
267
|
+
def display_stderr(stderr, verbose)
|
|
268
|
+
filtered = verbose ? stderr : filter_new_cops_notice(stderr)
|
|
269
|
+
return if filtered.strip.empty?
|
|
270
|
+
|
|
271
|
+
puts "\nStandard error:"
|
|
272
|
+
puts filtered
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def filter_new_cops_notice(stderr)
|
|
276
|
+
return stderr unless stderr.include?('opt-in to new cops')
|
|
277
|
+
|
|
278
|
+
lines = stderr.lines
|
|
279
|
+
start_idx = lines.index { |l| l.include?('opt-in to new cops') }
|
|
280
|
+
return stderr unless start_idx
|
|
281
|
+
|
|
282
|
+
end_idx = lines.index { |l| l.include?('docs.rubocop.org/rubocop/versioning') }
|
|
283
|
+
end_idx ||= lines.length - 1
|
|
284
|
+
|
|
285
|
+
(lines[0...start_idx] + lines[(end_idx + 1)..]).join
|
|
286
|
+
end
|
|
186
287
|
end
|
|
187
288
|
end
|
|
188
289
|
end
|