cide 0.0.3 → 0.0.4

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: 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
  - - ">="