cide 0.7.0 → 0.8.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: ade361f4711d1dbb93055c6a1877cdd72b14de85
4
- data.tar.gz: e4cad913efd19e87397c4ba10ee82a4ca25be701
3
+ metadata.gz: 929d1f4d583f4b694999c35c0f3ff12c56164c39
4
+ data.tar.gz: a83fdc09070c59410043c8d2670d051d321bc1fd
5
5
  SHA512:
6
- metadata.gz: 47d3acc50632780d3cd87dc1bdff7ee08b2388d0e4fa2df63239df2e18965f3e8f33072a2b3a79a5fef7f2fa62bcd332c1b3ec30d60eb4d90209a78463c9c084
7
- data.tar.gz: 4b702e34ac5ecb06f1437332418ec942cf99c4b85af972834c45dbf975aa9799d6496ea98abca24d744942e4e5aa06b18b21f779cc649d3cb28150b6d2bf3369
6
+ metadata.gz: b18d7c3a787a3c09de24e4b5168eebbbc440e512b5250ecb6374730872aebdb80b65d480af0a6950ec87f3cce6ed2ba66985718d85ee3ec70ce18ca8261bbd42
7
+ data.tar.gz: ea4ca7f133d9c877f06ba76056d3ddeeccf80bc1e947da6e4986f7db3abe91c473a63ff8f28a3a77132663982f1aba2686d476acd402e100dbae4878f579d797
@@ -0,0 +1,3 @@
1
+ .direnv
2
+ .envrc
3
+ .package
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  *.gem
2
+ .package-*
2
3
  man/*.1
3
4
  man/*.css
4
5
  man/*.html
@@ -1,4 +1,10 @@
1
1
 
2
+ 0.8.0 / 2016-02-15
3
+ ==================
4
+
5
+ * NEW: `cide package` command to build archives from a project
6
+ * CHANGE: Renamed `cide build` to `cide exec`
7
+
2
8
  0.7.0 / 2016-02-10
3
9
  ==================
4
10
 
data/Gemfile CHANGED
@@ -1,2 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
+
4
+ # Optional dependency, only used by `cide upload`
5
+ gem 'aws-sdk', '~> 2'
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cide (0.7.0)
4
+ cide (0.8.0)
5
5
  thor (~> 0.19)
6
6
  virtus (~> 1.0)
7
7
 
@@ -17,6 +17,12 @@ GEM
17
17
  ast (2.0.0)
18
18
  astrolabe (1.3.0)
19
19
  parser (>= 2.2.0.pre.3, < 3.0)
20
+ aws-sdk (2.2.16)
21
+ aws-sdk-resources (= 2.2.16)
22
+ aws-sdk-core (2.2.16)
23
+ jmespath (~> 1.0)
24
+ aws-sdk-resources (2.2.16)
25
+ aws-sdk-core (= 2.2.16)
20
26
  axiom-types (0.1.1)
21
27
  descendants_tracker (~> 0.0.4)
22
28
  ice_nine (~> 0.11.0)
@@ -30,7 +36,8 @@ GEM
30
36
  diff-lcs (1.2.5)
31
37
  equalizer (0.0.11)
32
38
  i18n (0.7.0)
33
- ice_nine (0.11.1)
39
+ ice_nine (0.11.2)
40
+ jmespath (1.1.3)
34
41
  json (1.8.1)
35
42
  md2man (4.0.0)
36
43
  binman (~> 4.0)
@@ -78,6 +85,7 @@ PLATFORMS
78
85
 
79
86
  DEPENDENCIES
80
87
  activesupport
88
+ aws-sdk (~> 2)
81
89
  cide!
82
90
  md2man
83
91
  rake
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'cide'
5
- s.version = '0.7.0'
5
+ s.version = '0.8.0'
6
6
  s.authors = ['zimbatm']
7
7
  s.email = ['zimbatm@zimbatm.com']
8
8
  s.summary = 'Isolated test runner with Docker'
data/cide.yml CHANGED
@@ -1,11 +1,9 @@
1
1
  ---
2
- from: ruby:2.1
3
- as_root:
4
- - chown -R cide:cide /usr/local/bundle
2
+ from: ruby:2.0
5
3
  before:
6
4
  add:
7
5
  - Gemfile
8
6
  - Gemfile.lock
9
7
  - cide.gemspec
10
8
  run: bundle install --jobs=3 --retry=3 --deployment
11
- run: bundle exec rake
9
+ run: script/ci
@@ -4,7 +4,7 @@ CIDE.YML 1 "JUNE 2015" cide.yml "Cide Manual"
4
4
  NAME
5
5
  ----
6
6
 
7
- cide.yml - file format that directs the `cide build` execution
7
+ cide.yml - file format that directs the `cide exec` execution
8
8
 
9
9
  DESCRIPTION
10
10
  -----------
@@ -0,0 +1,54 @@
1
+ require 'fileutils'
2
+
3
+ require 'cide/docker'
4
+
5
+ module CIDE
6
+ class Builder
7
+ include CIDE::Docker
8
+
9
+ attr_reader :config
10
+
11
+ def initialize(config)
12
+ @config = config
13
+ @tmp_files = []
14
+ end
15
+
16
+ def build(tag: nil, pull: nil, ssh_key: nil)
17
+ fail ArgumentError, 'tag missing' unless tag
18
+
19
+ if config.use_ssh
20
+ fail ArgumentError, 'ssh_key missing' unless ssh_key
21
+ unless File.exist?(ssh_key)
22
+ fail ArgumentError, "ssh_key #{ssh_key} not found"
23
+ end
24
+ create_tmp_file! TEMP_SSH_KEY, File.read(ssh_key)
25
+ end
26
+ create_tmp_file! DOCKERFILE, config.to_dockerfile
27
+
28
+ build_options = ['--force-rm']
29
+ build_options << '--pull' if pull
30
+ build_options.push '-f', DOCKERFILE
31
+ build_options.push '-t', tag
32
+ build_options << '.'
33
+ docker :build, *build_options
34
+ ensure
35
+ release_tmp_files!
36
+ end
37
+
38
+ protected
39
+
40
+ def create_tmp_file!(destination, content)
41
+ FileUtils.mkdir_p(File.dirname(destination))
42
+ @tmp_files << destination
43
+ File.write(destination, content)
44
+ # Dockerfile ADD compares content and mtime, we don't want that
45
+ File.utime(1_286_701_800, 1_286_701_800, destination)
46
+ end
47
+
48
+ def release_tmp_files!
49
+ @tmp_files.each do |file|
50
+ FileUtils.rm_f(file)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,6 +1,8 @@
1
+ require 'cide/builder'
2
+ require 'cide/config_file'
1
3
  require 'cide/constants'
2
4
  require 'cide/docker'
3
- require 'cide/build'
5
+ require 'cide/runner'
4
6
 
5
7
  require 'thor'
6
8
 
@@ -14,12 +16,12 @@ module CIDE
14
16
  include CIDE::Docker
15
17
  include Thor::Actions
16
18
 
17
- default_command 'build'
19
+ default_command 'exec'
18
20
 
19
- desc 'build', 'Builds an image and executes the run script'
21
+ desc 'exec', 'Builds an image and executes the run script'
20
22
 
21
23
  method_option 'name',
22
- desc: 'Name of the build',
24
+ desc: 'Name of the image',
23
25
  aliases: %w(-n -t),
24
26
  default: File.basename(Dir.pwd)
25
27
 
@@ -42,7 +44,7 @@ module CIDE
42
44
  desc: 'Override the script to run',
43
45
  type: :string,
44
46
  aliases: ['-r'],
45
- default: []
47
+ default: nil
46
48
 
47
49
  method_option 'pull',
48
50
  desc: 'Whenever to pull for new images on build',
@@ -54,117 +56,161 @@ module CIDE
54
56
  aliases: ['-s'],
55
57
  default: '~/.ssh/id_rsa'
56
58
 
57
- def build
58
- containers = []
59
-
59
+ def exec
60
60
  setup_docker
61
61
 
62
- ## Config ##
62
+ tag = name_to_tag options.name
63
+
63
64
  banner 'Config'
64
- build = Build::Config.load(Dir.pwd)
65
- exit 1 if build.nil?
66
- ssh_key = File.expand_path(options.ssh_key)
67
- build.run = ['sh', '-e', '-c', options.run] unless options.run.empty?
68
- name = CIDE::Docker.id options.name
69
- tag = "cide/#{name}"
70
- say_status :config, build.inspect
65
+ config = ConfigFile.load(Dir.pwd)
66
+ say_status :config, config.inspect
71
67
 
72
68
  ## Build ##
73
69
  banner 'Build'
74
- if build.use_ssh
75
- unless File.exist?(ssh_key)
76
- fail ArgumentError, "SSH key #{ssh_key} not found"
77
- end
78
- create_tmp_file TEMP_SSH_KEY, File.read(ssh_key)
79
- end
80
- create_tmp_file DOCKERFILE, build.to_dockerfile
81
- build_options = ['--force-rm']
82
- build_options << '--pull' if options.pull
83
- build_options.push '-f', DOCKERFILE
84
- build_options.push '-t', tag
85
- build_options << '.'
86
- docker :build, *build_options
87
-
88
- ## CI ##
70
+ builder = Builder.new(config)
71
+ builder.build(
72
+ pull: options.pull,
73
+ ssh_key: File.expand_path(options.ssh_key),
74
+ tag: tag,
75
+ )
76
+
77
+ ## Run ##
89
78
  banner 'Run'
90
- build.links.each do |link|
91
- args = ['--detach']
92
- link.env.each_pair do |key, value|
93
- args.push('--env', [key, value].join('='))
94
- end
95
- args << link.image
96
- args << link.run if link.run
97
- link.id = docker(:run, *args, capture: true).strip
98
- containers << link.id
99
- end
100
79
 
101
- run_options = ['--detach']
80
+ command = options.run ? ['sh', '-e', '-c', options.run] : config.run
102
81
 
103
- build.env.each_pair do |key, value|
104
- run_options.push '--env', [key, value].join('=')
105
- end
82
+ runner = Runner.new(
83
+ command: command,
84
+ env: config.env,
85
+ links: config.links,
86
+ tag: tag,
87
+ )
88
+ runner.run!
106
89
 
107
- build.links.each do |link|
108
- run_options.push '--link', [link.id, link.name].join(':')
109
- end
90
+ ## Export ##
91
+ return unless options.export
92
+ banner 'Export'
93
+ runner.export!(
94
+ guest_dir: options.guest_export_dir || config.export_dir,
95
+ host_dir: options.export_dir || config.export_dir,
96
+ )
97
+ rescue Docker::Error => ex
98
+ exit ex.exitstatus
99
+ rescue RuntimeError => ex
100
+ $stderr.puts ex.to_s
101
+ exit 1
102
+ ensure
103
+ runner.cleanup! if runner
104
+ end
105
+
106
+ desc 'package', 'Builds a package from the container script/build'
107
+ method_option 'name',
108
+ desc: 'Name of the image',
109
+ aliases: %w(-n -t),
110
+ default: File.basename(Dir.pwd)
111
+
112
+ method_option 'ssh_key',
113
+ desc: 'Path to a ssh key to import into the docker image',
114
+ aliases: ['-s'],
115
+ default: '~/.ssh/id_rsa'
110
116
 
111
- id = SecureRandom.hex
112
- run_options.push '--name', id
117
+ method_option 'package',
118
+ desc: 'Name of the package',
119
+ default: File.basename(Dir.pwd)
113
120
 
114
- run_options.push tag
115
- run_options.push(*build.run)
121
+ method_option 'upload',
122
+ desc: 'Whenever to upload the result to S3',
123
+ type: :boolean,
124
+ default: true
116
125
 
117
- containers << id
118
- docker(:run, *run_options, capture: true).strip
119
- docker(:attach, id)
126
+ method_option 'set-version',
127
+ desc: 'Tells cide the package version, otherwise extracted from git',
128
+ default: nil
120
129
 
121
- say_status :status, 'SUCCESS', :green
130
+ # AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_REGION need to be passed
131
+ # either trough the env or ~/.aws/credentials file
132
+ method_option 'aws_bucket',
133
+ desc: 'AWS_BUCKET',
134
+ default: ENV['AWS_BUCKET']
135
+
136
+ def package
137
+ fail 'missing AWS_BUCKET' if options.upload && !options.aws_bucket
138
+
139
+ tag = name_to_tag options.name
140
+
141
+ build_root = File.expand_path('.package')
142
+ guest_export_dir = '/cide/package'
143
+ host_export_dir = File.join(build_root, 'package')
144
+
145
+ version = options.version || (
146
+ git_branch = `git symbolic-ref --short -q HEAD || echo unknown`.strip
147
+ git_rev = `git rev-parse --short HEAD`.strip
148
+ "#{git_branch}-#{git_rev}"
149
+ )
150
+
151
+ timestamp = Time.now.strftime('%Y-%m-%d_%H%M%S')
152
+ tar_name = "#{options.package}.#{timestamp}.#{version}.tar.gz"
153
+ tar_path = File.join(build_root, tar_name)
154
+
155
+ banner 'Config'
156
+ config = ConfigFile.load(Dir.pwd)
157
+ say_status :config, config.inspect
158
+
159
+ ## Build ##
160
+ banner 'Build'
161
+ builder = Builder.new(config)
162
+ builder.build(
163
+ pull: options.pull,
164
+ ssh_key: File.expand_path(options.ssh_key),
165
+ tag: tag,
166
+ )
167
+
168
+ ## Run ##
169
+ banner 'Run'
170
+ runner = Runner.new(
171
+ command: ['script/build', guest_export_dir],
172
+ env: config.env,
173
+ links: config.links,
174
+ tag: tag,
175
+ )
176
+ runner.run!
122
177
 
123
178
  ## Export ##
124
- return unless options.export
125
179
  banner 'Export'
126
180
 
127
- source_export_dir = options.guest_export_dir || build.export_dir
128
- fail 'export flag set but no export_dir given' if source_export_dir.nil?
181
+ FileUtils.rm_rf(build_root)
182
+
183
+ runner.export!(
184
+ guest_dir: guest_export_dir,
185
+ host_dir: host_export_dir,
186
+ )
129
187
 
130
- target_export_dir = options.export_dir || source_export_dir
188
+ # Create archive
189
+ puts "Package: #{tar_name}"
190
+ system('tar', '-czf', tar_path, '-C', host_export_dir, '.')
131
191
 
132
- target_export_dir = File.dirname(target_export_dir)
192
+ ## Upload ##
133
193
 
134
- guest_export_dir = File.expand_path(source_export_dir, CIDE_SRC_DIR)
135
- host_export_dir = File.expand_path(target_export_dir, Dir.pwd)
194
+ return unless options.upload
195
+ banner 'Upload'
136
196
 
137
- docker :cp, [id, guest_export_dir].join(':'), host_export_dir
197
+ require 'aws-sdk'
198
+ s3 = Aws::S3::Client.new
199
+ resp = s3.put_object(
200
+ bucket: options.aws_bucket,
201
+ key: tar_name,
202
+ body: File.open(tar_path),
203
+ )
204
+ p resp
138
205
  rescue Docker::Error => ex
139
- say_status :status, 'ERROR', :red
140
206
  exit ex.exitstatus
207
+ rescue RuntimeError => ex
208
+ $stderr.puts ex.to_s
209
+ exit 1
141
210
  ensure
142
- linked_containers = containers - [id]
143
- unless linked_containers.empty?
144
- infos = docker(
145
- :inspect,
146
- *linked_containers,
147
- capture: true,
148
- verbose: false,
149
- )
150
- JSON.parse(infos).each do |info|
151
- config = info['Config']
152
- state = info['State']
153
-
154
- next unless state['Dead'] || state['ExitCode'] > 0
155
-
156
- $stderr.puts "=== Failed linked container #{info['Id']} ==="
157
- $stderr.puts "Image: #{config['Image']}"
158
- $stderr.puts "State: #{state.inspect}"
159
- docker(:logs, '--tail', 20, info['Id'])
160
- end
161
- end
162
- # Shutdown old containers
163
- unless containers.empty?
164
- docker :rm, '--force', '--volumes', *containers.reverse,
165
- verbose: false,
166
- capture: true
167
- end
211
+ FileUtils.rm_rf(build_root) if options.upload
212
+
213
+ runner.cleanup! if runner
168
214
  end
169
215
 
170
216
  desc 'debug', 'Opens a debug console in the last project image'
@@ -176,56 +222,33 @@ module CIDE
176
222
  desc: 'User to run under',
177
223
  default: 'cide'
178
224
  def debug
179
- containers = []
180
-
181
225
  setup_docker
182
226
 
227
+ tag = name_to_tag options.name
228
+
183
229
  ## Config ##
184
230
  banner 'Config'
185
- build = Build::Config.load
186
- exit 1 if build.nil?
187
- name = CIDE::Docker.id options.name
188
- tag = "cide/#{name}"
189
- say_status :config, build.inspect
231
+ config = ConfigFile.load(Dir.pwd)
232
+ say_status :config, config.inspect
190
233
 
191
- ## CI ##
234
+ ## Run ##
192
235
  banner 'Run'
193
- build.links.each do |link|
194
- args = ['--detach']
195
- link.env.each_pair do |key, value|
196
- args.push('--env', [key, value].join('='))
197
- end
198
- args << link.image
199
- args << link.run if link.run
200
- link.id = docker(:run, *args, capture: true).strip
201
- containers << link.id
202
- end
203
-
204
- run_options = ['--rm', '-t', '-i']
205
-
206
- run_options.push '--user', options.user
236
+ runner = Runner.new(
237
+ command: ['bash'],
238
+ env: config.env,
239
+ links: config.links,
240
+ tag: tag,
241
+ user: options.user,
242
+ )
243
+ runner.run!(interactive: true)
207
244
 
208
- build.env.each_pair do |key, value|
209
- run_options.push '--env', [key, value].join('=')
210
- end
211
-
212
- build.links.each do |link|
213
- run_options.push '--link', [link.id, link.name].join(':')
214
- end
215
-
216
- run_options.push tag
217
- run_options.push 'bash'
218
-
219
- docker(:run, *run_options)
220
245
  rescue Docker::Error => ex
221
246
  exit ex.exitstatus
247
+ rescue RuntimeError => ex
248
+ $stderr.puts ex.to_s
249
+ exit 1
222
250
  ensure
223
- # Shutdown old containers
224
- unless containers.empty?
225
- docker :rm, '--force', '--volumes', *containers.reverse,
226
- verbose: false,
227
- capture: true
228
- end
251
+ runner.cleanup! if runner
229
252
  end
230
253
 
231
254
  desc 'clean', 'Removes old containers'
@@ -287,18 +310,16 @@ module CIDE
287
310
 
288
311
  private
289
312
 
290
- def create_tmp_file(destination, *args, &block)
291
- create_file(destination, *args, &block)
292
- # Dockerfile ADD compares content and mtime, we don't want that
293
- File.utime(1_286_701_800, 1_286_701_800, destination)
294
- at_exit do
295
- remove_file(destination, verbose: false)
296
- end
313
+ # Prefixes the tag to make it recognizable by the cleaner
314
+ # Makes sure it's a valid tag
315
+ def name_to_tag(name)
316
+ "cide/#{CIDE::Docker.id name}"
297
317
  end
298
318
 
299
- LINE_SIZE = 78.0
319
+ LINE_WIDTH = 78.0
300
320
  def banner(text)
301
- pad = (LINE_SIZE - text.size - 4) / 2
321
+ pad = (LINE_WIDTH - text.size - 4) / 2
322
+ pad = 0 if pad < 0
302
323
  puts '=' * pad.floor + "[ #{text} ]" + '=' * pad.ceil
303
324
  end
304
325
  end