cide 0.2.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c84c09a6870e5146c1a42a86e03e1d01a2dc74aa
4
- data.tar.gz: 4a4b246cc68b455bd26c8185c6dde9aaa1a6e8b5
3
+ metadata.gz: c7fc120b6cb172fbbea20e8d325ba1ce9068b6e7
4
+ data.tar.gz: 43589b67baf32850c037b28e9a7ca3e597e3557e
5
5
  SHA512:
6
- metadata.gz: 8e69cb3488d0a15e18ab4031083aa3c33c720757236d28135de981e0d98ea8dbeb95807d48a236b4617492551880049129cee649a49508288b7d9e22c0662dbf
7
- data.tar.gz: 8b0a452b1d6a159f4362dc11674576d9c6ca34c9b6c56a0b1bdab0727b64ee07a6d13dfdcc2df93838d662048cb5817292ed5951e8d95170fe58ccbe6fc51dae
6
+ metadata.gz: 4c7f2f7cc1c0bed1b49d73f73eff33cb146f1ce4827579cbc7328d569a32de29552c71d562c4a3c196665b4b2ed153fa86eb73ffe0ec5f4460acd080904adae7
7
+ data.tar.gz: c93b2cd8962571d7f5fb66b13b71b24ab2f47f8c6dd8677d9d99af36c324637b1b897b0c095dd161d64706c00d4d3ac16b64c36f379bb2098dde15709706e137
data/.cide.yml CHANGED
@@ -7,6 +7,6 @@ before:
7
7
  - Gemfile
8
8
  - Gemfile.lock
9
9
  - cide.gemspec
10
- run: bundle
10
+ run: bundle install --jobs=3 --retry=3 --deployment
11
11
  use_ssh: false
12
- run: rake
12
+ run: bundle exec rake
data/.rubocop.yml CHANGED
@@ -1,24 +1,25 @@
1
1
  AllCops:
2
2
  Exclude:
3
3
  - 'spec/**/*'
4
+ - 'vendor/**/*'
4
5
 
5
6
  # Rubocop is not smart enough
6
7
  Metrics/AbcSize:
7
- Max: 100
8
+ Enabled: false
8
9
 
9
10
  # CIDE::CLI is essentially a big dispatcher, no need to break it
10
11
  # in smaller chunks.
11
12
  Metrics/ClassLength:
12
- Max: 200
13
+ Enabled: false
13
14
 
14
15
  # Offense count: 28
15
16
  Metrics/CyclomaticComplexity:
16
- Max: 15
17
+ Enabled: false
17
18
 
18
19
  # CIDE::CLI methods can be read top to bottom. No need to factor out
19
20
  # functionality unless it can be shared.
20
21
  Metrics/MethodLength:
21
- Max: 60
22
+ Enabled: false
22
23
 
23
24
  Metrics/PerceivedComplexity:
24
25
  Max: 20
data/.travis.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  language: ruby
2
+ sudo: false
2
3
  rvm:
3
- - 2.1.0
4
4
  - 2.0.0
5
+ - 2.1.0
data/CHANGELOG.md CHANGED
@@ -1,4 +1,21 @@
1
1
 
2
+ 0.4.0 / 2015-04-19
3
+ ==================
4
+
5
+ * NEW: Allow for arbitrary env on multiple levels
6
+ * NEW: Better link image to name coercion. `foo/bar:tag` will now get a `bar` name.
7
+ * NEW: Error reporting of broken linked containers
8
+ * NEW: More control over the `add` directive. It's now possible to set the target path.
9
+ * NEW: The `as_root` key is now a build step making it possible to set environment and add files.
10
+ * NEW: `cide build --no-pull`
11
+ * NEW: `cide debug` command to debug builds
12
+ * CHANGE: Force cleaning of old images
13
+ * FIX: Work around issue #7
14
+ * FIX: caching issues with ssh keys
15
+ * FIX: cide was using the wrong user to run the ci script
16
+ * FIX: export_dir and ssh_key command-line options
17
+ * FIX: running commands with arguments
18
+
2
19
  0.2.0 / 2015-04-15
3
20
  ==================
