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 +4 -4
- data/.cide.yml +2 -2
- data/.rubocop.yml +5 -4
- data/.travis.yml +2 -1
- data/CHANGELOG.md +17 -0
- data/Gemfile.lock +3 -3
- data/README.md +53 -22
- data/cide.gemspec +3 -3
- data/lib/cide/build/config.rb +13 -6
- data/lib/cide/build/config_loader.rb +106 -26
- data/lib/cide/cli.rb +115 -14
- data/lib/cide/dockerfile_template.erb +24 -12
- data/spec/build_config_loader_spec.rb +40 -23
- metadata +6 -7
- data/lib/cide/ssh_config +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7fc120b6cb172fbbea20e8d325ba1ce9068b6e7
|
4
|
+
data.tar.gz: 43589b67baf32850c037b28e9a7ca3e597e3557e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c7f2f7cc1c0bed1b49d73f73eff33cb146f1ce4827579cbc7328d569a32de29552c71d562c4a3c196665b4b2ed153fa86eb73ffe0ec5f4460acd080904adae7
|
7
|
+
data.tar.gz: c93b2cd8962571d7f5fb66b13b71b24ab2f47f8c6dd8677d9d99af36c324637b1b897b0c095dd161d64706c00d4d3ac16b64c36f379bb2098dde15709706e137
|
data/.cide.yml
CHANGED
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
|
-
|
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
|
-
|
13
|
+
Enabled: false
|
13
14
|
|
14
15
|
# Offense count: 28
|
15
16
|
Metrics/CyclomaticComplexity:
|
16
|
-
|
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
|
-
|
22
|
+
Enabled: false
|
22
23
|
|
23
24
|
Metrics/PerceivedComplexity:
|
24
25
|
Max: 20
|
data/.travis.yml
CHANGED
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
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
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
|
46
|
+
* can use linked containers for backend dependencies like MySQL or redis
|
47
|
+
* artefact export
|
36
48
|
|
37
|
-
|
38
|
-
|
49
|
+
Missing features
|
50
|
+
----------------
|
39
51
|
|
40
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
24
|
-
s.add_runtime_dependency 'virtus', '~> 1.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'
|
data/lib/cide/build/config.rb
CHANGED
@@ -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[
|
28
|
-
attribute :
|
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,
|
53
|
+
attribute :as_root, Step, required: false
|
47
54
|
attribute :use_ssh, Boolean, default: false
|
48
55
|
attribute :before, Step, required: false
|
49
|
-
attribute :
|
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 =
|
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
|
-
|
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 =
|
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
|
-
|
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
|
-
|
68
|
+
warn "Unknown key #{path}"
|
61
69
|
end
|
62
70
|
|
63
71
|
def type_error(path, wanted_type, value)
|
64
|
-
|
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
|
-
|
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 =
|
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,
|
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 =
|
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
|
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
|
-
|
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:
|
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
|
56
|
-
|
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?(
|
65
|
-
fail ArgumentError, "SSH key #{
|
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
|
-
|
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.
|
90
|
-
run_options.push '--env', [
|
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
|
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
|
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
|
-
|
19
|
-
RUN
|
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.
|
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 %> <%=
|
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
|
-
|
39
|
-
|
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
|
-
"
|
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" =>
|
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
|
-
"
|
43
|
+
#"add" => [["http://df.ru", "zzz"], "yyy" => ["."]],
|
44
|
+
"add" => [],
|
45
|
+
"env" => {"HOME" => "/"},
|
38
46
|
"run" => ["a", "b"],
|
39
47
|
},
|
40
|
-
"
|
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:
|
59
|
-
|
60
|
-
|
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
|
-
"
|
68
|
-
"run" => [
|
79
|
+
"add" => [{"src" => ["555"], "dest" => "bin"}],
|
80
|
+
"env" => {},
|
81
|
+
"run" => [],
|
69
82
|
},
|
70
83
|
"links" => [
|
71
|
-
{"name" => "mysql", "image" => "mysql", "
|
72
|
-
{"name" => "redis", "image" => "redis", "
|
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
|
-
"
|
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
|
-
|
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
|
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.
|
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-
|
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
|
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
|
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
|
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
|
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