codeclimate 0.69.0 → 0.70.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|