Skip to content

Commit 6a1b348

Browse files
refactor: shifted logic to a runner class
1 parent 942d39f commit 6a1b348

File tree

4 files changed

+226
-212
lines changed

4 files changed

+226
-212
lines changed

exe/codeclimate_diff

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
$LOAD_PATH.unshift("#{__dir__}/../lib")
55

66
require "optparse"
7-
require "codeclimate_diff"
7+
require "codeclimate_diff/runner"
88

99
options = {}
1010
OptionParser.new do |opts|
@@ -20,9 +20,9 @@ OptionParser.new do |opts|
2020
end.parse!(into: options)
2121

2222
if options[:baseline]
23-
CodeclimateDiff.generate_baseline
23+
CodeclimateDiff::Runner.generate_baseline
2424
elsif options[:"new-only"]
25-
CodeclimateDiff.run_diff_on_branch(options[:pattern], show_preexisting: false)
25+
CodeclimateDiff::Runner.run_diff_on_branch(options[:pattern], show_preexisting: false)
2626
else
27-
CodeclimateDiff.run_diff_on_branch(options[:pattern], show_preexisting: true)
27+
CodeclimateDiff::Runner.run_diff_on_branch(options[:pattern], show_preexisting: true)
2828
end

lib/codeclimate_diff.rb

Lines changed: 1 addition & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -2,213 +2,6 @@
22

33
require_relative "codeclimate_diff/version"
44

