construi 0.39.0 → 0.40.0

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.
@@ -3,3 +3,4 @@
3
3
  require 'construi'
4
4
 
5
5
  Construi.run ARGV
6
+
@@ -8,8 +8,10 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Construi::VERSION
9
9
  spec.authors = ['Levi Stephen']
10
10
  spec.email = ['levi.stephen@gmail.com']
11
+
11
12
  spec.summary = 'Build tool using Docker to specify build environment'
12
13
  spec.description = 'Build tool using Docker to specify build environment'
14
+
13
15
  spec.homepage = 'https://github.com/lstephen/construi'
14
16
  spec.license = 'MIT'
15
17
 
@@ -20,7 +22,7 @@ Gem::Specification.new do |spec|
20
22
 
21
23
  spec.required_ruby_version = '>= 1.9'
22
24
 
23
- spec.add_dependency 'docker-api', '=1.21.4'
25
+ spec.add_dependency 'docker-api', '=1.22.0'
24
26
  spec.add_dependency 'colorize', '=0.7.7'
25
27
 
26
28
  spec.add_development_dependency 'bundler', '~> 1.9'
@@ -1,6 +1,4 @@
1
1
 
2
- require 'yaml'
3
-
4
2
  # Entrypoint for the Construi application
5
3
  module Construi
6
4
  require 'construi/config'
@@ -1,6 +1,25 @@
1
+ require 'yaml'
1
2
 
2
3
  module Construi
3
4
  module Config
5
+ module WrappedYaml
6
+ def parent
7
+ nil
8
+ end
9
+
10
+ def key?(key)
11
+ yaml.is_a?(Hash) && yaml.key?(key.to_s)
12
+ end
13
+
14
+ def get(key, default = nil)
15
+ key?(key) ? yaml[key.to_s] : default
16
+ end
17
+
18
+ def with_parent(or_else = nil)
19
+ parent ? yield(parent) : or_else
20
+ end
21
+ end
22
+
4
23
  module Image
5
24
  def image
6
25
  image_configured :image
@@ -11,7 +30,7 @@ module Construi
11
30
  end
12
31
 
13
32
  def privileged?
14
- key?(:privileged) ? yaml['privileged'] : with_parent(false, &:privileged?)
33
+ key?(:privileged) ? get(:privileged) : with_parent(false, &:privileged?)
15
34
  end
16
35
 
17
36
  def image_configured?
@@ -19,7 +38,7 @@ module Construi
19
38
  end
20
39
 
21
40
  def image_configured(what)
22
- image_configured? ? yaml[what.to_s] : with_parent(&what)
41
+ image_configured? ? get(what) : with_parent(&what)
23
42
  end
24
43
  end
25
44
 
@@ -43,55 +62,84 @@ module Construi
43
62
  end
44
63
  end
45
64
 
46
- def files_configured?
47
- key? :files
48
- end
49
-
50
65
  def files
51
- fs = files_configured? ? yaml['files'].map { |str| File.parse(str) } : []
52
-
53
- with_parent([], &:files).concat fs
66
+ with_parent([], &:files).concat get(:files, []).map { |s| File.parse s }
54
67
  end
55
68
  end
56
69
 
57
- module Environment
58
- include Image
59
- include Files
70
+ module EnvironmentVariables
71
+ def env_configured?
72
+ key? :environment
73
+ end
60
74
 
61
- def parent
62
- nil
75
+ def env_hash
76
+ parent = with_parent({}, &:env_hash)
77
+
78
+ child = get(:environment, {}).each_with_object({}) do |v, h|
79
+ key, value = v.split '='
80
+
81
+ value = ENV[key] if value.nil? || value.empty?
82
+
83
+ h[key] = value
84
+ end
85
+
86
+ parent.merge child
63
87
  end
64
88
 
65
- def key?(key)
66
- yaml.is_a?(Hash) && yaml.key?(key.to_s)
89
+ def env
90
+ env_hash.each_with_object([]) do |(k, v), a|
91
+ a << "#{k}=#{v}" unless v.nil? || v.empty?
92
+ end
67
93
  end
94
+ end
68
95
 
69
- def with_parent(or_else = nil)
70
- parent ? yield(parent) : or_else
96
+ module Options
97
+ def options
98
+ { env: env, privileged: privileged? }
71
99
  end
72
100
  end
73
101
 
