cide 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 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