cide 0.0.3 → 0.0.4

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: a3dd99e04fa0893686988a2e80fc1082d5dcab15
4
- data.tar.gz: a2df7f906f19db7e98e47829d3864d05bf75cb0a
3
+ metadata.gz: cc53228b6d632d1c764d54c902e48d811ab2bc2c
4
+ data.tar.gz: 0bd7d12a1190e1a2f12c066a53147bb0d62b71a7
5
5
  SHA512:
6
- metadata.gz: 867b9f04b9ae236496e92bcc6b81eb8f434412de87591e7691ebf2ceb0c24c0e76f93274cda4abe191eee6838ea72598bfe2ed14a2e6db7009058f8581238ce7
7
- data.tar.gz: 93b5c43787f8bca0ca068a1fe232fe7bf4891b2a314f09f31cd79b422706aabab9661ff47fa03e1e7408a0807f49a0c04fe60b9db45413a3bf69e84bd034a67e
6
+ metadata.gz: 93cdf9ff055f8828493f60a29a55fdf21b83b8d77f56390881c79203a484ea767f82fd0eb0cf6dd1f577558a38f7e8206d9d6ee44151cbf4e383f3cc1b39c2d0
7
+ data.tar.gz: 9e47a1a744c63180b365637087a40721e7abd9ce9508f731b1f5297bf5bdfc60f30f0b9f3e49fde28f76397ba730a1180d64df4df3fd850e5436e199567f2491
data/.rubocop.yml ADDED
@@ -0,0 +1,18 @@
1
+ # CIDE::CLI is essentially a big dispatcher, no need to break it
2
+ # in smaller chunks.
3
+ Metrics/ClassLength:
4
+ Max: 200
5
+
6
+ # CIDE::CLI methods can be read top to bottom. No need to factor out
7
+ # functionality unless it can be shared.
8
+ Metrics/MethodLength:
9
+ Max: 60
10
+
11
+ # Don't align stuff vertically, bad for diffing
12
+ Style/AlignParameters:
13
+ EnforcedStyle: with_fixed_indentation
14
+
15
+ # Allows for easy diffing
16
+ # Keep them sorted alphabetically unless a meaningful order exists
17
+ Style/TrailingComma:
18
+ EnforcedStyleForMultiline: comma
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ - 2.0.0
5
+ - 1.9.3
data/CHANGELOG.md CHANGED
@@ -1,4 +1,17 @@
1
1
 
2
+ 0.0.4 / 2014-10-03
3
+ ==================
4
+
5
+ * ADD: forward_env to forward environment variables from the host
6
+ * ADD: Adds a "prepare" step that allows to cache build dependencies
7
+ * ADD: `cide init` to bootstrap projects
8
+ * ADD: `cide clean` to remove old images
9
+ * ADD: Can set export dir for artifacts
10
+ * CHANGE: Renames the "image" attribute to "from"
11
+ * CHANGE: Renames the "commmand" attribute to "run"
12
+ * Changes the generated Dockerfile quite a bit
13
+ * Use thor for command-line parsing, modularize
14
+
2
15
  0.0.3 / 2014-09-29
3
16
  ==================
4
17
 
data/Gemfile CHANGED
@@ -1,2 +1,2 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
  gemspec
data/Gemfile.lock CHANGED
@@ -1,14 +1,35 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cide (0.0.1)
4
+ cide (0.0.3)
5
+ thor
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
10
+ ast (2.0.0)
11
+ astrolabe (1.3.0)
12
+ parser (>= 2.2.0.pre.3, < 3.0)
13
+ parser (2.2.0.pre.4)
14
+ ast (>= 1.1, < 3.0)
15
+ slop (~> 3.4, >= 3.4.5)
16
+ powerpack (0.0.9)
17
+ rainbow (2.0.0)
18
+ rake (10.1.0)
19
+ rubocop (0.26.1)
20
+ astrolabe (~> 1.3)
21
+ parser (>= 2.2.0.pre.4, < 3.0)
22
+ powerpack (~> 0.0.6)
23
+ rainbow (>= 1.99.1, < 3.0)
24
+ ruby-progressbar (~> 1.4)
25
+ ruby-progressbar (1.6.0)
26
+ slop (3.6.0)
27
+ thor (0.19.1)
9
28
 