74
- class Global
75
- include Environment
102
+ module Links
103
+ class Link
104
+ include WrappedYaml
105
+ include Image
106
+ include Files
107
+ include EnvironmentVariables
108
+ include Options
76
109
 
77
- attr_reader :yaml
110
+ attr_reader :yaml
78
111
 
79
- def initialize(yaml)
80
- @yaml = yaml
112
+ def initialize(name, yaml)
113
+ @yaml = yaml
114
+ end
81
115
  end
82
116
 
83
- def env
84
- return [] if yaml['environment'].nil?
117
+ def links
118
+ parent = with_parent({}, &:links)
85
119
 
86
- yaml['environment'].reduce([]) do |acc, e|
87
- key = e.partition('=').first
88
- value = e.partition('=').last
120
+ child = get(:links, {}).each_with_object({}) do |(k, v), ls|
121
+ ls[k] = Link.new k, v
122
+ end
89
123
 
90
- value = ENV[key] if value.empty?
124
+ parent.merge child
125
+ end
126
+ end
91
127
 
92
- acc << "#{key}=#{value}" unless value.nil? or value.empty?
93
- acc
94
- end
128
+ module BuildEnvironment
129
+ include WrappedYaml
130
+ include Image
131
+ include Files
132
+ include EnvironmentVariables
133
+ include Links
134
+ end
135
+
136
+ class Global
137
+ include BuildEnvironment
138
+
139
+ attr_reader :yaml
140
+
141
+ def initialize(yaml)
142
+ @yaml = yaml
95
143
  end
96
144
 
97
145
  def target(target)
@@ -104,7 +152,8 @@ module Construi
104
152
  end
105
153
 
106
154
  class Target
107
- include Environment
155
+ include BuildEnvironment
156
+ include Options
108
157
 
109
158
  attr_reader :yaml, :parent
110
159
 
@@ -116,10 +165,6 @@ module Construi
116
165
  def commands
117
166
  Array(@yaml.is_a?(Hash) ? @yaml['run'] : @yaml)
118
167
  end
119
-
120
- def options
121
- { env: parent.env, privileged: parent.privileged? }
122
- end
123
168
  end
124
169
 
125
170
  def self.load(content)
@@ -0,0 +1,27 @@
1
+
2
+ module Construi
3
+ module Console
4
+ $stdout.sync = true
5
+
6
+ def self.warn(msg)
7
+ puts msg.yellow
8
+ end
9
+
10
+ def self.info(msg)
11
+ puts msg.green
12
+ end
13
+
14
+ def self.progress(msg)
15
+ puts
16
+ info msg
17
+ end
18
+
19
+ def self.output(from, msg)
20
+ msg.each_line do |m|
21
+ puts "#{from.rjust(13)} | ".blue << m
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+
@@ -1,28 +1,51 @@
1
1
  require 'construi/image'
2
2
 
3
3
  module Construi
4
-
5
4
  class Container
6
5
  private_class_method :new
7
6
 
8
- def initialize(container)
7
+ attr_reader :name
8
+
9
+ def initialize(container, options = {})
9
10
  @container = container
11
+ @name = options[:name] || @container.id
12
+ @log_lifecycle = options[:log_lifecycle] || false
10
13
  end
11
14
 
12
15
  def id
13
16
  @container.id
14
17
  end
15
18
 
19
+ def start
20
+ log_lifecycle "Starting container: '#{name}'..."
21
+ @container.start!
22
+ attach_stdout
23
+ end
24
+
25
+ def stop
26
+ log_lifecycle "Stopping container: '#{name}'..."
27
+ @container.stop
28
+ end
29
+
16
30
  def delete
31
+ stop
32
+ @container.kill
17
33
  @container.delete force: true, v: true
34
+ log_lifecycle "Deleted container: '#{name}'"
18
35
  end
19
36
 
20
37
  def attach_stdout
21
- @container.attach(:stream => true, :logs => true) { |s, c| puts c; $stdout.flush }
22
- true
23
- rescue Docker::Error::TimeoutError
24
- puts 'Failed to attach to stdout'.yellow
25
- false
38
+ Thread.new do
39
+ @container.attach(:stream => true, :logs => true) { |_, c| Console.output name, c }
40
+ end
41
+ end
42
+
43
+ def log_lifecycle?
44
+ @log_lifecycle
45
+ end
46
+
47
+ def log_lifecycle(msg)
48
+ Console.progress msg if log_lifecycle?
26
49
  end