4
21
 
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cide (0.1.1)
5
- thor (~> 0.19.0)
6
- virtus (~> 1.0.0)
4
+ cide (0.4.0)
5
+ thor (~> 0.19)
6
+ virtus (~> 1.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,29 +1,40 @@
1
- cide - Continuous Integration Docker Environment
1
+ *cide* - Continuous Integration Docker Environment
2
2
  ================================================
3
3
 
4
- `cide` makes it easy to reproduce CI builds on the developer computer by
5
- providing the same docker environment.
6
-
7
- Run `cide` in the project root to run a build. Configure by providing a
8
- `.cide.yml` file.
4
+ *cide* is a command-line tool that runs tests in an isolated (docker)
5
+ environment. It solves a problem where Jenkins workers need all the project's
6
+ dependencies (possibly conflicting) to be installed on the boxes. With *cide*
7
+ each run gets it's own set of temporary docker containers which are scratched
8
+ at the end. Incidentally it's also possible to the the same `cide` command on
9
+ the developer machine and get the same build environent as on the CI. This
10
+ makes configuration iterations much shorter and allow to converge on a working
11
+ configuration faster.
9
12
 
10
13
  Usage
11
14
  -----
12
15
 
13
- Just run `cide` inside of your project. cide will look for a .cide.yml file
14
- for configuration but all arguments are also passable trough command-line
15
- arguments. If a Dockerfile already exists it will be used instead.
16
+ Go to the target project's root and run `cide init` to populate a default
17
+ `.cide.yml`. This file contains all the instruction to build your project with
18
+ cide.
19
+
20
+ Once the file is configure run `cide` to execute the build. All the output
21
+ will appear in the console.
16
22
 
17
23
  Example
18
24
  -------
19
25
 
20
26
  `.cide.yml`
21
- ```
27
+ ```yaml
22
28
  ---
23
29
  from: "ruby:2.1"
24
30
  as_root:
25
31
  - apt-get update -qy && apt-get install -qy libxml2-dev
26
- command: bundle && bundle exec rspec
32
+ before:
33
+ add:
34
+ - Gemfile
35
+ - Gemfile.lock
36
+ run: bundle install --jobs=3 --retry=3 --deployment
37
+ run: bundle exec rspec
27
38
  ```
28
39
 
29
40
  Features
@@ -32,34 +43,54 @@ Features
32
43
  * straighforward to use, just run `cide` inside of your project
33
44
  * works on OSX with boot2docker
34
45
  * integrates easily with jenkins or other CI systems
35
- * can run with linked containers
46
+ * can use linked containers for backend dependencies like MySQL or redis
47
+ * artefact export
36
48
 
37
- Limitations
38
- -----------
49
+ Missing features
50
+ ----------------
39
51
 
40
- Docker version 1.5.0+ is required
52
+ * Linked container readiness detection. Some containers take a while to boot
53
+ up. Currently the `script/ci` must implement some sort of detection loop.
54
+ * Language detection: default settings per language (see Travis-CI). The
55
+ current format might be a bit daunting for non-experts.
56
+ * Build matrix: run variations of the tests, for example with different
57
+ versions of ruby to make sure all are supported (useful for libraries).
58
+
59
+ PR welcome !
41
60
 
42
61
  Installation
43
62
  ------------
44
63
 
45
- Install docker: https://docs.docker.com/installation/#installation
64
+ The current dependencies are [ruby
65
+ 2.0+](https://www.ruby-lang.org/en/documentation/installation/) and [docker
66
+ 1.5.0+](https://docs.docker.com/installation/#installation)
46
67
 
47
- ```sh
48
- gem install cide
49
- ```
68
+ On OSX, boot2docker is automatically used if installed.
50
69
 
51
- OSX docker install:
70
+ Quick OSX docker install:
52
71
  ```sh
53
72
  brew install boot2docker
54
73
  boot2docker init
55
74
  boot2docker up
56
- # cide auto-detects boot2docker on OSX
75
+ ```
76
+
77
+ Then install the *cide* ruby gem:
78
+ ```sh
79
+ gem install cide
57
80
  ```
58
81
 
59
82
  Similar projects
60
83
  ----------------
61
84
 
62
85
  * [Docker Compose](https://docs.docker.com/compose/) - Docker development environment
86
+ * [Travis CI](https://travis-ci.org/) - Great CI for Open Source projects
87
+ * [Construi](https://github.com/lstephen/construi) - Another ruby command-line builder
88
+
89
+ TODO
90
+ ----
63
91
 
64
- Open an issue if a project is missing.
92
+ * Document the `.cide.yml` format. Look into `lib/cide/build/config_loader.rb`
93
+ for now
94
+ * Explain how to use *cide* with Jenkins
95
+ * Explain Travis CI vs *cide* + Jenkins
65
96
 
data/cide.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'cide'
5
- s.version = '0.2.0'
5
+ s.version = '0.4.0'
6
6
  s.authors = ['zimbatm']
7
7
  s.email = ['zimbatm@zimbatm.com']
8
8
  s.summary = 'CI docker runner'
@@ -20,8 +20,8 @@ DESC
20
20
 
21
21
  s.required_ruby_version = '>= 1.9.3'
22
22
 
23
- s.add_runtime_dependency 'thor', '~> 0.19.0'
24
- s.add_runtime_dependency 'virtus', '~> 1.0.0'
23
+ s.add_runtime_dependency 'thor', '~> 0.19'
24
+ s.add_runtime_dependency 'virtus', '~> 1.0'
25
25
  s.add_development_dependency 'rake'
26
26
  s.add_development_dependency 'rubocop'
27
27
  s.add_development_dependency 'rspec'
@@ -21,11 +21,18 @@ module CIDE
21
21
  end
22
22
  end
23
23
 
24
+ class FileAdd
25
+ include Virtus.model(strict: true)
26
+ include NiceInspect
27
+ attribute :src, Array[String], default: []
28
+ attribute :dest, String
29
+ end
30
+
24
31
  class Step
25
32
  include Virtus.model(strict: true)
26
33
  include NiceInspect
27
- attribute :add, Array[String], default: []
28
- attribute :forward_env, Array[String], default: []
34
+ attribute :add, Array[FileAdd], default: []
35
+ attribute :env, Hash[String, String], default: {}
29
36
  attribute :run, Array[String], default: []
30
37
  end
31
38
 
@@ -34,7 +41,7 @@ module CIDE
34
41
  include NiceInspect
35
42
  attribute :name, String
36
43
  attribute :image, String
37
- attribute :env, Hash[String, String]
44
+ attribute :env, Hash[String, String], default: {}
38
45
  attribute :run, String
39
46
  # Container ID added after the fact
40
47
  attr_accessor :id
@@ -43,13 +50,13 @@ module CIDE
43
50
  include Virtus.model(strict: true)
44
51
  include NiceInspect
45
52
  attribute :from, String, default: 'ubuntu'
46
- attribute :as_root, Array[String], default: []
53
+ attribute :as_root, Step, required: false
47
54
  attribute :use_ssh, Boolean, default: false
48
55
  attribute :before, Step, required: false
49
- attribute :forward_env, Array[String], default: []
56
+ attribute :env, Hash[String, String], default: {}
50
57
  attribute :export_dir, String, required: false
51
58
  attribute :links, Array[Link], default: []
52
- attribute :run, String, default: 'script/ci'
59
+ attribute :run, Array[String], default: ['script/ci']
53
60
 
54
61
  attr_reader :warnings, :errors
55
62
 
@@ -27,20 +27,21 @@ module CIDE
27
27
  wanted_key(path, 'from', key)
28
28
  @config.from = expect_string(path, value)
29
29
  when 'as_root' then
30
- @config.as_root = expect_array(path, value)
30
+ @config.as_root = maybe_step(path, value)
31
31
  when 'use_ssh' then
32
32
  @config.use_ssh = expect_boolean(path, value)
33
33
  when 'before' then
34
34
  @config.before = maybe_step(path, value)
35
- when 'forward_env' then
36
- @config.forward_env = expect_array(path, value)
35
+ when 'env', 'forward_env' then
36
+ wanted_key(path, 'env', key)
37
+ @config.env = expect_env_hash(path, value)
37
38
  when 'export_dir' then
38
39
  @config.export_dir = maybe_string(path, value)
39
40
  when 'link', 'links' then
40
41
  @config.links = expect_links(path, value)
41
42
  when 'run', 'command' then
42
43
  wanted_key(path, 'run', key)
43
- @config.run = expect_string(path, value)
44
+ @config.run = expect_run(path, value)
44
45
  else
45
46
  unknown_key(path)
46
47
  end
@@ -50,19 +51,25 @@ module CIDE
50
51
 
51
52
  protected
52
53
 
54
+ def warn(message)
55
+ @config.warnings << message
56
+ end
57
+
58
+ def error(message)
59
+ @config.errors << message
60
+ end
61
+
53
62
  def wanted_key(path, wanted_key, key)
54
63
  return if key == wanted_key
55
- @config.warnings <<
56
- "#{path} is deprecated. use '#{wanted_key}' instead."
64
+ warn "#{path} is deprecated. use '#{wanted_key}' instead."
57
65
  end
58
66
 
59
67
  def unknown_key(path)
60
- @config.warnings << "Unknown key #{path}"
68
+ warn "Unknown key #{path}"
61
69
  end
62
70
 
63
71
  def type_error(path, wanted_type, value)
64
- @config.errors <<
65
- "expected #{path} to be a #{wanted_type} but got a #{value.class}"
72
+ error "expected #{path} to be a #{wanted_type} but got a #{value.class}"
66
73
  end
67
74
 
68
75
  def expect_string(path, value)
@@ -77,7 +84,7 @@ module CIDE
77
84
 
78
85
  def maybe_string(path, value)
79
86
  case value
80
- when String, Symbol
87
+ when String, Symbol, Integer
81
88
  value.to_s
82
89
  when nil
83
90
  nil
@@ -89,7 +96,7 @@ module CIDE
89
96
 
90
97
  def maybe_step(path, value)
91
98
  case value
92
- when String, Symbol, Array then
99
+ when String, Symbol, Integer, Array then
93
100
  load_step(path, run: value)
94
101
  when Hash then
95
102
  load_step(path, value)
@@ -109,10 +116,11 @@ module CIDE
109
116
  case key
110
117
  when 'run' then
111
118
  step.run = expect_array(path_, value)
112
- when 'forward_env' then
113
- step.forward_env = expect_array(path_, value)
119
+ when 'env', 'forward_env' then
120
+ wanted_key(path_, 'env', key)
121
+ step.env = expect_env_hash(path_, value)
114
122
  when 'add' then
115
- step.add = expect_array(path_, value)
123
+ step.add = expect_adds(path_, value)
116
124
  else
117
125
  unknown_key(path_)
118
126
  end
@@ -123,7 +131,7 @@ module CIDE
123
131
  def expect_links(path, value)
124
132
  array = []
125
133
  case value
126
- when String, Symbol, Hash then
134
+ when String, Symbol, Integer, Hash then
127
135
  array << expect_link(path, value)
128
136
  when Array then
129
137
  value.compact.each_with_index do |value_, i|
@@ -139,8 +147,8 @@ module CIDE
139
147
 
140
148
  def expect_link(path, value)
141
149
  case value
142
- when String, Symbol then
143
- load_link(path, name: value, image: value)
150
+ when String, Symbol, Integer then
151
+ load_link(path, image: value)
144
152
  when Hash
145
153
  load_link(path, value)
146
154
  else
@@ -160,14 +168,14 @@ module CIDE
160
168
  wanted_key(path_, 'image', key)
161
169
  link.image = expect_string(path_, value)
162
170
  when 'env' then
163
- link.env = expect_env(path_, value)
171
+ link.env = expect_env_hash(path_, value)
164
172
  when 'run' then
165
173
  link.run = maybe_string(path_, value)
166
174
  else
167
175
  unknown_key(path_)
168
176
  end
169
177
  end
170
- link.name ||= link.image
178
+ link.name ||= link.image.split(':').first.split('/').last if link.image
171
179
  link.image ||= link.name
172
180
  if link.name.nil?
173
181
  type_error(
@@ -199,7 +207,7 @@ module CIDE
199
207
  value.compact.each_with_index do |value_, i|
200
208
  array << expect_string(path.append(i), value_)
201
209
  end
202
- when String, Symbol then
210
+ when String, Symbol, Integer then
203
211
  array << value.to_s
204
212
  when nil then
205
213
  # nothing to do
@@ -209,18 +217,90 @@ module CIDE
209
217
  array.compact
210
218
  end
211
219
 
212
- def expect_env(path, value)
220
+ def expect_adds(path, value)
221
+ array = []
213
222
  case value
223
+ when Array then
224
+ value.compact.each_with_index do |value_, i|
225
+ str = expect_string(path.append(i), value_)
226
+ array << load_add_str(str)
227
+ end
228
+ when Hash then
229
+ value.each_pair do |key_, value_|
230
+ src = expect_array(path.append(key_), value_)
231
+ array << Config::FileAdd.new(src: src, dest: key_.to_s)
232
+ end
233
+ when String, Symbol, Integer then
234
+ array << load_add_str(value.to_s)
235
+ when nil then
236
+ # nothing to do
237
+ else
238
+ type_error(path, 'arrays of string, hash, string or nil', value)
239
+ end
240
+ array.compact
241
+ end
242
+
243
+ def load_add_str(str)
244
+ Config::FileAdd.new(
245
+ src: [str],
246
+ dest: str,
247
+ )
248
+ end
249
+
250
+ def expect_env(path, key)
251
+ str = expect_string(path, key)
252
+ return nil if str == ''
253
+ value = ENV[str]
254
+ error "Missing environment variable #{key} in #{path}" if value.nil?
255
+ value
256
+ end
257
+
258
+ def expect_env_hash(path, value)
259
+ hash = {}
260
+ case value
261
+ when String, Symbol, Integer
262
+ key1 = value
263
+ value1 = expect_env(path, key1)
264
+ hash[key1] = value1 if value1
265
+ when Array then
266
+ value.compact.each_with_index do |key, i|
267
+ value_ = expect_env(path.append(i), key)
268
+ hash[key.to_s] = value_ if value_
269
+ end
214
270
  when Hash then
215
- hash = {}
216
271
  value.each_pair do |key, value_|
217
- hash[key.to_s] = expect_string(path.append(key.to_s), value_)
272
+ key = key.to_s
273
+ path_ = path.append(key)
274
+ if value_.nil?
275
+ value_ = expect_env(path_, key)
276
+ else
277
+ value_ = expect_string(path_, value_)
278
+ end
279
+ hash[key.to_s] = value_ if value_
218
280
  end
219
- hash
220
281
  else
221
- type_error(path, 'hash', value)
222
- {}
282
+ type_error(path, 'hash or array of keys or just a string', value)
283
+ end
284
+ hash
285
+ end
286
+
287
+ def expect_run(path, value)
288
+ array = []
289
+ has_error = false
290
+ case value
291
+ when Array
292
+ value.compact.each_with_index do |key, i|
293
+ array << expect_string(path.append(i), key)
294
+ end
295
+ when String, Symbol, Integer
296
+ array.push('sh', '-e', '-c', value.to_s)
297
+ when nil then
298
+ else
299
+ has_error = true
300
+ type_error(path, 'string or array of string', value)
223
301
  end
302
+ error("#{path} shouldn't be empty") if array.empty? && !has_error
303
+ array
224
304
  end
225
305
  end
226
306
  end
data/lib/cide/cli.rb CHANGED
@@ -5,6 +5,7 @@ require 'cide/build'
5
5
  require 'thor'
6
6
 
7
7
  require 'json'
8
+ require 'securerandom'
8
9
  require 'time'
9
10
 
10
11
  module CIDE
@@ -35,8 +36,14 @@ module CIDE
35
36
 
36
37
  method_option 'run',
37
38
  desc: 'Override the script to run',
39
+ type: :array,
38
40
  aliases: ['r'],
39
- default: nil
41
+ default: []
42
+
43
+ method_option 'pull',
44
+ desc: 'Whenever to pull for new images on build',
45
+ type: :boolean,
46
+ default: true
40
47
 
41
48
  method_option 'ssh_key',
42
49
  desc: 'Path to a ssh key to import into the docker image',
@@ -52,8 +59,10 @@ module CIDE
52
59
  banner 'Config'
53
60
  build = Build::Config.load_file CONFIG_FILE
54
61
  exit 1 if build.nil?
55
- export_dir = options.export_dir || File.dirname(build.export_dir)
56
- build.run = options.run if options.run
62
+ export_dir = options.export_dir
63
+ export_dir ||= File.dirname(build.export_dir) if build.export_dir
64
+ ssh_key = File.expand_path(options.ssh_key)
65
+ build.run = options.run unless options.run.empty?
57
66
  name = CIDE::Docker.id options.name
58
67
  tag = "cide/#{name}"
59
68
  say_status :config, build.inspect
@@ -61,15 +70,18 @@ module CIDE
61
70
  ## Build ##
62
71
  banner 'Build'
63
72
  if build.use_ssh
64
- unless File.exist?(options.ssh_key)
65
- fail ArgumentError, "SSH key #{options.ssh_key} not found"
73
+ unless File.exist?(ssh_key)
74
+ fail ArgumentError, "SSH key #{ssh_key} not found"
66
75
  end
67
-
68
- create_tmp_file SSH_CONFIG_FILE, File.read(SSH_CONFIG_PATH)
69
- create_tmp_file TEMP_SSH_KEY, File.read(build.ssh_key)
76
+ create_tmp_file TEMP_SSH_KEY, File.read(ssh_key)
70
77
  end
71
78
  create_tmp_file DOCKERFILE, build.to_dockerfile
72
- docker :build, '--force-rm', '--pull', '-f', DOCKERFILE, '-t', tag, '.'
79
+ build_options = ['--force-rm']
80
+ build_options << '--pull' if options.pull
81
+ build_options.push '-f', DOCKERFILE
82
+ build_options.push '-t', tag
83
+ build_options << '.'
84
+ docker :build, *build_options
73
85
 
74
86
  ## CI ##
75
87
  banner 'Run'
@@ -86,21 +98,26 @@ module CIDE
86
98
 
87
99
  run_options = ['--detach']
88
100
 
89
- build.forward_env.each do |env|
90
- run_options.push '--env', [env, ENV[env]].join('=')
101
+ build.env.each_pair do |key, value|
102
+ run_options.push '--env', [key, value].join('=')
91
103
  end
92
104
 
93
105
  build.links.each do |link|
94
106
  run_options.push '--link', [link.id, link.name].join(':')
95
107
  end
96
108
 
109
+ id = SecureRandom.hex
110
+ run_options.push '--name', id
111
+
97
112
  run_options.push tag
98
- run_options.push build.run
113
+ run_options.push(*build.run)
99
114
 
100
- id = docker(:run, *run_options, capture: true).strip
101
115
  containers << id
116
+ docker(:run, *run_options, capture: true).strip
102
117
  docker(:attach, id)
103
118
 
119
+ say_status :status, 'SUCCESS', :green
120
+
104
121
  ## Export ##
105
122
  return unless options.export
106
123
  banner 'Export'
@@ -109,6 +126,88 @@ module CIDE
109
126
  guest_export_dir = File.expand_path(build.export_dir, CIDE_SRC_DIR)
110
127
  host_export_dir = File.expand_path(export_dir, Dir.pwd)
111
128
  docker :cp, [id, guest_export_dir].join(':'), host_export_dir
129
+ rescue Docker::Error => ex
130
+ say_status :status, 'ERROR', :red
131
+ exit ex.exitstatus
132
+ ensure
133
+ linked_containers = containers - [id]
134
+ unless linked_containers.empty?
135
+ infos = docker(
136
+ :inspect,
137
+ *linked_containers,
138
+ capture: true,
139
+ verbose: false,
140
+ )
141
+ JSON.parse(infos).each do |info|
142
+ config = info['Config']
143
+ state = info['State']
144
+
145
+ next unless state['Dead'] || state['ExitCode'] > 0
146
+
147
+ $stderr.puts "=== Failed linked container #{info['Id']} ==="
148
+ $stderr.puts "Image: #{config['Image']}"
149
+ $stderr.puts "State: #{state.inspect}"
150
+ docker(:logs, '--tail', 20, info['Id'])
151
+ end
152
+ end
153
+ # Shutdown old containers
154
+ unless containers.empty?
155
+ docker :rm, '--force', *containers.reverse,
156
+ verbose: false,
157
+ capture: true
158
+ end
159
+ end
160
+
161
+ desc 'debug', 'Opens a debug console in the last project image'
162
+ method_option 'name',
163
+ desc: 'Name of the build',
164
+ aliases: %w(n t),
165
+ default: File.basename(Dir.pwd)
166
+ method_option 'user',
167
+ desc: 'User to run under',
168
+ default: 'cide'
169
+ def debug
170
+ containers = []
171
+
172
+ setup_docker
173
+
174
+ ## Config ##
175
+ banner 'Config'
176
+ build = Build::Config.load_file CONFIG_FILE
177
+ exit 1 if build.nil?
178
+ name = CIDE::Docker.id options.name
179
+ tag = "cide/#{name}"
180
+ say_status :config, build.inspect
181
+
182
+ ## CI ##
183
+ banner 'Run'
184
+ build.links.each do |link|
185
+ args = ['--detach']
186
+ link.env.each_pair do |key, value|
187
+ args.push('--env', [key, value].join('='))
188
+ end
189
+ args << link.image
190
+ args << link.run if link.run
191
+ link.id = docker(:run, *args, capture: true).strip
192
+ containers << link.id
193
+ end
194
+
195
+ run_options = ['--rm', '-t', '-i']
196
+
197
+ run_options.push '--user', options.user
198
+
199
+ build.env.each_pair do |key, value|
200
+ run_options.push '--env', [key, value].join('=')
201
+ end
202
+
203
+ build.links.each do |link|
204
+ run_options.push '--link', [link.id, link.name].join(':')
205
+ end
206
+
207
+ run_options.push tag
208
+ run_options.push 'bash'
209
+
210
+ docker(:run, *run_options)
112
211
  rescue Docker::Error => ex
113
212
  exit ex.exitstatus
114
213
  ensure
@@ -168,7 +267,7 @@ module CIDE
168
267
  return
169
268
  end
170
269
 
171
- docker('rmi', *old_cide_images)
270
+ docker('rmi', '--force', *old_cide_images)
172
271
  end
173
272
 
174
273
  desc 'init', "Creates a blank #{CONFIG_FILE} into the project"
@@ -181,6 +280,8 @@ module CIDE
181
280
 
182
281
  def create_tmp_file(destination, *args, &block)
183
282
  create_file(destination, *args, &block)
283
+ # Dockerfile ADD compares content and mtime, we don't want that
284
+ File.utime(1_286_701_800, 1_286_701_800, destination)
184
285
  at_exit do
185
286
  remove_file(destination, verbose: false)
186
287
  end
@@ -4,8 +4,16 @@ RUN useradd -m -U -d <%= CIDE_DIR %> cide
4
4
 
5
5
  # Install system build dependencies here
6
6
 
7
- <% as_root.each do |cmd| -%>
7
+ <% if as_root -%>
8
+ <% as_root.add.each do |file| -%>
9
+ ADD <%= file.src.join(' ') %> <%= file.dest %>
10
+ <% end -%>
11
+ <% as_root.env.each_pair do |key, value| -%>
12
+ ENV <%= key %> <%= value %>
13
+ <% end -%>
14
+ <% as_root.run.each do |cmd| -%>
8
15
  RUN <%= cmd %>
16
+ <% end -%>
9
17
  <% end -%>
10
18
 
11
19
  # Common
@@ -14,32 +22,36 @@ ENV HOME <%= CIDE_DIR %>
14
22
  WORKDIR <%= CIDE_SRC_DIR %>
15
23
 
16
24
  # SSH config
25
+
17
26
  <% if use_ssh -%>
18
- ADD ssh_config <%= File.expand_path('config', CIDE_SSH_DIR) %>
19
- RUN chmod 400 <%= File.expand_path('config', CIDE_SSH_DIR) %>
27
+ RUN mkdir <%= CIDE_SSH_DIR %>
28
+ RUN echo StrictHostKeyChecking no > <%= File.join(CIDE_SSH_DIR, 'config') %>
29
+ RUN chmod 400 <%= File.join(CIDE_SSH_DIR, 'config') %>
20
30
 
21
- ADD <%= TEMP_SSH_KEY %> <%= File.expand_path('id_rsa', CIDE_SSH_DIR) %>
31
+ ADD <%= TEMP_SSH_KEY %> <%= File.join(CIDE_SSH_DIR, 'id_rsa') %>
22
32
  RUN chmod 400 <%= File.expand_path('id_rsa', CIDE_SSH_DIR) %>
23
- RUN chmod 755 <%= CIDE_SSH_DIR %>
24
33
  RUN chown -R cide:cide <%= CIDE_DIR %>
25
- <% end %>
34
+ <% end -%>
26
35
 
27
36
  # Before
28
37
 
29
38
  <% if before -%>
30
- <% before.forward_env.each do |key| -%>
31
- ENV <%= key %> <%= ENV[key] %>
32
- <% end %>
33
39
  <% before.add.each do |file| -%>
34
- ADD <%= file %> <%= File.expand_path(file, CIDE_SRC_DIR) %>
40
+ ADD <%= file.src.join(' ') %> <%= file.dest %>
35
41
  <% end %>
36
42
  RUN chown -R cide:cide <%= CIDE_DIR %>
43
+ <% before.env.each_pair do |key, value| -%>
44
+ ENV <%= key %> <%= value %>
45
+ <% end %>
37
46
  USER cide
38
- RUN <%= before.run %>
39
- USER root
47
+ <% before.run.each do |cmd| -%>
48
+ RUN <%= cmd %>
49
+ <% end %>
40
50
  <% end -%>
41
51
 
42
52
  # Add project data
43
53
 
54
+ USER root
44
55
  ADD . <%= CIDE_SRC_DIR %>
45
56
  RUN chown -R cide:cide <%= CIDE_DIR %>
57
+ USER cide
@@ -7,17 +7,19 @@ describe "CIDE::Build::Config::Loader" do
7
7
  before do
8
8
  @config = CIDE::Build::Config.new
9
9
  @loader = CIDE::Build::ConfigLoader.new(@config)
10
+ ENV['TEST1'] = 'test1'
11
+ ENV['TEST2'] = 'test2'
10
12
  end
11
13
 
12
14
  default_config = {
13
15
  "from" => "ubuntu",
14
- "as_root" => [],
16
+ "as_root" => nil,
15
17
  "use_ssh" => false,
16
18
  "before" => nil,
17
- "forward_env" => [],
19
+ "env" => {},
18
20
  "export_dir" => nil,
19
21
  "links" => [],
20
- "run" => "script/ci",
22
+ "run" => ["script/ci"],
21
23
  }
22
24
 
23
25
  it "works - empty config" do
@@ -30,19 +32,25 @@ describe "CIDE::Build::Config::Loader" do
30
32
  it "works2 - full config" do
31
33
  full_config = {
32
34
  "from" => "god",
33
- "as_root" => ["one", "two"],
35
+ "as_root" => {
36
+ #"add" => [["http://df.ru", "zzz"], "yyy" => ["."]],
37
+ "add" => [],
38
+ "env" => {"HOME" => "/"},
39
+ "run" => ["one", "two"],
40
+ },
34
41
  "use_ssh" => true,
35
42
  "before" => {
36
- "add" => ["zzz", "yyy"],
37
- "forward_env" => ["HOME"],
43
+ #"add" => [["http://df.ru", "zzz"], "yyy" => ["."]],
44
+ "add" => [],
45
+ "env" => {"HOME" => "/"},
38
46
  "run" => ["a", "b"],
39
47
  },
40
- "forward_env" => ["PWD"],
48
+ "env" => {"HOME" => "/"},
41
49
  "links" => [
42
50
  {"name" => "redis", "image" => "redis2:foo", "env" => {"HOME" => "/"}, "run" => "redis-server"}
43
51
  ],
44
52
  "export_dir" => "./artifacts",
45
- "run" => "do/something",
53
+ "run" => ["do/something"],
46
54
  }
47
55
 
48
56
  @loader.load(full_config)
@@ -53,25 +61,31 @@ describe "CIDE::Build::Config::Loader" do
53
61
  end
54
62
 
55
63
  it "coerces things around" do
64
+ ENV['LOL'] = 'zzz'
65
+ ENV['555'] = '666'
56
66
  @loader.load(
57
67
  as_root: "xxxxx",
58
- before: :zzzzz,
59
- links: ["mysql", {image: "redis", env: {PATH: "/bin"}}, nil],
60
- forward_env: ["HOME", nil, 555]
68
+ before: {
69
+ add: {bin: 555}
70
+ },
71
+ links: ["mysql:5.6", {image: "redis", env: {PATH: "/bin", TEST1: nil}}, nil],
72
+ env: ["LOL", nil, 555],
73
+ run: "hello world",
61
74
  )
62
75
 
63
76
  expect(@config.to_h.as_json).to eq(default_config.merge(
64
- "as_root" => ["xxxxx"],
77
+ "as_root" => {"add" => [], "env" => {}, "run" => ["xxxxx"]},
65
78
  "before" => {
66
- "add" => [],
67
- "forward_env" => [],
68
- "run" => ["zzzzz"],
79
+ "add" => [{"src" => ["555"], "dest" => "bin"}],
80
+ "env" => {},
81
+ "run" => [],
69
82
  },
70
83
  "links" => [
71
- {"name" => "mysql", "image" => "mysql", "run" => nil, "env" => {}},
72
- {"name" => "redis", "image" => "redis", "run" => nil, "env" => {"PATH" => "/bin"}},
84
+ {"name" => "mysql", "image" => "mysql:5.6", "env" => {}, "run" => nil},
85
+ {"name" => "redis", "image" => "redis", "env" => {"PATH" => "/bin", "TEST1" => "test1"}, "run" => nil},
73
86
  ],
74
- "forward_env" => ["HOME", "555"],
87
+ "env" => {"LOL" => "zzz", "555" => "666"},
88
+ "run" => ["sh", "-e", "-c", "hello world"],
75
89
  ))
76
90
  expect(@config.warnings).to eq([])
77
91
  expect(@config.errors).to eq([])
@@ -79,15 +93,18 @@ describe "CIDE::Build::Config::Loader" do
79
93
 
80
94
  it "notifies deprecations" do
81
95
  @loader.load(
96
+ link: { from: "hoho" },
82
97
  image: "foo",
83
98
  command: "lol",
84
99
  zzz: 4,
85
100
  )
86
101
  expect(@config.as_json).to eq(default_config.merge(
102
+ "links" => [{"name" => "hoho", "image" => "hoho", "env" => {}, "run" => nil}],
87
103
  "from" => "foo",
88
- "run" => "lol",
104
+ "run" => ["sh", "-e", "-c", "lol"],
89
105
  ))
90
106
  expect(@config.warnings).to eq([
107
+ "link.from is deprecated. use 'image' instead.",
91
108
  "image is deprecated. use 'from' instead.",
92
109
  "command is deprecated. use 'run' instead.",
93
110
  "Unknown key zzz",
@@ -98,17 +115,17 @@ describe "CIDE::Build::Config::Loader" do
98
115
  it "reports type errors" do
99
116
  @loader.load(
100
117
  as_root: ["aaa", Time.now],
101
- forward_env: {},
118
+ env: Time.now,
102
119
  links: {},
103
120
  )
104
121
 
105
122
  expect(@config.to_h.as_json).to eq(default_config.merge(
106
- "as_root" => ["aaa", ""],
123
+ "as_root" => {"add" => [], "env" => {}, "run" => ["aaa", ""]}
107
124
  ))
108
125
  expect(@config.warnings).to eq([])
109
126
  expect(@config.errors).to eq([
110
- "expected as_root[1] to be a string but got a Time",
111
- "expected forward_env to be a array of string, string or nil but got a Hash",
127
+ "expected as_root.run[1] to be a string but got a Time",
128
+ "expected env to be a hash or array of keys or just a string but got a Time",
112
129
  "expected links to be a expected hash to either declare the name or image but got a Hash",
113
130
  ])
114
131
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cide
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - zimbatm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-15 00:00:00.000000000 Z
11
+ date: 2015-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.19.0
19
+ version: '0.19'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.19.0
26
+ version: '0.19'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: virtus
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.0.0
33
+ version: '1.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.0.0
40
+ version: '1.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -128,7 +128,6 @@ files:
128
128
  - lib/cide/default_cide.yml
129
129
  - lib/cide/docker.rb
130
130
  - lib/cide/dockerfile_template.erb
131
- - lib/cide/ssh_config
132
131
  - spec/build_config_loader_spec.rb
133
132
  - spec/spec_helper.rb
134
133
  homepage: https://github.com/zimbatm/cide
data/lib/cide/ssh_config DELETED
@@ -1,3 +0,0 @@
1
-
2
- StrictHostKeyChecking no
3
-