10
29
  PLATFORMS
11
30
  ruby
12
31
 
13
32
  DEPENDENCIES
14
33
  cide!
34
+ rake
35
+ rubocop
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rubocop/rake_task'
2
+
3
+ task default: [:rubocop]
4
+
5
+ RuboCop::RakeTask.new
data/bin/cide CHANGED
@@ -3,140 +3,6 @@
3
3
  # cide - Continuous Integration Docker Environment
4
4
  #
5
5
 
6
- require "erb"
7
- require "optparse"
8
- require "yaml"
9
-
10
- DOCKERFILE = 'Dockerfile'
11
- DOCKERFILE_TEMPLATE = <<EOF
12
- FROM <%= image %>
13
- RUN useradd -m -U -d /cide cide
14
-
15
- # Install system build dependencies here
16
-
17
- <% as_root.each do |cmd| %>
18
- RUN <%= cmd %>
19
- <% end %>
20
-
21
- # Add project data
22
-
23
- ADD . /cide/src
24
- RUN chown -R cide:cide /cide
25
-
26
- # Switch to cide user
27
-
28
- USER cide
29
-
30
- WORKDIR /cide/src
31
- ENV HOME /cide
32
- EOF
33
-
34
- module Fmt; extend self
35
- def fmt(obj)
36
- dump_obj obj
37
- end
38
-
39
- protected
40
-
41
- def dump(obj)
42
- case obj
43
- when Array
44
- "[" + obj.map{|v| dump v}.join(' ') + "]"
45
- when Hash
46
- "{" + dump_obj(obj) + "}"
47
- else
48
- obj = obj.to_s
49
- obj.index(/['"\s\{\}\[\]\=]/) ? obj.inspect : obj
50
- end
51
- rescue
52
- dump $!.to_s
53
- end
54
-
55
- def dump_obj(obj)
56
- obj.map do |(k, v)|
57
- "#{k}=#{dump v}"
58
- end.join ' '
59
- end
60
- end
61
-
62
- def log(opts={})
63
- $stdout.puts "cide: #{Fmt.fmt opts}"
64
- end
65
-
66
- def sh(*args)
67
- log cmd: args
68
- unless system(*args.map(&:to_s))
69
- fail "command failed"
70
- end
71
- end
72
-
73
- def docker(*args)
74
- sh "docker", *args
75
- end
76
-
77
- def struct(opts={}, &block)
78
- Class.new(Struct.new(*opts.keys), &block).new(*opts.values)
79
- end
80
-
81
- config = struct(
82
- image: "ubuntu",
83
- tag: File.basename(Dir.pwd),
84
- as_root: [],
85
- command: 'script/ci',
86
- ) do
87
- def set(name)
88
- method "#{name}="
89
- end
90
-
91
- def tag=(str)
92
- # Replaces invalid docker tag characters by underscores
93
- super "#{str}".downcase.gsub(/[^a-z0-9\-_.]/, '_')
94
- end
95
-
96
- def to_dockerfile
97
- ERB.new(DOCKERFILE_TEMPLATE).result(binding)
98
- end
99
-
100
- def merge!(opts={})
101
- opts.each_pair{|k,v| self[k] = v }
102
- end
103
- end
104
-
105
- config.merge! YAML.load_file('.cide.yml')
106
-
107
- OptionParser.new do |opts|
108
- opts.on('-i', '--image IMAGE', "Select the source image (#{config.image})", &config.set(:image))
109
- opts.on('-t', '--tag TAG', "Name of the build (#{config.tag})", &config.set(:tag))
110
- opts.on('-c', '--command CMD', "CI script to run (#{config.command})", &config.set(:command))
111
- opts.on_tail('-h', '--help', 'Shows this help') do
112
- puts opts
113
- exit
114
- end
115
- end.parse!
116
-
117
- log config: config.to_h
118
-
119
- if !File.exist?(DOCKERFILE)
120
- log msg: "Creating temporary Dockerfile"
121
- File.write(DOCKERFILE, config.to_dockerfile)
122
- at_exit do
123
- File.unlink(DOCKERFILE)
124
- end
125
- else
126
- log msg: "Using existing Dockerfile"
127
- end
128
-
129
- if `uname`.strip == "Darwin" && !ENV['DOCKER_HOST']
130
- if !system("which boot2docker >/dev/null 2>&1")
131
- puts "make sure boot2docker is installed and running"
132
- puts
133
- puts "> brew install boot2docker"
134
- exit 1
135
- end
136
- ENV['DOCKER_HOST'] = `boot2docker socket 2>/dev/null`.strip
137
- end
138
-
139
- tag = "cide-#{config.tag}"
140
-
141
- docker :build, '-t', tag, '.'
142
- docker :run, '--rm', '-t', tag, "sh", "-c", config.command
6
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
7
+ require 'cide'
8
+ CIDE::CLI.start
data/cide ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ direnv exec `dirname $0` cide "$@"
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.0.3'
5
+ s.version = '0.0.4'
6
6
  s.authors = ['zimbatm']
7
7
  s.email = ['zimbatm@zimbatm.com']
8
8
  s.summary = 'CI docker runner'
@@ -14,7 +14,13 @@ DESC
14
14
  s.license = 'MIT'
15
15
 
16
16
  s.executables << 'cide'
17
- s.files = `git ls-files`.split($/)
18
- s.test_files = `git ls-files spec`.split($/)
17
+ s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
18
+ s.test_files = `git ls-files spec`.split($INPUT_RECORD_SEPARATOR)
19
19
  s.require_paths = ['lib']
20
+
21
+ s.required_ruby_version = '>= 1.9.3'
22
+
23
+ s.add_runtime_dependency 'thor'
24
+ s.add_development_dependency 'rake'
25
+ s.add_development_dependency 'rubocop'
20
26
  end
data/lib/cide.rb ADDED
@@ -0,0 +1,223 @@
1
+ require 'erb'
2
+ require 'json'
3
+ require 'optparse'
4
+ require 'shellwords'
5
+ require 'time'
6
+ require 'yaml'
7
+
8
+ require 'thor'
9
+
10
+ # CIDE is a Continuous Integration Docker Environment runner
11
+ #
12
+ # The juicy bits are defined in CIDE::CLI
13
+ module CIDE
14
+ DOCKERFILE = 'Dockerfile'
15
+ TEMPLATE = File.read(File.expand_path('../cide_template.erb', __FILE__))
16
+ CONFIG_FILE = '.cide.yml'
17
+
18
+ CIDE_DIR = '/cide'
19
+ CIDE_SRC_DIR = File.join(CIDE_DIR, '/src')
20
+
21
+ module_function
22
+
23
+ def docker_id(str)
24
+ # Replaces invalid docker tag characters by underscores
25
+ "#{str}".downcase.gsub(/[^a-z0-9\-_.]/, '_')
26
+ end
27
+
28
+ def self.struct(opts = {}, &block)
29
+ Class.new(Struct.new(*opts.keys), &block).new(*opts.values)
30
+ end
31
+
32
+ DefaultConfig = struct(
33
+ name: nil,
34
+ from: 'ubuntu',
35
+ as_root: [],
36
+ forward_env: [],
37
+ before: {},
38
+ export: false,
39
+ export_dir: './artifacts',
40
+ host_export_dir: nil,
41
+ run: 'script/ci',
42
+ ) do
43
+
44
+ alias_method :image=, :from=
45
+ alias_method :command=, :run=
46
+
47
+ def to_dockerfile
48
+ ERB.new(TEMPLATE, nil, '<>-').result(binding)
49
+ end
50
+
51
+ def merge!(opts = {})
52
+ opts.each_pair { |k, v| public_send("#{k}=", v) }
53
+ self
54
+ end
55
+
56
+ def merge(opts = {})
57
+ dup.merge!(opts)
58
+ end
59
+
60
+ def to_yaml
61
+ members.each_with_object({}) do |k, obj|
62
+ obj[k.to_s] = self[k]
63
+ end.to_yaml
64
+ end
65
+ end
66
+
67
+ # Command-line option-parsing and execution for cide
68
+ class CLI < Thor
69
+ include CIDE
70
+ include Thor::Actions
71
+
72
+ default_command 'build'
73
+
74
+ desc 'build', 'Builds an image and executes the run script'
75
+
76
+ method_option 'name',
77
+ desc: 'Name of the build',
78
+ aliases: %w(n t),
79
+ default: CIDE.docker_id(File.basename(Dir.pwd))
80
+
81
+ method_option 'host_export_dir',
82
+ desc: 'Output directory on host to put build artefacts in',
83
+ aliases: ['o'],
84
+ default: nil
85
+
86
+ method_option 'run',
87
+ desc: 'The script to run',
88
+ aliases: ['r'],
89
+ default: DefaultConfig.run
90
+
91
+ method_option 'export',
92
+ desc: 'Are we expecting to export artifacts',
93
+ type: :boolean,
94
+ default: false
95
+
96
+ def build
97
+ setup_docker
98
+
99
+ config = DefaultConfig.merge(YAML.load_file(CONFIG_FILE)).merge(options)
100
+ config.host_export_dir ||= config.export_dir
101
+ tag = "cide/#{docker_id(options[:name])}"
102
+
103
+ say_status :config, config.to_h
104
+
105
+ if !File.exist?(DOCKERFILE)
106
+ say_status :Dockerfile, 'Creating temporary Dockerfile'
107
+ File.write(DOCKERFILE, config.to_dockerfile)
108
+ at_exit do
109
+ File.unlink(DOCKERFILE)
110
+ end
111
+ else
112
+ say_status :Dockerfile, 'Using existing Dockerfile'
113
+ end
114
+
115
+ docker :build, '-t', tag, '.'
116
+
117
+ return unless config.export
118
+
119
+ unless config.export_dir
120
+ fail 'Fail: export flag set but no export dir given'
121
+ end
122
+
123
+ id = docker(:run, '-d', tag, true, capture: true).strip
124
+ begin
125
+ guest_export_dir = File.expand_path(config.export_dir, CIDE_SRC_DIR)
126
+
127
+ host_export_dir = File.expand_path(
128
+ config.host_export_dir || config.export_dir,
129
+ Dir.pwd)
130
+
131
+ docker :cp, [id, guest_export_dir].join(':'), host_export_dir
132
+
133
+ ensure
134
+ docker :rm, '-f', id
135
+ end
136
+ end
137
+
138
+ desc 'clean', 'Removes old containers'
139
+ method_option 'days',
140
+ desc: 'Number of days to keep the images',
141
+ default: 7,
142
+ type: :numeric
143
+ method_option 'count',
144
+ desc: 'Maximum number of images to keep',
145
+ default: 10,
146
+ type: :numeric
147
+ def clean
148
+ setup_docker
149
+
150
+ days_to_keep = options[:days]
151
+ max_images = options[:count]
152
+
153
+ x = run('docker images --no-trunc', capture: true)
154
+ iter = x.lines.each
155
+ iter.next
156
+ cide_image_ids = iter
157
+ .map { |line| line.split(/\s+/) }
158
+ .select { |line| line[0] =~ /^cide\// || line[0] == '<none>' }
159
+ .map { |line| line[2] }
160
+
161
+ if cide_image_ids.empty?
162
+ puts 'No images found to be cleaned'
163
+ return
164
+ end
165
+
166
+ x = run("docker inspect #{cide_image_ids.join(' ')}", capture: true)
167
+ cide_images = JSON.parse(x.strip)
168
+ .each { |image| image['Created'] = Time.iso8601(image['Created']) }
169
+ .sort { |a, b| a['Created'] <=> b['Created'] }
170
+
171
+ if cide_images.size > max_images
172
+ old_cide_images = cide_images[0..-max_images]
173
+ .map { |image| image['Id'] }
174
+ else
175
+ old_times = Time.now - (days_to_keep * 24 * 60 * 60)
176
+ old_cide_images = cide_images
177
+ .select { |image| image['Created'] < old_times }
178
+ .map { |image| image['Id'] }
179
+ end
180
+
181
+ if old_cide_images.empty?
182
+ puts 'No images found to be cleaned'
183
+ return
184
+ end
185
+
186
+ run("docker rmi #{old_cide_images.join(' ')}")
187
+ end
188
+
189
+ desc 'init', "Creates a blank #{CONFIG_FILE} into the project"
190
+ def init
191
+ if File.exist?(CONFIG_FILE)
192
+ puts "#{CONFIG_FILE} already exists"
193
+ return
194
+ end
195
+ puts "Creating #{CONFIG_FILE} with default values"
196
+ create_file CONFIG_FILE, DefaultConfig.to_yaml
197
+ end
198
+
199
+ protected
200
+
201
+ def setup_docker
202
+ @setup_docker ||= (
203
+ if `uname`.strip == 'Darwin' && !ENV['DOCKER_HOST']
204
+ unless system('which boot2docker >/dev/null 2>&1')
205
+ puts 'make sure boot2docker is installed and running'
206
+ puts
207
+ puts '> brew install boot2docker'
208
+ exit 1
209
+ end
210
+ ENV['DOCKER_HOST'] = `boot2docker socket 2>/dev/null`.strip
211
+ end
212
+ true
213
+ )
214
+ end
215
+
216
+ def docker(*args)
217
+ opts = args.last.is_a?(Hash) ? args.pop : {}
218
+ ret = run Shellwords.join(['docker'] + args), opts
219
+ fail 'Command failed' if $CHILD_STATUS.exitstatus > 0
220
+ ret
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,45 @@
1
+ FROM <%= from %>
2
+ USER root
3
+ RUN useradd -m -U -d <%= CIDE_DIR %> cide
4
+
5
+ # Install system build dependencies here
6
+
7
+ <% as_root.each do |cmd| -%>
8
+ RUN <%= cmd %>
9
+ <% end -%>
10
+
11
+ # Common
12
+
13
+ ENV HOME <%= CIDE_DIR %>
14
+ WORKDIR <%= CIDE_SRC_DIR %>
15
+
16
+ # Before
17
+
18
+ <% if before['run'] -%>
19
+ <% before['forward_env'].to_a.each do |key| -%>
20
+ ENV <%= key %> <%= ENV[key] %>
21
+ <% end %>
22
+ <% before['add'].to_a.each do |file| -%>
23
+ ADD <%= file %> <%= File.expand_path(file, CIDE_SRC_DIR) %>
24
+ <% end %>
25
+ RUN chown -R cide:cide <%= CIDE_DIR %>
26
+ USER cide
27
+ RUN <%= before['run'] %>
28
+ USER root
29
+ <% end -%>
30
+
31
+ # Add project data
32
+
33
+ ADD . <%= CIDE_SRC_DIR %>
34
+ RUN chown -R cide:cide <%= CIDE_DIR %>
35
+
36
+ # ENV
37
+
38
+ <% forward_env.to_a.each do |key| -%>
39
+ ENV <%= key %> <%= ENV[key] %>
40
+ <% end %>
41
+
42
+ # Test !
43
+
44
+ USER cide
45
+ RUN <%= run %>
metadata CHANGED
@@ -1,15 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cide
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - zimbatm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-29 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2014-10-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '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'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
13
55
  description: |
14
56
  cide makes it easy to reproduce CI builds on the developer computer by
15
57
  providing the same docker environment.
@@ -21,13 +63,19 @@ extensions: []
21
63
  extra_rdoc_files: []
22
64
  files:
23
65
  - ".gitignore"
66
+ - ".rubocop.yml"
67
+ - ".travis.yml"
24
68
  - CHANGELOG.md
25
69
  - Gemfile
26
70
  - Gemfile.lock
27
71
  - LICENSE
28
72
  - README.md
73
+ - Rakefile
29
74
  - bin/cide
75
+ - cide
30
76
  - cide.gemspec
77
+ - lib/cide.rb
78
+ - lib/cide_template.erb
31
79
  homepage: https://github.com/zimbatm/cide
32
80
  licenses:
33
81
  - MIT
@@ -40,7 +88,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
40
88
  requirements:
41
89
  - - ">="
42
90
  - !ruby/object:Gem::Version
43
- version: '0'
91
+ version: 1.9.3
44
92
  required_rubygems_version: !ruby/object:Gem::Requirement
45
93
  requirements:
46
94
  - - ">="