27
50
 
28
51
  def commit
@@ -30,13 +53,10 @@ module Construi
30
53
  end
31
54
 
32
55
  def run
33
- @container.start
34
- attached = attach_stdout
56
+ start
35
57
  status_code = @container.wait['StatusCode']
36
58
 
37
- puts @container.logs(:stdout => true) unless attached
38
-
39
- raise Error, "Cmd returned status code: #{status_code}" unless status_code == 0
59
+ raise RunError, "Cmd returned status code: #{status_code}" unless status_code == 0
40
60
 
41
61
  commit
42
62
  end
@@ -45,42 +65,49 @@ module Construi
45
65
  other.is_a? Container and id == other.id
46
66
  end
47
67
 
48
- def self.create(image, cmd, options = {})
68
+ def self.create(image, options = {})
49
69
  env = options[:env] || []
50
70
  privileged = options[:privileged] || false
71
+ links = options[:links] || []
51
72
 
52
73
  host_config = {
53
74
  'Binds' => ["#{Dir.pwd}:/var/workspace"],
54
- 'Privileged' => privileged
75
+ 'Privileged' => privileged,
76
+ 'Links' => links
55
77
  }
56
78
 
57
- wrap Docker::Container.create(
58
- 'Cmd' => cmd.split,
79
+ create_options = {
59
80
  'Image' => image.id,
60
81
  'Env' => env,
61
82
  'Tty' => false,
62
83
  'WorkingDir' => '/var/workspace',
63
- 'HostConfig' => host_config)
84
+ 'HostConfig' => host_config
85
+ }
86
+
87
+ create_options['Cmd'] = options[:cmd].split if options.key?(:cmd)
88
+
89
+ wrap Docker::Container.create(create_options), options
64
90
  end
65
91
 
66
- def self.wrap(container)
67
- new container
92
+ def self.wrap(container, options = {})
93
+ new container, options
68
94
  end
69
95
 
70
- def self.use(image, cmd, options = {})
71
- container = create image, cmd, options
96
+ def self.use(image, options = {})
97
+ container = create image, options
72
98
  yield container
73
99
  ensure
74
100
  container.delete unless container.nil?
75
101
  end
76
102
 
77
- def self.run(image, cmd, options = {})
78
- use image, cmd, options, &:run
103
+ def self.run(image, options = {})
104
+ use image, options, &:run
79
105
  end
80
106
 
81
- class Error < StandardError
107
+ class RunError < StandardError
82
108
  end
83
109
 
84
110
  end
85
111
 
112
+
86
113
  end
@@ -59,8 +59,12 @@ module Construi
59
59
  run chmod, options
60
60
  end
61
61
 
62
+ def start(options = {})
63
+ Container.create(self, options).tap(&:start)
64
+ end
65
+
62
66
  def run(cmd, options = {})
63
- Container.run self, cmd, options
67
+ Container.run self, options.merge(cmd: cmd)
64
68
  end
65
69
 
66
70
  def ==(other)
@@ -77,8 +81,8 @@ module Construi
77
81
  end
78
82
 
79
83
  def self.create(image)
80
- puts
81
- puts "Creating image: '#{image}'...".green
84
+ Console.progress "Creating image: '#{image}'..."
85
+
82
86
  wrap Docker::Image.create('fromImage' => image) { |s|
83
87
  status = JSON.parse(s)
84
88
 
@@ -86,17 +90,19 @@ module Construi
86
90
  progress = status['progressDetail']
87
91
 
88
92
  if progress.nil? || progress.empty?
89
- print "#{id}: " unless id.nil?
90
- puts "#{status['status']}"
93
+ msg = ''
94
+ msg << "#{id}: " unless id.nil?
95
+ msg << status['status']
96
+ Console.output image, msg
91
97
  end
92
98
  }
93
99
  end
94
100
 
95
101
  def self.build(build)
96
- puts
97
- puts "Building image: '#{build}'...".green
102
+ Console.progress "Building image: '#{build}'..."
103
+
98
104
  wrap Docker::Image.build_from_dir(build, rm: 0) { |s|
99
- puts JSON.parse(s)['stream']
105
+ Console.output build, JSON.parse(s)['stream']
100
106
  }
101
107
  end
102
108
 
