codeclimate 0.69.0 → 0.70.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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/bin/prep-release +1 -1
  3. data/config/engines.yml +32 -323
  4. data/lib/cc/analyzer.rb +5 -4
  5. data/lib/cc/analyzer/bridge.rb +106 -0
  6. data/lib/cc/analyzer/composite_container_listener.rb +4 -8
  7. data/lib/cc/analyzer/container.rb +44 -41
  8. data/lib/cc/analyzer/container/result.rb +74 -0
  9. data/lib/cc/analyzer/container_listener.rb +2 -7
  10. data/lib/cc/analyzer/engine.rb +53 -45
  11. data/lib/cc/analyzer/engine_output.rb +40 -10
  12. data/lib/cc/analyzer/formatters/formatter.rb +2 -0
  13. data/lib/cc/analyzer/formatters/html_formatter.rb +4 -0
  14. data/lib/cc/analyzer/formatters/json_formatter.rb +1 -0
  15. data/lib/cc/analyzer/formatters/plain_text_formatter.rb +8 -1
  16. data/lib/cc/analyzer/issue.rb +4 -2
  17. data/lib/cc/analyzer/issue_validations/relative_path_validation.rb +6 -2
  18. data/lib/cc/analyzer/issue_validator.rb +3 -32
  19. data/lib/cc/analyzer/logging_container_listener.rb +9 -7
  20. data/lib/cc/analyzer/measurement.rb +22 -0
  21. data/lib/cc/analyzer/measurement_validations.rb +16 -0
  22. data/lib/cc/analyzer/measurement_validations/name_validation.rb +23 -0
  23. data/lib/cc/analyzer/measurement_validations/type_validation.rb +15 -0
  24. data/lib/cc/analyzer/measurement_validations/validation.rb +27 -0
  25. data/lib/cc/analyzer/measurement_validations/value_validation.rb +21 -0
  26. data/lib/cc/analyzer/measurement_validator.rb +11 -0
  27. data/lib/cc/analyzer/raising_container_listener.rb +18 -18
  28. data/lib/cc/analyzer/statsd_container_listener.rb +22 -22
  29. data/lib/cc/analyzer/validator.rb +38 -0
  30. data/lib/cc/cli.rb +12 -12
  31. data/lib/cc/cli/analyze.rb +42 -60
  32. data/lib/cc/cli/analyze/engine_failure.rb +11 -0
  33. data/lib/cc/cli/command.rb +0 -10
  34. data/lib/cc/cli/engines.rb +0 -3
  35. data/lib/cc/cli/engines/engine_command.rb +2 -34
  36. data/lib/cc/cli/engines/install.rb +11 -17
  37. data/lib/cc/cli/engines/list.rb +5 -3
  38. data/lib/cc/cli/prepare.rb +5 -11
  39. data/lib/cc/cli/runner.rb +1 -2
  40. data/lib/cc/cli/test.rb +0 -1
  41. data/lib/cc/cli/validate_config.rb +49 -63
  42. data/lib/cc/cli/version_checker.rb +3 -3
  43. data/lib/cc/config.rb +70 -0
  44. data/lib/cc/config/checks_adapter.rb +40 -0
  45. data/lib/cc/config/default_adapter.rb +52 -0
  46. data/lib/cc/config/engine.rb +41 -0
  47. data/lib/cc/config/engine_set.rb +47 -0
  48. data/lib/cc/config/json_adapter.rb +17 -0
  49. data/lib/cc/config/prepare.rb +92 -0
  50. data/lib/cc/config/validation/check_validator.rb +34 -0
  51. data/lib/cc/config/validation/engine_validator.rb +89 -0
  52. data/lib/cc/config/validation/fetch_validator.rb +78 -0
  53. data/lib/cc/config/validation/file_validator.rb +112 -0
  54. data/lib/cc/config/validation/hash_validations.rb +52 -0
  55. data/lib/cc/config/validation/json.rb +31 -0
  56. data/lib/cc/config/validation/prepare_validator.rb +40 -0
  57. data/lib/cc/config/validation/yaml.rb +66 -0
  58. data/lib/cc/config/yaml_adapter.rb +73 -0
  59. data/lib/cc/engine_registry.rb +74 -0
  60. data/lib/cc/workspace/path_tree/dir_node.rb +1 -1
  61. metadata +36 -55
  62. data/bin/codeclimate-init +0 -6
  63. data/config/coffeelint/coffeelint.json +0 -129
  64. data/config/csslint/.csslintrc +0 -2
  65. data/config/eslint/.eslintignore +0 -1
  66. data/config/eslint/.eslintrc.yml +0 -277
  67. data/config/rubocop/.rubocop.yml +0 -1156
  68. data/lib/cc/analyzer/config.rb +0 -86
  69. data/lib/cc/analyzer/engine_registry.rb +0 -36
  70. data/lib/cc/analyzer/engines_config_builder.rb +0 -97
  71. data/lib/cc/analyzer/engines_runner.rb +0 -64
  72. data/lib/cc/cli/config.rb +0 -44
  73. data/lib/cc/cli/config_generator.rb +0 -108
  74. data/lib/cc/cli/engines/disable.rb +0 -38
  75. data/lib/cc/cli/engines/enable.rb +0 -41
  76. data/lib/cc/cli/engines/remove.rb +0 -35
  77. data/lib/cc/cli/init.rb +0 -117
  78. data/lib/cc/cli/prepare/quality.rb +0 -64
  79. data/lib/cc/cli/upgrade_config_generator.rb +0 -42
