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,21 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module MeasurementValidations
|
4
|
+
class ValueValidation < Validation
|
5
|
+
def valid?
|
6
|
+
value&.is_a?(Numeric)
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
"Value must be present and numeric"
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def value
|
16
|
+
object["value"]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
class MountedPath
|
4
|
+
DEFAULT_CODECLIMATE_TMP = "/tmp/cc".freeze
|
5
|
+
|
6
|
+
def self.code
|
7
|
+
host_prefix = ENV["CODECLIMATE_CODE"]
|
8
|
+
host_prefix ||= ENV["CODE_PATH"] # deprecated
|
9
|
+
|
10
|
+
if ENV["CODECLIMATE_DOCKER"]
|
11
|
+
new(host_prefix, "/code")
|
12
|
+
else
|
13
|
+
host_prefix ||= Dir.pwd
|
14
|
+
|
15
|
+
new(host_prefix, host_prefix)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.tmp
|
20
|
+
host_prefix = ENV["CODECLIMATE_TMP"]
|
21
|
+
host_prefix ||= DEFAULT_CODECLIMATE_TMP
|
22
|
+
|
23
|
+
if ENV["CODECLIMATE_DOCKER"]
|
24
|
+
new(host_prefix, "/tmp/cc")
|
25
|
+
else
|
26
|
+
new(host_prefix, host_prefix)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(host_prefix, container_prefix, path = nil)
|
31
|
+
@host_prefix = host_prefix
|
32
|
+
@container_prefix = container_prefix
|
33
|
+
@path = path
|
34
|
+
end
|
35
|
+
|
36
|
+
def host_path
|
37
|
+
if path
|
38
|
+
File.join(host_prefix, path)
|
39
|
+
else
|
40
|
+
host_prefix
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def container_path
|
45
|
+
if path
|
46
|
+
File.join(container_prefix, path)
|
47
|
+
else
|
48
|
+
container_prefix
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def join(path)
|
53
|
+
@path = path
|
54
|
+
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def file?
|
59
|
+
File.file?(container_path)
|
60
|
+
end
|
61
|
+
|
62
|
+
def read
|
63
|
+
File.read(container_path)
|
64
|
+
end
|
65
|
+
|
66
|
+
def write(content)
|
67
|
+
FileUtils.mkdir_p(File.dirname(container_path))
|
68
|
+
File.write(container_path, content)
|
69
|
+
end
|
70
|
+
|
71
|
+
def delete
|
72
|
+
File.delete(container_path)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
attr_reader :host_prefix, :container_prefix, :path
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
class RaisingContainerListener < ContainerListener
|
4
|
+
def initialize(failure_ex, timeout_ex = nil, maximum_output_ex = nil)
|
5
|
+
@failure_ex = failure_ex
|
6
|
+
@timeout_ex = timeout_ex || failure_ex
|
7
|
+
@maximum_output_ex = maximum_output_ex || failure_ex
|
8
|
+
end
|
9
|
+
|
10
|
+
def finished(engine, _details, result)
|
11
|
+
if result.timed_out?
|
12
|
+
message = "engine #{engine.name} ran for #{result.duration / 1000}"
|
13
|
+
message << " seconds and was killed"
|
14
|
+
raise timeout_ex.new(message, engine.name)
|
15
|
+
elsif result.maximum_output_exceeded?
|
16
|
+
message = "engine #{engine.name} produced too much output"
|
17
|
+
message << " (#{result.output_byte_count} bytes)"
|
18
|
+
raise maximum_output_ex.new(message, engine.name)
|
19
|
+
elsif result.exit_status.nonzero?
|
20
|
+
message = "engine #{engine.name} failed"
|
21
|
+
message << " with status #{result.exit_status}"
|
22
|
+
message << " and stderr \n#{result.stderr}"
|
23
|
+
raise failure_ex.new(message, engine.name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :failure_ex, :timeout_ex, :maximum_output_ex
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Adapted from https://github.com/whitequark/parser/blob/master/lib/parser/source/buffer.rb
|
2
|
+
module CC
|
3
|
+
module Analyzer
|
4
|
+
class SourceBuffer
|
5
|
+
attr_reader :name
|
6
|
+
attr_reader :source
|
7
|
+
|
8
|
+
def initialize(name, source)
|
9
|
+
@name = name
|
10
|
+
@source = source
|
11
|
+
end
|
12
|
+
|
13
|
+
def decompose_position(position)
|
14
|
+
line_no, line_begin = line_for(position)
|
15
|
+
|
16
|
+
[1 + line_no, position - line_begin]
|
17
|
+
end
|
18
|
+
|
19
|
+
def line_count
|
20
|
+
@source.lines.count
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def line_for(position)
|
26
|
+
line_begins.bsearch do |_, line_begin|
|
27
|
+
line_begin <= position
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def line_begins
|
32
|
+
unless @line_begins
|
33
|
+
@line_begins = [[0, 0]]
|
34
|
+
index = 1
|
35
|
+
|
36
|
+
@source.each_char do |char|
|
37
|
+
@line_begins.unshift [@line_begins.length, index] if char == "\n"
|
38
|
+
|
39
|
+
index += 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
@line_begins
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
class SourceExtractor
|
4
|
+
InvalidLocation = Class.new(StandardError)
|
5
|
+
|
6
|
+
def initialize(source)
|
7
|
+
@source = source
|
8
|
+
end
|
9
|
+
|
10
|
+
def extract(location)
|
11
|
+
validate_location(location)
|
12
|
+
|
13
|
+
if (lines = location["lines"])
|
14
|
+
extract_from_lines(lines)
|
15
|
+
elsif (positions = location["positions"])
|
16
|
+
extract_from_positions(positions)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :source
|
23
|
+
|
24
|
+
def validate_location(location)
|
25
|
+
validator = IssueValidations::LocationFormatValidation::Validator.new(location)
|
26
|
+
unless validator.valid?
|
27
|
+
raise InvalidLocation, validator.message
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def extract_from_lines(lines)
|
32
|
+
begin_index = lines.fetch("begin") - 1
|
33
|
+
end_index = lines.fetch("end") - 1
|
34
|
+
range = (begin_index..end_index)
|
35
|
+
|
36
|
+
source.each_line.with_object("").with_index do |(source_line, memo), index|
|
37
|
+
memo << source_line if range.include?(index)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def extract_from_positions(positions)
|
42
|
+
positions = convert_to_offsets(positions)
|
43
|
+
begin_offset = positions.fetch("begin").fetch("offset")
|
44
|
+
end_offset = positions.fetch("end").fetch("offset")
|
45
|
+
length = end_offset - begin_offset
|
46
|
+
|
47
|
+
source[begin_offset, length + 1]
|
48
|
+
end
|
49
|
+
|
50
|
+
def convert_to_offsets(positions)
|
51
|
+
positions.each_with_object({}) do |(key, value), memo|
|
52
|
+
memo[key] =
|
53
|
+
if value.key?("offset")
|
54
|
+
value
|
55
|
+
else
|
56
|
+
{
|
57
|
+
"offset" => to_offset(value["line"] - 1, value["column"] - 1),
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_offset(line, column, offset = 0)
|
64
|
+
source.each_line.with_index do |source_line, index|
|
65
|
+
offset +=
|
66
|
+
if line == index
|
67
|
+
column
|
68
|
+
else
|
69
|
+
source_line.length
|
70
|
+
end
|
71
|
+
|
72
|
+
break if index >= line
|
73
|
+
end
|
74
|
+
|
75
|
+
offset
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "digest/md5"
|
2
|
+
|
3
|
+
module CC
|
4
|
+
module Analyzer
|
5
|
+
class SourceFingerprint
|
6
|
+
def initialize(issue)
|
7
|
+
@issue = issue
|
8
|
+
end
|
9
|
+
|
10
|
+
def compute
|
11
|
+
md5 = Digest::MD5.new
|
12
|
+
md5 << issue.path
|
13
|
+
md5 << issue.check_name.to_s
|
14
|
+
md5 << relevant_source.gsub(/\s+/, "") if relevant_source
|
15
|
+
md5.hexdigest
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :issue
|
21
|
+
|
22
|
+
def relevant_source
|
23
|
+
source = SourceExtractor.new(raw_source).extract(issue.location)
|
24
|
+
|
25
|
+
if source && !source.empty?
|
26
|
+
source.encode(Encoding::UTF_8, "binary", invalid: :replace, undef: :replace, replace: "")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def raw_source
|
31
|
+
@raw_source ||=
|
32
|
+
if File.file?(issue.path)
|
33
|
+
File.read(issue.path)
|
34
|
+
else
|
35
|
+
""
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
class StatsdContainerListener < ContainerListener
|
4
|
+
def initialize(statsd)
|
5
|
+
@statsd = statsd
|
6
|
+
end
|
7
|
+
|
8
|
+
def started(engine, _details)
|
9
|
+
increment(engine, "started")
|
10
|
+
end
|
11
|
+
|
12
|
+
def finished(engine, _details, result)
|
13
|
+
timing(engine, "time", result.duration)
|
14
|
+
increment(engine, "finished")
|
15
|
+
|
16
|
+
if result.timed_out?
|
17
|
+
timing(engine, "time", result.duration)
|
18
|
+
increment(engine, "result.error")
|
19
|
+
increment(engine, "result.error.timeout")
|
20
|
+
elsif result.maximum_output_exceeded?
|
21
|
+
increment(engine, "result.error")
|
22
|
+
increment(engine, "result.error.output_exceeded")
|
23
|
+
elsif result.exit_status.nonzero?
|
24
|
+
increment(engine, "result.error")
|
25
|
+
else
|
26
|
+
increment(engine, "result.success")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :statsd
|
33
|
+
|
34
|
+
def increment(engine, metric)
|
35
|
+
statsd.increment("engines.#{metric}")
|
36
|
+
statsd.increment("engines.names.#{engine.name}.#{metric}")
|
37
|
+
if engine.respond_to?(:channel) && engine.channel
|
38
|
+
statsd.increment("engines.names.#{engine.name}.#{engine.channel}.#{metric}")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def timing(engine, metric, ms)
|
43
|
+
statsd.timing("engines.#{metric}", ms)
|
44
|
+
statsd.timing("engines.names.#{engine.name}.#{metric}", ms)
|
45
|
+
if engine.respond_to?(:channel) && engine.channel
|
46
|
+
statsd.timing("engines.names.#{engine.name}.#{engine.channel}.#{metric}", ms)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module Validator
|
4
|
+
attr_reader :error
|
5
|
+
|
6
|
+
def initialize(document)
|
7
|
+
@document = document
|
8
|
+
validate
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate
|
12
|
+
return @valid unless @valid.nil?
|
13
|
+
|
14
|
+
if document && invalid_messages.any?
|
15
|
+
@error = {
|
16
|
+
message: "#{invalid_messages.join("; ")}: `#{document}`.",
|
17
|
+
document: document,
|
18
|
+
}
|
19
|
+
@valid = false
|
20
|
+
else
|
21
|
+
@valid = true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
alias valid? validate
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :document
|
29
|
+
|
30
|
+
def invalid_messages
|
31
|
+
@invalid_messages ||= self.class.validations.each_with_object([]) do |check, result|
|
32
|
+
validator = check.new(document)
|
33
|
+
result << validator.message unless validator.valid?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/cc/cli.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext"
|
3
|
+
require "yaml"
|
4
|
+
require "cc/analyzer"
|
5
|
+
require "cc/config"
|
6
|
+
require "cc/engine_registry"
|
7
|
+
require "cc/workspace"
|
8
|
+
|
9
|
+
require "cc/cli/analyze"
|
10
|
+
require "cc/cli/command"
|
11
|
+
require "cc/cli/console"
|
12
|
+
require "cc/cli/engines"
|
13
|
+
require "cc/cli/help"
|
14
|
+
require "cc/cli/output"
|
15
|
+
require "cc/cli/prepare"
|
16
|
+
require "cc/cli/runner"
|
17
|
+
require "cc/cli/validate_config"
|
18
|
+
require "cc/cli/version"
|
19
|
+
|
20
|
+
module CC
|
21
|
+
module CLI
|
22
|
+
def self.debug?
|
23
|
+
ENV["CODECLIMATE_DEBUG"].present?
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.logger
|
27
|
+
@logger ||= ::Logger.new(STDERR).tap do |logger|
|
28
|
+
logger.level =
|
29
|
+
if debug?
|
30
|
+
::Logger::DEBUG
|
31
|
+
else
|
32
|
+
::Logger::ERROR
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Analyzer.logger = CLI.logger
|
39
|
+
end
|