codeclimate-fede 0.85.23
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 +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
data/lib/cc/analyzer.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
|
|
3
|
+
module CC
|
|
4
|
+
module Analyzer
|
|
5
|
+
autoload :Bridge, "cc/analyzer/bridge"
|
|
6
|
+
autoload :CompositeContainerListener, "cc/analyzer/composite_container_listener"
|
|
7
|
+
autoload :Container, "cc/analyzer/container"
|
|
8
|
+
autoload :ContainerListener, "cc/analyzer/container_listener"
|
|
9
|
+
autoload :Engine, "cc/analyzer/engine"
|
|
10
|
+
autoload :EngineOutput, "cc/analyzer/engine_output"
|
|
11
|
+
autoload :EngineOutputFilter, "cc/analyzer/engine_output_filter"
|
|
12
|
+
autoload :EngineOutputOverrider, "cc/analyzer/engine_output_overrider"
|
|
13
|
+
autoload :Filesystem, "cc/analyzer/filesystem"
|
|
14
|
+
autoload :Formatters, "cc/analyzer/formatters"
|
|
15
|
+
autoload :Issue, "cc/analyzer/issue"
|
|
16
|
+
autoload :IssueSorter, "cc/analyzer/issue_sorter"
|
|
17
|
+
autoload :IssueValidations, "cc/analyzer/issue_validations"
|
|
18
|
+
autoload :IssueValidator, "cc/analyzer/issue_validator"
|
|
19
|
+
autoload :LocationDescription, "cc/analyzer/location_description"
|
|
20
|
+
autoload :LoggingContainerListener, "cc/analyzer/logging_container_listener"
|
|
21
|
+
autoload :Measurement, "cc/analyzer/measurement"
|
|
22
|
+
autoload :MeasurementValidations, "cc/analyzer/measurement_validations"
|
|
23
|
+
autoload :MeasurementValidator, "cc/analyzer/measurement_validator"
|
|
24
|
+
autoload :MountedPath, "cc/analyzer/mounted_path"
|
|
25
|
+
autoload :RaisingContainerListener, "cc/analyzer/raising_container_listener"
|
|
26
|
+
autoload :SourceBuffer, "cc/analyzer/source_buffer"
|
|
27
|
+
autoload :SourceExtractor, "cc/analyzer/source_extractor"
|
|
28
|
+
autoload :SourceFingerprint, "cc/analyzer/source_fingerprint"
|
|
29
|
+
autoload :StatsdContainerListener, "cc/analyzer/statsd_container_listener"
|
|
30
|
+
autoload :Validator, "cc/analyzer/validator"
|
|
31
|
+
|
|
32
|
+
class DummyStatsd
|
|
33
|
+
def method_missing(*)
|
|
34
|
+
yield if block_given?
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class DummyLogger
|
|
39
|
+
def method_missing(*)
|
|
40
|
+
yield if block_given?
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
cattr_accessor :statsd, :logger
|
|
45
|
+
self.statsd = DummyStatsd.new
|
|
46
|
+
self.logger = DummyLogger.new
|
|
47
|
+
|
|
48
|
+
UnreadableFileError = Class.new(StandardError)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
module Analyzer
|
|
3
|
+
# The shared interface, invoked by Builder or CLI::Analyze
|
|
4
|
+
#
|
|
5
|
+
# Input:
|
|
6
|
+
# - config
|
|
7
|
+
# - engines
|
|
8
|
+
# - exclude_patterns
|
|
9
|
+
# - development?
|
|
10
|
+
# - analysis_paths
|
|
11
|
+
# - formatter
|
|
12
|
+
# - started
|
|
13
|
+
# - engine_running
|
|
14
|
+
# - finished
|
|
15
|
+
# - close
|
|
16
|
+
# - listener
|
|
17
|
+
# - started(engine, details)
|
|
18
|
+
# - finished(engine, details, result)
|
|
19
|
+
# - registry
|
|
20
|
+
#
|
|
21
|
+
# Only raises if Listener raises
|
|
22
|
+
#
|
|
23
|
+
class Bridge
|
|
24
|
+
def initialize(config:, formatter:, listener:, registry:)
|
|
25
|
+
@config = config
|
|
26
|
+
@formatter = formatter
|
|
27
|
+
@listener = listener
|
|
28
|
+
@registry = registry
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def run
|
|
32
|
+
formatter.started
|
|
33
|
+
|
|
34
|
+
config.engines.each do |engine|
|
|
35
|
+
next unless engine.enabled?
|
|
36
|
+
|
|
37
|
+
formatter.engine_running(engine) do
|
|
38
|
+
result = nil
|
|
39
|
+
engine_details = nil
|
|
40
|
+
|
|
41
|
+
begin
|
|
42
|
+
engine_details = registry.fetch_engine_details(
|
|
43
|
+
engine,
|
|
44
|
+
development: config.development?,
|
|
45
|
+
)
|
|
46
|
+
listener.started(engine, engine_details)
|
|
47
|
+
result = run_engine(engine, engine_details)
|
|
48
|
+
rescue CC::EngineRegistry::EngineDetailsNotFoundError => ex
|
|
49
|
+
result = Container::Result.skipped(ex)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
listener.finished(engine, engine_details, result)
|
|
53
|
+
result
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
formatter.finished
|
|
58
|
+
ensure
|
|
59
|
+
formatter.close
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
attr_reader :config, :formatter, :listener, :registry
|
|
65
|
+
|
|
66
|
+
def run_engine(engine, engine_details)
|
|
67
|
+
# Analyzer::Engine doesn't have the best interface, but we're limiting
|
|
68
|
+
# our refactors for now.
|
|
69
|
+
Engine.new(
|
|
70
|
+
engine.name,
|
|
71
|
+
{
|
|
72
|
+
"image" => engine_details.image,
|
|
73
|
+
"command" => engine_details.command,
|
|
74
|
+
"memory" => engine_details.memory,
|
|
75
|
+
},
|
|
76
|
+
engine.config.merge(
|
|
77
|
+
"channel" => engine.channel,
|
|
78
|
+
"include_paths" => engine_workspace(engine).paths,
|
|
79
|
+
),
|
|
80
|
+
engine.container_label,
|
|
81
|
+
).run(formatter)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def engine_workspace(engine)
|
|
85
|
+
if engine.exclude_patterns.any?
|
|
86
|
+
workspace.clone.tap do |engine_workspace|
|
|
87
|
+
engine_workspace.remove(engine.exclude_patterns)
|
|
88
|
+
end
|
|
89
|
+
else
|
|
90
|
+
workspace
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def workspace
|
|
95
|
+
@workspace ||= Workspace.new.tap do |workspace|
|
|
96
|
+
workspace.add(config.analysis_paths)
|
|
97
|
+
|
|
98
|
+
unless config.analysis_paths.any?
|
|
99
|
+
workspace.remove([".git"])
|
|
100
|
+
workspace.remove(config.exclude_patterns)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
module Analyzer
|
|
3
|
+
class CompositeContainerListener < ContainerListener
|
|
4
|
+
def initialize(*listeners)
|
|
5
|
+
@listeners = listeners
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def started(*args)
|
|
9
|
+
listeners.each { |listener| listener.started(*args) }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def finished(*args)
|
|
13
|
+
listeners.each { |listener| listener.finished(*args) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
attr_reader :listeners
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
require "posix/spawn"
|
|
2
|
+
require "thread"
|
|
3
|
+
|
|
4
|
+
require "cc/analyzer/container/result"
|
|
5
|
+
|
|
6
|
+
module CC
|
|
7
|
+
module Analyzer
|
|
8
|
+
#
|
|
9
|
+
# Running an abstract docker container
|
|
10
|
+
#
|
|
11
|
+
# Input:
|
|
12
|
+
# - image
|
|
13
|
+
# - name
|
|
14
|
+
# - command (Optional)
|
|
15
|
+
#
|
|
16
|
+
# Output:
|
|
17
|
+
# - Result
|
|
18
|
+
# - exit_status
|
|
19
|
+
# - timed_out?
|
|
20
|
+
# - duration
|
|
21
|
+
# - maximum_output_exceeded?
|
|
22
|
+
# - output_byte_count
|
|
23
|
+
# - stderr
|
|
24
|
+
#
|
|
25
|
+
# Never raises (unless broken)
|
|
26
|
+
#
|
|
27
|
+
class Container
|
|
28
|
+
DEFAULT_TIMEOUT = 15 * 60 # 15m
|
|
29
|
+
DEFAULT_MAXIMUM_OUTPUT_BYTES = 500_000_000
|
|
30
|
+
|
|
31
|
+
def initialize(image:, name:, command: nil)
|
|
32
|
+
@image = image
|
|
33
|
+
@name = name
|
|
34
|
+
@command = command
|
|
35
|
+
@timed_out = false
|
|
36
|
+
@maximum_output_exceeded = false
|
|
37
|
+
@stdout_io = StringIO.new
|
|
38
|
+
@stderr_io = StringIO.new
|
|
39
|
+
@output_byte_count = 0
|
|
40
|
+
@counter_mutex = Mutex.new
|
|
41
|
+
|
|
42
|
+
# By default accumulate and include stdout in result
|
|
43
|
+
@output_delimeter = "\n"
|
|
44
|
+
@on_output = ->(output) { @stdout_io.puts(output) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def on_output(delimeter = "\n", &block)
|
|
48
|
+
@output_delimeter = delimeter
|
|
49
|
+
@on_output = block
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def run(options = [])
|
|
53
|
+
started = Time.now
|
|
54
|
+
|
|
55
|
+
command = docker_run_command(options)
|
|
56
|
+
Analyzer.logger.debug("docker run: #{command.inspect}")
|
|
57
|
+
pid, _, out, err = POSIX::Spawn.popen4(*command)
|
|
58
|
+
|
|
59
|
+
@t_out = read_stdout(out)
|
|
60
|
+
@t_err = read_stderr(err)
|
|
61
|
+
t_timeout = timeout_thread
|
|
62
|
+
|
|
63
|
+
# blocks until the engine stops. this is put in a thread so that we can
|
|
64
|
+
# explicitly abort it as part of #stop. otherwise a run-away container
|
|
65
|
+
# could still block here forever if the docker-kill/wait is not
|
|
66
|
+
# successful. there may still be stdout in flight if it was being
|
|
67
|
+
# produced more quickly than consumed.
|
|
68
|
+
@t_wait = Thread.new { _, @status = Process.waitpid2(pid) }
|
|
69
|
+
@t_wait.join
|
|
70
|
+
|
|
71
|
+
# blocks until all readers are done. they're still governed by the
|
|
72
|
+
# timeout thread at this point. if we hit the timeout while processing
|
|
73
|
+
# output, the threads will be Thread#killed as part of #stop and this
|
|
74
|
+
# will unblock with the correct value in @timed_out
|
|
75
|
+
[@t_out, @t_err].each(&:join)
|
|
76
|
+
|
|
77
|
+
duration =
|
|
78
|
+
if @timed_out
|
|
79
|
+
timeout * 1000
|
|
80
|
+
else
|
|
81
|
+
((Time.now - started) * 1000).round
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
Result.new(
|
|
85
|
+
container_name: @name,
|
|
86
|
+
duration: duration,
|
|
87
|
+
exit_status: @status&.exitstatus,
|
|
88
|
+
maximum_output_exceeded: @maximum_output_exceeded,
|
|
89
|
+
output_byte_count: output_byte_count,
|
|
90
|
+
stderr: @stderr_io.string,
|
|
91
|
+
stdout: @stdout_io.string,
|
|
92
|
+
timed_out: @timed_out,
|
|
93
|
+
)
|
|
94
|
+
ensure
|
|
95
|
+
kill_reader_threads
|
|
96
|
+
t_timeout&.kill
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def stop(message = nil)
|
|
100
|
+
reap_running_container(message)
|
|
101
|
+
kill_reader_threads
|
|
102
|
+
kill_wait_thread
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
attr_reader :output_byte_count, :counter_mutex
|
|
108
|
+
|
|
109
|
+
def docker_run_command(options)
|
|
110
|
+
[
|
|
111
|
+
"docker", "run",
|
|
112
|
+
"--name", @name,
|
|
113
|
+
options,
|
|
114
|
+
@image,
|
|
115
|
+
@command
|
|
116
|
+
].flatten.compact
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def read_stdout(out)
|
|
120
|
+
Thread.new do
|
|
121
|
+
out.each_line(@output_delimeter) do |chunk|
|
|
122
|
+
output = chunk.chomp(@output_delimeter)
|
|
123
|
+
|
|
124
|
+
Analyzer.logger.debug("engine stdout: #{output}")
|
|
125
|
+
@on_output.call(output)
|
|
126
|
+
check_output_bytes(output.bytesize)
|
|
127
|
+
end
|
|
128
|
+
ensure
|
|
129
|
+
out.close
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def read_stderr(err)
|
|
134
|
+
Thread.new do
|
|
135
|
+
err.each_line do |line|
|
|
136
|
+
Analyzer.logger.debug("engine stderr: #{line.chomp}")
|
|
137
|
+
@stderr_io.write(line)
|
|
138
|
+
check_output_bytes(line.bytesize)
|
|
139
|
+
end
|
|
140
|
+
ensure
|
|
141
|
+
err.close
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def timeout_thread
|
|
146
|
+
Thread.new do
|
|
147
|
+
# Doing one long `sleep timeout` seems to fail sometimes, so
|
|
148
|
+
# we do a series of short timeouts before exiting
|
|
149
|
+
start_time = Time.now
|
|
150
|
+
loop do
|
|
151
|
+
sleep 10
|
|
152
|
+
duration = Time.now - start_time
|
|
153
|
+
break if duration >= timeout
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
@timed_out = true
|
|
157
|
+
stop("timed out")
|
|
158
|
+
end.run
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def check_output_bytes(last_read_byte_count)
|
|
162
|
+
counter_mutex.synchronize do
|
|
163
|
+
@output_byte_count += last_read_byte_count
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
if output_byte_count > maximum_output_bytes
|
|
167
|
+
@maximum_output_exceeded = true
|
|
168
|
+
stop("maximum output exceeded")
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def kill_reader_threads
|
|
173
|
+
@t_out&.kill
|
|
174
|
+
@t_err&.kill
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def kill_wait_thread
|
|
178
|
+
@t_wait&.kill
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def reap_running_container(message)
|
|
182
|
+
Analyzer.logger.warn("killing container name=#{@name} message=#{message.inspect}")
|
|
183
|
+
POSIX::Spawn::Child.new("docker", "kill", @name, timeout: 2.minutes)
|
|
184
|
+
POSIX::Spawn::Child.new("docker", "wait", @name, timeout: 2.minutes)
|
|
185
|
+
rescue POSIX::Spawn::TimeoutExceeded
|
|
186
|
+
Analyzer.logger.error("unable to kill container name=#{@name} message=#{message.inspect}")
|
|
187
|
+
Analyzer.statsd.increment("container.zombie")
|
|
188
|
+
Analyzer.statsd.increment("container.zombie.#{metric_name}") if metric_name
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def timeout
|
|
192
|
+
ENV.fetch("CONTAINER_TIMEOUT_SECONDS", DEFAULT_TIMEOUT).to_i
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def maximum_output_bytes
|
|
196
|
+
ENV.fetch("CONTAINER_MAXIMUM_OUTPUT_BYTES", DEFAULT_MAXIMUM_OUTPUT_BYTES).to_i
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def metric_name
|
|
200
|
+
if /^cc-engines-(?<engine>[^-]+)-(?<channel>[^-]+)-/ =~ @name
|
|
201
|
+
"engine.#{engine}.#{channel}"
|
|
202
|
+
elsif /^builder-(?<action>[^-]+)-/ =~ @name
|
|
203
|
+
"builder.#{action}"
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
module Analyzer
|
|
3
|
+
class Container
|
|
4
|
+
class Result
|
|
5
|
+
attr_reader \
|
|
6
|
+
:container_name,
|
|
7
|
+
:duration,
|
|
8
|
+
:exit_status,
|
|
9
|
+
:output_byte_count,
|
|
10
|
+
:stderr,
|
|
11
|
+
:stdout
|
|
12
|
+
|
|
13
|
+
def initialize(
|
|
14
|
+
container_name: "",
|
|
15
|
+
duration: 0,
|
|
16
|
+
exit_status: 0,
|
|
17
|
+
maximum_output_exceeded: false,
|
|
18
|
+
output_byte_count: 0,
|
|
19
|
+
skipped: false,
|
|
20
|
+
stderr: "",
|
|
21
|
+
stdout: "",
|
|
22
|
+
timed_out: false
|
|
23
|
+
)
|
|
24
|
+
@container_name = container_name
|
|
25
|
+
@duration = duration
|
|
26
|
+
@exit_status = exit_status
|
|
27
|
+
@maximum_output_exceeded = maximum_output_exceeded
|
|
28
|
+
@output_byte_count = output_byte_count
|
|
29
|
+
@skipped = skipped
|
|
30
|
+
@stderr = stderr
|
|
31
|
+
@stdout = stdout
|
|
32
|
+
@timed_out = timed_out
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.skipped(ex)
|
|
36
|
+
new(
|
|
37
|
+
exit_status: 0,
|
|
38
|
+
skipped: true,
|
|
39
|
+
stderr: ex.message,
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def merge_from_exception(ex)
|
|
44
|
+
self.exit_status = 99
|
|
45
|
+
self.stderr = ex.message
|
|
46
|
+
self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def timed_out?
|
|
50
|
+
@timed_out
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def maximum_output_exceeded?
|
|
54
|
+
@maximum_output_exceeded
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def errored?
|
|
58
|
+
timed_out? ||
|
|
59
|
+
maximum_output_exceeded? ||
|
|
60
|
+
exit_status.nil? ||
|
|
61
|
+
exit_status.nonzero?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def skipped?
|
|
65
|
+
@skipped
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
attr_writer :exit_status, :stderr
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|