@@ -9,6 +9,8 @@ require 'colorize'
9
9
  require 'docker'
10
10
 
11
11
  module Construi
12
+ DOCKER_TIMEOUT = 60
13
+
12
14
  # Runs Construi
13
15
  class Runner
14
16
  def initialize(config)
@@ -25,8 +27,6 @@ module Construi
25
27
 
26
28
  Docker.validate_version!
27
29
 
28
- # Don't time out. We can't differentiate between a long running
29
- # task and a time out.
30
30
  Docker.options[:read_timeout] = nil
31
31
 
32
32
  # Low chunk size as we wish to receive streaming output ASAP
@@ -43,4 +43,5 @@ module Construi
43
43
  targets.map { |t| Target.new t, @config.target(t) } .each(&:run)
44
44
  end
45
45
  end
46
+
46
47
  end
@@ -1,7 +1,6 @@
1
-
1
+ require 'construi/console'
2
2
 
3
3
  module Construi
4
-
5
4
  class Target
6
5
  attr_reader :name, :config
7
6
 
@@ -15,20 +14,38 @@ module Construi
15
14
  end
16
15
 
17
16
  def run
18
- puts "Running #{name}...".green
17
+ Console.progress "Running #{name}..."
18
+
19
+ links = start_linked_images
20
+
21
+ begin
22
+ final_image = IntermediateImage.seed(create_initial_image).reduce(commands) do |image, command|
23
+ Console.progress " > #{command}"
24
+
25
+ link_option = links.each_with_object([]) do |l, o|
26
+ o << "#{l.id}:#{l.name}"
27
+ end
19
28
 
20
- final_image = IntermediateImage.seed(initial_image).reduce(commands) do |image, command|
21
- puts
22
- puts " > #{command}".green
23
- image.run command, @config.options
29
+ image.run command, @config.options.merge(links: link_option, name: name)
30
+ end
31
+
32
+ final_image.delete
33
+ ensure
34
+ links.map(&:delete)
24
35
  end
25
36
 
26
- final_image.delete
37
+ Console.progress "Build Successful."
27
38
  end
28
39
 
29
- def initial_image
40
+ def create_initial_image
30
41
  return Image.from(@config)
31
42
  end
43
+
44
+ def start_linked_images
45
+ @config.links.map do |(name, config)|
46
+ Image.from(config).start(config.options.merge name: name, log_lifecycle: true)
47
+ end
48
+ end
32
49
  end
33
50
 
34
51
  end
@@ -1,3 +1,3 @@
1
1
  module Construi
2
- VERSION = "0.39.0"
2
+ VERSION = "0.40.0"
3
3
  end
@@ -104,6 +104,7 @@ Declares environment variables that will be passed through or set in the build e
104
104
  If no value is provided then the value from the host environment will be used.
105
105
  In this example `NEXUS_SERVER_URL` will be set as provided, while `NEXUS_USERNAME` and
106
106
  `NEXUS_PASSWORD` will be retrieved from the host.
107
+ Can be used on a per target basis.
107
108
 
108
109
  ```
109
110
  image: maven:3-jdk-7
@@ -135,6 +136,32 @@ targets:
135
136
  run: scripts/construi/deploy.sh
136
137
  ```
137
138
 
139
+ ### Links
140
+
141
+ Allows specifying containers that will be linked to the build container during the build
142
+ process.
143
+ This is useful, for example, in having a database running durings tests.
144
+ Linked containers allow setting of environment, files, and privileged as per the build
145
+ container.
146
+ Links can be specified on a global or per target level.
147
+
148
+ ```
149
+ image: ruby:1.9
150
+
151
+ targets:
152
+ integration-tests:
153
+ environment:
154
+ - MYSQL_HOST=mysql
155
+ run:
156
+ - bundle install --path=vendor/bundle
157
+ - rake test:integration
158
+ links:
159
+ mysql:
160
+ image: mysql:5.5
161
+ environment:
162
+ - MYSQL_ALLOW_EMPTY_PASSWORD=yes
163
+ ```
164
+
138
165
  ### Targets
139
166
 
140
167
  Any number of targets can be specified.
@@ -65,9 +65,16 @@ RSpec.describe Construi::Config do
65
65
  end
66
66
 
67
67
  describe '#env' do
68
- subject { config.env }
68
+ subject { config.target('build').env }
69
69
 
