codeclimate 0.2.7 → 0.2.9
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/lib/cc/analyzer.rb +2 -0
- data/lib/cc/analyzer/container.rb +98 -0
- data/lib/cc/analyzer/engine.rb +20 -81
- data/lib/cc/analyzer/engine/container_log.rb +48 -0
- data/lib/cc/analyzer/engines_runner.rb +16 -5
- data/lib/cc/analyzer/null_container_log.rb +14 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5b6caa0a6dc7642c6e5abb6533c8c24c7bd49f4
|
4
|
+
data.tar.gz: 98bb65ce704a03ba8ed98a4ff697d2a424ef0fd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17ecabe1ace73fbd5f37c83a78b2bd1e5a683aa1b518684a33ebd8885246c8bb289f804c692d63bd443b35ef1f0f576cfbc382691c6c89368516c8aa8a6edc1c
|
7
|
+
data.tar.gz: f62c7be59d492698e22496c265b4f6c3c9993100ec861bce94227f9e4ffceda8bc8809ba2c75be9fae94a3d7d355c5b167ba30119e03ee42835d0e80b77e6906
|
data/lib/cc/analyzer.rb
CHANGED
@@ -2,6 +2,7 @@ module CC
|
|
2
2
|
module Analyzer
|
3
3
|
autoload :Accumulator, "cc/analyzer/accumulator"
|
4
4
|
autoload :Config, "cc/analyzer/config"
|
5
|
+
autoload :Container, "cc/analyzer/container"
|
5
6
|
autoload :Engine, "cc/analyzer/engine"
|
6
7
|
autoload :EngineClient, "cc/analyzer/engine_client"
|
7
8
|
autoload :EngineOutputFilter, "cc/analyzer/engine_output_filter"
|
@@ -12,6 +13,7 @@ module CC
|
|
12
13
|
autoload :IssueSorter, "cc/analyzer/issue_sorter"
|
13
14
|
autoload :LocationDescription,"cc/analyzer/location_description"
|
14
15
|
autoload :NullConfig, "cc/analyzer/null_config"
|
16
|
+
autoload :NullContainerLog, "cc/analyzer/null_container_log"
|
15
17
|
autoload :PathPatterns, "cc/analyzer/path_patterns"
|
16
18
|
autoload :SourceBuffer, "cc/analyzer/source_buffer"
|
17
19
|
autoload :UnitName, "cc/analyzer/unit_name"
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require "posix/spawn"
|
2
|
+
|
3
|
+
module CC
|
4
|
+
module Analyzer
|
5
|
+
class Container
|
6
|
+
DEFAULT_TIMEOUT = 15 * 60 # 15m
|
7
|
+
|
8
|
+
def initialize(
|
9
|
+
image:,
|
10
|
+
name:,
|
11
|
+
command: nil,
|
12
|
+
log: NullContainerLog.new,
|
13
|
+
timeout: DEFAULT_TIMEOUT
|
14
|
+
)
|
15
|
+
@image = image
|
16
|
+
@name = name
|
17
|
+
@command = command
|
18
|
+
@log = log
|
19
|
+
@timeout = timeout
|
20
|
+
|
21
|
+
@output_delimeter = "\n"
|
22
|
+
@on_output = ->(*) { }
|
23
|
+
|
24
|
+
@timed_out = false
|
25
|
+
@stderr_io = StringIO.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_output(delimeter = "\n", &block)
|
29
|
+
@output_delimeter = delimeter
|
30
|
+
@on_output = block
|
31
|
+
end
|
32
|
+
|
33
|
+
def run(options = [])
|
34
|
+
@log.started(@image, @name)
|
35
|
+
|
36
|
+
pid, _, out, err = POSIX::Spawn.popen4(*docker_run_command(options))
|
37
|
+
|
38
|
+
t_out = read_stdout(out)
|
39
|
+
t_err = read_stderr(err)
|
40
|
+
t_timeout = timeout_thread(pid)
|
41
|
+
|
42
|
+
_, status = Process.waitpid2(pid)
|
43
|
+
|
44
|
+
@log.finished(@image, @name, status, @stderr_io.string)
|
45
|
+
|
46
|
+
t_timeout.kill
|
47
|
+
ensure
|
48
|
+
t_timeout.kill if t_timeout
|
49
|
+
|
50
|
+
if @timed_out
|
51
|
+
@log.timed_out(@image, @name, @timeout)
|
52
|
+
t_out.kill if t_out
|
53
|
+
t_err.kill if t_err
|
54
|
+
else
|
55
|
+
t_out.join if t_out
|
56
|
+
t_err.join if t_err
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def docker_run_command(options)
|
63
|
+
[
|
64
|
+
"docker", "run",
|
65
|
+
"--rm",
|
66
|
+
"--name", @name,
|
67
|
+
options,
|
68
|
+
@image,
|
69
|
+
@command,
|
70
|
+
].flatten.compact
|
71
|
+
end
|
72
|
+
|
73
|
+
def read_stdout(out)
|
74
|
+
Thread.new do
|
75
|
+
out.each_line(@output_delimeter) do |chunk|
|
76
|
+
output = chunk.chomp(@output_delimeter)
|
77
|
+
|
78
|
+
@on_output.call(output)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def read_stderr(err)
|
84
|
+
Thread.new do
|
85
|
+
err.each_line { |line| @stderr_io.write(line) }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def timeout_thread(pid)
|
90
|
+
Thread.new do
|
91
|
+
sleep @timeout
|
92
|
+
@timed_out = true
|
93
|
+
Process.kill("KILL", pid)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/cc/analyzer/engine.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
-
require "posix/spawn"
|
2
1
|
require "securerandom"
|
3
2
|
|
4
3
|
module CC
|
5
4
|
module Analyzer
|
6
5
|
class Engine
|
7
|
-
|
6
|
+
autoload :ContainerLog, "cc/analyzer/engine/container_log"
|
8
7
|
|
9
|
-
|
8
|
+
attr_reader :name
|
10
9
|
|
11
10
|
def initialize(name, metadata, code_path, config, label)
|
12
11
|
@name = name
|
@@ -16,88 +15,40 @@ module CC
|
|
16
15
|
@label = label.to_s
|
17
16
|
end
|
18
17
|
|
19
|
-
def run(stdout_io
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
t_err = Thread.new do
|
35
|
-
err.each_line do |line|
|
36
|
-
if stderr_io
|
37
|
-
stderr_io.write(line)
|
38
|
-
end
|
18
|
+
def run(stdout_io:, container_log: NullContainerLog.new)
|
19
|
+
container = Container.new(
|
20
|
+
image: @metadata["image"],
|
21
|
+
command: @metadata["command"],
|
22
|
+
name: container_name,
|
23
|
+
log: ContainerLog.new(name, container_log)
|
24
|
+
)
|
25
|
+
|
26
|
+
container.on_output("\0") do |output|
|
27
|
+
unless output_filter.filter?(output)
|
28
|
+
stdout_io.write(output)
|
39
29
|
end
|
40
30
|
end
|
41
31
|
|
42
|
-
|
43
|
-
sleep TIMEOUT
|
44
|
-
run_command("docker kill #{container_name} || true")
|
45
|
-
timed_out = true
|
46
|
-
end
|
47
|
-
|
48
|
-
pid, status = Process.waitpid2(pid)
|
49
|
-
t_timeout.kill
|
50
|
-
|
51
|
-
Analyzer.statsd.increment("cli.engines.finished")
|
52
|
-
|
53
|
-
if timed_out
|
54
|
-
Analyzer.statsd.increment("cli.engines.result.error")
|
55
|
-
Analyzer.statsd.increment("cli.engines.result.error.timeout")
|
56
|
-
Analyzer.statsd.increment("cli.engines.names.#{name}.result.error")
|
57
|
-
Analyzer.statsd.increment("cli.engines.names.#{name}.result.error.timeout")
|
58
|
-
raise EngineTimeout, "engine #{name} ran past #{TIMEOUT} seconds and was killed"
|
59
|
-
elsif status.success?
|
60
|
-
Analyzer.statsd.increment("cli.engines.names.#{name}.result.success")
|
61
|
-
Analyzer.statsd.increment("cli.engines.result.success")
|
62
|
-
else
|
63
|
-
Analyzer.statsd.increment("cli.engines.names.#{name}.result.error")
|
64
|
-
Analyzer.statsd.increment("cli.engines.result.error")
|
65
|
-
raise EngineFailure, "engine #{name} failed with status #{status.exitstatus} and stderr \n#{stderr_io.string}"
|
66
|
-
end
|
67
|
-
ensure
|
68
|
-
t_timeout.kill if t_timeout
|
69
|
-
|
70
|
-
if timed_out
|
71
|
-
t_out.kill if t_out
|
72
|
-
t_err.kill if t_err
|
73
|
-
else
|
74
|
-
t_out.join if t_out
|
75
|
-
t_err.join if t_err
|
76
|
-
end
|
32
|
+
container.run(container_options)
|
77
33
|
end
|
78
34
|
|
79
35
|
private
|
80
36
|
|
81
|
-
def
|
82
|
-
@container_name ||= "cc-engines-#{name}-#{SecureRandom.uuid}"
|
83
|
-
end
|
84
|
-
|
85
|
-
def docker_run_command
|
37
|
+
def container_options
|
86
38
|
[
|
87
|
-
"docker", "run",
|
88
|
-
"--rm",
|
89
39
|
"--cap-drop", "all",
|
90
40
|
"--label", "com.codeclimate.label=#{@label}",
|
91
|
-
"--name", container_name,
|
92
41
|
"--memory", 512_000_000.to_s, # bytes
|
93
42
|
"--memory-swap", "-1",
|
94
43
|
"--net", "none",
|
95
44
|
"--volume", "#{@code_path}:/code:ro",
|
96
45
|
"--volume", "#{config_file}:/config.json:ro",
|
97
46
|
"--user", "9000:9000",
|
98
|
-
|
99
|
-
|
100
|
-
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
def container_name
|
51
|
+
@container_name ||= "cc-engines-#{name}-#{SecureRandom.uuid}"
|
101
52
|
end
|
102
53
|
|
103
54
|
def config_file
|
@@ -106,21 +57,9 @@ module CC
|
|
106
57
|
path
|
107
58
|
end
|
108
59
|
|
109
|
-
def run_command(command)
|
110
|
-
spawn = POSIX::Spawn::Child.new(command)
|
111
|
-
|
112
|
-
unless spawn.status.success?
|
113
|
-
raise CommandFailure, "command '#{command}' failed with status #{spawn.status.exitstatus} and output #{spawn.err}"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
60
|
def output_filter
|
118
61
|
@output_filter ||= EngineOutputFilter.new(@config)
|
119
62
|
end
|
120
|
-
|
121
|
-
CommandFailure = Class.new(StandardError)
|
122
|
-
EngineFailure = Class.new(StandardError)
|
123
|
-
EngineTimeout = Class.new(StandardError)
|
124
63
|
end
|
125
64
|
end
|
126
65
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
class Engine
|
4
|
+
EngineFailure = Class.new(StandardError)
|
5
|
+
EngineTimeout = Class.new(StandardError)
|
6
|
+
|
7
|
+
class ContainerLog
|
8
|
+
def initialize(name, inner_log)
|
9
|
+
@name = name
|
10
|
+
@inner_log = inner_log
|
11
|
+
end
|
12
|
+
|
13
|
+
def started(image, name)
|
14
|
+
@inner_log.started(image, name)
|
15
|
+
|
16
|
+
Analyzer.statsd.increment("cli.engines.started")
|
17
|
+
end
|
18
|
+
|
19
|
+
def timed_out(image, name, timeout)
|
20
|
+
@inner_log.timed_out(image, name, timeout)
|
21
|
+
|
22
|
+
Analyzer.statsd.increment("cli.engines.result.error")
|
23
|
+
Analyzer.statsd.increment("cli.engines.result.error.timeout")
|
24
|
+
Analyzer.statsd.increment("cli.engines.names.#{@name}.result.error")
|
25
|
+
Analyzer.statsd.increment("cli.engines.names.#{@name}.result.error.timeout")
|
26
|
+
|
27
|
+
raise EngineTimeout, "engine #{@name} ran past #{timeout} seconds and was killed"
|
28
|
+
end
|
29
|
+
|
30
|
+
def finished(image, name, status, stderr)
|
31
|
+
@inner_log.finished(image, name, status, stderr)
|
32
|
+
|
33
|
+
Analyzer.statsd.increment("cli.engines.finished")
|
34
|
+
|
35
|
+
if status.success?
|
36
|
+
Analyzer.statsd.increment("cli.engines.result.success")
|
37
|
+
Analyzer.statsd.increment("cli.engines.names.#{@name}.result.success")
|
38
|
+
else
|
39
|
+
Analyzer.statsd.increment("cli.engines.result.error")
|
40
|
+
Analyzer.statsd.increment("cli.engines.names.#{@name}.result.error")
|
41
|
+
|
42
|
+
raise EngineFailure, "engine #{@name} failed with status #{status.exitstatus} and stderr \n#{stderr}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -14,14 +14,14 @@ module CC
|
|
14
14
|
@container_label = container_label
|
15
15
|
end
|
16
16
|
|
17
|
-
def run
|
17
|
+
def run(container_log = NullContainerLog.new)
|
18
18
|
raise NoEnabledEngines if engines.empty?
|
19
19
|
|
20
20
|
Analyzer.logger.info("running #{engines.size} engines")
|
21
21
|
|
22
22
|
@formatter.started
|
23
23
|
|
24
|
-
engines.each { |engine| run_engine(engine) }
|
24
|
+
engines.each { |engine| run_engine(engine, container_log) }
|
25
25
|
|
26
26
|
@formatter.finished
|
27
27
|
ensure
|
@@ -30,13 +30,16 @@ module CC
|
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
def run_engine(engine)
|
33
|
+
def run_engine(engine, container_log)
|
34
34
|
Analyzer.logger.info("starting engine #{engine.name}")
|
35
35
|
|
36
36
|
Analyzer.statsd.time("engines.time") do
|
37
37
|
Analyzer.statsd.time("engines.names.#{engine.name}.time") do
|
38
38
|
@formatter.engine_running(engine) do
|
39
|
-
engine.run(
|
39
|
+
engine.run(
|
40
|
+
stdout_io: @formatter,
|
41
|
+
container_log: container_log
|
42
|
+
)
|
40
43
|
end
|
41
44
|
end
|
42
45
|
end
|
@@ -80,7 +83,15 @@ module CC
|
|
80
83
|
end
|
81
84
|
|
82
85
|
def exclude_paths
|
83
|
-
PathPatterns.new(@config.exclude_paths || []).expanded
|
86
|
+
PathPatterns.new(@config.exclude_paths || []).expanded + gitignore_paths
|
87
|
+
end
|
88
|
+
|
89
|
+
def gitignore_paths
|
90
|
+
if File.exist?(".gitignore")
|
91
|
+
`git ls-files --others -i -z --exclude-from .gitignore`.split("\0")
|
92
|
+
else
|
93
|
+
[]
|
94
|
+
end
|
84
95
|
end
|
85
96
|
end
|
86
97
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: codeclimate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code Climate
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -191,7 +191,9 @@ files:
|
|
191
191
|
- config/engines.yml
|
192
192
|
- lib/cc/analyzer.rb
|
193
193
|
- lib/cc/analyzer/config.rb
|
194
|
+
- lib/cc/analyzer/container.rb
|
194
195
|
- lib/cc/analyzer/engine.rb
|
196
|
+
- lib/cc/analyzer/engine/container_log.rb
|
195
197
|
- lib/cc/analyzer/engine_output_filter.rb
|
196
198
|
- lib/cc/analyzer/engine_registry.rb
|
197
199
|
- lib/cc/analyzer/engines_runner.rb
|
@@ -204,6 +206,7 @@ files:
|
|
204
206
|
- lib/cc/analyzer/issue_sorter.rb
|
205
207
|
- lib/cc/analyzer/location_description.rb
|
206
208
|
- lib/cc/analyzer/null_config.rb
|
209
|
+
- lib/cc/analyzer/null_container_log.rb
|
207
210
|
- lib/cc/analyzer/path_patterns.rb
|
208
211
|
- lib/cc/analyzer/source_buffer.rb
|
209
212
|
- lib/cc/analyzer/unit_name.rb
|