docker-template 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +13 -0
- data/LICENSE +13 -0
- data/README.md +20 -0
- data/Rakefile +20 -0
- data/lib/docker/template.rb +86 -0
- data/lib/docker/template/alias.rb +28 -0
- data/lib/docker/template/ansi.rb +75 -0
- data/lib/docker/template/auth.rb +25 -0
- data/lib/docker/template/common.rb +126 -0
- data/lib/docker/template/config.rb +84 -0
- data/lib/docker/template/error.rb +20 -0
- data/lib/docker/template/error/bad_exit_status.rb +17 -0
- data/lib/docker/template/error/bad_repo_name.rb +15 -0
- data/lib/docker/template/error/invalid_repo_type.rb +16 -0
- data/lib/docker/template/error/invalid_targz_file.rb +15 -0
- data/lib/docker/template/error/no_rootfs_copy_dir.rb +15 -0
- data/lib/docker/template/error/no_rootfs_mkimg.rb +15 -0
- data/lib/docker/template/error/no_setup_context_found.rb +15 -0
- data/lib/docker/template/error/not_implemented.rb +15 -0
- data/lib/docker/template/error/repo_not_found.rb +16 -0
- data/lib/docker/template/interface.rb +119 -0
- data/lib/docker/template/metadata.rb +160 -0
- data/lib/docker/template/parser.rb +66 -0
- data/lib/docker/template/patches.rb +9 -0
- data/lib/docker/template/patches/array.rb +11 -0
- data/lib/docker/template/patches/hash.rb +76 -0
- data/lib/docker/template/patches/object.rb +9 -0
- data/lib/docker/template/patches/pathname.rb +46 -0
- data/lib/docker/template/patches/string.rb +9 -0
- data/lib/docker/template/repo.rb +180 -0
- data/lib/docker/template/rootfs.rb +75 -0
- data/lib/docker/template/routable.rb +28 -0
- data/lib/docker/template/scratch.rb +139 -0
- data/lib/docker/template/simple.rb +51 -0
- data/lib/docker/template/stream.rb +62 -0
- data/lib/docker/template/templates/rootfs.erb +8 -0
- data/lib/docker/template/templates/scratch.erb +7 -0
- data/lib/docker/template/util.rb +39 -0
- data/lib/docker/template/util/copy.rb +82 -0
- data/lib/docker/template/util/data.rb +26 -0
- data/lib/docker/template/version.rb +9 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5f3b7843f510727766a84aa47a990fa24e1d75db
|
4
|
+
data.tar.gz: 385b436c17f1ac1c3dcf71236f0d80c833945384
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4d0e8cb2c9bb3f852be270b072abf34b7cbc8b895d5e3e61c4191af9f21e6e7dacda55e2970b93722add97f0c41e5621d1d5f4a37239274b2ccfb106ced82dcf
|
7
|
+
data.tar.gz: f7615f982ea856ee912d46fe5e2973c0a6932f34d61b0f47c176928b01fb07a7ae928b6c2fdfaff116f25e80c1acb431fc96381ee98666ce0d33e28b41d20732
|
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Frozen-string-literal: true
|
2
|
+
# Copyright: 2015 Jordon Bedwell - Apache v2.0 License
|
3
|
+
# Encoding: utf-8
|
4
|
+
|
5
|
+
source "https://rubygems.org"
|
6
|
+
gem "rake", :require => false
|
7
|
+
gemspec
|
8
|
+
|
9
|
+
group :development do
|
10
|
+
gem "guard-rspec", :require => false
|
11
|
+
gem "benchmark-ips", :require => false
|
12
|
+
gem "pry", :require => false
|
13
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2015 Jordon Bedwell
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/envygeeks/docker-template.svg?branch=master)][travis]
|
2
|
+
[![Coverage Status](https://coveralls.io/repos/envygeeks/docker-template/badge.svg?branch=master&service=github)][coveralls]
|
3
|
+
[![Code Climate](https://codeclimate.com/github/envygeeks/docker-template/badges/gpa.svg)][codeclimate]
|
4
|
+
|
5
|
+
[codeclimate]: https://codeclimate.com/github/envygeeks/docker-template
|
6
|
+
[coveralls]: https://coveralls.io/github/envygeeks/docker-template?branch=master
|
7
|
+
[travis]: https://travis-ci.org/envygeeks/docker-template
|
8
|
+
|
9
|
+
# Docker Template
|
10
|
+
|
11
|
+
Docker template is a way to organize your Docker repos into multiple types with
|
12
|
+
multiple templates for multiple tags but those templates aren't actually templates,
|
13
|
+
they are JSON or YAML files and copy folders with tons of shared or split data.
|
14
|
+
The idea is that you can have many tags for a single repo and that each tag
|
15
|
+
can have many different things going on.
|
16
|
+
|
17
|
+
Docker template makes it so that you can facilitate and organize those things in
|
18
|
+
a clean way and just get work done. It also makes building "scratch" images 100%
|
19
|
+
easier for anybody by doing most of the work for you and just asking that you
|
20
|
+
build the file system script that pumps out a tar.gz file.
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Frozen-string-literal: true
|
2
|
+
# Copyright: 2015 Jordon Bedwell - Apache v2.0 License
|
3
|
+
# Encoding: utf-8
|
4
|
+
|
5
|
+
$:.unshift(File.expand_path("../lib", __FILE__))
|
6
|
+
require "rspec/core/rake_task"
|
7
|
+
task :default => [:spec]
|
8
|
+
RSpec::Core::RakeTask.new :spec
|
9
|
+
task :test => :spec
|
10
|
+
|
11
|
+
task :build do
|
12
|
+
exec "bundle", "exec", "bin/docker-template", *ARGV[
|
13
|
+
1..-1
|
14
|
+
]
|
15
|
+
end
|
16
|
+
|
17
|
+
task :pry do
|
18
|
+
exec "bundle", "exec", "pry", "-Ilib/", \
|
19
|
+
"-rdocker/template"
|
20
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# Frozen-string-literal: true
|
2
|
+
# Copyright: 2015 Jordon Bedwell - Apache v2.0 License
|
3
|
+
# Encoding: utf-8
|
4
|
+
|
5
|
+
require "docker/template/version"
|
6
|
+
require "docker/template/patches"
|
7
|
+
|
8
|
+
require "docker"
|
9
|
+
require "forwardable"
|
10
|
+
require "json"
|
11
|
+
require "erb"
|
12
|
+
|
13
|
+
module Docker
|
14
|
+
module Template module_function
|
15
|
+
autoload :Util, "docker/template/util"
|
16
|
+
autoload :Config, "docker/template/config"
|
17
|
+
autoload :Ansi, "docker/template/ansi"
|
18
|
+
autoload :Parser, "docker/template/parser"
|
19
|
+
autoload :Routable, "docker/template/routable"
|
20
|
+
autoload :Interface, "docker/template/interface"
|
21
|
+
autoload :Metadata, "docker/template/metadata"
|
22
|
+
autoload :Stream, "docker/template/stream"
|
23
|
+
autoload :Safe, "docker/template/safe"
|
24
|
+
autoload :Repo, "docker/template/repo"
|
25
|
+
autoload :Error, "docker/template/error"
|
26
|
+
autoload :Common, "docker/template/common"
|
27
|
+
autoload :Rootfs, "docker/template/rootfs"
|
28
|
+
autoload :Scratch, "docker/template/scratch"
|
29
|
+
autoload :Simple, "docker/template/simple"
|
30
|
+
autoload :Alias, "docker/template/alias"
|
31
|
+
autoload :Auth, "docker/template/auth"
|
32
|
+
|
33
|
+
def config
|
34
|
+
return @config ||= begin
|
35
|
+
Config.new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
|
41
|
+
def root
|
42
|
+
return @root ||= begin
|
43
|
+
Pathname.new(Dir.pwd)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# The location of the standard repos/ dir, you can change this by adding
|
48
|
+
# `repos_dir` into your configuration file. I'm not saying it has to be but
|
49
|
+
# it should probably be relative rather than absolute, ther are no
|
50
|
+
# guarantees that an absolute path will work.
|
51
|
+
|
52
|
+
def repos_root
|
53
|
+
return @repos_root ||= begin
|
54
|
+
root.join(config["repos_dir"])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Provides the root to Docker template, wherever it is installed so that
|
59
|
+
# we can do things, mostly ignore files for the profiler. Otherwise it's
|
60
|
+
# not really used, it's just an encapsulator.
|
61
|
+
|
62
|
+
def gem_root
|
63
|
+
return @gem_root ||= begin
|
64
|
+
path = File.expand_path("../../", __dir__)
|
65
|
+
Pathname.new(path)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Provides the templates directory so you can quickly pull a template
|
70
|
+
# from our templates and use it if you wish to.
|
71
|
+
|
72
|
+
def template_root
|
73
|
+
return @template_root ||= begin
|
74
|
+
gem_root.join("lib/docker/template/templates")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
|
80
|
+
def get(name, data = {})
|
81
|
+
data = Util::Data.new(data)
|
82
|
+
template = template_root.join("#{name}.erb").read
|
83
|
+
ERB.new(template).result(data._binding)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Docker
|
2
|
+
module Template
|
3
|
+
class Alias
|
4
|
+
def initialize(aliased)
|
5
|
+
@aliased = aliased
|
6
|
+
end
|
7
|
+
|
8
|
+
#
|
9
|
+
|
10
|
+
def build
|
11
|
+
Util.notify_alias(@aliased)
|
12
|
+
prebuild unless @aliased.parent_img
|
13
|
+
@aliased.parent_img.tag(@aliased.repo.to_tag_h)
|
14
|
+
@aliased.push
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
|
19
|
+
private
|
20
|
+
def prebuild
|
21
|
+
repo = @aliased.parent_repo
|
22
|
+
simple = @aliased.repo.type == "simple"
|
23
|
+
@aliased.class.new(repo, @aliased.rootfs_img) unless simple
|
24
|
+
@aliased.class.new(repo).build if simple
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Frozen-string-literal: true
|
2
|
+
# Copyright: 2015 Jordon Bedwell - Apache v2.0 License
|
3
|
+
# Encoding: utf-8
|
4
|
+
|
5
|
+
module Docker
|
6
|
+
module Template
|
7
|
+
module Ansi module_function
|
8
|
+
Escape = "%c" % 27
|
9
|
+
Match = /#{Escape}\[(?:\d+)(?:;\d+)?(j|k|m|s|u|A|B)|\e\(B\e\[m/ix.freeze
|
10
|
+
Colors = { :red => 31, :green => 32, :black => 30, :magenta => 35,
|
11
|
+
:yellow => 33, :white => 37, :blue => 34, :cyan => 36 }
|
12
|
+
|
13
|
+
# Strip ANSI from the current string. It also strips cursor stuff,
|
14
|
+
# well some of it, and it also strips some other stuff that a lot of
|
15
|
+
# the other ANSI strippers don't.
|
16
|
+
|
17
|
+
def strip(str)
|
18
|
+
str.gsub Match, ""
|
19
|
+
end
|
20
|
+
|
21
|
+
# Reset the vterm view if it's supported. Depending on how badly
|
22
|
+
# your vterm is implemented it might reset rather than clear scrollback
|
23
|
+
# with a few empty lines added on the top.
|
24
|
+
|
25
|
+
def clear
|
26
|
+
$stdout.print("%c[H%c[2J" % [27, 27])
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
|
31
|
+
def has?(str)
|
32
|
+
!!(str =~ Match)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Jump the cursor up and then back down or just up and down. This is
|
36
|
+
# useful when streaming async downloads from something like Docker. It
|
37
|
+
# also works better than using `tput`
|
38
|
+
|
39
|
+
def jump(str = "", up: nil, down: nil, both: nil, clear: true)
|
40
|
+
str = clear_line(str) if clear
|
41
|
+
|
42
|
+
return "%c[%dA%s%c[%dB" % [27, up || both, str, 27, down || both] if (up && down) || both
|
43
|
+
up ? "%c[%dA%s" % [27, up, str] : "%s%c[%dB" % [str, 27, down]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Reset the color back to the default color so that you do not leak any
|
47
|
+
# colors when you move onto the next line. This is probably normally
|
48
|
+
# used as part of a wrapper so that we don't leak colors.
|
49
|
+
|
50
|
+
def reset(str = "")
|
51
|
+
@ansi_reset ||= "%c[0m" % 27
|
52
|
+
"#{@ansi_reset}#{str}"
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
|
57
|
+
def clear_line(str = "")
|
58
|
+
@ansi_clear_line ||= "%c[2K\r" % 27
|
59
|
+
"#{@ansi_clear_line}#{str}\r"
|
60
|
+
end
|
61
|
+
|
62
|
+
# SEE: `self::Colors` for a list of methods. They are mostly
|
63
|
+
# standard base colors supported by pretty much any xterm-color, we do
|
64
|
+
# not need more than the base colors so we do not include them.
|
65
|
+
# Actually... if I'm honest we don't even need most of the
|
66
|
+
# base colors.
|
67
|
+
|
68
|
+
Colors.each do |color, num|
|
69
|
+
define_method color do |str|
|
70
|
+
"#{"%c" % 27}[#{num}m#{str}#{reset}"
|
71
|
+
end; module_function color
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Docker
|
2
|
+
module Template
|
3
|
+
module Auth
|
4
|
+
module_function
|
5
|
+
|
6
|
+
def auth!
|
7
|
+
return unless login = get_info
|
8
|
+
login["auths"].each do |server, auth|
|
9
|
+
username, password = Base64.decode64(auth["auth"]).split(":", 2)
|
10
|
+
Docker.authenticate!({
|
11
|
+
"username" => username,
|
12
|
+
"serveraddress" => server,
|
13
|
+
"email" => auth["email"],
|
14
|
+
"password" => password
|
15
|
+
})
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_info
|
20
|
+
path = Pathname.new("~/.docker/config.json").expand_path
|
21
|
+
return JSON.parse(path.read) if path.exist?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# Frozen-string-literal: true
|
2
|
+
# Copyright: 2015 Jordon Bedwell - Apache v2.0 License
|
3
|
+
# Encoding: utf-8
|
4
|
+
|
5
|
+
module Docker
|
6
|
+
module Template
|
7
|
+
class Common
|
8
|
+
CopyMethods = [
|
9
|
+
:setup_context, :copy_global,
|
10
|
+
:copy_all, :copy_type, :copy_tag, :build_context,
|
11
|
+
:verify_context].freeze
|
12
|
+
|
13
|
+
def push
|
14
|
+
return if rootfs? || !Interface.push?
|
15
|
+
|
16
|
+
Auth.auth!
|
17
|
+
img = @img || Docker::Image.get(@repo.to_s)
|
18
|
+
logger = Stream.new.method(:log)
|
19
|
+
img.push(&logger)
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
|
24
|
+
def aliased?
|
25
|
+
@repo.tag != @repo.aliased && !rootfs?
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
|
30
|
+
def rootfs?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
|
36
|
+
def parent_repo
|
37
|
+
return false unless aliased?
|
38
|
+
@parent_repo ||= Repo.new(@repo.to_h.merge({
|
39
|
+
"tag" => @repo.aliased
|
40
|
+
}))
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
|
45
|
+
def parent_img
|
46
|
+
return false unless aliased?
|
47
|
+
@parent_img ||= Docker::Image.get(parent_repo.to_s)
|
48
|
+
rescue Docker::Error::NotFoundError
|
49
|
+
if aliased?
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
|
56
|
+
def build
|
57
|
+
return Alias.new(self).build if aliased?
|
58
|
+
|
59
|
+
Ansi.clear
|
60
|
+
Util.notify_build(@repo, rootfs: rootfs?)
|
61
|
+
logger = Stream.new.method(:log)
|
62
|
+
copy_build_and_verify
|
63
|
+
|
64
|
+
Dir.chdir(@context) do
|
65
|
+
@img = Docker::Image.build_from_dir(".", &logger)
|
66
|
+
@img.tag rootfs?? @repo.to_rootfs_h : @repo.to_tag_h
|
67
|
+
push
|
68
|
+
end
|
69
|
+
rescue SystemExit => exit_
|
70
|
+
unlink img: true
|
71
|
+
raise exit_
|
72
|
+
ensure
|
73
|
+
if rootfs?
|
74
|
+
unlink img: false else unlink
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
|
80
|
+
private
|
81
|
+
def copy_build_and_verify
|
82
|
+
unless respond_to?(:setup_context, true)
|
83
|
+
raise Error::NoSetupContextFound
|
84
|
+
end
|
85
|
+
|
86
|
+
CopyMethods.each do |val|
|
87
|
+
send(val) if respond_to?(val, true)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
def copy_tag
|
93
|
+
return if rootfs?
|
94
|
+
dir = @repo.copy_dir("tag", @repo.tag)
|
95
|
+
Util::Copy.directory(dir, @copy)
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
|
100
|
+
private
|
101
|
+
def copy_global
|
102
|
+
return if rootfs?
|
103
|
+
dir = Template.root.join(@repo.metadata["copy_dir"])
|
104
|
+
Util::Copy.directory(dir, @copy)
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
|
109
|
+
private
|
110
|
+
def copy_type
|
111
|
+
return unless !rootfs? && build_type = @repo.metadata["tags"][@repo.tag]
|
112
|
+
dir = @repo.copy_dir("type", build_type)
|
113
|
+
Util::Copy.directory(dir, @copy)
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
|
118
|
+
private
|
119
|
+
def copy_all
|
120
|
+
return if rootfs?
|
121
|
+
dir = @repo.copy_dir("all")
|
122
|
+
Util::Copy.directory(dir, @copy)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Frozen-string-literal: true
|
2
|
+
# Copyright: 2015 Jordon Bedwell - Apache v2.0 License
|
3
|
+
# Encoding: utf-8
|
4
|
+
|
5
|
+
require "json"
|
6
|
+
require "yaml"
|
7
|
+
|
8
|
+
module Docker
|
9
|
+
module Template
|
10
|
+
|
11
|
+
# Configuration is a global version of meatadata, where anything
|
12
|
+
# that can be set on configuration can be optimized and stored globally
|
13
|
+
# in a opts.{json,yml} file in the current working directory.
|
14
|
+
|
15
|
+
class Config
|
16
|
+
extend Forwardable
|
17
|
+
|
18
|
+
def_delegator :@config, :keys
|
19
|
+
def_delegator :@config, :to_h
|
20
|
+
def_delegator :@config, :to_enum
|
21
|
+
def_delegator :@config, :has_key?
|
22
|
+
def_delegator :@config, :each
|
23
|
+
def_delegator :@config, :[]
|
24
|
+
|
25
|
+
Defaults = {
|
26
|
+
"type" => "simple",
|
27
|
+
"user" => "envygeeks",
|
28
|
+
"local_prefix" => "local",
|
29
|
+
"rootfs_base_img" => "envygeeks/ubuntu:tiny",
|
30
|
+
"maintainer" => "Jordon Bedwell <jordon@envygeeks.io>",
|
31
|
+
"dockerhub_copy" => false,
|
32
|
+
"repos_dir" => "repos",
|
33
|
+
"copy_dir" => "copy",
|
34
|
+
"tag" => "latest",
|
35
|
+
|
36
|
+
"aliases" => {},
|
37
|
+
"pkgs" => { "tag" => {}, "type" => {}, "all" => nil },
|
38
|
+
"entries" => { "tag" => {}, "type" => {}, "all" => nil },
|
39
|
+
"releases" => { "tag" => {}, "type" => {}, "all" => nil },
|
40
|
+
"versions" => { "tag" => {}, "type" => {}, "all" => nil },
|
41
|
+
"env" => { "tag" => {}, "type" => {}, "all" => nil },
|
42
|
+
"tags" => {}
|
43
|
+
}.freeze
|
44
|
+
|
45
|
+
EmptyDefaults = {
|
46
|
+
"tags" => { "latest" => "normal" }
|
47
|
+
}
|
48
|
+
|
49
|
+
#
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
@config = Defaults.deep_merge(read_config_from)
|
53
|
+
@config = @config.merge(EmptyDefaults) do |key, oval, nval|
|
54
|
+
oval.nil? || oval.empty?? nval : oval
|
55
|
+
end
|
56
|
+
|
57
|
+
@config.freeze
|
58
|
+
end
|
59
|
+
|
60
|
+
# Allows you to read a configuration file from a root and get back
|
61
|
+
# either the parsed data or a blank hash that can be merged the way you
|
62
|
+
# wish to merge it (if you even care to merge it.)
|
63
|
+
|
64
|
+
def read_config_from(dir = Docker::Template.root)
|
65
|
+
file = Dir[dir.join("*.{json,yml}")].first
|
66
|
+
return {} unless file && (file = Pathname.new(file)).file?
|
67
|
+
return JSON.parse(file.read).stringify if file.extname == ".json"
|
68
|
+
YAML.load_file(file).stringify
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
|
73
|
+
def has_default?(key)
|
74
|
+
return @config.has_key?(key)
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
|
79
|
+
def build_types
|
80
|
+
return @build_types ||= %W(simple scratch).freeze
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|