70
70
  context 'when no environment section' do
71
+ let(:config_content) do
72
+ <<-YAML
73
+ targets:
74
+ build: cmd1
75
+ YAML
76
+ end
77
+
71
78
  it { is_expected.to eq([]) }
72
79
  end
73
80
 
@@ -77,6 +84,8 @@ RSpec.describe Construi::Config do
77
84
  environment:
78
85
  - VAR1=VALUE_1
79
86
  - VAR2=VALUE_2
87
+ targets:
88
+ build: cmd1
80
89
  YAML
81
90
  end
82
91
 
@@ -94,10 +103,32 @@ RSpec.describe Construi::Config do
94
103
  environment:
95
104
  - VAR1
96
105
  - VAR2
106
+ targets:
107
+ build: cmd1
97
108
  YAML
98
109
  end
99
110
 
100
- it { is_expected.to contain_exactly('VAR1=VALUE_1', 'VAR2=VALUE_2') }
111
+ it { is_expected.to contain_exactly 'VAR1=VALUE_1', 'VAR2=VALUE_2' }
112
+ end
113
+
114
+ context 'when target specific environment values' do
115
+ let(:config_content) do
116
+ <<-YAML
117
+ environment:
118
+ - VAR1=VALUE_1
119
+ - VAR2=VALUE_2
120
+ targets:
121
+ build:
122
+ environment:
123
+ - VAR1=VALUE_OVERIDEN
124
+ - VAR3=VALUE_3
125
+ YAML
126
+ end
127
+
128
+ it do
129
+ is_expected.to contain_exactly(
130
+ 'VAR1=VALUE_OVERIDEN', 'VAR2=VALUE_2', 'VAR3=VALUE_3')
131
+ end
101
132
  end
102
133
  end
103
134
 
@@ -195,11 +226,13 @@ RSpec.describe Construi::Config do
195
226
  targets:
196
227
  build:
197
228
  image: build:image
229
+ privileged: true
198
230
  run: cmd1
199
231
  YAML
200
232
  end
201
233
 
202
234
  it { is_expected.to have_attributes(:image => 'build:image', :build => nil) }
235
+ it { expect(subject.options).to include(privileged: true) }
203
236
  end
204
237
 
205
238
  context 'when build for target and image for global' do
@@ -325,5 +358,80 @@ RSpec.describe Construi::Config do
325
358
  it { is_expected.to eq(false) }
326
359
  end
327
360
  end
361
+
362
+ describe '#links' do
363
+ subject { config.target('build').links }
364
+
365
+ context 'no links' do
366
+ let(:config_content) do
367
+ <<-YAML
368
+ targets:
369
+ build: cmd1
370
+ YAML
371
+ end
372
+
373
+ it { is_expected.to eq({}) }
374
+ end
375
+
376
+ context 'link on target' do
377
+ let(:config_content) do
378
+ <<-YAML
379
+ targets:
380
+ build:
381
+ run: cmd1
382
+ links:
383
+ db:
384
+ image: mysql
385
+ environment:
386
+ - VAR1=VALUE1
387
+ YAML
388
+ end
389
+
390
+ it { is_expected.to include('db') }
391
+ it { expect(subject['db'].image).to eq('mysql') }
392
+ it { expect(subject['db'].env).to contain_exactly('VAR1=VALUE1') }
393
+ end
394
+
395
+ context 'link on global level' do
396
+ let(:config_content) do
397
+ <<-YAML
398
+ links:
399
+ sshd:
400
+ build: construi/my-sshd
401
+ privileged: true
402
+ targets:
403
+ build:
404
+ run: cmd1
405
+ YAML
406
+ end
407
+
408
+ it { is_expected.to include('sshd') }
409
+ it { expect(subject['sshd'].build).to eq('construi/my-sshd') }
410
+ it { expect(subject['sshd'].privileged?).to be_truthy }
411
+ end
412
+
413
+ context 'multiple images' do
414
+ let(:config_content) do
415
+ <<-YAML
416
+ links:
417
+ sshd:
418
+ build: construi/my-sshd
419
+ targets:
420
+ build:
421
+ run: cmd1
422
+ links:
423
+ db:
424
+ image: mysql
425
+ nginx:
426
+ image: mydockers/nginx
427
+ YAML
428
+ end
429
+
430
+ it { is_expected.to include('sshd', 'db', 'nginx') }
431
+ it { expect(subject['db'].image).to eq('mysql') }
432
+ it { expect(subject['nginx'].image).to eq('mydockers/nginx') }
433
+ it { expect(subject['sshd'].build).to eq('construi/my-sshd') }
434
+ end
435
+ end
328
436
  end
