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.
- checksums.yaml +4 -4
- data/bin/prep-release +1 -1
- data/config/engines.yml +32 -323
- data/lib/cc/analyzer.rb +5 -4
- data/lib/cc/analyzer/bridge.rb +106 -0
- data/lib/cc/analyzer/composite_container_listener.rb +4 -8
- data/lib/cc/analyzer/container.rb +44 -41
- data/lib/cc/analyzer/container/result.rb +74 -0
- data/lib/cc/analyzer/container_listener.rb +2 -7
- data/lib/cc/analyzer/engine.rb +53 -45
- data/lib/cc/analyzer/engine_output.rb +40 -10
- data/lib/cc/analyzer/formatters/formatter.rb +2 -0
- data/lib/cc/analyzer/formatters/html_formatter.rb +4 -0
- data/lib/cc/analyzer/formatters/json_formatter.rb +1 -0
- data/lib/cc/analyzer/formatters/plain_text_formatter.rb +8 -1
- data/lib/cc/analyzer/issue.rb +4 -2
- data/lib/cc/analyzer/issue_validations/relative_path_validation.rb +6 -2
- data/lib/cc/analyzer/issue_validator.rb +3 -32
- data/lib/cc/analyzer/logging_container_listener.rb +9 -7
- 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/raising_container_listener.rb +18 -18
- data/lib/cc/analyzer/statsd_container_listener.rb +22 -22
- data/lib/cc/analyzer/validator.rb +38 -0
- data/lib/cc/cli.rb +12 -12
- data/lib/cc/cli/analyze.rb +42 -60
- data/lib/cc/cli/analyze/engine_failure.rb +11 -0
- data/lib/cc/cli/command.rb +0 -10
- data/lib/cc/cli/engines.rb +0 -3
- data/lib/cc/cli/engines/engine_command.rb +2 -34
- data/lib/cc/cli/engines/install.rb +11 -17
- data/lib/cc/cli/engines/list.rb +5 -3
- data/lib/cc/cli/prepare.rb +5 -11
- data/lib/cc/cli/runner.rb +1 -2
- data/lib/cc/cli/test.rb +0 -1
- data/lib/cc/cli/validate_config.rb +49 -63
- data/lib/cc/cli/version_checker.rb +3 -3
- data/lib/cc/config.rb +70 -0
- data/lib/cc/config/checks_adapter.rb +40 -0
- data/lib/cc/config/default_adapter.rb +52 -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 +89 -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/workspace/path_tree/dir_node.rb +1 -1
- metadata +36 -55
- data/bin/codeclimate-init +0 -6
- data/config/coffeelint/coffeelint.json +0 -129
- data/config/csslint/.csslintrc +0 -2
- data/config/eslint/.eslintignore +0 -1
- data/config/eslint/.eslintrc.yml +0 -277
- data/config/rubocop/.rubocop.yml +0 -1156
- data/lib/cc/analyzer/config.rb +0 -86
- data/lib/cc/analyzer/engine_registry.rb +0 -36
- data/lib/cc/analyzer/engines_config_builder.rb +0 -97
- data/lib/cc/analyzer/engines_runner.rb +0 -64
- data/lib/cc/cli/config.rb +0 -44
- data/lib/cc/cli/config_generator.rb +0 -108
- data/lib/cc/cli/engines/disable.rb +0 -38
- data/lib/cc/cli/engines/enable.rb +0 -41
- data/lib/cc/cli/engines/remove.rb +0 -35
- data/lib/cc/cli/init.rb +0 -117
- data/lib/cc/cli/prepare/quality.rb +0 -64
- 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
|