cide 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.editorconfig +13 -0
- data/.rspec +1 -0
- data/.rubocop.yml +28 -11
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +52 -10
- data/README.md +10 -11
- data/Rakefile +3 -1
- data/cide.gemspec +6 -3
- data/lib/cide/build/config.rb +84 -0
- data/lib/cide/build/config_loader.rb +227 -0
- data/lib/cide/build.rb +2 -0
- data/lib/cide/cli.rb +195 -0
- data/lib/cide/constants.rb +12 -0
- data/lib/cide/default_cide.yml +9 -0
- data/lib/cide/docker.rb +9 -0
- data/lib/{cide_template.erb → cide/dockerfile_template.erb} +4 -15
- data/lib/{ssh_config → cide/ssh_config} +0 -0
- data/lib/cide.rb +2 -224
- data/spec/build_config_loader_spec.rb +116 -0
- data/spec/spec_helper.rb +1 -0
- metadata +63 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c84c09a6870e5146c1a42a86e03e1d01a2dc74aa
|
4
|
+
data.tar.gz: 4a4b246cc68b455bd26c8185c6dde9aaa1a6e8b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e69cb3488d0a15e18ab4031083aa3c33c720757236d28135de981e0d98ea8dbeb95807d48a236b4617492551880049129cee649a49508288b7d9e22c0662dbf
|
7
|
+
data.tar.gz: 8b0a452b1d6a159f4362dc11674576d9c6ca34c9b6c56a0b1bdab0727b64ee07a6d13dfdcc2df93838d662048cb5817292ed5951e8d95170fe58ccbe6fc51dae
|
data/.editorconfig
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# EditorConfig is awesome: http://EditorConfig.org
|
2
|
+
|
3
|
+
# top-most EditorConfig file
|
4
|
+
root = true
|
5
|
+
|
6
|
+
# Unix-style newlines with a newline ending every file
|
7
|
+
[*]
|
8
|
+
end_of_line = lf
|
9
|
+
insert_final_newline = true
|
10
|
+
|
11
|
+
[*.rb]
|
12
|
+
indent_style = space
|
13
|
+
indent_size = 2
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour --format documentation -rspec_helper
|
data/.rubocop.yml
CHANGED
@@ -1,33 +1,50 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- 'spec/**/*'
|
4
|
+
|
5
|
+
# Rubocop is not smart enough
|
6
|
+
Metrics/AbcSize:
|
7
|
+
Max: 100
|
8
|
+
|
1
9
|
# CIDE::CLI is essentially a big dispatcher, no need to break it
|
2
10
|
# in smaller chunks.
|
3
11
|
Metrics/ClassLength:
|
4
12
|
Max: 200
|
5
13
|
|
14
|
+
# Offense count: 28
|
15
|
+
Metrics/CyclomaticComplexity:
|
16
|
+
Max: 15
|
17
|
+
|
6
18
|
# CIDE::CLI methods can be read top to bottom. No need to factor out
|
7
19
|
# functionality unless it can be shared.
|
8
20
|
Metrics/MethodLength:
|
9
21
|
Max: 60
|
10
22
|
|
23
|
+
Metrics/PerceivedComplexity:
|
24
|
+
Max: 20
|
25
|
+
|
11
26
|
# Don't align stuff vertically, bad for diffing
|
12
27
|
Style/AlignParameters:
|
13
28
|
EnforcedStyle: with_fixed_indentation
|
14
29
|
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
EnforcedStyleForMultiline: comma
|
30
|
+
# Don't obsess over missing documentation for now
|
31
|
+
Style/Documentation:
|
32
|
+
Enabled: false
|
19
33
|
|
20
|
-
#
|
21
|
-
Style/
|
34
|
+
# Don't agree with rubocop here
|
35
|
+
Style/MultilineOperationIndentation:
|
22
36
|
Enabled: false
|
23
37
|
|
24
38
|
# Prefering the short style
|
25
39
|
Style/PerlBackrefs:
|
26
40
|
Enabled: false
|
27
41
|
|
28
|
-
#
|
29
|
-
|
30
|
-
|
42
|
+
# $? is not equivalent to $CHILD_STATUS
|
43
|
+
Style/SpecialGlobalVars:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
# Allows for easy diffing
|
47
|
+
# Keep them sorted alphabetically unless a meaningful order exists
|
48
|
+
Style/TrailingComma:
|
49
|
+
EnforcedStyleForMultiline: comma
|
31
50
|
|
32
|
-
Metrics/PerceivedComplexity:
|
33
|
-
Max: 20
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,14 @@
|
|
1
1
|
|
2
|
+
0.2.0 / 2015-04-15
|
3
|
+
==================
|
4
|
+
|
5
|
+
* NEW: .cide.yml schema loader
|
6
|
+
* NEW: Support for linked containers
|
7
|
+
* NEW: Much better build output
|
8
|
+
* CHANGE: Docker version: 1.5.0+ is required
|
9
|
+
* FIX: Avoid name clashes with projects who have a Dockerfile already
|
10
|
+
* FIX: cide --export
|
11
|
+
|
2
12
|
0.1.1 / 2015-01-27
|
3
13
|
==================
|
4
14
|
|
data/Gemfile.lock
CHANGED
@@ -2,34 +2,76 @@ PATH
|
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
4
|
cide (0.1.1)
|
5
|
-
thor
|
5
|
+
thor (~> 0.19.0)
|
6
|
+
virtus (~> 1.0.0)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
9
10
|
specs:
|
11
|
+
activesupport (4.2.1)
|
12
|
+
i18n (~> 0.7)
|
13
|
+
json (~> 1.7, >= 1.7.7)
|
14
|
+
minitest (~> 5.1)
|
15
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
16
|
+
tzinfo (~> 1.1)
|
10
17
|
ast (2.0.0)
|
11
18
|
astrolabe (1.3.0)
|
12
19
|
parser (>= 2.2.0.pre.3, < 3.0)
|
13
|
-
|
20
|
+
axiom-types (0.1.1)
|
21
|
+
descendants_tracker (~> 0.0.4)
|
22
|
+
ice_nine (~> 0.11.0)
|
23
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
24
|
+
coercible (1.0.0)
|
25
|
+
descendants_tracker (~> 0.0.1)
|
26
|
+
descendants_tracker (0.0.4)
|
27
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
28
|
+
diff-lcs (1.2.5)
|
29
|
+
equalizer (0.0.11)
|
30
|
+
i18n (0.7.0)
|
31
|
+
ice_nine (0.11.1)
|
32
|
+
json (1.8.1)
|
33
|
+
minitest (5.4.3)
|
34
|
+
parser (2.2.0.3)
|
14
35
|
ast (>= 1.1, < 3.0)
|
15
|
-
|
16
|
-
powerpack (0.0.9)
|
36
|
+
powerpack (0.1.0)
|
17
37
|
rainbow (2.0.0)
|
18
|
-
rake (10.
|
19
|
-
|
38
|
+
rake (10.4.2)
|
39
|
+
rspec (3.2.0)
|
40
|
+
rspec-core (~> 3.2.0)
|
41
|
+
rspec-expectations (~> 3.2.0)
|
42
|
+
rspec-mocks (~> 3.2.0)
|
43
|
+
rspec-core (3.2.3)
|
44
|
+
rspec-support (~> 3.2.0)
|
45
|
+
rspec-expectations (3.2.1)
|
46
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
47
|
+
rspec-support (~> 3.2.0)
|
48
|
+
rspec-mocks (3.2.1)
|
49
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
50
|
+
rspec-support (~> 3.2.0)
|
51
|
+
rspec-support (3.2.2)
|
52
|
+
rubocop (0.30.0)
|
20
53
|
astrolabe (~> 1.3)
|
21
|
-
parser (>= 2.2.0.
|
22
|
-
powerpack (~> 0.
|
54
|
+
parser (>= 2.2.0.1, < 3.0)
|
55
|
+
powerpack (~> 0.1)
|
23
56
|
rainbow (>= 1.99.1, < 3.0)
|
24
57
|
ruby-progressbar (~> 1.4)
|
25
|
-
ruby-progressbar (1.
|
26
|
-
slop (3.6.0)
|
58
|
+
ruby-progressbar (1.7.5)
|
27
59
|
thor (0.19.1)
|
60
|
+
thread_safe (0.3.5)
|
61
|
+
tzinfo (1.2.2)
|
62
|
+
thread_safe (~> 0.1)
|
63
|
+
virtus (1.0.5)
|
64
|
+
axiom-types (~> 0.1)
|
65
|
+
coercible (~> 1.0)
|
66
|
+
descendants_tracker (~> 0.0, >= 0.0.3)
|
67
|
+
equalizer (~> 0.0, >= 0.0.9)
|
28
68
|
|
29
69
|
PLATFORMS
|
30
70
|
ruby
|
31
71
|
|
32
72
|
DEPENDENCIES
|
73
|
+
activesupport
|
33
74
|
cide!
|
34
75
|
rake
|
76
|
+
rspec
|
35
77
|
rubocop
|
data/README.md
CHANGED
@@ -20,7 +20,7 @@ Example
|
|
20
20
|
`.cide.yml`
|
21
21
|
```
|
22
22
|
---
|
23
|
-
|
23
|
+
from: "ruby:2.1"
|
24
24
|
as_root:
|
25
25
|
- apt-get update -qy && apt-get install -qy libxml2-dev
|
26
26
|
command: bundle && bundle exec rspec
|
@@ -32,35 +32,34 @@ Features
|
|
32
32
|
* straighforward to use, just run `cide` inside of your project
|
33
33
|
* works on OSX with boot2docker
|
34
34
|
* integrates easily with jenkins or other CI systems
|
35
|
+
* can run with linked containers
|
35
36
|
|
36
37
|
Limitations
|
37
38
|
-----------
|
38
39
|
|
39
|
-
|
40
|
-
doesn't allow referencing files outside of the directory (even with a symlink)
|
40
|
+
Docker version 1.5.0+ is required
|
41
41
|
|
42
42
|
Installation
|
43
43
|
------------
|
44
44
|
|
45
45
|
Install docker: https://docs.docker.com/installation/#installation
|
46
46
|
|
47
|
-
```
|
47
|
+
```sh
|
48
48
|
gem install cide
|
49
49
|
```
|
50
50
|
|
51
51
|
OSX docker install:
|
52
|
-
```
|
52
|
+
```sh
|
53
53
|
brew install boot2docker
|
54
54
|
boot2docker init
|
55
55
|
boot2docker up
|
56
56
|
# cide auto-detects boot2docker on OSX
|
57
57
|
```
|
58
58
|
|
59
|
-
|
60
|
-
|
59
|
+
Similar projects
|
60
|
+
----------------
|
61
|
+
|
62
|
+
* [Docker Compose](https://docs.docker.com/compose/) - Docker development environment
|
61
63
|
|
62
|
-
|
63
|
-
* schema validation
|
64
|
-
* find a way to import SSH keys
|
65
|
-
* cleaner output. on error is just outputs a backtrace
|
64
|
+
Open an issue if a project is missing.
|
66
65
|
|
data/Rakefile
CHANGED
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.2.0'
|
6
6
|
s.authors = ['zimbatm']
|
7
7
|
s.email = ['zimbatm@zimbatm.com']
|
8
8
|
s.summary = 'CI docker runner'
|
@@ -13,14 +13,17 @@ DESC
|
|
13
13
|
s.homepage = 'https://github.com/zimbatm/cide'
|
14
14
|
s.license = 'MIT'
|
15
15
|
|
16
|
-
s.executables
|
16
|
+
s.executables = ['cide']
|
17
17
|
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
18
18
|
s.test_files = `git ls-files spec`.split($INPUT_RECORD_SEPARATOR)
|
19
19
|
s.require_paths = ['lib']
|
20
20
|
|
21
21
|
s.required_ruby_version = '>= 1.9.3'
|
22
22
|
|
23
|
-
s.add_runtime_dependency 'thor'
|
23
|
+
s.add_runtime_dependency 'thor', '~> 0.19.0'
|
24
|
+
s.add_runtime_dependency 'virtus', '~> 1.0.0'
|
24
25
|
s.add_development_dependency 'rake'
|
25
26
|
s.add_development_dependency 'rubocop'
|
27
|
+
s.add_development_dependency 'rspec'
|
28
|
+
s.add_development_dependency 'activesupport'
|
26
29
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'virtus'
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
require 'cide/build/config_loader'
|
7
|
+
|
8
|
+
module CIDE
|
9
|
+
module Build
|
10
|
+
class Config
|
11
|
+
DOCKERFILE_TEMPLATE =
|
12
|
+
File.expand_path('../../dockerfile_template.erb', __FILE__)
|
13
|
+
|
14
|
+
module NiceInspect
|
15
|
+
def inspect
|
16
|
+
out = []
|
17
|
+
attributes.each_pair do |key, value|
|
18
|
+
out << "#{key}=#{value.inspect}"
|
19
|
+
end
|
20
|
+
"<#{out.join(' ')}>"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Step
|
25
|
+
include Virtus.model(strict: true)
|
26
|
+
include NiceInspect
|
27
|
+
attribute :add, Array[String], default: []
|
28
|
+
attribute :forward_env, Array[String], default: []
|
29
|
+
attribute :run, Array[String], default: []
|
30
|
+
end
|
31
|
+
|
32
|
+
class Link
|
33
|
+
include Virtus.model
|
34
|
+
include NiceInspect
|
35
|
+
attribute :name, String
|
36
|
+
attribute :image, String
|
37
|
+
attribute :env, Hash[String, String]
|
38
|
+
attribute :run, String
|
39
|
+
# Container ID added after the fact
|
40
|
+
attr_accessor :id
|
41
|
+
end
|
42
|
+
|
43
|
+
include Virtus.model(strict: true)
|
44
|
+
include NiceInspect
|
45
|
+
attribute :from, String, default: 'ubuntu'
|
46
|
+
attribute :as_root, Array[String], default: []
|
47
|
+
attribute :use_ssh, Boolean, default: false
|
48
|
+
attribute :before, Step, required: false
|
49
|
+
attribute :forward_env, Array[String], default: []
|
50
|
+
attribute :export_dir, String, required: false
|
51
|
+
attribute :links, Array[Link], default: []
|
52
|
+
attribute :run, String, default: 'script/ci'
|
53
|
+
|
54
|
+
attr_reader :warnings, :errors
|
55
|
+
|
56
|
+
def initialize(*)
|
57
|
+
super
|
58
|
+
@warnings = []
|
59
|
+
@errors = []
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_dockerfile
|
63
|
+
ERB.new(File.read(DOCKERFILE_TEMPLATE), nil, '<>-').result(binding)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.load_file(file_path, output = $stderr)
|
67
|
+
obj = new
|
68
|
+
loader = ConfigLoader.new(obj)
|
69
|
+
loader.load YAML.load_file(file_path)
|
70
|
+
|
71
|
+
obj.warnings.each do |warn|
|
72
|
+
output.puts "WARN: #{warn}"
|
73
|
+
end
|
74
|
+
|
75
|
+
obj.errors.each do |error|
|
76
|
+
output.puts "ERROR: #{error}"
|
77
|
+
end
|
78
|
+
|
79
|
+
return obj if obj.errors.empty?
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
module CIDE
|
2
|
+
module Build
|
3
|
+
class ConfigLoader
|
4
|
+
class Path
|
5
|
+
attr_reader :to_s
|
6
|
+
def initialize(str)
|
7
|
+
@to_s = str.to_s
|
8
|
+
end
|
9
|
+
|
10
|
+
def append(value)
|
11
|
+
self.class.new(
|
12
|
+
@to_s + (value.is_a?(Integer) ? "[#{value}]" : ".#{value}"),
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(config)
|
18
|
+
@config = config
|
19
|
+
end
|
20
|
+
|
21
|
+
def load(data)
|
22
|
+
data.each_pair do |key, value|
|
23
|
+
key = key.to_s
|
24
|
+
path = Path.new(key)
|
25
|
+
case key
|
26
|
+
when 'from', 'image' then
|
27
|
+
wanted_key(path, 'from', key)
|
28
|
+
@config.from = expect_string(path, value)
|
29
|
+
when 'as_root' then
|
30
|
+
@config.as_root = expect_array(path, value)
|
31
|
+
when 'use_ssh' then
|
32
|
+
@config.use_ssh = expect_boolean(path, value)
|
33
|
+
when 'before' then
|
34
|
+
@config.before = maybe_step(path, value)
|
35
|
+
when 'forward_env' then
|
36
|
+
@config.forward_env = expect_array(path, value)
|
37
|
+
when 'export_dir' then
|
38
|
+
@config.export_dir = maybe_string(path, value)
|
39
|
+
when 'link', 'links' then
|
40
|
+
@config.links = expect_links(path, value)
|
41
|
+
when 'run', 'command' then
|
42
|
+
wanted_key(path, 'run', key)
|
43
|
+
@config.run = expect_string(path, value)
|
44
|
+
else
|
45
|
+
unknown_key(path)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
@config
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def wanted_key(path, wanted_key, key)
|
54
|
+
return if key == wanted_key
|
55
|
+
@config.warnings <<
|
56
|
+
"#{path} is deprecated. use '#{wanted_key}' instead."
|
57
|
+
end
|
58
|
+
|
59
|
+
def unknown_key(path)
|
60
|
+
@config.warnings << "Unknown key #{path}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def type_error(path, wanted_type, value)
|
64
|
+
@config.errors <<
|
65
|
+
"expected #{path} to be a #{wanted_type} but got a #{value.class}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def expect_string(path, value)
|
69
|
+
case value
|
70
|
+
when String, Symbol, Integer
|
71
|
+
value.to_s
|
72
|
+
else
|
73
|
+
type_error(path, 'string', value)
|
74
|
+
''
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def maybe_string(path, value)
|
79
|
+
case value
|
80
|
+
when String, Symbol
|
81
|
+
value.to_s
|
82
|
+
when nil
|
83
|
+
nil
|
84
|
+
else
|
85
|
+
type_error(path, 'string or nil', value)
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def maybe_step(path, value)
|
91
|
+
case value
|
92
|
+
when String, Symbol, Array then
|
93
|
+
load_step(path, run: value)
|
94
|
+
when Hash then
|
95
|
+
load_step(path, value)
|
96
|
+
when nil then
|
97
|
+
nil
|
98
|
+
else
|
99
|
+
type_error(path, 'string, array, hash or nil', value)
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def load_step(path, data)
|
105
|
+
step = Config::Step.new
|
106
|
+
data.each_pair do |key, value|
|
107
|
+
key = key.to_s
|
108
|
+
path_ = path.append(key)
|
109
|
+
case key
|
110
|
+
when 'run' then
|
111
|
+
step.run = expect_array(path_, value)
|
112
|
+
when 'forward_env' then
|
113
|
+
step.forward_env = expect_array(path_, value)
|
114
|
+
when 'add' then
|
115
|
+
step.add = expect_array(path_, value)
|
116
|
+
else
|
117
|
+
unknown_key(path_)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
step
|
121
|
+
end
|
122
|
+
|
123
|
+
def expect_links(path, value)
|
124
|
+
array = []
|
125
|
+
case value
|
126
|
+
when String, Symbol, Hash then
|
127
|
+
array << expect_link(path, value)
|
128
|
+
when Array then
|
129
|
+
value.compact.each_with_index do |value_, i|
|
130
|
+
array << expect_link(path.append(i), value_)
|
131
|
+
end
|
132
|
+
when nil then
|
133
|
+
# nothing to do
|
134
|
+
else
|
135
|
+
type_error(path, 'string, array of links, hash or nil', value)
|
136
|
+
end
|
137
|
+
array.compact
|
138
|
+
end
|
139
|
+
|
140
|
+
def expect_link(path, value)
|
141
|
+
case value
|
142
|
+
when String, Symbol then
|
143
|
+
load_link(path, name: value, image: value)
|
144
|
+
when Hash
|
145
|
+
load_link(path, value)
|
146
|
+
else
|
147
|
+
type_error(path, 'string or hash expected', value)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def load_link(path, data)
|
152
|
+
link = Config::Link.new
|
153
|
+
data.each_pair do |key, value|
|
154
|
+
key = key.to_s
|
155
|
+
path_ = path.append(key)
|
156
|
+
case key
|
157
|
+
when 'name' then
|
158
|
+
link.name = expect_string(path_, value)
|
159
|
+
when 'image', 'from' then
|
160
|
+
wanted_key(path_, 'image', key)
|
161
|
+
link.image = expect_string(path_, value)
|
162
|
+
when 'env' then
|
163
|
+
link.env = expect_env(path_, value)
|
164
|
+
when 'run' then
|
165
|
+
link.run = maybe_string(path_, value)
|
166
|
+
else
|
167
|
+
unknown_key(path_)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
link.name ||= link.image
|
171
|
+
link.image ||= link.name
|
172
|
+
if link.name.nil?
|
173
|
+
type_error(
|
174
|
+
path,
|
175
|
+
'expected hash to either declare the name or image',
|
176
|
+
data,
|
177
|
+
)
|
178
|
+
return nil
|
179
|
+
end
|
180
|
+
link
|
181
|
+
end
|
182
|
+
|
183
|
+
def expect_boolean(path, value)
|
184
|
+
case value
|
185
|
+
when true then
|
186
|
+
true
|
187
|
+
when false then
|
188
|
+
false
|
189
|
+
else
|
190
|
+
type_error(path, 'boolean', value)
|
191
|
+
false
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def expect_array(path, value)
|
196
|
+
array = []
|
197
|
+
case value
|
198
|
+
when Array then
|
199
|
+
value.compact.each_with_index do |value_, i|
|
200
|
+
array << expect_string(path.append(i), value_)
|
201
|
+
end
|
202
|
+
when String, Symbol then
|
203
|
+
array << value.to_s
|
204
|
+
when nil then
|
205
|
+
# nothing to do
|
206
|
+
else
|
207
|
+
type_error(path, 'array of string, string or nil', value)
|
208
|
+
end
|
209
|
+
array.compact
|
210
|
+
end
|
211
|
+
|
212
|
+
def expect_env(path, value)
|
213
|
+
case value
|
214
|
+
when Hash then
|
215
|
+
hash = {}
|
216
|
+
value.each_pair do |key, value_|
|
217
|
+
hash[key.to_s] = expect_string(path.append(key.to_s), value_)
|
218
|
+
end
|
219
|
+
hash
|
220
|
+
else
|
221
|
+
type_error(path, 'hash', value)
|
222
|
+
{}
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
data/lib/cide/build.rb
ADDED
data/lib/cide/cli.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'cide/constants'
|
2
|
+
require 'cide/docker'
|
3
|
+
require 'cide/build'
|
4
|
+
|
5
|
+
require 'thor'
|
6
|
+
|
7
|
+
require 'json'
|
8
|
+
require 'time'
|
9
|
+
|
10
|
+
module CIDE
|
11
|
+
# Command-line option-parsing and execution for cide
|
12
|
+
class CLI < Thor
|
13
|
+
include CIDE::Docker
|
14
|
+
include Thor::Actions
|
15
|
+
add_runtime_options!
|
16
|
+
|
17
|
+
default_command 'build'
|
18
|
+
|
19
|
+
desc 'build', 'Builds an image and executes the run script'
|
20
|
+
|
21
|
+
method_option 'name',
|
22
|
+
desc: 'Name of the build',
|
23
|
+
aliases: %w(n t),
|
24
|
+
default: File.basename(Dir.pwd)
|
25
|
+
|
26
|
+
method_option 'export',
|
27
|
+
desc: 'Whenever to export artifacts',
|
28
|
+
type: :boolean,
|
29
|
+
default: nil
|
30
|
+
|
31
|
+
method_option 'export_dir',
|
32
|
+
desc: 'Change the ouput directory on the host',
|
33
|
+
aliases: %w(o host_export_dir),
|
34
|
+
default: nil
|
35
|
+
|
36
|
+
method_option 'run',
|
37
|
+
desc: 'Override the script to run',
|
38
|
+
aliases: ['r'],
|
39
|
+
default: nil
|
40
|
+
|
41
|
+
method_option 'ssh_key',
|
42
|
+
desc: 'Path to a ssh key to import into the docker image',
|
43
|
+
aliases: ['s'],
|
44
|
+
default: '~/.ssh/id_rsa'
|
45
|
+
|
46
|
+
def build
|
47
|
+
containers = []
|
48
|
+
|
49
|
+
setup_docker
|
50
|
+
|
51
|
+
## Config ##
|
52
|
+
banner 'Config'
|
53
|
+
build = Build::Config.load_file CONFIG_FILE
|
54
|
+
exit 1 if build.nil?
|
55
|
+
export_dir = options.export_dir || File.dirname(build.export_dir)
|
56
|
+
build.run = options.run if options.run
|
57
|
+
name = CIDE::Docker.id options.name
|
58
|
+
tag = "cide/#{name}"
|
59
|
+
say_status :config, build.inspect
|
60
|
+
|
61
|
+
## Build ##
|
62
|
+
banner 'Build'
|
63
|
+
if build.use_ssh
|
64
|
+
unless File.exist?(options.ssh_key)
|
65
|
+
fail ArgumentError, "SSH key #{options.ssh_key} not found"
|
66
|
+
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)
|
70
|
+
end
|
71
|
+
create_tmp_file DOCKERFILE, build.to_dockerfile
|
72
|
+
docker :build, '--force-rm', '--pull', '-f', DOCKERFILE, '-t', tag, '.'
|
73
|
+
|
74
|
+
## CI ##
|
75
|
+
banner 'Run'
|
76
|
+
build.links.each do |link|
|
77
|
+
args = ['--detach']
|
78
|
+
link.env.each_pair do |key, value|
|
79
|
+
args.push('--env', [key, value].join('='))
|
80
|
+
end
|
81
|
+
args << link.image
|
82
|
+
args << link.run if link.run
|
83
|
+
link.id = docker(:run, *args, capture: true).strip
|
84
|
+
containers << link.id
|
85
|
+
end
|
86
|
+
|
87
|
+
run_options = ['--detach']
|
88
|
+
|
89
|
+
build.forward_env.each do |env|
|
90
|
+
run_options.push '--env', [env, ENV[env]].join('=')
|
91
|
+
end
|
92
|
+
|
93
|
+
build.links.each do |link|
|
94
|
+
run_options.push '--link', [link.id, link.name].join(':')
|
95
|
+
end
|
96
|
+
|
97
|
+
run_options.push tag
|
98
|
+
run_options.push build.run
|
99
|
+
|
100
|
+
id = docker(:run, *run_options, capture: true).strip
|
101
|
+
containers << id
|
102
|
+
docker(:attach, id)
|
103
|
+
|
104
|
+
## Export ##
|
105
|
+
return unless options.export
|
106
|
+
banner 'Export'
|
107
|
+
fail 'export flag set but no export_dir given' if build.export_dir.nil?
|
108
|
+
|
109
|
+
guest_export_dir = File.expand_path(build.export_dir, CIDE_SRC_DIR)
|
110
|
+
host_export_dir = File.expand_path(export_dir, Dir.pwd)
|
111
|
+
docker :cp, [id, guest_export_dir].join(':'), host_export_dir
|
112
|
+
rescue Docker::Error => ex
|
113
|
+
exit ex.exitstatus
|
114
|
+
ensure
|
115
|
+
# Shutdown old containers
|
116
|
+
unless containers.empty?
|
117
|
+
docker :rm, '--force', *containers.reverse,
|
118
|
+
verbose: false,
|
119
|
+
capture: true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
desc 'clean', 'Removes old containers'
|
124
|
+
method_option 'days',
|
125
|
+
desc: 'Number of days to keep the images',
|
126
|
+
default: 7,
|
127
|
+
type: :numeric
|
128
|
+
method_option 'count',
|
129
|
+
desc: 'Maximum number of images to keep',
|
130
|
+
default: 10,
|
131
|
+
type: :numeric
|
132
|
+
def clean
|
133
|
+
setup_docker
|
134
|
+
|
135
|
+
days_to_keep = options[:days]
|
136
|
+
max_images = options[:count]
|
137
|
+
|
138
|
+
x = docker('images', '--no-trunc', capture: true)
|
139
|
+
iter = x.lines.each
|
140
|
+
iter.next
|
141
|
+
cide_image_ids = iter
|
142
|
+
.map { |line| line.split(/\s+/) }
|
143
|
+
.select { |line| line[0] =~ %r{^cide/} || line[0] == '<none>' }
|
144
|
+
.map { |line| line[2] }
|
145
|
+
|
146
|
+
if cide_image_ids.empty?
|
147
|
+
puts 'No images found to be cleaned'
|
148
|
+
return
|
149
|
+
end
|
150
|
+
|
151
|
+
x = docker('inspect', *cide_image_ids, capture: true)
|
152
|
+
cide_images = JSON.parse(x.strip)
|
153
|
+
.each { |image| image['Created'] = Time.iso8601(image['Created']) }
|
154
|
+
.sort { |a, b| a['Created'] <=> b['Created'] }
|
155
|
+
|
156
|
+
if cide_images.size > max_images
|
157
|
+
old_cide_images = cide_images[0..-max_images]
|
158
|
+
.map { |image| image['Id'] }
|
159
|
+
else
|
160
|
+
old_times = Time.now - (days_to_keep * 24 * 60 * 60)
|
161
|
+
old_cide_images = cide_images
|
162
|
+
.select { |image| image['Created'] < old_times }
|
163
|
+
.map { |image| image['Id'] }
|
164
|
+
end
|
165
|
+
|
166
|
+
if old_cide_images.empty?
|
167
|
+
puts 'No images found to be cleaned'
|
168
|
+
return
|
169
|
+
end
|
170
|
+
|
171
|
+
docker('rmi', *old_cide_images)
|
172
|
+
end
|
173
|
+
|
174
|
+
desc 'init', "Creates a blank #{CONFIG_FILE} into the project"
|
175
|
+
def init
|
176
|
+
puts "Creating #{CONFIG_FILE} with default values"
|
177
|
+
create_file CONFIG_FILE, File.read(DEFAULT_CIDEFILE)
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
|
182
|
+
def create_tmp_file(destination, *args, &block)
|
183
|
+
create_file(destination, *args, &block)
|
184
|
+
at_exit do
|
185
|
+
remove_file(destination, verbose: false)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
LINE_SIZE = 78.0
|
190
|
+
def banner(text)
|
191
|
+
pad = (LINE_SIZE - text.size - 4) / 2
|
192
|
+
puts '=' * pad.floor + "[ #{text} ]" + '=' * pad.ceil
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module CIDE
|
2
|
+
dir = File.expand_path('..', __FILE__)
|
3
|
+
DOCKERFILE = 'Dockerfile.cide'
|
4
|
+
DEFAULT_CIDEFILE = File.join(dir, 'default_cide.yml')
|
5
|
+
TEMP_SSH_KEY = 'id_rsa.tmp'
|
6
|
+
SSH_CONFIG_FILE = 'ssh_config'
|
7
|
+
SSH_CONFIG_PATH = File.join(dir, SSH_CONFIG_FILE)
|
8
|
+
CONFIG_FILE = '.cide.yml'
|
9
|
+
CIDE_DIR = '/cide'
|
10
|
+
CIDE_SRC_DIR = File.join(CIDE_DIR, 'src')
|
11
|
+
CIDE_SSH_DIR = File.join(CIDE_DIR, '.ssh')
|
12
|
+
end
|
data/lib/cide/docker.rb
CHANGED
@@ -17,6 +17,8 @@ module CIDE
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
class VersionError < StandardError; end
|
21
|
+
|
20
22
|
def docker(*args, **opts)
|
21
23
|
setup_docker
|
22
24
|
|
@@ -42,6 +44,13 @@ module CIDE
|
|
42
44
|
.lines
|
43
45
|
.grep(/export (\w+)=(.*)/) { ENV[$1] = $2.strip }
|
44
46
|
end
|
47
|
+
|
48
|
+
# Check docker version
|
49
|
+
unless `docker version 2>/dev/null` =~ /Client version: ([^\s]+)/
|
50
|
+
fail VersionError, 'Unknown docker version'
|
51
|
+
end
|
52
|
+
fail VersionError, "Docker version #{$1} too old" if $1 < '1.5.0'
|
53
|
+
|
45
54
|
true
|
46
55
|
)
|
47
56
|
end
|
@@ -26,16 +26,16 @@ RUN chown -R cide:cide <%= CIDE_DIR %>
|
|
26
26
|
|
27
27
|
# Before
|
28
28
|
|
29
|
-
<% if before
|
30
|
-
<% before
|
29
|
+
<% if before -%>
|
30
|
+
<% before.forward_env.each do |key| -%>
|
31
31
|
ENV <%= key %> <%= ENV[key] %>
|
32
32
|
<% end %>
|
33
|
-
<% before
|
33
|
+
<% before.add.each do |file| -%>
|
34
34
|
ADD <%= file %> <%= File.expand_path(file, CIDE_SRC_DIR) %>
|
35
35
|
<% end %>
|
36
36
|
RUN chown -R cide:cide <%= CIDE_DIR %>
|
37
37
|
USER cide
|
38
|
-
RUN <%= before
|
38
|
+
RUN <%= before.run %>
|
39
39
|
USER root
|
40
40
|
<% end -%>
|
41
41
|
|
@@ -43,14 +43,3 @@ USER root
|
|
43
43
|
|
44
44
|
ADD . <%= CIDE_SRC_DIR %>
|
45
45
|
RUN chown -R cide:cide <%= CIDE_DIR %>
|
46
|
-
|
47
|
-
# ENV
|
48
|
-
|
49
|
-
<% forward_env.to_a.each do |key| -%>
|
50
|
-
ENV <%= key %> <%= ENV[key] %>
|
51
|
-
<% end %>
|
52
|
-
|
53
|
-
# Test !
|
54
|
-
|
55
|
-
USER cide
|
56
|
-
RUN <%= run %>
|
File without changes
|
data/lib/cide.rb
CHANGED
@@ -1,229 +1,7 @@
|
|
1
|
-
require 'erb'
|
2
|
-
require 'json'
|
3
|
-
require 'optparse'
|
4
|
-
require 'time'
|
5
|
-
require 'yaml'
|
6
|
-
|
7
|
-
require 'thor'
|
8
|
-
|
9
|
-
require 'cide/docker'
|
10
|
-
|
11
1
|
# CIDE is a Continuous Integration Docker Environment runner
|
12
2
|
#
|
13
3
|
# The juicy bits are defined in CIDE::CLI
|
14
4
|
module CIDE
|
15
|
-
DIR = File.expand_path('..', __FILE__)
|
16
|
-
DOCKERFILE = 'Dockerfile'
|
17
|
-
TEMP_SSH_KEY = 'id_rsa.tmp'
|
18
|
-
DOCKERFILE_TEMPLATE = File.join(DIR, 'cide_template.erb')
|
19
|
-
SSH_CONFIG_FILE = 'ssh_config'
|
20
|
-
SSH_CONFIG_PATH = File.join(DIR, SSH_CONFIG_FILE)
|
21
|
-
CONFIG_FILE = '.cide.yml'
|
22
|
-
|
23
|
-
CIDE_DIR = '/cide'
|
24
|
-
CIDE_SRC_DIR = File.join(CIDE_DIR, '/src')
|
25
|
-
CIDE_SSH_DIR = File.join(CIDE_DIR, '/.ssh')
|
26
|
-
|
27
|
-
def self.struct(opts = {}, &block)
|
28
|
-
Class.new(Struct.new(*opts.keys), &block).new(*opts.values)
|
29
|
-
end
|
30
|
-
|
31
|
-
DefaultConfig = struct(
|
32
|
-
name: nil,
|
33
|
-
from: 'ubuntu',
|
34
|
-
as_root: [],
|
35
|
-
forward_env: [],
|
36
|
-
before: {},
|
37
|
-
export: false,
|
38
|
-
export_dir: './artifacts',
|
39
|
-
host_export_dir: nil,
|
40
|
-
run: 'script/ci',
|
41
|
-
use_ssh: false,
|
42
|
-
ssh_key: '~/.ssh/id_rsa',
|
43
|
-
) do
|
44
|
-
|
45
|
-
alias_method :image=, :from=
|
46
|
-
alias_method :command=, :run=
|
47
|
-
|
48
|
-
def name=(str)
|
49
|
-
super CIDE::Docker.id(str)
|
50
|
-
end
|
51
|
-
|
52
|
-
def ssh_key_path
|
53
|
-
File.expand_path(ssh_key)
|
54
|
-
end
|
55
|
-
|
56
|
-
def to_dockerfile
|
57
|
-
ERB.new(File.read(DOCKERFILE_TEMPLATE), nil, '<>-').result(binding)
|
58
|
-
end
|
59
|
-
|
60
|
-
def merge!(opts = {})
|
61
|
-
opts.each_pair { |k, v| public_send("#{k}=", v) }
|
62
|
-
self
|
63
|
-
end
|
64
|
-
|
65
|
-
def merge(opts = {})
|
66
|
-
dup.merge!(opts)
|
67
|
-
end
|
68
|
-
|
69
|
-
def to_yaml
|
70
|
-
members.each_with_object({}) do |k, obj|
|
71
|
-
v = self[k]
|
72
|
-
obj[k.to_s] = v unless v.nil?
|
73
|
-
end.to_yaml
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# Command-line option-parsing and execution for cide
|
78
|
-
class CLI < Thor
|
79
|
-
include CIDE::Docker
|
80
|
-
include Thor::Actions
|
81
|
-
add_runtime_options!
|
82
|
-
|
83
|
-
default_command 'build'
|
84
|
-
|
85
|
-
desc 'build', 'Builds an image and executes the run script'
|
86
|
-
|
87
|
-
method_option 'name',
|
88
|
-
desc: 'Name of the build',
|
89
|
-
aliases: %w(n t),
|
90
|
-
default: nil
|
91
|
-
|
92
|
-
method_option 'host_export_dir',
|
93
|
-
desc: 'Output directory on host to put build artefacts in',
|
94
|
-
aliases: ['o'],
|
95
|
-
default: nil
|
96
|
-
|
97
|
-
method_option 'export',
|
98
|
-
desc: 'Are we expecting to export artifacts',
|
99
|
-
type: :boolean,
|
100
|
-
default: nil
|
101
|
-
|
102
|
-
method_option 'run',
|
103
|
-
desc: 'The script to run',
|
104
|
-
aliases: ['r'],
|
105
|
-
default: nil
|
106
|
-
|
107
|
-
method_option 'ssh_key',
|
108
|
-
desc: 'Path to a ssh key to import into the docker image',
|
109
|
-
aliases: ['s'],
|
110
|
-
default: '~/.ssh/id_rsa'
|
111
|
-
|
112
|
-
def build
|
113
|
-
setup_docker
|
114
|
-
|
115
|
-
config = DefaultConfig.merge YAML.load_file(CONFIG_FILE)
|
116
|
-
options.each_pair do |k, v|
|
117
|
-
config[k] = v unless v.nil?
|
118
|
-
end
|
119
|
-
config.name ||= File.basename(Dir.pwd)
|
120
|
-
config.host_export_dir ||= config.export_dir
|
121
|
-
|
122
|
-
tag = "cide/#{config.name}"
|
123
|
-
|
124
|
-
if config.use_ssh
|
125
|
-
unless File.exist?(config.ssh_key_path)
|
126
|
-
fail MalformattedArgumentError, "SSH key #{config.ssh_key} not found"
|
127
|
-
end
|
128
|
-
|
129
|
-
create_tmp_file SSH_CONFIG_FILE, File.read(SSH_CONFIG_PATH)
|
130
|
-
create_tmp_file TEMP_SSH_KEY, File.read(config.ssh_key_path)
|
131
|
-
end
|
132
|
-
|
133
|
-
say_status :config, config.to_h
|
134
|
-
|
135
|
-
create_tmp_file DOCKERFILE, config.to_dockerfile
|
136
|
-
|
137
|
-
docker :build, '--force-rm', '-t', tag, '.'
|
138
|
-
|
139
|
-
return unless config.export
|
140
|
-
|
141
|
-
unless config.export_dir
|
142
|
-
fail 'Fail: export flag set but no export dir given'
|
143
|
-
end
|
144
|
-
|
145
|
-
id = docker(:run, '-d', tag, true, capture: true).strip
|
146
|
-
begin
|
147
|
-
guest_export_dir = File.expand_path(config.export_dir, CIDE_SRC_DIR)
|
148
|
-
|
149
|
-
host_export_dir = File.expand_path(
|
150
|
-
config.host_export_dir || config.export_dir,
|
151
|
-
Dir.pwd,
|
152
|
-
)
|
153
|
-
|
154
|
-
docker :cp, [id, guest_export_dir].join(':'), host_export_dir
|
155
|
-
|
156
|
-
ensure
|
157
|
-
docker :rm, '-f', id
|
158
|
-
end
|
159
|
-
rescue Docker::Error => ex
|
160
|
-
exit ex.exitstatus
|
161
|
-
end
|
162
|
-
|
163
|
-
desc 'clean', 'Removes old containers'
|
164
|
-
method_option 'days',
|
165
|
-
desc: 'Number of days to keep the images',
|
166
|
-
default: 7,
|
167
|
-
type: :numeric
|
168
|
-
method_option 'count',
|
169
|
-
desc: 'Maximum number of images to keep',
|
170
|
-
default: 10,
|
171
|
-
type: :numeric
|
172
|
-
def clean
|
173
|
-
setup_docker
|
174
|
-
|
175
|
-
days_to_keep = options[:days]
|
176
|
-
max_images = options[:count]
|
177
|
-
|
178
|
-
x = docker('images', '--no-trunc', capture: true)
|
179
|
-
iter = x.lines.each
|
180
|
-
iter.next
|
181
|
-
cide_image_ids = iter
|
182
|
-
.map { |line| line.split(/\s+/) }
|
183
|
-
.select { |line| line[0] =~ /^cide\// || line[0] == '<none>' }
|
184
|
-
.map { |line| line[2] }
|
185
|
-
|
186
|
-
if cide_image_ids.empty?
|
187
|
-
puts 'No images found to be cleaned'
|
188
|
-
return
|
189
|
-
end
|
190
|
-
|
191
|
-
x = docker('inspect', *cide_image_ids, capture: true)
|
192
|
-
cide_images = JSON.parse(x.strip)
|
193
|
-
.each { |image| image['Created'] = Time.iso8601(image['Created']) }
|
194
|
-
.sort { |a, b| a['Created'] <=> b['Created'] }
|
195
|
-
|
196
|
-
if cide_images.size > max_images
|
197
|
-
old_cide_images = cide_images[0..-max_images]
|
198
|
-
.map { |image| image['Id'] }
|
199
|
-
else
|
200
|
-
old_times = Time.now - (days_to_keep * 24 * 60 * 60)
|
201
|
-
old_cide_images = cide_images
|
202
|
-
.select { |image| image['Created'] < old_times }
|
203
|
-
.map { |image| image['Id'] }
|
204
|
-
end
|
205
|
-
|
206
|
-
if old_cide_images.empty?
|
207
|
-
puts 'No images found to be cleaned'
|
208
|
-
return
|
209
|
-
end
|
210
|
-
|
211
|
-
docker('rmi', *old_cide_images)
|
212
|
-
end
|
213
|
-
|
214
|
-
desc 'init', "Creates a blank #{CONFIG_FILE} into the project"
|
215
|
-
def init
|
216
|
-
puts "Creating #{CONFIG_FILE} with default values"
|
217
|
-
create_file CONFIG_FILE, DefaultConfig.to_yaml
|
218
|
-
end
|
219
|
-
|
220
|
-
private
|
221
|
-
|
222
|
-
def create_tmp_file(destination, *args, &block)
|
223
|
-
create_file(destination, *args, &block)
|
224
|
-
at_exit do
|
225
|
-
remove_file(destination, verbose: false)
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
5
|
end
|
6
|
+
|
7
|
+
require 'cide/cli'
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require "cide/build"
|
2
|
+
require "stringio"
|
3
|
+
require "ostruct"
|
4
|
+
require "active_support/json"
|
5
|
+
|
6
|
+
describe "CIDE::Build::Config::Loader" do
|
7
|
+
before do
|
8
|
+
@config = CIDE::Build::Config.new
|
9
|
+
@loader = CIDE::Build::ConfigLoader.new(@config)
|
10
|
+
end
|
11
|
+
|
12
|
+
default_config = {
|
13
|
+
"from" => "ubuntu",
|
14
|
+
"as_root" => [],
|
15
|
+
"use_ssh" => false,
|
16
|
+
"before" => nil,
|
17
|
+
"forward_env" => [],
|
18
|
+
"export_dir" => nil,
|
19
|
+
"links" => [],
|
20
|
+
"run" => "script/ci",
|
21
|
+
}
|
22
|
+
|
23
|
+
it "works - empty config" do
|
24
|
+
@loader.load({})
|
25
|
+
expect(@config.as_json).to eq(default_config)
|
26
|
+
expect(@config.warnings).to eq([])
|
27
|
+
expect(@config.errors).to eq([])
|
28
|
+
end
|
29
|
+
|
30
|
+
it "works2 - full config" do
|
31
|
+
full_config = {
|
32
|
+
"from" => "god",
|
33
|
+
"as_root" => ["one", "two"],
|
34
|
+
"use_ssh" => true,
|
35
|
+
"before" => {
|
36
|
+
"add" => ["zzz", "yyy"],
|
37
|
+
"forward_env" => ["HOME"],
|
38
|
+
"run" => ["a", "b"],
|
39
|
+
},
|
40
|
+
"forward_env" => ["PWD"],
|
41
|
+
"links" => [
|
42
|
+
{"name" => "redis", "image" => "redis2:foo", "env" => {"HOME" => "/"}, "run" => "redis-server"}
|
43
|
+
],
|
44
|
+
"export_dir" => "./artifacts",
|
45
|
+
"run" => "do/something",
|
46
|
+
}
|
47
|
+
|
48
|
+
@loader.load(full_config)
|
49
|
+
|
50
|
+
expect(@config.as_json).to eq(default_config.merge(full_config))
|
51
|
+
expect(@config.warnings).to eq([])
|
52
|
+
expect(@config.errors).to eq([])
|
53
|
+
end
|
54
|
+
|
55
|
+
it "coerces things around" do
|
56
|
+
@loader.load(
|
57
|
+
as_root: "xxxxx",
|
58
|
+
before: :zzzzz,
|
59
|
+
links: ["mysql", {image: "redis", env: {PATH: "/bin"}}, nil],
|
60
|
+
forward_env: ["HOME", nil, 555]
|
61
|
+
)
|
62
|
+
|
63
|
+
expect(@config.to_h.as_json).to eq(default_config.merge(
|
64
|
+
"as_root" => ["xxxxx"],
|
65
|
+
"before" => {
|
66
|
+
"add" => [],
|
67
|
+
"forward_env" => [],
|
68
|
+
"run" => ["zzzzz"],
|
69
|
+
},
|
70
|
+
"links" => [
|
71
|
+
{"name" => "mysql", "image" => "mysql", "run" => nil, "env" => {}},
|
72
|
+
{"name" => "redis", "image" => "redis", "run" => nil, "env" => {"PATH" => "/bin"}},
|
73
|
+
],
|
74
|
+
"forward_env" => ["HOME", "555"],
|
75
|
+
))
|
76
|
+
expect(@config.warnings).to eq([])
|
77
|
+
expect(@config.errors).to eq([])
|
78
|
+
end
|
79
|
+
|
80
|
+
it "notifies deprecations" do
|
81
|
+
@loader.load(
|
82
|
+
image: "foo",
|
83
|
+
command: "lol",
|
84
|
+
zzz: 4,
|
85
|
+
)
|
86
|
+
expect(@config.as_json).to eq(default_config.merge(
|
87
|
+
"from" => "foo",
|
88
|
+
"run" => "lol",
|
89
|
+
))
|
90
|
+
expect(@config.warnings).to eq([
|
91
|
+
"image is deprecated. use 'from' instead.",
|
92
|
+
"command is deprecated. use 'run' instead.",
|
93
|
+
"Unknown key zzz",
|
94
|
+
])
|
95
|
+
expect(@config.errors).to eq([])
|
96
|
+
end
|
97
|
+
|
98
|
+
it "reports type errors" do
|
99
|
+
@loader.load(
|
100
|
+
as_root: ["aaa", Time.now],
|
101
|
+
forward_env: {},
|
102
|
+
links: {},
|
103
|
+
)
|
104
|
+
|
105
|
+
expect(@config.to_h.as_json).to eq(default_config.merge(
|
106
|
+
"as_root" => ["aaa", ""],
|
107
|
+
))
|
108
|
+
expect(@config.warnings).to eq([])
|
109
|
+
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",
|
112
|
+
"expected links to be a expected hash to either declare the name or image but got a Hash",
|
113
|
+
])
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
metadata
CHANGED
@@ -1,23 +1,51 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cide
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.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-
|
11
|
+
date: 2015-04-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.19.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.19.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: virtus
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
16
44
|
requirements:
|
17
45
|
- - ">="
|
18
46
|
- !ruby/object:Gem::Version
|
19
47
|
version: '0'
|
20
|
-
type: :
|
48
|
+
type: :development
|
21
49
|
prerelease: false
|
22
50
|
version_requirements: !ruby/object:Gem::Requirement
|
23
51
|
requirements:
|
@@ -25,7 +53,7 @@ dependencies:
|
|
25
53
|
- !ruby/object:Gem::Version
|
26
54
|
version: '0'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
56
|
+
name: rubocop
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
30
58
|
requirements:
|
31
59
|
- - ">="
|
@@ -39,7 +67,21 @@ dependencies:
|
|
39
67
|
- !ruby/object:Gem::Version
|
40
68
|
version: '0'
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activesupport
|
43
85
|
requirement: !ruby/object:Gem::Requirement
|
44
86
|
requirements:
|
45
87
|
- - ">="
|
@@ -63,7 +105,9 @@ extensions: []
|
|
63
105
|
extra_rdoc_files: []
|
64
106
|
files:
|
65
107
|
- ".cide.yml"
|
108
|
+
- ".editorconfig"
|
66
109
|
- ".gitignore"
|
110
|
+
- ".rspec"
|
67
111
|
- ".rubocop.yml"
|
68
112
|
- ".travis.yml"
|
69
113
|
- CHANGELOG.md
|
@@ -76,9 +120,17 @@ files:
|
|
76
120
|
- cide
|
77
121
|
- cide.gemspec
|
78
122
|
- lib/cide.rb
|
123
|
+
- lib/cide/build.rb
|
124
|
+
- lib/cide/build/config.rb
|
125
|
+
- lib/cide/build/config_loader.rb
|
126
|
+
- lib/cide/cli.rb
|
127
|
+
- lib/cide/constants.rb
|
128
|
+
- lib/cide/default_cide.yml
|
79
129
|
- lib/cide/docker.rb
|
80
|
-
- lib/
|
81
|
-
- lib/ssh_config
|
130
|
+
- lib/cide/dockerfile_template.erb
|
131
|
+
- lib/cide/ssh_config
|
132
|
+
- spec/build_config_loader_spec.rb
|
133
|
+
- spec/spec_helper.rb
|
82
134
|
homepage: https://github.com/zimbatm/cide
|
83
135
|
licenses:
|
84
136
|
- MIT
|
@@ -99,8 +151,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
151
|
version: '0'
|
100
152
|
requirements: []
|
101
153
|
rubyforge_project:
|
102
|
-
rubygems_version: 2.4.
|
154
|
+
rubygems_version: 2.4.6
|
103
155
|
signing_key:
|
104
156
|
specification_version: 4
|
105
157
|
summary: CI docker runner
|
106
|
-
test_files:
|
158
|
+
test_files:
|
159
|
+
- spec/build_config_loader_spec.rb
|
160
|
+
- spec/spec_helper.rb
|