codeclimate-fede 0.85.23
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/check +18 -0
- data/bin/codeclimate +21 -0
- data/bin/prep-release +45 -0
- data/bin/release +41 -0
- data/bin/validate-release +18 -0
- data/config/engines.yml +322 -0
- data/lib/cc/analyzer.rb +50 -0
- data/lib/cc/analyzer/bridge.rb +106 -0
- data/lib/cc/analyzer/composite_container_listener.rb +21 -0
- data/lib/cc/analyzer/container.rb +208 -0
- data/lib/cc/analyzer/container/result.rb +74 -0
- data/lib/cc/analyzer/container_listener.rb +9 -0
- data/lib/cc/analyzer/engine.rb +125 -0
- data/lib/cc/analyzer/engine_output.rb +74 -0
- data/lib/cc/analyzer/engine_output_filter.rb +36 -0
- data/lib/cc/analyzer/engine_output_overrider.rb +31 -0
- data/lib/cc/analyzer/filesystem.rb +50 -0
- data/lib/cc/analyzer/formatters.rb +21 -0
- data/lib/cc/analyzer/formatters/formatter.rb +53 -0
- data/lib/cc/analyzer/formatters/html_formatter.rb +415 -0
- data/lib/cc/analyzer/formatters/json_formatter.rb +38 -0
- data/lib/cc/analyzer/formatters/plain_text_formatter.rb +101 -0
- data/lib/cc/analyzer/formatters/spinner.rb +35 -0
- data/lib/cc/analyzer/issue.rb +69 -0
- data/lib/cc/analyzer/issue_sorter.rb +30 -0
- data/lib/cc/analyzer/issue_validations.rb +26 -0
- data/lib/cc/analyzer/issue_validations/category_validation.rb +32 -0
- data/lib/cc/analyzer/issue_validations/check_name_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/content_validation.rb +21 -0
- data/lib/cc/analyzer/issue_validations/description_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/location_format_validation.rb +72 -0
- data/lib/cc/analyzer/issue_validations/other_locations_format_validation.rb +41 -0
- data/lib/cc/analyzer/issue_validations/path_existence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/path_is_file_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/path_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/relative_path_validation.rb +32 -0
- data/lib/cc/analyzer/issue_validations/remediation_points_validation.rb +25 -0
- data/lib/cc/analyzer/issue_validations/severity_validation.rb +39 -0
- data/lib/cc/analyzer/issue_validations/type_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/validation.rb +35 -0
- data/lib/cc/analyzer/issue_validator.rb +11 -0
- data/lib/cc/analyzer/location_description.rb +45 -0
- data/lib/cc/analyzer/logging_container_listener.rb +24 -0
- data/lib/cc/analyzer/measurement.rb +22 -0
- data/lib/cc/analyzer/measurement_validations.rb +16 -0
- data/lib/cc/analyzer/measurement_validations/name_validation.rb +23 -0
- data/lib/cc/analyzer/measurement_validations/type_validation.rb +15 -0
- data/lib/cc/analyzer/measurement_validations/validation.rb +27 -0
- data/lib/cc/analyzer/measurement_validations/value_validation.rb +21 -0
- data/lib/cc/analyzer/measurement_validator.rb +11 -0
- data/lib/cc/analyzer/mounted_path.rb +80 -0
- data/lib/cc/analyzer/raising_container_listener.rb +32 -0
- data/lib/cc/analyzer/source_buffer.rb +47 -0
- data/lib/cc/analyzer/source_extractor.rb +79 -0
- data/lib/cc/analyzer/source_fingerprint.rb +40 -0
- data/lib/cc/analyzer/statsd_container_listener.rb +51 -0
- data/lib/cc/analyzer/validator.rb +38 -0
- data/lib/cc/cli.rb +39 -0
- data/lib/cc/cli/analyze.rb +90 -0
- data/lib/cc/cli/analyze/engine_failure.rb +11 -0
- data/lib/cc/cli/command.rb +85 -0
- data/lib/cc/cli/console.rb +12 -0
- data/lib/cc/cli/engines.rb +5 -0
- data/lib/cc/cli/engines/engine_command.rb +15 -0
- data/lib/cc/cli/engines/install.rb +35 -0
- data/lib/cc/cli/engines/list.rb +18 -0
- data/lib/cc/cli/file_store.rb +42 -0
- data/lib/cc/cli/global_cache.rb +47 -0
- data/lib/cc/cli/global_config.rb +35 -0
- data/lib/cc/cli/help.rb +51 -0
- data/lib/cc/cli/output.rb +34 -0
- data/lib/cc/cli/prepare.rb +98 -0
- data/lib/cc/cli/runner.rb +75 -0
- data/lib/cc/cli/validate_config.rb +84 -0
- data/lib/cc/cli/version.rb +16 -0
- data/lib/cc/cli/version_checker.rb +107 -0
- data/lib/cc/config.rb +70 -0
- data/lib/cc/config/checks_adapter.rb +40 -0
- data/lib/cc/config/default_adapter.rb +54 -0
- data/lib/cc/config/engine.rb +41 -0
- data/lib/cc/config/engine_set.rb +47 -0
- data/lib/cc/config/json_adapter.rb +17 -0
- data/lib/cc/config/prepare.rb +92 -0
- data/lib/cc/config/validation/check_validator.rb +34 -0
- data/lib/cc/config/validation/engine_validator.rb +93 -0
- data/lib/cc/config/validation/fetch_validator.rb +78 -0
- data/lib/cc/config/validation/file_validator.rb +112 -0
- data/lib/cc/config/validation/hash_validations.rb +52 -0
- data/lib/cc/config/validation/json.rb +31 -0
- data/lib/cc/config/validation/prepare_validator.rb +40 -0
- data/lib/cc/config/validation/yaml.rb +66 -0
- data/lib/cc/config/yaml_adapter.rb +73 -0
- data/lib/cc/engine_registry.rb +74 -0
- data/lib/cc/resolv.rb +39 -0
- data/lib/cc/workspace.rb +39 -0
- data/lib/cc/workspace/exclusion.rb +34 -0
- data/lib/cc/workspace/path_tree.rb +49 -0
- data/lib/cc/workspace/path_tree/dir_node.rb +67 -0
- data/lib/cc/workspace/path_tree/file_node.rb +31 -0
- metadata +277 -0
@@ -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,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
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
class IssueSorter
|
4
|
+
def initialize(issues)
|
5
|
+
@issues = issues
|
6
|
+
end
|
7
|
+
|
8
|
+
def by_location
|
9
|
+
@issues.sort_by { |i| line_or_offset(i) }
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def line_or_offset(issue)
|
15
|
+
location = issue["location"]
|
16
|
+
|
17
|
+
case
|
18
|
+
when location["lines"]
|
19
|
+
[location["lines"]["begin"].to_i]
|
20
|
+
when location["positions"] && location["positions"]["begin"]["line"]
|
21
|
+
[location["positions"]["begin"]["line"].to_i, location["positions"]["begin"]["column"].to_i]
|
22
|
+
when location["positions"] && location["positions"]["begin"]["offset"]
|
23
|
+
[1_000_000_000] # push offsets to end of list
|
24
|
+
else
|
25
|
+
[0] # whole-file issues are first
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module IssueValidations
|
4
|
+
autoload :CategoryValidation, "cc/analyzer/issue_validations/category_validation"
|
5
|
+
autoload :CheckNamePresenceValidation, "cc/analyzer/issue_validations/check_name_presence_validation"
|
6
|
+
autoload :ContentValidation, "cc/analyzer/issue_validations/content_validation"
|
7
|
+
autoload :DescriptionPresenceValidation, "cc/analyzer/issue_validations/description_presence_validation"
|
8
|
+
autoload :LocationFormatValidation, "cc/analyzer/issue_validations/location_format_validation"
|
9
|
+
autoload :OtherLocationsFormatValidation, "cc/analyzer/issue_validations/other_locations_format_validation"
|
10
|
+
autoload :PathExistenceValidation, "cc/analyzer/issue_validations/path_existence_validation"
|
11
|
+
autoload :PathIsFileValidation, "cc/analyzer/issue_validations/path_is_file_validation"
|
12
|
+
autoload :PathPresenceValidation, "cc/analyzer/issue_validations/path_presence_validation"
|
13
|
+
autoload :RelativePathValidation, "cc/analyzer/issue_validations/relative_path_validation"
|
14
|
+
autoload :RemediationPointsValidation, "cc/analyzer/issue_validations/remediation_points_validation"
|
15
|
+
autoload :SeverityValidation, "cc/analyzer/issue_validations/severity_validation"
|
16
|
+
autoload :TypeValidation, "cc/analyzer/issue_validations/type_validation"
|
17
|
+
autoload :Validation, "cc/analyzer/issue_validations/validation"
|
18
|
+
|
19
|
+
def self.validations
|
20
|
+
constants.sort.map(&method(:const_get)).select do |klass|
|
21
|
+
klass.is_a?(Class) && klass.superclass == Validation
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module IssueValidations
|
4
|
+
class CategoryValidation < Validation
|
5
|
+
CATEGORIES = [
|
6
|
+
"Bug Risk".freeze,
|
7
|
+
"Clarity".freeze,
|
8
|
+
"Compatibility".freeze,
|
9
|
+
"Complexity".freeze,
|
10
|
+
"Duplication".freeze,
|
11
|
+
"Performance".freeze,
|
12
|
+
"Security".freeze,
|
13
|
+
"Style".freeze,
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
def valid?
|
17
|
+
object["categories"].present? && no_invalid_categories?
|
18
|
+
end
|
19
|
+
|
20
|
+
def message
|
21
|
+
"Category must be at least one of #{CATEGORIES.join(", ")}"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def no_invalid_categories?
|
27
|
+
(CATEGORIES | object["categories"]) == CATEGORIES
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module IssueValidations
|
4
|
+
class ContentValidation < Validation
|
5
|
+
def valid?
|
6
|
+
!has_content? || (content.is_a?(Hash) && content["body"].is_a?(String))
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
"Content must be a hash containing a 'body' key with string contents"
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def has_content?
|
16
|
+
object.key?("content")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module IssueValidations
|
4
|
+
class LocationFormatValidation < Validation
|
5
|
+
class Validator
|
6
|
+
def initialize(location)
|
7
|
+
@location = location
|
8
|
+
check_validity
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid?
|
12
|
+
error.nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
def message
|
16
|
+
if error
|
17
|
+
"Location is not formatted correctly: #{error}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_accessor :error
|
24
|
+
|
25
|
+
attr_reader :location
|
26
|
+
|
27
|
+
def check_validity
|
28
|
+
if location["lines"]
|
29
|
+
self.error = "location.lines is not valid: #{JSON.dump(location["lines"])}" unless valid_lines?(location["lines"])
|
30
|
+
elsif location["positions"]
|
31
|
+
self.error = "location.positions is not valid: #{JSON.dump(location["positions"])}" unless valid_positions?(location["positions"])
|
32
|
+
else
|
33
|
+
self.error = "location.lines or location.positions must be present"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def valid_positions?(positions)
|
38
|
+
positions.is_a?(Hash) &&
|
39
|
+
valid_position?(positions["begin"]) &&
|
40
|
+
valid_position?(positions["end"])
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid_position?(position)
|
44
|
+
position &&
|
45
|
+
(
|
46
|
+
[position["line"], position["column"]].all? { |value| value.is_a?(Integer) } ||
|
47
|
+
position["offset"].is_a?(Integer)
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def valid_lines?(lines)
|
52
|
+
lines.is_a?(Hash) && [lines["begin"], lines["end"]].all? { |value| value.is_a?(Integer) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def valid?
|
57
|
+
validation.valid?
|
58
|
+
end
|
59
|
+
|
60
|
+
def message
|
61
|
+
validation.message
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def validation
|
67
|
+
@validation ||= Validator.new(object.fetch("location", {}))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|