lib/cide.rb in cide-0.0.8 vs lib/cide.rb in cide-0.1.0
- old
+ new
@@ -1,38 +1,31 @@
require 'erb'
require 'json'
require 'optparse'
-require 'shellwords'
require 'time'
require 'yaml'
require 'thor'
+require 'cide/docker'
+
# CIDE is a Continuous Integration Docker Environment runner
#
# The juicy bits are defined in CIDE::CLI
module CIDE
+ DIR = File.expand_path('..', __FILE__)
DOCKERFILE = 'Dockerfile'
- SSH_CONFIG_FILE = 'ssh_config'
TEMP_SSH_KEY = 'id_rsa.tmp'
- DOCKERFILE_TEMPLATE = File.read(
- File.expand_path('../cide_template.erb', __FILE__),
- )
- SSH_CONFIG_CONTENTS = File.read(File.expand_path('../ssh_config', __FILE__))
+ DOCKERFILE_TEMPLATE = File.join(DIR, 'cide_template.erb')
+ SSH_CONFIG_FILE = 'ssh_config'
+ SSH_CONFIG_PATH = File.join(DIR, SSH_CONFIG_FILE)
CONFIG_FILE = '.cide.yml'
CIDE_DIR = '/cide'
CIDE_SRC_DIR = File.join(CIDE_DIR, '/src')
CIDE_SSH_DIR = File.join(CIDE_DIR, '/.ssh')
- module_function
-
- def docker_id(str)
- # Replaces invalid docker tag characters by underscores
- "#{str}".downcase.gsub(/[^a-z0-9\-_.]/, '_')
- end
-
def self.struct(opts = {}, &block)
Class.new(Struct.new(*opts.keys), &block).new(*opts.values)
end
DefaultConfig = struct(
@@ -43,22 +36,27 @@
before: {},
export: false,
export_dir: './artifacts',
host_export_dir: nil,
run: 'script/ci',
- ssh_key: nil,
+ use_ssh: false,
+ ssh_key: '~/.ssh/id_rsa',
) do
alias_method :image=, :from=
alias_method :command=, :run=
def name=(str)
- super CIDE.docker_id(str)
+ super CIDE::Docker.id(str)
end
+ def ssh_key_path
+ File.expand_path(ssh_key)
+ end
+
def to_dockerfile
- ERB.new(DOCKERFILE_TEMPLATE, nil, '<>-').result(binding)
+ ERB.new(File.read(DOCKERFILE_TEMPLATE), nil, '<>-').result(binding)
end
def merge!(opts = {})
opts.each_pair { |k, v| public_send("#{k}=", v) }
self
@@ -76,12 +74,13 @@
end
end
# Command-line option-parsing and execution for cide
class CLI < Thor
- include CIDE
+ include CIDE::Docker
include Thor::Actions
+ add_runtime_options!
default_command 'build'
desc 'build', 'Builds an image and executes the run script'
@@ -104,13 +103,13 @@
desc: 'The script to run',
aliases: ['r'],
default: nil
method_option 'ssh_key',
- desc: 'The ssh key to put into the docker image',
+ desc: 'Path to a ssh key to import into the docker image',
aliases: ['s'],
- default: nil
+ default: '~/.ssh/id_rsa'
def build
setup_docker
config = DefaultConfig.merge YAML.load_file(CONFIG_FILE)
@@ -120,45 +119,23 @@
config.name ||= File.basename(Dir.pwd)
config.host_export_dir ||= config.export_dir
tag = "cide/#{config.name}"
- if config.ssh_key && File.exist?(config.ssh_key)
- say_status :SSHkey, 'Creating temp ssh key file within directory'
- ssh_key_contents = File.read(config.ssh_key)
- File.write(TEMP_SSH_KEY, ssh_key_contents)
- config.ssh_key = TEMP_SSH_KEY
- at_exit do
- File.unlink(TEMP_SSH_KEY)
+ if config.use_ssh
+ unless File.exist?(config.ssh_key_path)
+ fail MalformattedArgumentError, "SSH key #{config.ssh_key} not found"
end
- else
- say_status :SSHKey, 'No SSH key specified'
+
+ create_tmp_file SSH_CONFIG_FILE, File.read(SSH_CONFIG_PATH)
+ create_tmp_file TEMP_SSH_KEY, File.read(config.ssh_key_path)
end
say_status :config, config.to_h
- # FIXME: Move Dockerfile out of the way if it exists
- if !File.exist?(DOCKERFILE)
- say_status :Dockerfile, 'Creating temporary Dockerfile'
- File.write(DOCKERFILE, config.to_dockerfile)
- at_exit do
- File.unlink(DOCKERFILE)
- end
- else
- say_status :Dockerfile, 'Using existing Dockerfile'
- end
+ create_tmp_file DOCKERFILE, config.to_dockerfile
- if !File.exist?(SSH_CONFIG_FILE)
- say_status :ssh_config, 'Creating temporary ssh config'
- File.write(SSH_CONFIG_FILE, SSH_CONFIG_CONTENTS)
- at_exit do
- File.unlink(SSH_CONFIG_FILE)
- end
- else
- say_status :ssh_config, 'Using existing ssh config'
- end
-
docker :build, '-t', tag, '.'
return unless config.export
unless config.export_dir
@@ -169,17 +146,20 @@
begin
guest_export_dir = File.expand_path(config.export_dir, CIDE_SRC_DIR)
host_export_dir = File.expand_path(
config.host_export_dir || config.export_dir,
- Dir.pwd)
+ Dir.pwd,
+ )
docker :cp, [id, guest_export_dir].join(':'), host_export_dir
ensure
docker :rm, '-f', id
end
+ rescue Docker::Error => ex
+ exit ex.exitstatus
end
desc 'clean', 'Removes old containers'
method_option 'days',
desc: 'Number of days to keep the images',
@@ -193,11 +173,11 @@
setup_docker
days_to_keep = options[:days]
max_images = options[:count]
- x = run('docker images --no-trunc', capture: true)
+ x = docker('images', '--no-trunc', capture: true)
iter = x.lines.each
iter.next
cide_image_ids = iter
.map { |line| line.split(/\s+/) }
.select { |line| line[0] =~ /^cide\// || line[0] == '<none>' }
@@ -206,11 +186,11 @@
if cide_image_ids.empty?
puts 'No images found to be cleaned'
return
end
- x = run("docker inspect #{cide_image_ids.join(' ')}", capture: true)
+ x = docker('inspect', *cide_image_ids, capture: true)
cide_images = JSON.parse(x.strip)
.each { |image| image['Created'] = Time.iso8601(image['Created']) }
.sort { |a, b| a['Created'] <=> b['Created'] }
if cide_images.size > max_images
@@ -226,46 +206,24 @@
if old_cide_images.empty?
puts 'No images found to be cleaned'
return
end
- run("docker rmi #{old_cide_images.join(' ')}")
+ docker('rmi', *old_cide_images)
end
desc 'init', "Creates a blank #{CONFIG_FILE} into the project"
def init
- if File.exist?(CONFIG_FILE)
- puts "#{CONFIG_FILE} already exists"
- return
- end
puts "Creating #{CONFIG_FILE} with default values"
create_file CONFIG_FILE, DefaultConfig.to_yaml
end
- protected
+ private
- def setup_docker
- @setup_docker ||= (
- if `uname`.strip == 'Darwin' && !ENV['DOCKER_HOST']
- unless system('which boot2docker >/dev/null 2>&1')
- puts 'make sure boot2docker is installed and running'
- puts
- puts '> brew install boot2docker'
- exit 1
- end
-
- `boot2docker shellinit 2>/dev/null`
- .lines
- .grep(/export (\w+)=(.*)/) { ENV[$1] = $2.strip }
- end
- true
- )
- end
-
- def docker(*args)
- opts = args.last.is_a?(Hash) ? args.pop : {}
- ret = run Shellwords.join(['docker'] + args), opts
- fail 'Command failed' if $?.exitstatus > 0
- ret
+ def create_tmp_file(destination, *args, &block)
+ create_file(destination, *args, &block)
+ at_exit do
+ remove_file(destination, verbose: false)
+ end
end
end
end