codeclimate-fede 0.85.21

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 (102) hide show
  1. checksums.yaml +7 -0
  2. data/bin/check +18 -0
  3. data/bin/codeclimate +21 -0
  4. data/bin/prep-release +45 -0
  5. data/bin/publish +47 -0
  6. data/bin/release +41 -0
  7. data/bin/validate-release +18 -0
  8. data/config/engines.yml +322 -0
  9. data/lib/cc/analyzer/bridge.rb +106 -0
  10. data/lib/cc/analyzer/composite_container_listener.rb +21 -0
  11. data/lib/cc/analyzer/container/result.rb +74 -0
  12. data/lib/cc/analyzer/container.rb +208 -0
  13. data/lib/cc/analyzer/container_listener.rb +9 -0
  14. data/lib/cc/analyzer/engine.rb +125 -0
  15. data/lib/cc/analyzer/engine_output.rb +74 -0
  16. data/lib/cc/analyzer/engine_output_filter.rb +36 -0
  17. data/lib/cc/analyzer/engine_output_overrider.rb +31 -0
  18. data/lib/cc/analyzer/filesystem.rb +50 -0
  19. data/lib/cc/analyzer/formatters/formatter.rb +53 -0
  20. data/lib/cc/analyzer/formatters/html_formatter.rb +415 -0
  21. data/lib/cc/analyzer/formatters/json_formatter.rb +38 -0
  22. data/lib/cc/analyzer/formatters/plain_text_formatter.rb +101 -0
  23. data/lib/cc/analyzer/formatters/spinner.rb +35 -0
  24. data/lib/cc/analyzer/formatters.rb +21 -0
  25. data/lib/cc/analyzer/issue.rb +69 -0
  26. data/lib/cc/analyzer/issue_sorter.rb +30 -0
  27. data/lib/cc/analyzer/issue_validations/category_validation.rb +32 -0
  28. data/lib/cc/analyzer/issue_validations/check_name_presence_validation.rb +15 -0
  29. data/lib/cc/analyzer/issue_validations/content_validation.rb +21 -0
  30. data/lib/cc/analyzer/issue_validations/description_presence_validation.rb +15 -0
  31. data/lib/cc/analyzer/issue_validations/location_format_validation.rb +72 -0
  32. data/lib/cc/analyzer/issue_validations/other_locations_format_validation.rb +41 -0
  33. data/lib/cc/analyzer/issue_validations/path_existence_validation.rb +15 -0
  34. data/lib/cc/analyzer/issue_validations/path_is_file_validation.rb +15 -0
  35. data/lib/cc/analyzer/issue_validations/path_presence_validation.rb +15 -0
  36. data/lib/cc/analyzer/issue_validations/relative_path_validation.rb +32 -0
  37. data/lib/cc/analyzer/issue_validations/remediation_points_validation.rb +25 -0
  38. data/lib/cc/analyzer/issue_validations/severity_validation.rb +39 -0
  39. data/lib/cc/analyzer/issue_validations/type_validation.rb +15 -0
  40. data/lib/cc/analyzer/issue_validations/validation.rb +35 -0
  41. data/lib/cc/analyzer/issue_validations.rb +26 -0
  42. data/lib/cc/analyzer/issue_validator.rb +11 -0
  43. data/lib/cc/analyzer/location_description.rb +45 -0
  44. data/lib/cc/analyzer/logging_container_listener.rb +24 -0
  45. data/lib/cc/analyzer/measurement.rb +22 -0
  46. data/lib/cc/analyzer/measurement_validations/name_validation.rb +23 -0
  47. data/lib/cc/analyzer/measurement_validations/type_validation.rb +15 -0
  48. data/lib/cc/analyzer/measurement_validations/validation.rb +27 -0
  49. data/lib/cc/analyzer/measurement_validations/value_validation.rb +21 -0
  50. data/lib/cc/analyzer/measurement_validations.rb +16 -0
  51. data/lib/cc/analyzer/measurement_validator.rb +11 -0
  52. data/lib/cc/analyzer/mounted_path.rb +80 -0
  53. data/lib/cc/analyzer/raising_container_listener.rb +32 -0
  54. data/lib/cc/analyzer/source_buffer.rb +47 -0
  55. data/lib/cc/analyzer/source_extractor.rb +79 -0
  56. data/lib/cc/analyzer/source_fingerprint.rb +40 -0
  57. data/lib/cc/analyzer/statsd_container_listener.rb +51 -0
  58. data/lib/cc/analyzer/validator.rb +38 -0
  59. data/lib/cc/analyzer.rb +50 -0
  60. data/lib/cc/cli/analyze/engine_failure.rb +11 -0
  61. data/lib/cc/cli/analyze.rb +90 -0
  62. data/lib/cc/cli/command.rb +85 -0
  63. data/lib/cc/cli/console.rb +12 -0
  64. data/lib/cc/cli/engines/engine_command.rb +15 -0
  65. data/lib/cc/cli/engines/install.rb +35 -0
  66. data/lib/cc/cli/engines/list.rb +18 -0
  67. data/lib/cc/cli/engines.rb +5 -0
  68. data/lib/cc/cli/file_store.rb +42 -0
  69. data/lib/cc/cli/global_cache.rb +47 -0
  70. data/lib/cc/cli/global_config.rb +35 -0
  71. data/lib/cc/cli/help.rb +51 -0
  72. data/lib/cc/cli/output.rb +34 -0
  73. data/lib/cc/cli/prepare.rb +98 -0
  74. data/lib/cc/cli/runner.rb +75 -0
  75. data/lib/cc/cli/validate_config.rb +84 -0
  76. data/lib/cc/cli/version.rb +16 -0
  77. data/lib/cc/cli/version_checker.rb +107 -0
  78. data/lib/cc/cli.rb +39 -0
  79. data/lib/cc/config/checks_adapter.rb +40 -0
  80. data/lib/cc/config/default_adapter.rb +54 -0
  81. data/lib/cc/config/engine.rb +41 -0
  82. data/lib/cc/config/engine_set.rb +47 -0
  83. data/lib/cc/config/json_adapter.rb +17 -0
  84. data/lib/cc/config/prepare.rb +92 -0
  85. data/lib/cc/config/validation/check_validator.rb +34 -0
  86. data/lib/cc/config/validation/engine_validator.rb +93 -0
  87. data/lib/cc/config/validation/fetch_validator.rb +78 -0
  88. data/lib/cc/config/validation/file_validator.rb +112 -0
  89. data/lib/cc/config/validation/hash_validations.rb +52 -0
  90. data/lib/cc/config/validation/json.rb +31 -0
  91. data/lib/cc/config/validation/prepare_validator.rb +40 -0
  92. data/lib/cc/config/validation/yaml.rb +66 -0
  93. data/lib/cc/config/yaml_adapter.rb +73 -0
  94. data/lib/cc/config.rb +70 -0
  95. data/lib/cc/engine_registry.rb +74 -0
  96. data/lib/cc/resolv.rb +39 -0
  97. data/lib/cc/workspace/exclusion.rb +34 -0
  98. data/lib/cc/workspace/path_tree/dir_node.rb +67 -0
  99. data/lib/cc/workspace/path_tree/file_node.rb +31 -0
  100. data/lib/cc/workspace/path_tree.rb +49 -0
  101. data/lib/cc/workspace.rb +39 -0
  102. metadata +279 -0