@@ -0,0 +1,17 @@
1
+ module CC
2
+ class Config
3
+ class JSONAdapter < Config
4
+ DEFAULT_PATH = ".codeclimate.json".freeze
5
+
6
+ attr_reader :config
7
+
8
+ def self.load(path = DEFAULT_PATH)
9
+ new(::JSON.parse(File.open(path).read))
10
+ end
11
+
12
+ def initialize(json = {})
13
+ @config = json
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,92 @@
1
+ module CC
2
+ class Config
3
+ class Prepare
4
+ attr_reader :fetch
5
+
6
+ def self.from_data(data)
7
+ if data.present?
8
+ fetch = Fetch.from_data(data.fetch("fetch", []))
9
+
10
+ new(fetch: fetch)
11
+ else
12
+ new
13
+ end
14
+ end
15
+
16
+ def initialize(fetch: Fetch.new)
17
+ @fetch = fetch
18
+ end
19
+
20
+ def merge(other)
21
+ Prepare.new(fetch: fetch.merge(other.fetch))
22
+ end
23
+
24
+ class Fetch
25
+ def self.from_data(data)
26
+ new(data.map { |d| Entry.from_data(d) })
27
+ end
28
+
29
+ def initialize(entries = [])
30
+ @entries = Set.new(entries)
31
+ end
32
+
33
+ def each(&block)
34
+ entries.each(&block)
35
+ end
36
+
37
+ def merge(other)
38
+ Fetch.new(each.to_a | other.each.to_a)
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :entries
44
+
45
+ class Entry
46
+ attr_reader :url, :path
47
+
48
+ def self.from_data(data)
49
+ case data
50
+ when String then new(data)
51
+ when Hash then new(data.fetch("url"), data["path"])
52
+ end
53
+ end
54
+
55
+ def initialize(url, path = nil)
56
+ @url = url
57
+ @path = path || url.split("/").last
58
+
59
+ validate_path!
60
+ end
61
+
62
+ # Useful in specs
63
+ def ==(other)
64
+ other.is_a?(self.class) &&
65
+ other.url == url &&
66
+ other.path == path
67
+ end
68
+
69
+ private
70
+
71
+ # Duplicate a validation which has security implication. This should
72
+ # always be caught upstream, so raising loudly is fine.
73
+ def validate_path!
74
+ if path.blank?
75
+ raise ArgumentError, "path cannot be be blank"
76
+ end
77
+
78
+ pathname = Pathname.new(path)
79
+
80
+ if pathname.absolute?
81
+ raise ArgumentError, "path cannot be absolute: #{path}"
82
+ end
83
+
84
+ if pathname.cleanpath.to_s != pathname.to_s || path.include?("..")
85
+ raise ArgumentError, "path cannot point outside the current directory: #{path}"
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,34 @@
1
+ module CC
2
+ class Config
3
+ module Validation
4
+ class CheckValidator
5
+ include HashValidations
6
+
7
+ attr_reader :errors, :warnings
8
+
9
+ def initialize(data)
10
+ @data = data
11
+
12
+ @errors = []
13
+ @warnings = []
14
+
15
+ validate
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :data
21
+
22
+ def validate
23
+ unless data.is_a?(Hash)
24
+ errors << "must be a hash"
25
+ return
26
+ end
27
+
28
+ validate_key_type("enabled", [TrueClass, FalseClass])
29
+ validate_key_type("config", Hash)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,89 @@
1
+ module CC
2
+ class Config
3
+ module Validation
4
+ class EngineValidator
5
+ include HashValidations
6
+
7
+ RECOGNIZED_KEYS = %w[
8
+ enabled
9
+ channel
10
+ checks
11
+ config
12
+ exclude_fingerprints
13
+ exclude_patterns
14
+ ].freeze
15
+
16
+ attr_reader :errors, :warnings
17
+
18
+ def initialize(data, legacy: false)
19
+ @data = data
20
+ @legacy = legacy
21
+
22
+ @errors = []
23
+ @warnings = []
24
+
25
+ validate
26
+ end
27
+
28
+ def valid?
29
+ errors.none?
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :data
35
+
36
+ def legacy?
37
+ @legacy
38
+ end
39
+
40
+ def validate
41
+ validate_root
42
+ return unless data.is_a?(Hash)
43
+
44
+ validate_key_type("enabled", [TrueClass, FalseClass])
45
+ validate_key_type("channel", String)
46
+ validate_key_type("config", [String, Hash])
47
+ validate_key_type("exclude_patterns", Array)
48
+ if legacy?
49
+ validate_key_type("exclude_paths", [Array, String])
50
+ end
51
+
52
+ validate_checks
53
+ validate_exclude_fingerprints
54
+
55
+ if legacy?
56
+ warn_unrecognized_keys(RECOGNIZED_KEYS + %w[exclude_paths])
57
+ else
58
+ warn_unrecognized_keys(RECOGNIZED_KEYS)
59
+ end
60
+ end
61
+
62
+ def validate_root
63
+ if !data.is_a?(Hash) && ![true, false].include?(data)
64
+ errors << "section must be a boolean or a hash"
65
+ return false
66
+ end
67
+ true
68
+ end
69
+
70
+ def validate_checks
71
+ return unless validate_key_type("checks", Hash)
72
+
73
+ data.fetch("checks", {}).each do |_check_name, check_data|
74
+ validator = CheckValidator.new(check_data)
75
+ errors.push(*validator.errors)
76
+ warnings.push(*validator.warnings)
77
+ end
78
+ end
79
+
80
+ def validate_exclude_fingerprints
81
+ validate_key_type("exclude_fingerprints", Array)
82
+ if data.key?("exclude_fingerprints")
83
+ warnings << "'exclude_fingerprints' is deprecated, and support may be removed in the future"
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,78 @@
1
+ require "uri"
2
+ require "pathname"
3
+
4
+ module CC
5
+ class Config
6
+ module Validation
7
+ class FetchValidator
8
+ include HashValidations
9
+
10
+ attr_reader :errors, :warnings
11
+
12
+ def initialize(data)
13
+ @data = data
14
+
15
+ @errors = []
16
+ @warnings = []
17
+
18
+ validate
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :data
24
+
25
+ def validate
26
+ if data.is_a?(String)
27
+ validate_url(data)
28
+ elsif data.is_a?(Hash)
29
+ validate_fetch_hash
30
+ else
31
+ errors << "fetch section should be a string or a hash"
32
+ end
33
+ end
34
+
35
+ def validate_url(url)
36
+ unless valid_url?(url)
37
+ errors << "fetch section: invalid URL '#{url}'"
38
+ end
39
+ end
40
+
41
+ def valid_url?(url)
42
+ uri = URI.parse(url)
43
+ uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
44
+ rescue URI::InvalidURIError
45
+ false
46
+ end
47
+
48
+ def validate_fetch_hash
49
+ if !data.key?("path") || !data.key?("url")
50
+ errors << "fetch section must include 'url' & 'path'"
51
+ end
52
+
53
+ validate_key_type("path", String)
54
+ validate_key_type("url", String)
55
+
56
+ validate_path(data["path"])
57
+ validate_url(data["url"])
58
+
59
+ warn_unrecognized_keys(%w[path url])
60
+ end
61
+
62
+ def validate_path(path)
63
+ if path.nil? || path.length.zero?
64
+ errors << "fetch section's 'path' cannot be empty"
65
+ else
66
+ pathname = Pathname.new(path)
67
+ if pathname.absolute?
68
+ errors << "fetch section: absolute path '#{path}' is invalid"
69
+ end
70
+ if pathname.cleanpath.to_s != pathname.to_s || path.include?("..")
71
+ errors << "fetch section: relative path elements in '#{path}' are invalid"
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,112 @@
1
+ module CC
2
+ class Config
3
+ module Validation
4
+ class FileValidator
5
+ include HashValidations
6
+
7
+ attr_reader :errors, :path, :warnings
8
+
9
+ def initialize(path, registry)
10
+ @path = path
11
+ @registry = registry
12
+
13
+ @errors = []
14
+ @warnings = []
15
+
16
+ validate
17
+ end
18
+
19
+ def valid?
20
+ errors.none?
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :data, :registry
26
+
27
+ def validate
28
+ raise NotImplementedError, "use a subclass"
29
+ end
30
+
31
+ def denormalize_subvalidator(validator, prefix)
32
+ validator.errors.each do |msg|
33
+ errors << "#{prefix}: #{msg}"
34
+ end
35
+ validator.warnings.each do |msg|
36
+ warnings << "#{prefix}: #{msg}"
37
+ end
38
+ end
39
+
40
+ def validate_prepare
41
+ return unless validate_key_type("prepare", Hash)
42
+
43
+ validator = PrepareValidator.new(data.fetch("prepare", {}))
44
+ denormalize_subvalidator(validator, "prepare section")
45
+ end
46
+
47
+ def validate_engines(key, legacy: false)
48
+ return unless validate_key_type(key, Hash)
49
+
50
+ data.fetch(key, {}).each do |engine_name, engine_data|
51
+ engine_validator = EngineValidator.new(engine_data, legacy: legacy)
52
+ denormalize_subvalidator(engine_validator, "engine #{engine_name}")
53
+
54
+ if engine_validator.valid?
55
+ validate_engine_existence(engine_name, engine_data)
56
+ end
57
+ end
58
+ end
59
+
60
+ def validate_engine_existence(engine_name, engine_data)
61
+ if [true, false].include?(engine_data)
62
+ engine_data = {
63
+ "enabled" => true,
64
+ "channel" => Engine::DEFAULT_CHANNEL,
65
+ }
66
+ end
67
+
68
+ engine = Engine.new(
69
+ engine_name,
70
+ enabled: engine_data.fetch("enabled", true),
71
+ channel: engine_data["channel"],
72
+ config: engine_data["config"],
73
+ )
74
+ unless engine_exists?(engine)
75
+ warnings << "unknown engine or channel <#{engine.name}:#{engine.channel}>"
76
+ end
77
+ end
78
+
79
+ def engine_exists?(engine)
80
+ !registry.fetch_engine_details(engine).nil?
81
+ rescue CC::EngineRegistry::EngineDetailsNotFoundError
82
+ false
83
+ end
84
+
85
+ def validate_checks
86
+ return unless validate_key_type("checks", Hash)
87
+
88
+ data.fetch("checks", {}).each do |check_name, check_data|
89
+ validator = CheckValidator.new(check_data)
90
+ denormalize_subvalidator(validator, "check #{check_name}")
91
+ end
92
+ end
93
+
94
+ def validate_exclude_pattern(key, legacy: false)
95
+ types =
96
+ if legacy
97
+ [Array, String]
98
+ else
99
+ Array
100
+ end
101
+ return unless validate_key_type(key, types)
102
+
103
+ Array(data.fetch(key, [])).each do |pattern|
104
+ unless pattern.is_a?(String)
105
+ errors << "each exclude pattern should be a string, but '#{pattern.inspect}' is a #{pattern.class.to_s.downcase}"
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,52 @@
1
+ module CC
2
+ class Config
3
+ module Validation
4
+ module HashValidations
5
+ def validate_hash_data
6
+ unless data.is_a?(Hash)
7
+ errors << "Config file should contain a hash, not a #{data.class.to_s.downcase}"
8
+ return false
9
+ end
10
+ true
11
+ end
12
+
13
+ def validate_key_type(key, types)
14
+ if types.is_a?(Class)
15
+ return validate_key_type(key, [types])
16
+ elsif data.key?(key)
17
+ unless types.include?(data[key].class)
18
+ errors << key_type_error_message(key, types)
19
+ return false
20
+ end
21
+ end
22
+ true
23
+ end
24
+
25
+ def key_type_error_message(key, types)
26
+ if types.one?
27
+ klass_name = types[0].to_s.downcase
28
+ article =
29
+ if klass_name[0] == "a"
30
+ "an"
31
+ else
32
+ "a"
33
+ end
34
+ "'#{key}' must be #{article} #{klass_name}"
35
+ elsif types == [TrueClass, FalseClass]
36
+ "'#{key}' must be a boolean"
37
+ else
38
+ type_names = types.map(&:to_s).map(&:downcase)
39
+ "'#{key}' must be one of #{type_names.join(", ")}"
40
+ end
41
+ end
42
+
43
+ def warn_unrecognized_keys(recognized_keys)
44
+ unknown_keys = data.keys.reject { |k| recognized_keys.include?(k) }
45
+ unknown_keys.each do |key|
46
+ warnings << "unrecognized key '#{key}'"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end