5-
require "json"
6-
require "colorize"
7-
85
module CodeclimateDiff
9-
def self.generate_baseline
10-
puts "Generating the baseline. Should take about 5 minutes..."
11-
`codeclimate analyze -f json > codeclimate_diff_baseline.json`
12-
puts "Done!"
13-
end
14-
15-
def self.calculate_changed_filenames(pattern)
16-
extra_grep_filter = pattern ? " | grep '#{pattern}'" : ""
17-
files_changed = `git diff --name-only main | grep --invert-match spec/ | grep --extended-regexp '.js$|.rb$'#{extra_grep_filter}`
18-
files_changed.split("\n")
19-
end
20-
21-
def self.calculate_issues_in_changed_files(changed_filenames)
22-
changed_file_issues = []
23-
24-
changed_filenames.each do |filename|
25-
next if filename == "codeclimate_diff.rb" # TODO: fix this file's code quality issues when we make a Gem!
26-
27-
puts "Analysing '#{filename}'..."
28-
result = `codeclimate analyze -f json #{filename}`
29-
JSON.parse(result).each do |issue|
30-
next if issue["type"] != "issue"
31-
32-
changed_file_issues.append(issue)
33-
end
34-
end
35-
36-
changed_file_issues
37-
end
38-
39-
def self.calculate_preexisting_issues_in_changed_files(changed_filenames)
40-
puts "Extracting relevant preexisting issues..."
41-
all_issues = JSON.parse(File.read("./codeclimate_diff_baseline.json"))
42-
43-
all_issues.filter { |issue| issue.key?("location") && changed_filenames.include?(issue["location"]["path"]) }
44-
end
45-
46-
def self.remove_closest_match_from_list(issue_to_match, list)
47-
# check for exact match first
48-
index = list.index do |issue|
49-
issue["fingerprint"] == issue_to_match["fingerprint"] &&
50-
issue["location"]["lines"]["begin"] == issue_to_match["location"]["lines"]["begin"] &&
51-
issue["description"] == issue_to_match["description"]
52-
end
53-
54-
if index
55-
list.delete_at(index)
56-
return
57-
end
58-
59-
# check for same method name (description often has method name or variable name in it)
60-
index = list.index do |issue|
61-
issue["fingerprint"] == issue_to_match["fingerprint"] &&
62-
issue["description"] == issue_to_match["description"]
63-
end
64-
65-
if index
66-
list.delete_at(index)
67-
return
68-
end
69-
70-
# otherwise just remove the first one
71-
list.pop
72-
end
73-
74-
def self.sort_issues(preexisting_issues, changed_file_issues)
75-
puts "Sorting into :preexisting, :new and :fixed lists..."
76-
77-
result = {}
78-
result[:preexisting] = []
79-
result[:new] = []
80-
result[:fixed] = []
81-
82-
# fingerprints are unique per issue type and file
83-
# so there could be multiple if the same issue shows up multiple times
84-
# plus line numbers and method names could have changed
85-
unique_fingerprints = (preexisting_issues + changed_file_issues).map { |issue| issue["fingerprint"] }.uniq
86-
87-
unique_fingerprints.each do |fingerprint|
88-
baseline_issues = preexisting_issues.filter { |issue| issue["fingerprint"] == fingerprint }
89-
current_issues = changed_file_issues.filter { |issue| issue["fingerprint"] == fingerprint }
90-
91-
if baseline_issues.count == current_issues.count
92-
# current issues are most up to date (line numbers could have changed etc.)
93-
result[:preexisting] += current_issues
94-
elsif current_issues.count < baseline_issues.count
95-
# less issues than there were before
96-
current_issues.each do |issue_to_match|
97-
CodeclimateDiff.remove_closest_match_from_list(issue_to_match, baseline_issues)
98-
end
99-
result[:fixed] += baseline_issues
100-
else
101-
# more issues than there were before
102-
baseline_issues.each do |issue_to_match|
103-
CodeclimateDiff.remove_closest_match_from_list(issue_to_match, current_issues)
104-
end
105-
result[:new] += current_issues
106-
end
107-
end
108-
109-
# do a check to make sure the maths works out
110-
puts "#{preexisting_issues.count} issues in matching files in baseline"
111-
puts "#{changed_file_issues.count} current issues in matching files"
112-
113-
result
114-
end
115-
116-
def self.print_issues_in_category(issues_list)
117-
issues_list.each do |issue|
118-
filename = issue["location"]["path"]
119-
line_number = issue["location"]["lines"]["begin"]
120-
description = issue["description"]
121-
122-
print "\u2022 #{filename}:#{line_number}".encode("utf-8").bold
123-
puts " #{description}"
124-
end
125-
puts "\n"
126-
end
127-
128-
def self.print_category(bullet_emoji, severity, engine_name, check_name, color)
129-
message = "#{bullet_emoji} [#{severity}] #{engine_name} #{check_name}:".encode("utf-8")
130-
131-
case color
132-
when "red"
133-
puts message.red
134-
when "yellow"
135-
puts message.yellow
136-
when "green"
137-
puts message.green
138-
else
139-
puts message
140-
end
141-
end
142-
143-
def self.print_issues(issues_list, color, bullet_emoji)
144-
issue_categories = issues_list.map { |issue| [issue["engine_name"], issue["check_name"], issue["severity"]] }.uniq
145-
issue_categories.each do |issue_category|
146-
engine_name = issue_category[0]
147-
check_name = issue_category[1]
148-
severity = issue_category[2]
149-
issues = issues_list.filter do |issue|
150-
issue["engine_name"] == engine_name &&
151-
issue["check_name"] == check_name &&
152-
issue["severity"] == severity
153-
end
154-
print_category(bullet_emoji, severity, engine_name, check_name, color)
155-
print_issues_in_category(issues)
156-
end
157-
end
158-
159-
def self.print_result(sorted_issues, show_preexisting)
160-
if show_preexisting
161-
preexisting_issues = sorted_issues[:preexisting]
162-
if preexisting_issues.count.positive?
163-
puts "\n#{preexisting_issues.count} preexisting issues in changed files:\n".bold.yellow
164-
print_issues(preexisting_issues, "yellow", "\u2718")
165-
else
166-
puts "\n0 issues in changed files!".encode("utf-8").bold.green
167-
end
168-
end
169-
170-
new_issues = sorted_issues[:new]
171-
if new_issues.count.positive?
172-
puts "\n#{new_issues.count} new issues:\n".bold.red
173-
print_issues(new_issues, "red", "\u2718")
174-
else
175-
puts "\n0 new issues :)\n".encode("utf-8").bold
176-
end
177-
178-
fixed_issues = sorted_issues[:fixed]
179-
if fixed_issues.count.positive?
180-
puts "\n#{fixed_issues.count} fixed issues: \n".encode("utf-8").bold.green
181-
print_issues(fixed_issues, "green", "\u2714")
182-
else
183-
puts "\n0 fixed issues\n".bold
184-
end
185-
end
186-
187-
def self.print_call_to_action(sorted_issues)
188-
fixed_count = sorted_issues[:fixed].count
189-
new_count = sorted_issues[:new].count
190-
outstanding_count = sorted_issues[:preexisting].count + new_count
191-
if fixed_count > new_count
192-
puts "\n\u{1F389}\u{1F389} Well done! You made the code even better!! \u{1F389}\u{1F389} \n".bold.green.encode("utf-8")
193-
elsif new_count > fixed_count
194-
puts "\n\ Uh oh, you've introduced more issues than you've fixed. Better fix that! \n".bold.red.encode("utf-8")
195-
elsif outstanding_count.positive?
196-
puts "\n\ Why don't you see if you can fix some of those outstanding issues while you're here? \n".bold.encode("utf-8")
197-
else
198-
puts "\n\u{1F389}\u{1F389} Nothing to do here, the code is immaculate!! \u{1F389}\u{1F389} \n".bold.green.encode("utf-8")
199-
end
200-
end
201-
202-
def self.run_diff_on_branch(pattern, show_preexisting: true)
203-
changed_filenames = calculate_changed_filenames(pattern)
204-
205-
changed_file_issues = calculate_issues_in_changed_files(changed_filenames)
206-
207-
preexisting_issues = calculate_preexisting_issues_in_changed_files(changed_filenames)
208-
209-
sorted_issues = sort_issues(preexisting_issues, changed_file_issues)
210-
211-
print_result(sorted_issues, show_preexisting)
212-
print_call_to_action(sorted_issues)
213-
end
6+
2147
end

lib/codeclimate_diff/configuration.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# frozen_string_literal: true
2+
3+
module CodeclimateDiff
4+
class Configuration
5+
attr_accessor :main_branch_name
6+
end
7+
end

0 commit comments

Comments
 (0)