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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 89202f61cd85559f4e0531916fa59183a2ea9a01
4
- data.tar.gz: 1d7073a763b74d1813b28d2b892f43c2e3ffeced
3
+ metadata.gz: b5b6caa0a6dc7642c6e5abb6533c8c24c7bd49f4
4
+ data.tar.gz: 98bb65ce704a03ba8ed98a4ff697d2a424ef0fd3
5
5
  SHA512:
6
- metadata.gz: 2ec65cca81cf694dd51200dc44367bba0044afca7c48f90f5305b1ba8685f29da5b3c993f98350b36b64832321fd4521c017e8b09e75e8d11eb09c4c6d026251
7
- data.tar.gz: 1833d8881ea7881a34bc9ed565af19fee320755b1b7a739610dcecb00f845ec404fb8229a84e7d40c373eccacc64c2fccde076e37533275bccc0a5879d1c3a82
6
+ metadata.gz: 17ecabe1ace73fbd5f37c83a78b2bd1e5a683aa1b518684a33ebd8885246c8bb289f804c692d63bd443b35ef1f0f576cfbc382691c6c89368516c8aa8a6edc1c
7
+ data.tar.gz: f62c7be59d492698e22496c265b4f6c3c9993100ec861bce94227f9e4ffceda8bc8809ba2c75be9fae94a3d7d355c5b167ba30119e03ee42835d0e80b77e6906
@@ -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
@@ -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
- attr_reader :name
6
+ autoload :ContainerLog, "cc/analyzer/engine/container_log"
8
7
 
9
- TIMEOUT = 15 * 60 # 15m
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, stderr_io = StringIO.new)
20
- timed_out = false
21
- pid, _, out, err = POSIX::Spawn.popen4(*docker_run_command)
22
- Analyzer.statsd.increment("cli.engines.started")
23
-
24
- t_out = Thread.new do
25
- out.each_line("\0") do |chunk|
26
- output = chunk.chomp("\0")
27
-
28
- unless output_filter.filter?(output)
29
- stdout_io.write(output)
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
- t_timeout = Thread.new do
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 container_name
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
- @metadata["image"],
99
- @metadata["command"], # String or Array
100
- ].flatten.compact
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(@formatter)
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
@@ -0,0 +1,14 @@
1
+ module CC
2
+ module Analyzer
3
+ class NullContainerLog
4
+ def started(_image, _name)
5
+ end
6
+
7
+ def timed_out(_image, _name, _seconds)
8
+ end
9
+
10
+ def finished(_image, _name, _status, _stderr)
11
+ end
12
+ end
13
+ end
14
+ 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.7
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-03 00:00:00.000000000 Z
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