329
437
 
@@ -30,22 +30,11 @@ RSpec.describe Construi::Container do
30
30
  .and_yield('stream', 'msg2')
31
31
  end
32
32
 
33
- subject! { attach_stdout.call }
33
+ subject! { attach_stdout.call.join }
34
34
 
35
35
  it { expect(docker_container).to have_received(:attach).with(:stream => true, :logs => true) }
36
- it { expect($stdout.string).to include("msg1\nmsg2") }
37
- end
38
-
39
- context 'when attach times out' do
40
- before do
41
- allow(docker_container)
42
- .to receive(:attach)
43
- .and_raise(Docker::Error::TimeoutError.new)
44
- end
45
-
46
- subject! { attach_stdout.call }
47
-
48
- it { expect($stdout.string).to include('Failed to attach to stdout'.yellow) }
36
+ it { expect($stdout.string).to include("msg1\n") }
37
+ it { expect($stdout.string).to include("msg2\n") }
49
38
  end
50
39
  end
51
40
 
@@ -69,8 +58,7 @@ RSpec.describe Construi::Container do
69
58
 
70
59
  subject! { run.call }
71
60
 
72
- it { expect(docker_container).to have_received(:start) }
73
- it { expect(docker_container).to have_received(:attach).with(:stream => true, :logs => true) }
61
+ it { expect(docker_container).to have_received(:start!) }
74
62
  it { expect(docker_container).to have_received(:wait) }
75
63
  it { expect(docker_container).to have_received(:commit) }
76
64
  it { is_expected.to eq(Construi::Image.wrap(image)) }
@@ -79,7 +67,7 @@ RSpec.describe Construi::Container do
79
67
  context 'when command fails' do
80
68
  let(:status_code) { 1 }
81
69
 
82
- it { expect { run.call }.to raise_error Construi::Container::Error, /status code: 1/}
70
+ it { expect { run.call }.to raise_error Construi::Container::RunError, /status code: 1/}
83
71
  end
84
72
  end
85
73
 
@@ -92,7 +80,7 @@ RSpec.describe Construi::Container do
92
80
  before { allow(docker_container_class).to receive(:create).and_return docker_container }
93
81
  before { allow(Dir).to receive(:pwd).and_return(pwd) }
94
82
 
95
- subject! { Construi::Container::create(image, cmd, env: env, privileged: privileged) }
83
+ subject! { Construi::Container::create image, cmd: cmd, env: env, privileged: privileged }
96
84
 
97
85
  it do
98
86
  expect(docker_container_class).to have_received(:create).with( {
@@ -103,7 +91,8 @@ RSpec.describe Construi::Container do
103
91
  'WorkingDir' => '/var/workspace',
104
92
  'HostConfig' => {
105
93
  'Binds' => ["#{pwd}:/var/workspace"],
106
- 'Privileged' => true
94
+ 'Privileged' => true,
95
+ 'Links' => []
107
96
  }
108
97
  } )
109
98
  end
@@ -117,7 +106,7 @@ RSpec.describe Construi::Container do
117
106
  before { allow(docker_container).to receive(:wait).and_return({'StatusCode' => 0}) }
118
107
  before { allow(docker_container).to receive(:commit).and_return image }
119
108
 
120
- subject! { Construi::Container.run(image, cmd) }
109
+ subject! { Construi::Container.run image, cmd: cmd }
121
110
 
122
111
  it do
123
112
  expect(docker_container_class).to have_received(:create).with(
@@ -127,9 +116,11 @@ RSpec.describe Construi::Container do
127
116
  }))
128
117
  end
129
118
  it { is_expected.to eq(Construi::Image.wrap image) }
130
- it { expect(docker_container).to have_received(:start) }
119
+ it { expect(docker_container).to have_received(:start!) }
131
120
  it { expect(docker_container).to have_received(:commit) }
132
- it { expect(docker_container).to have_received(:delete) }
121
+ it { expect(docker_container).to have_received(:stop) }
122
+ it { expect(docker_container).to have_received(:kill) }
123
+ it { expect(docker_container).to have_received(:delete).with(hash_including(v: true)) }
133
124
  end