@@ -0,0 +1,415 @@
1
+ require "redcarpet"
2
+
3
+ module CC
4
+ module Analyzer
5
+ module Formatters
6
+ class HTMLFormatter < Formatter # rubocop: disable Metrics/ClassLength
7
+ LANGUAGES = Hash.new { |_, ext| ext }.
8
+ merge(
9
+ # abap
10
+ # ada
11
+ "appraisals" => "ruby",
12
+ "as" => "actionscript",
13
+ "asm" => "nasm",
14
+ "bas" => "basic",
15
+ # c
16
+ "c++" => "cpp",
17
+ "capfile" => "ruby",
18
+ "cc" => "cpp",
19
+ "cfc" => "markup",
20
+ "cfm" => "markup",
21
+ "coffee" => "coffeescript",
22
+ "cp" => "cpp",
23
+ # cpp
24
+ "cr" => "crystal",
25
+ "cs" => "csharp",
26
+ "css" => %w[css css-extras],
27
+ "cu" => "cpp",
28
+ "cxx" => "cpp",
29
+ # d
30
+ # dart
31
+ # diff
32
+ "dockerfile" => "docker",
33
+ "dpr" => "pascal",
34
+ "erl" => "erlang",
35
+ "ex" => "elixir",
36
+ "f" => "fortran",
37
+ "f90" => "fortran",
38
+ "f95" => "fortran",
39
+ "feature" => "gherkin",
40
+ "for" => "fortran",
41
+ "fs" => "fsharp",
42
+ "fsi" => "fsharp",
43
+ "fsscript" => "fsharp",
44
+ "fsx" => "fsharp",
45
+ "gemfile" => "ruby",
46
+ "gemspec" => "ruby",
47
+ # glsl
48
+ # go
49
+ # groovy
50
+ "gvy" => "groovy",
51
+ "h" => "c",
52
+ "h++" => "cpp",
53
+ # haml
54
+ # handlebars
55
+ "hbr" => "handlebars",
56
+ "hh" => "cpp",
57
+ "hpp" => "cpp",
58
+ "hs" => "haskell",
59
+ "htm" => "markup",
60
+ "html" => "markup",
61
+ "hx" => "haxe",
62
+ "hxml" => "haxe",
63
+ "icn" => "icon",
64
+ "ijs" => "j",
65
+ # ini
66
+ "iol" => "jolie",
67
+ # java
68
+ "jl" => "julia",
69
+ "js" => "javascript",
70
+ # json
71
+ # jsx
72
+ "kt" => "kotlin",
73
+ "kts" => "kotlin",
74
+ # less
75
+ "lhs" => "haskell",
76
+ "lol" => "lolcode",
77
+ "lols" => "lolcode",
78
+ "ls" => "livescript",
79
+ # lua
80
+ "m" => "objective-c",
81
+ "mab" => "ruby",
82
+ # makefile
83
+ # markdown
84
+ "md" => "markdown",
85
+ # mel
86
+ "mkd" => "markdown",
87
+ "ml" => "ocaml",
88
+ "mli" => "ocaml",
89
+ "mm" => "objective-c",
90
+ # nim
91
+ # nix
92
+ "nsi" => "nsis",
93
+ "ol" => "jolie",
94
+ # oz
95
+ "pas" => "pascal",
96
+ "patch" => "diff",
97
+ "pde" => "processing",
98
+ "php" => %w[php php-extras],
99
+ "php3" => %w[php php-extras],
100
+ "php4" => %w[php php-extras],
101
+ "php5" => %w[php php-extras],
102
+ "phtml" => %w[php php-extras],
103
+ "pl" => "perl",
104
+ "pp" => "puppet",
105
+ "prawn" => "ruby",
106
+ "pro" => "prolog",
107
+ # properties
108
+ # pure
109
+ "py" => "python",
110
+ "py3" => "python",
111
+ "pyw" => "python",
112
+ "q" => "qore",
113
+ "qm" => "qore",
114
+ "qtest" => "qore",
115
+ # r
116
+ "rake" => "ruby",
117
+ "rakefile" => "ruby",
118
+ "rantfile" => "ruby",
119
+ "rb" => "ruby",
120
+ "rbw" => "ruby",
121
+ "rjs" => "ruby",
122
+ "rpdf" => "ruby",
123
+ "rs" => "rust",
124
+ "rst" => "rest",
125
+ "ru" => "ruby",
126
+ "rxml" => "ruby",
127
+ # sass
128
+ "sc" => "scala",
129
+ # scala
130
+ "scs" => "scheme",
131
+ # scss
132
+ "shader" => "glsl",
133
+ # sql
134
+ "ss" => "scheme",
135
+ "st" => "smalltalk",
136
+ "styl" => "stylus",
137
+ # swift
138
+ # tcl
139
+ "template" => "json",
140
+ "tex" => "latex",
141
+ # textile
142
+ "tmproj" => "markup",
143
+ "tpl" => "smarty",
144
+ "ts" => "typescript",
145
+ "v" => "verilog",
146
+ "vagrantfile" => "ruby",
147
+ "vhd" => "vhdl",
148
+ # vim
149
+ "xaml" => "markup",
150
+ "xhtml" => "markup",
151
+ "xml" => "markup",
152
+ # yaml
153
+ "yaws" => "erlang",
154
+ "yml" => "yaml",
155
+ ).freeze
156
+
157
+ class Location
158
+ CONTEXT_LINES = 2
159
+ MAX_LINES = 10
160
+
161
+ def initialize(source_buffer, location)
162
+ @source_buffer = source_buffer
163
+ @location = location
164
+ end
165
+
166
+ def begin_line
167
+ @begin_line ||= line("begin")
168
+ end
169
+
170
+ def end_line
171
+ @end_line ||= line("end")
172
+ end
173
+
174
+ def to_s
175
+ [
176
+ begin_line,
177
+ end_line,
178
+ ].uniq.join("-")
179
+ end
180
+
181
+ def start
182
+ [begin_line - CONTEXT_LINES, 1].max
183
+ end
184
+
185
+ def line_offset
186
+ start - 1
187
+ end
188
+
189
+ def code
190
+ first_line = start
191
+ last_line = [
192
+ end_line + CONTEXT_LINES,
193
+ begin_line + MAX_LINES + CONTEXT_LINES,
194
+ source_buffer.line_count,
195
+ ].min
196
+ source_buffer.source.lines[(first_line - 1)..(last_line - 1)].join("")
197
+ end
198
+
199
+ private
200
+
201
+ attr_reader :location, :source_buffer
202
+
203
+ def line(type)
204
+ if location["lines"]
205
+ location["lines"][type]
206
+ elsif location["positions"]
207
+ position_to_line(location["positions"][type])
208
+ end
209
+ end
210
+
211
+ def position_to_line(position)
212
+ if position["line"]
213
+ position["line"]
214
+ else
215
+ @source_buffer.decompose_position(position["offset"]).first
216
+ end
217
+ end
218
+ end
219
+
220
+ class SourceFile
221
+ def initialize(path, filesystem)
222
+ @path = path
223
+ @filesystem = filesystem
224
+ end
225
+
226
+ attr_reader :path
227
+
228
+ def syntaxes
229
+ ext = File.basename(path).split(".").last.downcase
230
+ Array(LANGUAGES[ext])
231
+ end
232
+
233
+ def code
234
+ filesystem.read_path(path)
235
+ end
236
+
237
+ def buffer
238
+ @buffer ||= SourceBuffer.new(path, code)
239
+ end
240
+
241
+ def location(loc)
242
+ Location.new(buffer, loc)
243
+ end
244
+
245
+ private
246
+
247
+ attr_reader :filesystem
248
+ end
249
+
250
+ class Issue
251
+ MARKDOWN_CONFIG = { autolink: true, fenced_code_blocks: true, no_intra_emphasis: true, tables: true }.freeze
252
+
253
+ def initialize(data, filesystem)
254
+ @data = data
255
+ @filesystem = filesystem
256
+ end
257
+
258
+ def description
259
+ data["description"]
260
+ end
261
+
262
+ def body
263
+ @body ||=
264
+ begin
265
+ text = data.fetch("content", {}).fetch("body", "").strip
266
+ unless text.empty?
267
+ markdown(text)
268
+ end
269
+ end
270
+ end
271
+
272
+ def source
273
+ @source ||= SourceFile.new(
274
+ data.fetch("location", {}).fetch("path", ""),
275
+ filesystem,
276
+ )
277
+ end
278
+
279
+ def location
280
+ @location ||=
281
+ Location.new(
282
+ source.buffer,
283
+ data["location"],
284
+ )
285
+ end
286
+
287
+ def other_locations
288
+ @other_locations ||=
289
+ begin
290
+ data.fetch("other_locations", []).map do |loc|
291
+ [SourceFile.new(loc["path"], filesystem), loc]
292
+ end.to_h
293
+ end
294
+ end
295
+
296
+ def categories
297
+ data.fetch("categories", [])
298
+ end
299
+
300
+ def engine_name
301
+ data["engine_name"]
302
+ end
303
+
304
+ private
305
+
306
+ attr_reader :data, :filesystem
307
+
308
+ def markdown(text)
309
+ html = Redcarpet::Render::HTML.new(
310
+ escape_html: false,
311
+ link_attributes: { target: "_blank" },
312
+ )
313
+ redcarpet = Redcarpet::Markdown.new(html, MARKDOWN_CONFIG)
314
+ redcarpet.render(text)
315
+ end
316
+ end
317
+
318
+ class IssueCollection
319
+ def initialize(filesystem)
320
+ @collection = []
321
+ @filesystem = filesystem
322
+ end
323
+
324
+ def each(&block)
325
+ collection.each(&block)
326
+ end
327
+
328
+ def <<(issue)
329
+ if issue.is_a? Hash
330
+ issue = Issue.new(issue, filesystem)
331
+ end
332
+ collection.push(issue)
333
+ end
334
+
335
+ def any?
336
+ collection.any?
337
+ end
338
+
339
+ def syntaxes
340
+ collection.flat_map do |issue|
341
+ issue.source.syntaxes
342
+ end.uniq.sort
343
+ end
344
+
345
+ def categories
346
+ collection.flat_map(&:categories).uniq.sort
347
+ end
348
+
349
+ def engines
350
+ collection.map(&:engine_name).uniq.compact.sort
351
+ end
352
+
353
+ private
354
+
355
+ attr_reader :collection, :filesystem
356
+ end
357
+
358
+ class ReportTemplate
359
+ include ERB::Util
360
+ attr_reader :issues
361
+
362
+ TEMPLATE_PATH = File.expand_path(File.join(File.dirname(__FILE__), "templates/html.erb"))
363
+
364
+ def initialize(issues, filesystem)
365
+ @issues = issues
366
+ @filesystem = filesystem
367
+ end
368
+
369
+ def render
370
+ template = File.read(TEMPLATE_PATH)
371
+ ERB.new(template, nil, "-").result(binding)
372
+ end
373
+
374
+ def project_name
375
+ File.basename(filesystem.root)
376
+ end
377
+
378
+ def param(str)
379
+ str.downcase.gsub(/\s+/, "-")
380
+ end
381
+
382
+ def params(values)
383
+ values.map { |c| param c }.join(" ")
384
+ end
385
+
386
+ private
387
+
388
+ attr_reader :filesystem
389
+ end
390
+
391
+ def finished
392
+ puts ReportTemplate.new(issues, @filesystem).render
393
+ end
394
+
395
+ def failed(_)
396
+ exit 1
397
+ end
398
+
399
+ private
400
+
401
+ def issues
402
+ @issues ||= IssueCollection.new(@filesystem)
403
+ end
404
+
405
+ def warnings
406
+ @warnings ||= []
407
+ end
408
+
409
+ def measurements
410
+ @measurements ||= []
411
+ end
412
+ end
413
+ end
414
+ end
415
+ end
@@ -0,0 +1,38 @@
1
+ module CC
2
+ module Analyzer
3
+ module Formatters
4
+ class JSONFormatter < Formatter
5
+ def initialize(filesystem)
6
+ @filesystem = filesystem
7
+ @has_begun = false
8
+ end
9
+
10
+ def started
11
+ print "["
12
+ end
13
+
14
+ def finished
15
+ print "]\n"
16
+ end
17
+
18
+ def write(data)
19
+ document = JSON.parse(data)
20
+ document["engine_name"] = current_engine.name
21
+
22
+ if @has_begun
23
+ print ",\n"
24
+ end
25
+
26
+ print document.to_json
27
+ @has_begun = true
28
+ end
29
+
30
+ def failed(output)
31
+ $stderr.puts "\nAnalysis failed with the following output:"
32
+ $stderr.puts output
33
+ exit 1
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,101 @@
1
+ require "rainbow"
2
+ require "tty/spinner"
3
+ require "active_support/number_helper"
4
+
5
+ module CC
6
+ module Analyzer
7
+ module Formatters
8
+ class PlainTextFormatter < Formatter
9
+ def started
10
+ puts colorize("Starting analysis", :green)
11
+ end
12
+
13
+ def finished
14
+ puts
15
+
16
+ issues_by_path.each do |path, file_issues|
17
+ puts colorize("== #{path} (#{pluralize(file_issues.size, "issue")}) ==", :yellow)
18
+
19
+ IssueSorter.new(file_issues).by_location.each do |issue|
20
+ if (location = issue["location"])
21
+ source_buffer = @filesystem.source_buffer_for(location["path"])
22
+ print(colorize(LocationDescription.new(source_buffer, location, ": "), :cyan))
23
+ end
24
+
25
+ print(issue["description"])
26
+ print(colorize(" [#{issue["engine_name"]}]", "#333333"))
27
+ puts
28
+ end
29
+ puts
30
+ end
31
+
32
+ print(colorize("Analysis complete! Found #{pluralize(issues.size, "issue")}", :green))
33
+ unless warnings.empty?
34
+ print(colorize(" and #{pluralize(warnings.size, "warning")}", :green))
35
+ end
36
+ puts(colorize(".", :green))
37
+ end
38
+
39
+ def engine_running(engine, &block)
40
+ super(engine) do
41
+ result = with_spinner("Running #{current_engine.name}: ", &block)
42
+ if result.skipped?
43
+ puts(colorize("Skipped #{current_engine.name}: #{result.stderr}", :yellow))
44
+ end
45
+ end
46
+ end
47
+
48
+ def failed(output)
49
+ spinner.stop("Failed")
50
+ puts colorize("\nAnalysis failed with the following output:", :red)
51
+ puts output
52
+ exit 1
53
+ end
54
+
55
+ private
56
+
57
+ def spinner(text = nil)
58
+ @spinner ||= Spinner.new(text)
59
+ end
60
+
61
+ def with_spinner(text)
62
+ spinner(text).start
63
+ yield
64
+ ensure
65
+ spinner.stop
66
+ @spinner = nil
67
+ end
68
+
69
+ def colorize(string, *args)
70
+ rainbow.wrap(string).color(*args)
71
+ end
72
+
73
+ def rainbow
74
+ @rainbow ||= Rainbow.new.tap do |rainbow|
75
+ rainbow.enabled = false unless @output.tty?
76
+ end
77
+ end
78
+
79
+ def issues
80
+ @issues ||= []
81
+ end
82
+
83
+ def issues_by_path
84
+ issues.group_by { |i| i["location"]["path"] }.sort
85
+ end
86
+
87
+ def warnings
88
+ @warnings ||= []
89
+ end
90
+
91
+ def measurements
92
+ @measurements ||= []
93
+ end
94
+
95
+ def pluralize(number, noun)
96
+ "#{ActiveSupport::NumberHelper.number_to_delimited(number)} #{noun.pluralize(number)}"
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,35 @@
1
+ module CC
2
+ module Analyzer
3
+ module Formatters
4
+ class Spinner
5
+ def initialize(text)
6
+ @spinner = TTY::Spinner.new(text)
7
+ end
8
+
9
+ def start
10
+ return unless $stdout.tty? && !CLI.debug?
11
+ @thread = Thread.new do
12
+ loop do
13
+ @spinning = true
14
+ spinner.spin
15
+ sleep 0.075
16
+ end
17
+ end
18
+ end
19
+
20
+ def stop(text = "Done!")
21
+ if @spinning
22
+ spinner.stop(text)
23
+ print("\n")
24
+ @thread.kill
25
+ end
26
+ @spinning = false
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :spinner
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ module CC
2
+ module Analyzer
3
+ module Formatters
4
+ autoload :Formatter, "cc/analyzer/formatters/formatter"
5
+ autoload :HTMLFormatter, "cc/analyzer/formatters/html_formatter"
6
+ autoload :JSONFormatter, "cc/analyzer/formatters/json_formatter"
7
+ autoload :PlainTextFormatter, "cc/analyzer/formatters/plain_text_formatter"
8
+ autoload :Spinner, "cc/analyzer/formatters/spinner"
9
+
10
+ FORMATTERS = {
11
+ html: HTMLFormatter,
12
+ json: JSONFormatter,
13
+ text: PlainTextFormatter,
14
+ }.freeze
15
+
16
+ def self.resolve(name)
17
+ FORMATTERS[name.to_sym] or raise Formatter::InvalidFormatterError, "'#{name}' is not a valid formatter. Valid options are: #{FORMATTERS.keys.join(", ")}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,69 @@
1
+ module CC
2
+ module Analyzer
3
+ class Issue
4
+ DEFAULT_SEVERITY = IssueValidations::SeverityValidation::MINOR
5
+ DEPRECATED_SEVERITY = IssueValidations::SeverityValidation::NORMAL
6
+
7
+ SPEC_ISSUE_ATTRIBUTES = %w[
8
+ categories
9
+ check_name
10
+ content
11
+ description
12
+ location
13
+ other_locations
14
+ remediation_points
15
+ severity
16
+ type
17
+ ]
18
+
19
+ def initialize(engine_name, output)
20
+ @engine_name = engine_name
21
+ @output = output
22
+ end
23
+
24
+ def as_json(*)
25
+ parsed_output.reverse_merge!(
26
+ "engine_name" => engine_name,
27
+ "fingerprint" => fingerprint,
28
+ ).merge!("severity" => severity)
29
+ end
30
+
31
+ def fingerprint
32
+ parsed_output.fetch("fingerprint") { default_fingerprint }
33
+ end
34
+
35
+ # Allow access to hash keys as methods
36
+ SPEC_ISSUE_ATTRIBUTES.each do |key|
37
+ define_method(key) do
38
+ parsed_output[key]
39
+ end
40
+ end
41
+
42
+ def path
43
+ parsed_output.fetch("location", {}).fetch("path", "")
44
+ end
45
+
46
+ private
47
+
48
+ attr_reader :engine_name, :output
49
+
50
+ def default_fingerprint
51
+ SourceFingerprint.new(self).compute
52
+ end
53
+
54
+ def severity
55
+ severity = parsed_output.fetch("severity", DEFAULT_SEVERITY)
56
+
57
+ if severity == DEPRECATED_SEVERITY
58
+ DEFAULT_SEVERITY
59
+ else
60
+ severity
61
+ end
62
+ end
63
+
64
+ def parsed_output
65
+ @parsed_output ||= JSON.parse(output)
66
+ end
67
+ end
68
+ end
69
+ end