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.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +290 -0
  3. data/CONTRIBUTING.md +93 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +259 -0
  7. data/ROADMAP.md +56 -0
  8. data/Rakefile +28 -0
  9. data/bin/rubycritic +10 -0
  10. data/lib/rubycritic/analysers/attributes.rb +29 -0
  11. data/lib/rubycritic/analysers/churn.rb +30 -0
  12. data/lib/rubycritic/analysers/complexity.rb +30 -0
  13. data/lib/rubycritic/analysers/coverage.rb +118 -0
  14. data/lib/rubycritic/analysers/helpers/ast_node.rb +76 -0
  15. data/lib/rubycritic/analysers/helpers/flay.rb +13 -0
  16. data/lib/rubycritic/analysers/helpers/flog.rb +17 -0
  17. data/lib/rubycritic/analysers/helpers/methods_counter.rb +25 -0
  18. data/lib/rubycritic/analysers/helpers/modules_locator.rb +42 -0
  19. data/lib/rubycritic/analysers/helpers/parser.rb +14 -0
  20. data/lib/rubycritic/analysers/helpers/reek.rb +11 -0
  21. data/lib/rubycritic/analysers/smells/flay.rb +79 -0
  22. data/lib/rubycritic/analysers/smells/flog.rb +69 -0
  23. data/lib/rubycritic/analysers/smells/reek.rb +53 -0
  24. data/lib/rubycritic/analysers_runner.rb +41 -0
  25. data/lib/rubycritic/analysis_summary.rb +40 -0
  26. data/lib/rubycritic/browser.rb +19 -0
  27. data/lib/rubycritic/cli/application.rb +34 -0
  28. data/lib/rubycritic/cli/options/argv.rb +137 -0
  29. data/lib/rubycritic/cli/options/file.rb +100 -0
  30. data/lib/rubycritic/cli/options.rb +33 -0
  31. data/lib/rubycritic/colorize.rb +17 -0
  32. data/lib/rubycritic/command_factory.rb +25 -0
  33. data/lib/rubycritic/commands/base.rb +18 -0
  34. data/lib/rubycritic/commands/ci.rb +14 -0
  35. data/lib/rubycritic/commands/compare.rb +106 -0
  36. data/lib/rubycritic/commands/default.rb +38 -0
  37. data/lib/rubycritic/commands/help.rb +18 -0
  38. data/lib/rubycritic/commands/status_reporter.rb +45 -0
  39. data/lib/rubycritic/commands/utils/build_number_file.rb +37 -0
  40. data/lib/rubycritic/commands/version.rb +16 -0
  41. data/lib/rubycritic/configuration.rb +69 -0
  42. data/lib/rubycritic/core/analysed_module.rb +93 -0
  43. data/lib/rubycritic/core/analysed_modules_collection.rb +95 -0
  44. data/lib/rubycritic/core/location.rb +47 -0
  45. data/lib/rubycritic/core/rating.rb +30 -0
  46. data/lib/rubycritic/core/smell.rb +84 -0
  47. data/lib/rubycritic/generators/console_report.rb +20 -0
  48. data/lib/rubycritic/generators/html/assets/fonts/FontAwesome.otf +0 -0
  49. data/lib/rubycritic/generators/html/assets/fonts/Roboto-Medium.ttf +0 -0
  50. data/lib/rubycritic/generators/html/assets/fonts/Roboto-Regular.ttf +0 -0
  51. data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.eot +0 -0
  52. data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.svg +2671 -0
  53. data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.ttf +0 -0
  54. data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.woff +0 -0
  55. data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.woff2 +0 -0
  56. data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.eot +0 -0
  57. data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.svg +288 -0
  58. data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
  59. data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.woff +0 -0
  60. data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.woff2 +0 -0
  61. data/lib/rubycritic/generators/html/assets/images/logo.png +0 -0
  62. data/lib/rubycritic/generators/html/assets/javascripts/application.js +281 -0
  63. data/lib/rubycritic/generators/html/assets/javascripts/bootstrap.min.js +7 -0
  64. data/lib/rubycritic/generators/html/assets/javascripts/highcharts.src-4.0.1.js +17672 -0
  65. data/lib/rubycritic/generators/html/assets/javascripts/jquery.filtertable.min.js +13 -0
  66. data/lib/rubycritic/generators/html/assets/javascripts/jquery.min.js +4 -0
  67. data/lib/rubycritic/generators/html/assets/javascripts/jquery.scrollTo.min.js +7 -0
  68. data/lib/rubycritic/generators/html/assets/javascripts/jquery.tablesorter.js +1031 -0
  69. data/lib/rubycritic/generators/html/assets/javascripts/jquery.tablesorter.min.js +4 -0
  70. data/lib/rubycritic/generators/html/assets/javascripts/jquery.timeago.js +231 -0
  71. data/lib/rubycritic/generators/html/assets/javascripts/prettify.js +46 -0
  72. data/lib/rubycritic/generators/html/assets/stylesheets/application.css +570 -0
  73. data/lib/rubycritic/generators/html/assets/stylesheets/bootstrap.min.css +6 -0
  74. data/lib/rubycritic/generators/html/assets/stylesheets/font-awesome.min.css +4 -0
  75. data/lib/rubycritic/generators/html/assets/stylesheets/prettify.css +1 -0
  76. data/lib/rubycritic/generators/html/assets/stylesheets/prettify.custom_theme.css +69 -0
  77. data/lib/rubycritic/generators/html/base.rb +54 -0
  78. data/lib/rubycritic/generators/html/code_file.rb +51 -0
  79. data/lib/rubycritic/generators/html/code_index.rb +33 -0
  80. data/lib/rubycritic/generators/html/line.rb +37 -0
  81. data/lib/rubycritic/generators/html/overview.rb +38 -0
  82. data/lib/rubycritic/generators/html/simple_cov_index.rb +44 -0
  83. data/lib/rubycritic/generators/html/smells_index.rb +45 -0
  84. data/lib/rubycritic/generators/html/templates/code_file.html.erb +61 -0
  85. data/lib/rubycritic/generators/html/templates/code_index.html.erb +55 -0
  86. data/lib/rubycritic/generators/html/templates/layouts/application.html.erb +66 -0
  87. data/lib/rubycritic/generators/html/templates/line.html.erb +1 -0
  88. data/lib/rubycritic/generators/html/templates/overview.html.erb +68 -0
  89. data/lib/rubycritic/generators/html/templates/simple_cov_index.html.erb +44 -0
  90. data/lib/rubycritic/generators/html/templates/smells_index.html.erb +47 -0
  91. data/lib/rubycritic/generators/html/templates/smelly_line.html.erb +23 -0
  92. data/lib/rubycritic/generators/html/turbulence.rb +17 -0
  93. data/lib/rubycritic/generators/html/view_helpers.rb +58 -0
  94. data/lib/rubycritic/generators/html_report.rb +77 -0
  95. data/lib/rubycritic/generators/json/simple.rb +43 -0
  96. data/lib/rubycritic/generators/json_report.rb +26 -0
  97. data/lib/rubycritic/generators/lint_report.rb +30 -0
  98. data/lib/rubycritic/generators/text/lint.rb +41 -0
  99. data/lib/rubycritic/generators/text/list.rb +45 -0
  100. data/lib/rubycritic/generators/text/templates/lint.erb +3 -0
  101. data/lib/rubycritic/generators/text/templates/list.erb +12 -0
  102. data/lib/rubycritic/rake_task.rb +71 -0
  103. data/lib/rubycritic/reporter.rb +39 -0
  104. data/lib/rubycritic/revision_comparator.rb +45 -0
  105. data/lib/rubycritic/serializer.rb +32 -0
  106. data/lib/rubycritic/smells_status_setter.rb +18 -0
  107. data/lib/rubycritic/source_control_systems/base.rb +41 -0
  108. data/lib/rubycritic/source_control_systems/double.rb +19 -0
  109. data/lib/rubycritic/source_control_systems/git.rb +95 -0
  110. data/lib/rubycritic/source_control_systems/mercurial.rb +29 -0
  111. data/lib/rubycritic/source_control_systems/perforce.rb +116 -0
  112. data/lib/rubycritic/source_locator.rb +52 -0
  113. data/lib/rubycritic/version.rb +5 -0
  114. data/lib/rubycritic.rb +6 -0
  115. 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