rubycritic-simplecov 4.1.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 +7 -0
- data/CHANGELOG.md +290 -0
- data/CONTRIBUTING.md +93 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +259 -0
- data/ROADMAP.md +56 -0
- data/Rakefile +28 -0
- data/bin/rubycritic +10 -0
- data/lib/rubycritic/analysers/attributes.rb +29 -0
- data/lib/rubycritic/analysers/churn.rb +30 -0
- data/lib/rubycritic/analysers/complexity.rb +30 -0
- data/lib/rubycritic/analysers/coverage.rb +118 -0
- data/lib/rubycritic/analysers/helpers/ast_node.rb +76 -0
- data/lib/rubycritic/analysers/helpers/flay.rb +13 -0
- data/lib/rubycritic/analysers/helpers/flog.rb +17 -0
- data/lib/rubycritic/analysers/helpers/methods_counter.rb +25 -0
- data/lib/rubycritic/analysers/helpers/modules_locator.rb +42 -0
- data/lib/rubycritic/analysers/helpers/parser.rb +14 -0
- data/lib/rubycritic/analysers/helpers/reek.rb +11 -0
- data/lib/rubycritic/analysers/smells/flay.rb +79 -0
- data/lib/rubycritic/analysers/smells/flog.rb +69 -0
- data/lib/rubycritic/analysers/smells/reek.rb +53 -0
- data/lib/rubycritic/analysers_runner.rb +41 -0
- data/lib/rubycritic/analysis_summary.rb +40 -0
- data/lib/rubycritic/browser.rb +19 -0
- data/lib/rubycritic/cli/application.rb +34 -0
- data/lib/rubycritic/cli/options/argv.rb +137 -0
- data/lib/rubycritic/cli/options/file.rb +100 -0
- data/lib/rubycritic/cli/options.rb +33 -0
- data/lib/rubycritic/colorize.rb +17 -0
- data/lib/rubycritic/command_factory.rb +25 -0
- data/lib/rubycritic/commands/base.rb +18 -0
- data/lib/rubycritic/commands/ci.rb +14 -0
- data/lib/rubycritic/commands/compare.rb +106 -0
- data/lib/rubycritic/commands/default.rb +38 -0
- data/lib/rubycritic/commands/help.rb +18 -0
- data/lib/rubycritic/commands/status_reporter.rb +45 -0
- data/lib/rubycritic/commands/utils/build_number_file.rb +37 -0
- data/lib/rubycritic/commands/version.rb +16 -0
- data/lib/rubycritic/configuration.rb +69 -0
- data/lib/rubycritic/core/analysed_module.rb +93 -0
- data/lib/rubycritic/core/analysed_modules_collection.rb +95 -0
- data/lib/rubycritic/core/location.rb +47 -0
- data/lib/rubycritic/core/rating.rb +30 -0
- data/lib/rubycritic/core/smell.rb +84 -0
- data/lib/rubycritic/generators/console_report.rb +20 -0
- data/lib/rubycritic/generators/html/assets/fonts/FontAwesome.otf +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/Roboto-Medium.ttf +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/Roboto-Regular.ttf +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.eot +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.svg +2671 -0
- data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.ttf +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.woff +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.woff2 +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.eot +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.svg +288 -0
- data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.woff +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/lib/rubycritic/generators/html/assets/images/logo.png +0 -0
- data/lib/rubycritic/generators/html/assets/javascripts/application.js +281 -0
- data/lib/rubycritic/generators/html/assets/javascripts/bootstrap.min.js +7 -0
- data/lib/rubycritic/generators/html/assets/javascripts/highcharts.src-4.0.1.js +17672 -0
- data/lib/rubycritic/generators/html/assets/javascripts/jquery.filtertable.min.js +13 -0
- data/lib/rubycritic/generators/html/assets/javascripts/jquery.min.js +4 -0
- data/lib/rubycritic/generators/html/assets/javascripts/jquery.scrollTo.min.js +7 -0
- data/lib/rubycritic/generators/html/assets/javascripts/jquery.tablesorter.js +1031 -0
- data/lib/rubycritic/generators/html/assets/javascripts/jquery.tablesorter.min.js +4 -0
- data/lib/rubycritic/generators/html/assets/javascripts/jquery.timeago.js +231 -0
- data/lib/rubycritic/generators/html/assets/javascripts/prettify.js +46 -0
- data/lib/rubycritic/generators/html/assets/stylesheets/application.css +570 -0
- data/lib/rubycritic/generators/html/assets/stylesheets/bootstrap.min.css +6 -0
- data/lib/rubycritic/generators/html/assets/stylesheets/font-awesome.min.css +4 -0
- data/lib/rubycritic/generators/html/assets/stylesheets/prettify.css +1 -0
- data/lib/rubycritic/generators/html/assets/stylesheets/prettify.custom_theme.css +69 -0
- data/lib/rubycritic/generators/html/base.rb +54 -0
- data/lib/rubycritic/generators/html/code_file.rb +51 -0
- data/lib/rubycritic/generators/html/code_index.rb +33 -0
- data/lib/rubycritic/generators/html/line.rb +37 -0
- data/lib/rubycritic/generators/html/overview.rb +38 -0
- data/lib/rubycritic/generators/html/simple_cov_index.rb +44 -0
- data/lib/rubycritic/generators/html/smells_index.rb +45 -0
- data/lib/rubycritic/generators/html/templates/code_file.html.erb +61 -0
- data/lib/rubycritic/generators/html/templates/code_index.html.erb +55 -0
- data/lib/rubycritic/generators/html/templates/layouts/application.html.erb +66 -0
- data/lib/rubycritic/generators/html/templates/line.html.erb +1 -0
- data/lib/rubycritic/generators/html/templates/overview.html.erb +68 -0
- data/lib/rubycritic/generators/html/templates/simple_cov_index.html.erb +44 -0
- data/lib/rubycritic/generators/html/templates/smells_index.html.erb +47 -0
- data/lib/rubycritic/generators/html/templates/smelly_line.html.erb +23 -0
- data/lib/rubycritic/generators/html/turbulence.rb +17 -0
- data/lib/rubycritic/generators/html/view_helpers.rb +58 -0
- data/lib/rubycritic/generators/html_report.rb +77 -0
- data/lib/rubycritic/generators/json/simple.rb +43 -0
- data/lib/rubycritic/generators/json_report.rb +26 -0
- data/lib/rubycritic/generators/lint_report.rb +30 -0
- data/lib/rubycritic/generators/text/lint.rb +41 -0
- data/lib/rubycritic/generators/text/list.rb +45 -0
- data/lib/rubycritic/generators/text/templates/lint.erb +3 -0
- data/lib/rubycritic/generators/text/templates/list.erb +12 -0
- data/lib/rubycritic/rake_task.rb +71 -0
- data/lib/rubycritic/reporter.rb +39 -0
- data/lib/rubycritic/revision_comparator.rb +45 -0
- data/lib/rubycritic/serializer.rb +32 -0
- data/lib/rubycritic/smells_status_setter.rb +18 -0
- data/lib/rubycritic/source_control_systems/base.rb +41 -0
- data/lib/rubycritic/source_control_systems/double.rb +19 -0
- data/lib/rubycritic/source_control_systems/git.rb +95 -0
- data/lib/rubycritic/source_control_systems/mercurial.rb +29 -0
- data/lib/rubycritic/source_control_systems/perforce.rb +116 -0
- data/lib/rubycritic/source_locator.rb +52 -0
- data/lib/rubycritic/version.rb +5 -0
- data/lib/rubycritic.rb +6 -0
- metadata +528 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/analysers/helpers/reek'
|
|
4
|
+
require 'rubycritic/core/smell'
|
|
5
|
+
require 'rubycritic/colorize'
|
|
6
|
+
|
|
7
|
+
module RubyCritic
|
|
8
|
+
module Analyser
|
|
9
|
+
class ReekSmells
|
|
10
|
+
include Colorize
|
|
11
|
+
def initialize(analysed_modules)
|
|
12
|
+
@analysed_modules = analysed_modules
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def run
|
|
16
|
+
@analysed_modules.each do |analysed_module|
|
|
17
|
+
add_smells_to(analysed_module)
|
|
18
|
+
print green '.'
|
|
19
|
+
end
|
|
20
|
+
puts ''
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_s
|
|
24
|
+
'reek smells'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def add_smells_to(analysed_module)
|
|
30
|
+
Reek.new(analysed_module.pathname).smells.each do |smell|
|
|
31
|
+
analysed_module.smells << create_smell(smell)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def create_smell(smell)
|
|
36
|
+
Smell.new(
|
|
37
|
+
locations: smell_locations(smell.source, smell.lines),
|
|
38
|
+
context: smell.context,
|
|
39
|
+
message: smell.message,
|
|
40
|
+
type: smell.smell_type,
|
|
41
|
+
analyser: 'reek',
|
|
42
|
+
cost: 0
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def smell_locations(file_path, file_lines)
|
|
47
|
+
file_lines.uniq.map do |file_line|
|
|
48
|
+
Location.new(file_path, file_line)
|
|
49
|
+
end.sort
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/core/analysed_modules_collection'
|
|
4
|
+
require 'rubycritic/analysers/smells/flay'
|
|
5
|
+
require 'rubycritic/analysers/smells/flog'
|
|
6
|
+
require 'rubycritic/analysers/smells/reek'
|
|
7
|
+
require 'rubycritic/analysers/complexity'
|
|
8
|
+
require 'rubycritic/analysers/churn'
|
|
9
|
+
require 'rubycritic/analysers/attributes'
|
|
10
|
+
require 'rubycritic/analysers/coverage'
|
|
11
|
+
|
|
12
|
+
module RubyCritic
|
|
13
|
+
class AnalysersRunner
|
|
14
|
+
ANALYSERS = [
|
|
15
|
+
Analyser::FlaySmells,
|
|
16
|
+
Analyser::FlogSmells,
|
|
17
|
+
Analyser::ReekSmells,
|
|
18
|
+
Analyser::Complexity,
|
|
19
|
+
Analyser::Attributes,
|
|
20
|
+
Analyser::Churn,
|
|
21
|
+
Analyser::Coverage
|
|
22
|
+
].freeze
|
|
23
|
+
|
|
24
|
+
def initialize(paths)
|
|
25
|
+
@paths = paths
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def run
|
|
29
|
+
ANALYSERS.each do |analyser_class|
|
|
30
|
+
analyser_instance = analyser_class.new(analysed_modules)
|
|
31
|
+
puts "running #{analyser_instance}"
|
|
32
|
+
analyser_instance.run
|
|
33
|
+
end
|
|
34
|
+
analysed_modules
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def analysed_modules
|
|
38
|
+
@analysed_modules ||= AnalysedModulesCollection.new(@paths)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyCritic
|
|
4
|
+
class AnalysisSummary
|
|
5
|
+
def self.generate(analysed_modules)
|
|
6
|
+
new(analysed_modules).generate
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def initialize(analysed_modules)
|
|
10
|
+
@analysed_modules = analysed_modules
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def generate
|
|
14
|
+
%w[A B C D F].each_with_object({}) do |rating, summary|
|
|
15
|
+
summary[rating] = generate_for(rating)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
attr_reader :analysed_modules, :modules
|
|
22
|
+
|
|
23
|
+
def generate_for(rating)
|
|
24
|
+
@modules = analysed_modules.for_rating(rating)
|
|
25
|
+
{
|
|
26
|
+
files: modules.count,
|
|
27
|
+
churns: churns,
|
|
28
|
+
smells: smells
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def churns
|
|
33
|
+
modules.inject(0) { |acc, elem| acc + elem.churn }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def smells
|
|
37
|
+
modules.inject(0) { |acc, elem| acc + elem.smells.count }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'launchy'
|
|
4
|
+
|
|
5
|
+
module RubyCritic
|
|
6
|
+
class Browser
|
|
7
|
+
attr_reader :report_path
|
|
8
|
+
|
|
9
|
+
def initialize(report_path)
|
|
10
|
+
@report_path = report_path
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def open
|
|
14
|
+
Launchy.open(report_path) do |exception|
|
|
15
|
+
puts "Attempted to open #{report_path} and failed because #{exception}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic'
|
|
4
|
+
require 'rubycritic/browser'
|
|
5
|
+
require 'rubycritic/cli/options'
|
|
6
|
+
require 'rubycritic/command_factory'
|
|
7
|
+
|
|
8
|
+
module RubyCritic
|
|
9
|
+
module Cli
|
|
10
|
+
class Application
|
|
11
|
+
STATUS_SUCCESS = 0
|
|
12
|
+
STATUS_ERROR = 1
|
|
13
|
+
|
|
14
|
+
def initialize(argv)
|
|
15
|
+
@options = Options.new(argv)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def execute
|
|
19
|
+
parsed_options = @options.parse.to_h
|
|
20
|
+
|
|
21
|
+
reporter = RubyCritic::CommandFactory.create(parsed_options).execute
|
|
22
|
+
print(reporter.status_message)
|
|
23
|
+
reporter.status
|
|
24
|
+
rescue OptionParser::InvalidOption => error
|
|
25
|
+
warn "Error: #{error}"
|
|
26
|
+
STATUS_ERROR
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def print(message)
|
|
30
|
+
$stdout.puts message
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'optparse'
|
|
4
|
+
|
|
5
|
+
module RubyCritic
|
|
6
|
+
module Cli
|
|
7
|
+
class Options
|
|
8
|
+
# rubocop:disable Metrics/ClassLength
|
|
9
|
+
class Argv
|
|
10
|
+
def initialize(argv)
|
|
11
|
+
@argv = argv
|
|
12
|
+
self.parser = OptionParser.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
|
16
|
+
def parse
|
|
17
|
+
parser.new do |opts|
|
|
18
|
+
opts.banner = 'Usage: rubycritic [options] [paths]'
|
|
19
|
+
|
|
20
|
+
opts.on('-p', '--path [PATH]', 'Set path where report will be saved (tmp/rubycritic by default)') do |path|
|
|
21
|
+
@root = path
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
opts.on('-b', '--branch BRANCH', 'Set branch to compare') do |branch|
|
|
25
|
+
self.base_branch = String(branch)
|
|
26
|
+
set_current_branch
|
|
27
|
+
self.mode = :compare_branches
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
opts.on(
|
|
31
|
+
'-t',
|
|
32
|
+
'--maximum-decrease [MAX_DECREASE]',
|
|
33
|
+
'Set a threshold for score difference between two branches (works only with -b)'
|
|
34
|
+
) do |threshold_score|
|
|
35
|
+
self.threshold_score = Integer(threshold_score)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
formats = []
|
|
39
|
+
self.formats = formats
|
|
40
|
+
opts.on(
|
|
41
|
+
'-f', '--format [FORMAT]',
|
|
42
|
+
%i[html json console lint],
|
|
43
|
+
'Report smells in the given format:',
|
|
44
|
+
' html (default; will open in a browser)',
|
|
45
|
+
' json',
|
|
46
|
+
' console',
|
|
47
|
+
' lint',
|
|
48
|
+
'Multiple formats are supported.'
|
|
49
|
+
) do |format|
|
|
50
|
+
formats << format
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
formatters = []
|
|
54
|
+
self.formatters = formatters
|
|
55
|
+
opts.on(
|
|
56
|
+
'--custom-format [REQUIREPATH]:[CLASSNAME]|[CLASSNAME]',
|
|
57
|
+
'Instantiate a given class as formatter and call report for reporting.',
|
|
58
|
+
'Two ways are possible to load the formatter.',
|
|
59
|
+
'If the class is not autorequired the REQUIREPATH can be given together',
|
|
60
|
+
'with the CLASSNAME to be loaded seperated by a :.',
|
|
61
|
+
'Example: rubycritic/markdown/reporter.rb:RubyCritic::MarkDown::Reporter',
|
|
62
|
+
'or if the file is already required the CLASSNAME is enough',
|
|
63
|
+
'Example: RubyCritic::MarkDown::Reporter',
|
|
64
|
+
'Multiple formatters are supported.'
|
|
65
|
+
) do |formatter|
|
|
66
|
+
formatters << formatter
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
opts.on('-s', '--minimum-score [MIN_SCORE]', 'Set a minimum score') do |min_score|
|
|
70
|
+
self.minimum_score = Float(min_score)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
opts.on('-m', '--mode-ci [BASE_BRANCH]',
|
|
74
|
+
'Use CI mode (faster, analyses diffs w.r.t base_branch (default: master))') do |branch|
|
|
75
|
+
self.base_branch = branch || 'master'
|
|
76
|
+
set_current_branch
|
|
77
|
+
self.mode = :ci
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
opts.on('--deduplicate-symlinks', 'De-duplicate symlinks based on their final target') do
|
|
81
|
+
self.deduplicate_symlinks = true
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
opts.on('--suppress-ratings', 'Suppress letter ratings') do
|
|
85
|
+
self.suppress_ratings = true
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
opts.on('--no-browser', 'Do not open html report with browser') do
|
|
89
|
+
self.no_browser = true
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
opts.on_tail('-v', '--version', "Show gem's version") do
|
|
93
|
+
self.mode = :version
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
|
97
|
+
self.mode = :help
|
|
98
|
+
end
|
|
99
|
+
end.parse!(@argv)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def to_h
|
|
103
|
+
{
|
|
104
|
+
mode: mode,
|
|
105
|
+
root: root,
|
|
106
|
+
formats: formats,
|
|
107
|
+
formatters: formatters,
|
|
108
|
+
deduplicate_symlinks: deduplicate_symlinks,
|
|
109
|
+
paths: paths,
|
|
110
|
+
suppress_ratings: suppress_ratings,
|
|
111
|
+
help_text: parser.help,
|
|
112
|
+
minimum_score: minimum_score,
|
|
113
|
+
no_browser: no_browser,
|
|
114
|
+
base_branch: base_branch,
|
|
115
|
+
feature_branch: feature_branch,
|
|
116
|
+
threshold_score: threshold_score
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
# rubocop:enable Metrics/MethodLength,Metrics/AbcSize
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
attr_accessor :mode, :root, :formats, :formatters, :deduplicate_symlinks,
|
|
124
|
+
:suppress_ratings, :minimum_score, :no_browser,
|
|
125
|
+
:parser, :base_branch, :feature_branch, :threshold_score
|
|
126
|
+
def paths
|
|
127
|
+
@argv unless @argv.empty?
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def set_current_branch
|
|
131
|
+
self.feature_branch = SourceControlSystem::Git.current_branch
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
# rubocop:enable Metrics/ClassLength
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module RubyCritic
|
|
6
|
+
module Cli
|
|
7
|
+
class Options
|
|
8
|
+
class File
|
|
9
|
+
attr_reader :filename, :options
|
|
10
|
+
|
|
11
|
+
def initialize(filename = './.rubycritic.yml')
|
|
12
|
+
@filename = filename
|
|
13
|
+
@options = {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def parse
|
|
17
|
+
@options = YAML.load_file(filename) if ::File.file?(filename)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# rubocop:disable Metrics/MethodLength
|
|
21
|
+
def to_h
|
|
22
|
+
{
|
|
23
|
+
mode: mode,
|
|
24
|
+
root: root,
|
|
25
|
+
formats: formats,
|
|
26
|
+
deduplicate_symlinks: deduplicate_symlinks,
|
|
27
|
+
paths: paths,
|
|
28
|
+
suppress_ratings: suppress_ratings,
|
|
29
|
+
minimum_score: minimum_score,
|
|
30
|
+
no_browser: no_browser,
|
|
31
|
+
base_branch: base_branch,
|
|
32
|
+
feature_branch: feature_branch,
|
|
33
|
+
threshold_score: threshold_score
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
# rubocop:enable Metrics/MethodLength
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def base_branch
|
|
41
|
+
return options.dig('mode_ci', 'branch') || 'master' if options.dig('mode_ci', 'enabled').to_s == 'true'
|
|
42
|
+
|
|
43
|
+
options['branch']
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def mode
|
|
47
|
+
if options.dig('mode_ci', 'enabled').to_s == 'true'
|
|
48
|
+
:ci
|
|
49
|
+
elsif base_branch
|
|
50
|
+
:compare_branches
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def feature_branch
|
|
55
|
+
SourceControlSystem::Git.current_branch if base_branch
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def root
|
|
59
|
+
options['path']
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def threshold_score
|
|
63
|
+
options['threshold_score']
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def deduplicate_symlinks
|
|
67
|
+
value_for(options['deduplicate_symlinks'])
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def suppress_ratings
|
|
71
|
+
value_for(options['suppress_ratings'])
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def no_browser
|
|
75
|
+
value_for(options['no_browser'])
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def formats
|
|
79
|
+
formats = Array(options['formats'])
|
|
80
|
+
formats.select do |format|
|
|
81
|
+
%w[html json console lint].include?(format)
|
|
82
|
+
end.map(&:to_sym)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def minimum_score
|
|
86
|
+
options['minimum_score']
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def paths
|
|
90
|
+
options['paths']
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def value_for(value)
|
|
94
|
+
value = value.to_s
|
|
95
|
+
value == 'true' unless value.empty?
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/cli/options/argv'
|
|
4
|
+
require 'rubycritic/cli/options/file'
|
|
5
|
+
|
|
6
|
+
module RubyCritic
|
|
7
|
+
module Cli
|
|
8
|
+
class Options
|
|
9
|
+
attr_reader :argv_options, :file_options
|
|
10
|
+
|
|
11
|
+
def initialize(argv)
|
|
12
|
+
@argv_options = Argv.new(argv)
|
|
13
|
+
@file_options = File.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def parse
|
|
17
|
+
argv_options.parse
|
|
18
|
+
file_options.parse
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# :reek:NilCheck
|
|
23
|
+
def to_h
|
|
24
|
+
file_hash = file_options.to_h
|
|
25
|
+
argv_hash = argv_options.to_h
|
|
26
|
+
|
|
27
|
+
file_hash.merge(argv_hash) do |_, file_option, argv_option|
|
|
28
|
+
Array(argv_option).empty? ? file_option : argv_option
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyCritic
|
|
4
|
+
module Colorize
|
|
5
|
+
def colorize(text, color_code)
|
|
6
|
+
"\e[#{color_code}m#{text}\e[0m"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def red(text)
|
|
10
|
+
colorize(text, 31)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def green(text)
|
|
14
|
+
colorize(text, 32)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/configuration'
|
|
4
|
+
|
|
5
|
+
module RubyCritic
|
|
6
|
+
class CommandFactory
|
|
7
|
+
COMMAND_CLASS_MODES = %i[version help ci compare default].freeze
|
|
8
|
+
|
|
9
|
+
def self.create(options = {})
|
|
10
|
+
Config.set(options)
|
|
11
|
+
command_class(Config.mode).new(options)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.command_class(mode)
|
|
15
|
+
mode = mode.to_s.split('_').first.to_sym
|
|
16
|
+
if COMMAND_CLASS_MODES.include? mode
|
|
17
|
+
require "rubycritic/commands/#{mode}"
|
|
18
|
+
Command.const_get(mode.capitalize)
|
|
19
|
+
else
|
|
20
|
+
require 'rubycritic/commands/default'
|
|
21
|
+
Command::Default
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/commands/status_reporter'
|
|
4
|
+
|
|
5
|
+
module RubyCritic
|
|
6
|
+
module Command
|
|
7
|
+
class Base
|
|
8
|
+
def initialize(options)
|
|
9
|
+
@options = options
|
|
10
|
+
@status_reporter = RubyCritic::Command::StatusReporter.new(@options)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def execute
|
|
14
|
+
raise NotImplementedError
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/source_control_systems/base'
|
|
4
|
+
require 'rubycritic/analysers_runner'
|
|
5
|
+
require 'rubycritic/reporter'
|
|
6
|
+
require 'rubycritic/commands/default'
|
|
7
|
+
require 'rubycritic/commands/compare'
|
|
8
|
+
|
|
9
|
+
module RubyCritic
|
|
10
|
+
module Command
|
|
11
|
+
class Ci < Compare
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/source_control_systems/base'
|
|
4
|
+
require 'rubycritic/analysers_runner'
|
|
5
|
+
require 'rubycritic/revision_comparator'
|
|
6
|
+
require 'rubycritic/reporter'
|
|
7
|
+
require 'rubycritic/commands/base'
|
|
8
|
+
require 'rubycritic/commands/default'
|
|
9
|
+
require 'rubycritic/commands/utils/build_number_file'
|
|
10
|
+
|
|
11
|
+
module RubyCritic
|
|
12
|
+
module Command
|
|
13
|
+
class Compare < Default
|
|
14
|
+
def initialize(options)
|
|
15
|
+
super
|
|
16
|
+
@build_number = 0
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def execute
|
|
20
|
+
compare_branches
|
|
21
|
+
status_reporter.score = Config.feature_branch_score
|
|
22
|
+
status_reporter
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
attr_reader :paths, :status_reporter
|
|
28
|
+
|
|
29
|
+
def compare_branches
|
|
30
|
+
@build_number = Utils::BuildNumberFile.new.update_build_number
|
|
31
|
+
set_root_paths
|
|
32
|
+
original_no_browser_config = Config.no_browser
|
|
33
|
+
Config.no_browser = true
|
|
34
|
+
analyse_branch(:base_branch)
|
|
35
|
+
analyse_branch(:feature_branch)
|
|
36
|
+
Config.no_browser = original_no_browser_config
|
|
37
|
+
analyse_modified_files
|
|
38
|
+
compare_code_quality
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def set_root_paths
|
|
42
|
+
Config.base_root_directory = Pathname.new(branch_directory(:base_branch))
|
|
43
|
+
Config.feature_root_directory = Pathname.new(branch_directory(:feature_branch))
|
|
44
|
+
Config.compare_root_directory = Pathname.new("#{Config.root}/compare")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# switch branch and analyse files
|
|
48
|
+
def analyse_branch(branch)
|
|
49
|
+
SourceControlSystem::Git.switch_branch(Config.send(branch))
|
|
50
|
+
critic = critique(branch)
|
|
51
|
+
Config.send(:"#{branch}_score=", critic.score)
|
|
52
|
+
Config.root = branch_directory(branch)
|
|
53
|
+
report(critic)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# generate report only for modified files
|
|
57
|
+
def analyse_modified_files
|
|
58
|
+
modified_files = Config.feature_branch_collection.where(SourceControlSystem::Git.modified_files)
|
|
59
|
+
analysed_modules = AnalysedModulesCollection.new(modified_files.map(&:path), modified_files)
|
|
60
|
+
Config.root = "#{Config.root}/compare"
|
|
61
|
+
report(analysed_modules)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def compare_code_quality
|
|
65
|
+
build_details
|
|
66
|
+
compare_threshold
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# mark build as failed if the diff b/w the score of
|
|
70
|
+
# two branches is greater than threshold value
|
|
71
|
+
def compare_threshold
|
|
72
|
+
return unless mark_build_fail?
|
|
73
|
+
|
|
74
|
+
print("Threshold: #{Config.threshold_score}\n")
|
|
75
|
+
print("Difference: #{(Config.base_branch_score - Config.feature_branch_score).abs}\n")
|
|
76
|
+
abort('The score difference between the two branches is over the threshold.')
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def mark_build_fail?
|
|
80
|
+
Config.threshold_score >= 0 && threshold_reached?
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def threshold_reached?
|
|
84
|
+
(Config.base_branch_score - Config.feature_branch_score) > Config.threshold_score
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def branch_directory(branch)
|
|
88
|
+
"#{Config.root}/compare/#{Config.send(branch)}"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# create a txt file with the branch score details
|
|
92
|
+
def build_details
|
|
93
|
+
details = "Base branch (#{Config.base_branch}) score: #{Config.base_branch_score} \n"\
|
|
94
|
+
"Feature branch (#{Config.feature_branch}) score: #{Config.feature_branch_score} \n"
|
|
95
|
+
File.open("#{Config.compare_root_directory}/build_details.txt", 'w') { |file| file.write(details) }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# store the analysed moduled collection based on the branch
|
|
99
|
+
def critique(branch)
|
|
100
|
+
module_collection = AnalysersRunner.new(paths).run
|
|
101
|
+
Config.send(:"#{branch}_collection=", module_collection)
|
|
102
|
+
RevisionComparator.new(paths).statuses = module_collection
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/source_control_systems/base'
|
|
4
|
+
require 'rubycritic/analysers_runner'
|
|
5
|
+
require 'rubycritic/revision_comparator'
|
|
6
|
+
require 'rubycritic/reporter'
|
|
7
|
+
require 'rubycritic/commands/base'
|
|
8
|
+
|
|
9
|
+
module RubyCritic
|
|
10
|
+
module Command
|
|
11
|
+
class Default < Base
|
|
12
|
+
def initialize(options)
|
|
13
|
+
super
|
|
14
|
+
@paths = options[:paths] || ['.']
|
|
15
|
+
Config.source_control_system = SourceControlSystem::Base.create
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def execute
|
|
19
|
+
report(critique)
|
|
20
|
+
status_reporter
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def critique
|
|
24
|
+
analysed_modules = AnalysersRunner.new(paths).run
|
|
25
|
+
RevisionComparator.new(paths).statuses = analysed_modules
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def report(analysed_modules)
|
|
29
|
+
Reporter.generate_report(analysed_modules)
|
|
30
|
+
status_reporter.score = analysed_modules.score
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
attr_reader :paths, :status_reporter
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|