codeclimate-fede 0.85.23

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) 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/release +41 -0
  6. data/bin/validate-release +18 -0
  7. data/config/engines.yml +322 -0
  8. data/lib/cc/analyzer.rb +50 -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.rb +208 -0
  12. data/lib/cc/analyzer/container/result.rb +74 -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.rb +21 -0
  20. data/lib/cc/analyzer/formatters/formatter.rb +53 -0
  21. data/lib/cc/analyzer/formatters/html_formatter.rb +415 -0
  22. data/lib/cc/analyzer/formatters/json_formatter.rb +38 -0
  23. data/lib/cc/analyzer/formatters/plain_text_formatter.rb +101 -0
  24. data/lib/cc/analyzer/formatters/spinner.rb +35 -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.rb +26 -0
  28. data/lib/cc/analyzer/issue_validations/category_validation.rb +32 -0
  29. data/lib/cc/analyzer/issue_validations/check_name_presence_validation.rb +15 -0
  30. data/lib/cc/analyzer/issue_validations/content_validation.rb +21 -0
  31. data/lib/cc/analyzer/issue_validations/description_presence_validation.rb +15 -0
  32. data/lib/cc/analyzer/issue_validations/location_format_validation.rb +72 -0
  33. data/lib/cc/analyzer/issue_validations/other_locations_format_validation.rb +41 -0
  34. data/lib/cc/analyzer/issue_validations/path_existence_validation.rb +15 -0
  35. data/lib/cc/analyzer/issue_validations/path_is_file_validation.rb +15 -0
  36. data/lib/cc/analyzer/issue_validations/path_presence_validation.rb +15 -0
  37. data/lib/cc/analyzer/issue_validations/relative_path_validation.rb +32 -0
  38. data/lib/cc/analyzer/issue_validations/remediation_points_validation.rb +25 -0
  39. data/lib/cc/analyzer/issue_validations/severity_validation.rb +39 -0
  40. data/lib/cc/analyzer/issue_validations/type_validation.rb +15 -0
  41. data/lib/cc/analyzer/issue_validations/validation.rb +35 -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.rb +16 -0
  47. data/lib/cc/analyzer/measurement_validations/name_validation.rb +23 -0
  48. data/lib/cc/analyzer/measurement_validations/type_validation.rb +15 -0
  49. data/lib/cc/analyzer/measurement_validations/validation.rb +27 -0
  50. data/lib/cc/analyzer/measurement_validations/value_validation.rb +21 -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/cli.rb +39 -0
  60. data/lib/cc/cli/analyze.rb +90 -0
  61. data/lib/cc/cli/analyze/engine_failure.rb +11 -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.rb +5 -0
  65. data/lib/cc/cli/engines/engine_command.rb +15 -0
  66. data/lib/cc/cli/engines/install.rb +35 -0
  67. data/lib/cc/cli/engines/list.rb +18 -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/config.rb +70 -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/engine_registry.rb +74 -0
  95. data/lib/cc/resolv.rb +39 -0
  96. data/lib/cc/workspace.rb +39 -0
  97. data/lib/cc/workspace/exclusion.rb +34 -0
  98. data/lib/cc/workspace/path_tree.rb +49 -0
  99. data/lib/cc/workspace/path_tree/dir_node.rb +67 -0
  100. data/lib/cc/workspace/path_tree/file_node.rb +31 -0
  101. 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,15 @@
1
+ module CC
2
+ module Analyzer
3
+ module IssueValidations
4
+ class CheckNamePresenceValidation < Validation
5
+ def valid?
6
+ object["check_name"].present?
7
+ end
8
+
9
+ def message
10
+ "Check name must be present"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ 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,15 @@
1
+ module CC
2
+ module Analyzer
3
+ module IssueValidations
4
+ class DescriptionPresenceValidation < Validation
5
+ def valid?
6
+ object["description"].present?
7
+ end
8
+
9
+ def message
10
+ "Description must be present"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ 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