134
125
 
135
126
  end
@@ -52,7 +52,7 @@ RSpec.describe Construi::Image do
52
52
 
53
53
  subject! { image.run(cmd, env: env) }
54
54
 
55
- it { expect(container).to have_received(:run).with(image, cmd, env: env) }
55
+ it { expect(container).to have_received(:run).with(image, cmd: cmd, env: env) }
56
56
  end
57
57
 
58
58
  describe '#insert_local' do
@@ -80,7 +80,7 @@ RSpec.describe Construi::Image do
80
80
  .to have_received(:insert_local)
81
81
  .with 'localPath' => host, 'outputPath' => container
82
82
  end
83
- it { expect(container_class).to have_received(:run).with(image, "ls -l #{container}", default_options) }
83
+ it { expect(container_class).to have_received(:run).with(image, default_options.merge(cmd: "ls -l #{container}")) }
84
84
  end
85
85
 
86
86
  context 'with permissions' do
@@ -95,16 +95,16 @@ RSpec.describe Construi::Image do
95
95
  it do
96
96
  expect(container_class)
97
97
  .to have_received(:run)
98
- .with(image, "chmod -R #{permissions} #{container}", {})
98
+ .with(image, cmd: "chmod -R #{permissions} #{container}")
99
99
  end
100
100
  it { expect($stdout.string).to include(" > chmod -R #{permissions} #{container}") }
101
- it { expect(container_class).to have_received(:run).with(image, "ls -l #{container}", default_options) }
101
+ it { expect(container_class).to have_received(:run).with(image, default_options.merge(cmd: "ls -l #{container}")) }
102
102
  end
103
103
 
104
104
  end
105
105
 
106
106
  describe '.from' do
107
- let(:config) { instance_double(Construi::Config::Environment).as_null_object }
107
+ let(:config) { instance_double(Construi::Config::BuildEnvironment).as_null_object }
108
108
 
109
109
  let(:image) { nil }
110
110
  let(:build) { nil }
@@ -191,7 +191,7 @@ RSpec.describe Construi::Image do
191
191
  end
192
192
 
193
193
  it 'outputs build status messages' do
194
- expect($stdout.string).to include("msg1\nmsg2\n")
194
+ expect($stdout.string).to match(/msg1\n.*msg2\n/)
195
195
  end
196
196
  end
197
197
 
@@ -220,7 +220,7 @@ RSpec.describe Construi::Image do
220
220
  end
221
221
 
222
222
  it 'outputs create status messages' do
223
- expect($stdout.string).to include("id: msg1\nid: msg2\nmsg4\n")
223
+ expect($stdout.string).to match(/id: msg1\n.*id: msg2\n.*msg4\n/)
224
224
  end
225
225
  end
226
226
  end
@@ -32,7 +32,7 @@ RSpec.describe Construi::Runner do
32
32
  subject! { runner.run(targets) }
33
33
 
34
34
  it { expect(docker).to have_received(:validate_version!) }
35
- it { expect(image).to have_received(:run).with('cmd1', env: [], privileged: false) }
35
+ it { expect(image).to have_received(:run).with('cmd1', env: [], privileged: false, links: [], name: 'target1') }
36
36
  it { expect(image).to have_received(:delete) }
37
37
 
38
38
  it { expect($stdout.string).to include('Running target1...'.green) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: construi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.39.0
4
+ version: 0.40.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-07-17 00:00:00.000000000 Z
12
+ date: 2015-07-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: docker-api
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: 1.21.4
21
+ version: 1.22.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - '='
28
28
  - !ruby/object:Gem::Version
29
- version: 1.21.4
29
+ version: 1.22.0
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: colorize
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -243,6 +243,7 @@ files:
243
243
  - construi/site/run.sh
244
244
  - lib/construi.rb
245
245
  - lib/construi/config.rb
246
+ - lib/construi/console.rb
246
247
  - lib/construi/container.rb
247
248
  - lib/construi/image.rb
248
249
  - lib/construi/runner.rb
@@ -281,7 +282,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
281
282
  version: '0'
282
283
  segments:
283
284
  - 0
284
- hash: 472212049094284394
285
+ hash: 2884457678396287104
285
286
  requirements: []
286
287
  rubyforge_project:
287
288
  rubygems_version: 1.8.23.2