cide 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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
-