andys-mason 0.1.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.
- data/README.md +72 -0
- data/bin/mason +11 -0
- data/data/Vagrantfile.template +5 -0
- data/lib/mason.rb +5 -0
- data/lib/mason/buildpack.rb +99 -0
- data/lib/mason/buildpacks.rb +75 -0
- data/lib/mason/cli.rb +279 -0
- data/lib/mason/cp_r.rb +7 -0
- data/lib/mason/stacks.rb +113 -0
- data/lib/mason/version.rb +3 -0
- metadata +87 -0
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# Mason
|
2
|
+
|
3
|
+
Build things
|
4
|
+
|
5
|
+
$ gem install mason
|
6
|
+
|
7
|
+
### Install buildpacks locally
|
8
|
+
|
9
|
+
$ mason buildpacks
|
10
|
+
* buildpacks (~/.mason/buildpacks)
|
11
|
+
= nodejs: https://github.com/heroku/heroku-buildpack-nodejs.git
|
12
|
+
= ruby: https://github.com/ddollar/heroku-buildpack-ruby.git
|
13
|
+
|
14
|
+
$ mason buildpacks:install https://github.com/heroku/heroku-buildpack-python.git
|
15
|
+
* installing buildpack https://github.com/heroku/heroku-buildpack-python.git
|
16
|
+
|
17
|
+
### Use buildpacks to build things
|
18
|
+
|
19
|
+
$ mason build /tmp/app
|
20
|
+
* detecting buildpack... done
|
21
|
+
= name: Ruby
|
22
|
+
= url: https://github.com/heroku/heroku-buildpack-ruby.git
|
23
|
+
* compiling:
|
24
|
+
... COMPILE OUTPUT
|
25
|
+
* packaging... done
|
26
|
+
= type: dir
|
27
|
+
= file: /tmp/mason.out
|
28
|
+
|
29
|
+
$ mason build /tmp/app -t dir -o /tmp/compiledapp
|
30
|
+
* detecting buildpack... done
|
31
|
+
= name: Ruby
|
32
|
+
= url: https://github.com/heroku/heroku-buildpack-ruby.git
|
33
|
+
* compiling...
|
34
|
+
... COMPILE OUTPUT
|
35
|
+
* packaging... done
|
36
|
+
= type: dir
|
37
|
+
= dir: /tmp/compiledapp
|
38
|
+
|
39
|
+
$ mason build /tmp/app -b https://github.com/ddollar/buildpack-other.git -t tgz
|
40
|
+
* detecting buildpack... done
|
41
|
+
= name: Other
|
42
|
+
= url: https://github.com/ddollar/buildpack-other.git
|
43
|
+
* compiling...
|
44
|
+
... COMPILE OUTPUT
|
45
|
+
* packaging... done
|
46
|
+
= type: tgz
|
47
|
+
= file: /tmp/app.tgz
|
48
|
+
|
49
|
+
### Build things for other platforms using Vagrant
|
50
|
+
|
51
|
+
You will need [VirtualBox](https://www.virtualbox.org/wiki/Downloads) for Vagrant to function.
|
52
|
+
|
53
|
+
$ gem install vagrant
|
54
|
+
|
55
|
+
$ vagrant box add lucid64 http://files.vagrantup.com/lucid64.box
|
56
|
+
|
57
|
+
$ mason stacks:create lucid64
|
58
|
+
* creating stack lucid64... done
|
59
|
+
|
60
|
+
$ mason stacks:up lucid64
|
61
|
+
* booting stack lucid64 (this may take a while)... done
|
62
|
+
|
63
|
+
$ mason:build /tmp/app -t tgz -o /tmp/compiled.tgz -s lucid64
|
64
|
+
* booting stack lucid64 (this may take a while)... done
|
65
|
+
* detecting buildpack... done
|
66
|
+
= name: Baz
|
67
|
+
= url: https://github.com/ddollar/buildpack-baz.git
|
68
|
+
* compiling...
|
69
|
+
... COMPILE OUTPUT
|
70
|
+
* packaging... done
|
71
|
+
= type: tgz
|
72
|
+
= dir: /tmp/compiled.tgz
|
data/bin/mason
ADDED
data/lib/mason.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require "mason"
|
2
|
+
require "tmpdir"
|
3
|
+
require "yaml"
|
4
|
+
require "foreman/engine"
|
5
|
+
|
6
|
+
class Mason::Buildpack
|
7
|
+
|
8
|
+
attr_reader :dir, :name, :url
|
9
|
+
|
10
|
+
def initialize(dir)
|
11
|
+
@dir = dir
|
12
|
+
Dir.chdir(@dir) do
|
13
|
+
@name = File.basename(@dir)
|
14
|
+
@url = %x{ git config remote.origin.url }.chomp
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def <=>(other)
|
19
|
+
self.name <=> other.name
|
20
|
+
end
|
21
|
+
|
22
|
+
def detect(app)
|
23
|
+
mkchtmpdir do
|
24
|
+
output = %x{ #{script("detect")} "#{app}" }
|
25
|
+
$?.exitstatus.zero? ? output.chomp : nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def compile(app, env_file=nil, cache=nil)
|
30
|
+
cache_dir = cache || "#{app}/.git/cache"
|
31
|
+
puts " caching in #{cache_dir}"
|
32
|
+
compile_dir = Dir.mktmpdir
|
33
|
+
FileUtils.rm_rf compile_dir
|
34
|
+
FileUtils.cp_r app, compile_dir, :preserve => true
|
35
|
+
FileUtils.mkdir_p cache_dir
|
36
|
+
Dir.chdir(compile_dir) do
|
37
|
+
IO.popen(%{ #{script("compile")} "#{compile_dir}" "#{cache_dir}" }) do |io|
|
38
|
+
until io.eof?
|
39
|
+
data = io.gets
|
40
|
+
data.gsub!(/^-----> /, " + ")
|
41
|
+
data.gsub!(/^ /, " ")
|
42
|
+
data.gsub!(/^\s+\!\s+$/, "")
|
43
|
+
data.gsub!(/^\s+\!\s+/, " ! ")
|
44
|
+
data.gsub!(/^\s+$/, "")
|
45
|
+
print data
|
46
|
+
end
|
47
|
+
end
|
48
|
+
raise "compile failed" unless $?.exitstatus.zero?
|
49
|
+
end
|
50
|
+
release = YAML.load(`#{script('release')} "#{compile_dir}"`)
|
51
|
+
write_env(compile_dir, release, env_file)
|
52
|
+
write_procfile(compile_dir, release)
|
53
|
+
compile_dir
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def write_env(compile_dir, release, env_file)
|
59
|
+
env = env_file ? Foreman::Engine.new.load_env(env_file) : {}
|
60
|
+
config_vars = release["config_vars"] || {}
|
61
|
+
config = config_vars.merge(env)
|
62
|
+
|
63
|
+
File.open(File.join(compile_dir, ".env"), "w") do |f|
|
64
|
+
f.puts config.map{|k, v| "#{k}=#{v}"}.join("\n")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def write_procfile(compile_dir, release)
|
69
|
+
filename = File.join(compile_dir, "Procfile")
|
70
|
+
process_types = release["default_process_types"] || {}
|
71
|
+
|
72
|
+
if File.exists? filename
|
73
|
+
Foreman::Procfile.new(filename).entries.each do |e|
|
74
|
+
process_types[e.name] = e.command
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
File.open(filename, "w") do |f|
|
79
|
+
process_types.each do |name, command|
|
80
|
+
f.puts "#{name}: #{command}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def mkchtmpdir
|
86
|
+
ret = nil
|
87
|
+
Dir.mktmpdir do |dir|
|
88
|
+
Dir.chdir(dir) do
|
89
|
+
ret = yield(dir)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
ret
|
93
|
+
end
|
94
|
+
|
95
|
+
def script(name)
|
96
|
+
File.join(dir, "bin", name)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "mason"
|
3
|
+
require "mason/buildpack"
|
4
|
+
require "uri"
|
5
|
+
require "digest/sha1"
|
6
|
+
|
7
|
+
class Mason::Buildpacks
|
8
|
+
|
9
|
+
def self.install(url, ad_hoc=false)
|
10
|
+
root_dir = ad_hoc ? ad_hoc_root : root
|
11
|
+
FileUtils.mkdir_p root_dir
|
12
|
+
|
13
|
+
Dir.chdir(root_dir) do
|
14
|
+
uri = URI.parse(url)
|
15
|
+
if uri.path =~ /buildpack-(\w+)/
|
16
|
+
name = $1
|
17
|
+
name += "-#{Digest::SHA1.hexdigest(url).to_s[0 .. 8]}" if ad_hoc
|
18
|
+
branch = uri.fragment || "master"
|
19
|
+
if File.exists?(name)
|
20
|
+
# Can't do a fetch here as it won't update local branches
|
21
|
+
system "cd #{name} && git checkout master && git pull"
|
22
|
+
raise "failed to update buildpack checkout" unless $?.exitstatus.zero?
|
23
|
+
else
|
24
|
+
system "git clone #{url.split('#').first} #{name} >/dev/null 2>&1"
|
25
|
+
raise "failed to clone buildpack" unless $?.exitstatus.zero?
|
26
|
+
end
|
27
|
+
system "cd #{name} && git checkout #{branch} 2> /dev/null"
|
28
|
+
raise "failed to check out branch #{branch}" unless $?.exitstatus.zero?
|
29
|
+
File.expand_path(root_dir + "/" + name)
|
30
|
+
else
|
31
|
+
raise "BUILDPACK should be a url containing buildpack-NAME.git"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.uninstall(name)
|
37
|
+
Dir.chdir(root) do
|
38
|
+
raise "#{name} buildpack is not installed" unless File.exists?(name)
|
39
|
+
FileUtils.rm_rf name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.root(ad_hoc=false, expand=true)
|
44
|
+
dir = "~/.mason/buildpacks"
|
45
|
+
expand ? File.expand_path(dir) : dir
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.ad_hoc_root(expand=true)
|
49
|
+
dir = "~/.mason/buildpacks-ad-hoc"
|
50
|
+
expand ? File.expand_path(dir) : dir
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.buildpacks
|
54
|
+
@buildpacks ||= begin
|
55
|
+
Dir[File.join(root, "*")].map do |buildpack_dir|
|
56
|
+
Mason::Buildpack.new(buildpack_dir)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.detect(app, buildpack_url)
|
62
|
+
if buildpack_url
|
63
|
+
puts "Using $BUILDPACK_URL: #{buildpack_url}"
|
64
|
+
buildpack_dir = install(buildpack_url, true)
|
65
|
+
return Mason::Buildpack.new(buildpack_dir)
|
66
|
+
else
|
67
|
+
buildpacks.each do |buildpack|
|
68
|
+
ret = buildpack.detect(app)
|
69
|
+
return [buildpack, ret] if ret
|
70
|
+
end
|
71
|
+
end
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
data/lib/mason/cli.rb
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
require "mason"
|
2
|
+
require "mason/buildpacks"
|
3
|
+
require "mason/stacks"
|
4
|
+
require "mason/version"
|
5
|
+
require "thor"
|
6
|
+
require "thor/shell/basic"
|
7
|
+
|
8
|
+
class Mason::CLI < Thor
|
9
|
+
|
10
|
+
class_option :debug, :type => :boolean, :desc => "show backtraces"
|
11
|
+
class_option :help, :type => :boolean, :aliases => "-h", :desc => "help for this command"
|
12
|
+
|
13
|
+
map %w( -v -V --version ) => :version
|
14
|
+
|
15
|
+
desc "version", "display version"
|
16
|
+
|
17
|
+
def version
|
18
|
+
puts "mason v#{Mason::VERSION}"
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "build APP", "build an app"
|
22
|
+
|
23
|
+
method_option :buildpack, :type => :string, :aliases => "-b", :desc => "use a custom buildpack"
|
24
|
+
method_option :output, :type => :string, :aliases => "-o", :desc => "output location"
|
25
|
+
method_option :quiet, :type => :boolean, :aliases => "-q", :desc => "quiet packaging output"
|
26
|
+
method_option :stack, :type => :string, :aliases => "-s", :desc => "use a stack for building"
|
27
|
+
method_option :type, :type => :string, :aliases => "-t", :desc => "output type (dir, img, tgz)"
|
28
|
+
method_option :env_file, :type => :string, :aliases => "-e", :desc => "config environment file"
|
29
|
+
method_option :cache, :type => :string, :aliases => "-c", :desc => "cache directory"
|
30
|
+
|
31
|
+
def build(app)
|
32
|
+
app = File.expand_path(app)
|
33
|
+
|
34
|
+
raise "no such directory: #{app}" unless File.exists?(app)
|
35
|
+
|
36
|
+
type = options[:type]
|
37
|
+
output = options[:output]
|
38
|
+
|
39
|
+
type = File.extname(output)[1..-1] if !type && output
|
40
|
+
output = "#{app}.#{type}" if !output && type
|
41
|
+
|
42
|
+
type ||= "dir"
|
43
|
+
output ||= "/tmp/mason.out"
|
44
|
+
|
45
|
+
output = File.expand_path(output)
|
46
|
+
|
47
|
+
raise "no such output format: #{type}" unless %w( dir img tgz ).include?(type)
|
48
|
+
|
49
|
+
if stack = options[:stack]
|
50
|
+
if Mason::Stacks.state(stack) == :up
|
51
|
+
puts "* using stack #{stack}"
|
52
|
+
else
|
53
|
+
print "* booting stack #{stack} (this may take a while)... "
|
54
|
+
Mason::Stacks.up(stack)
|
55
|
+
puts "done"
|
56
|
+
end
|
57
|
+
|
58
|
+
buildpacks_dir = File.expand_path("~/.mason/share/#{stack}/buildpacks")
|
59
|
+
compile_dir = File.expand_path("~/.mason/share/#{stack}/app")
|
60
|
+
mason_dir = File.expand_path("~/.mason/share/#{stack}/mason")
|
61
|
+
|
62
|
+
FileUtils.rm_rf buildpacks_dir
|
63
|
+
FileUtils.rm_rf compile_dir
|
64
|
+
FileUtils.rm_rf mason_dir
|
65
|
+
|
66
|
+
FileUtils.cp_r(File.expand_path("~/.mason/buildpacks"), buildpacks_dir,
|
67
|
+
:preserve => true)
|
68
|
+
FileUtils.cp_r(File.expand_path("../../../", __FILE__), mason_dir,
|
69
|
+
:preserve => true)
|
70
|
+
FileUtils.cp_r(app, compile_dir, :preserve => true)
|
71
|
+
|
72
|
+
mason_args = %{ /share/app -q -o /share/output -t #{type} }
|
73
|
+
mason_args += %{ -b "#{options[:buildpack]}" } if options[:buildpack]
|
74
|
+
|
75
|
+
Mason::Stacks.run(stack, <<-COMMAND)
|
76
|
+
gem spec thor 2>&1 >/dev/null || sudo gem install thor
|
77
|
+
/usr/bin/env ruby -rubygems /share/mason/bin/mason build #{mason_args}
|
78
|
+
COMMAND
|
79
|
+
|
80
|
+
FileUtils.rm_rf output
|
81
|
+
FileUtils.cp_r(File.expand_path("~/.mason/share/#{stack}/output"), output,
|
82
|
+
:preserve => true)
|
83
|
+
|
84
|
+
puts "* packaging"
|
85
|
+
puts " = type: #{type}"
|
86
|
+
puts " = location: #{output}"
|
87
|
+
else
|
88
|
+
print "* detecting buildpack... "
|
89
|
+
|
90
|
+
buildpack_url = ENV["BUILDPACK_URL"] || options[:buildpack]
|
91
|
+
buildpack, ret = Mason::Buildpacks.detect(app, buildpack_url)
|
92
|
+
raise "no valid buildpack detected" unless buildpack
|
93
|
+
|
94
|
+
puts "done"
|
95
|
+
puts " = name: #{buildpack.name}"
|
96
|
+
puts " = url: #{buildpack.url}"
|
97
|
+
puts " = display: #{ret}"
|
98
|
+
|
99
|
+
puts "* compiling..."
|
100
|
+
compile_dir = buildpack.compile(app, options[:env_file], options[:cache])
|
101
|
+
|
102
|
+
print "* packaging... " unless options[:quiet]
|
103
|
+
case type.to_sym
|
104
|
+
when :tgz then
|
105
|
+
Dir.chdir(compile_dir) do
|
106
|
+
system %{ tar czf "#{output}" . }
|
107
|
+
end
|
108
|
+
when :img then
|
109
|
+
raise "img not supported yet"
|
110
|
+
when :dir then
|
111
|
+
FileUtils.rm_rf output
|
112
|
+
FileUtils.cp_r compile_dir, output, :preserve => true
|
113
|
+
else
|
114
|
+
raise "no such output type: #{type}"
|
115
|
+
end
|
116
|
+
|
117
|
+
unless options[:quiet]
|
118
|
+
puts "done"
|
119
|
+
puts " = type: #{type}"
|
120
|
+
puts " = location: #{output}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
desc "vagrant COMMAND", "run a vagrant command in the mason environment"
|
127
|
+
|
128
|
+
def vagrant(*args)
|
129
|
+
Mason::Stacks.vagrant(args)
|
130
|
+
end
|
131
|
+
|
132
|
+
desc "buildpacks", "list installed buildpacks"
|
133
|
+
|
134
|
+
def buildpacks
|
135
|
+
buildpacks = Mason::Buildpacks.buildpacks
|
136
|
+
|
137
|
+
puts "* buildpacks (#{Mason::Buildpacks.root(false)})"
|
138
|
+
buildpacks.sort.each do |buildpack|
|
139
|
+
puts " = #{buildpack.name}: #{buildpack.url}"
|
140
|
+
end
|
141
|
+
|
142
|
+
puts " - no buildpacks installed, use buildpacks:install" if buildpacks.length.zero?
|
143
|
+
end
|
144
|
+
|
145
|
+
class Buildpacks < Thor
|
146
|
+
|
147
|
+
desc "buildpacks:install URL", "install a buildpack"
|
148
|
+
|
149
|
+
def install(url)
|
150
|
+
puts "* installing buildpack #{url}"
|
151
|
+
Mason::Buildpacks.install url
|
152
|
+
end
|
153
|
+
|
154
|
+
desc "buildpacks:uninstall NAME", "uninstall a buildpack"
|
155
|
+
|
156
|
+
def uninstall(name)
|
157
|
+
puts "* uninstalling buildpack #{name}"
|
158
|
+
Mason::Buildpacks.uninstall name
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
desc "stacks", "list available stacks"
|
164
|
+
|
165
|
+
def stacks
|
166
|
+
stacks = Mason::Stacks.stacks
|
167
|
+
|
168
|
+
puts "* available stacks"
|
169
|
+
stacks.keys.each do |name|
|
170
|
+
puts " - #{name} [#{Mason::Stacks.state(name)}]"
|
171
|
+
end
|
172
|
+
|
173
|
+
puts " - no stacks created, use stacks:create" if stacks.length.zero?
|
174
|
+
end
|
175
|
+
|
176
|
+
class Stacks < Thor
|
177
|
+
|
178
|
+
# Hackery. Take the run method away from Thor so that we can redefine it.
|
179
|
+
class << self
|
180
|
+
def is_thor_reserved_word?(word, type)
|
181
|
+
return false if word == 'run'
|
182
|
+
super
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
desc "stacks:create VAGRANT_BOX_NAME", "create a new stack"
|
187
|
+
|
188
|
+
method_option :name, :type => :string, :aliases => "-n", :desc => "use an alternate stack name"
|
189
|
+
|
190
|
+
def create(box)
|
191
|
+
name = options[:name] || box
|
192
|
+
print "* creating stack #{name}... "
|
193
|
+
Mason::Stacks.create(name, box)
|
194
|
+
puts "done"
|
195
|
+
end
|
196
|
+
|
197
|
+
desc "stacks:destroy STACK", "destroy a stack"
|
198
|
+
|
199
|
+
def destroy(name)
|
200
|
+
print "* destroying stack #{name}... "
|
201
|
+
Mason::Stacks.destroy(name)
|
202
|
+
puts "done"
|
203
|
+
end
|
204
|
+
|
205
|
+
desc "stacks:up STACK", "boot a stack"
|
206
|
+
|
207
|
+
def up(name)
|
208
|
+
print "* booting stack #{name} (this will take a while)..."
|
209
|
+
Mason::Stacks.up(name)
|
210
|
+
puts "done"
|
211
|
+
end
|
212
|
+
|
213
|
+
desc "stacks:down STACK", "suspend a stack"
|
214
|
+
|
215
|
+
def down(name)
|
216
|
+
print "* stopping stack #{name}..."
|
217
|
+
Mason::Stacks.down(name)
|
218
|
+
puts "done"
|
219
|
+
end
|
220
|
+
|
221
|
+
desc "stacks:run STACK COMMAND", "run a command on a stack"
|
222
|
+
|
223
|
+
def run(name, *args)
|
224
|
+
Mason::Stacks.run(name, args.join(" "))
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
# hack thor
|
230
|
+
def self.run
|
231
|
+
args = ARGV.dup
|
232
|
+
parts = args.first.to_s.split(":")
|
233
|
+
method = parts.pop
|
234
|
+
ns = parts.pop
|
235
|
+
|
236
|
+
args[0] = method
|
237
|
+
|
238
|
+
klass = case ns
|
239
|
+
when "buildpacks" then Buildpacks
|
240
|
+
when "stacks" then Stacks
|
241
|
+
else self
|
242
|
+
end
|
243
|
+
|
244
|
+
unless (args & %w( -h --help )).empty?
|
245
|
+
klass.task_help(Thor::Shell::Basic.new, args.first)
|
246
|
+
return
|
247
|
+
end
|
248
|
+
|
249
|
+
klass.start(args)
|
250
|
+
rescue StandardError => ex
|
251
|
+
$stderr.puts " ! #{ex}"
|
252
|
+
$stderr.puts " " + ex.backtrace.join("\n ") if ARGV.include?("--debug")
|
253
|
+
exit 1
|
254
|
+
end
|
255
|
+
|
256
|
+
private
|
257
|
+
|
258
|
+
def vagrantfile
|
259
|
+
FileUtils.mkdir_p File.expand_path("~/.mason")
|
260
|
+
file = File.expand_path("~/.mason/Vagrantfile")
|
261
|
+
build_vagrantfile unless File.exists?(file)
|
262
|
+
file
|
263
|
+
end
|
264
|
+
|
265
|
+
def build_vagrantfile(boxes={})
|
266
|
+
data = File.read(File.expand_path("../../../data/Vagrantfile.template", __FILE__))
|
267
|
+
data.gsub! "BOXES", (boxes.map do |name, box|
|
268
|
+
<<-BOX
|
269
|
+
config.vm.define :#{name} do |config|
|
270
|
+
config.vm.box = "#{box}"
|
271
|
+
end
|
272
|
+
BOX
|
273
|
+
end.join("\n"))
|
274
|
+
File.open(File.expand_path("~/.mason/Vagrantfile"), "w") do |file|
|
275
|
+
file.puts data
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
data/lib/mason/cp_r.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
module Mason
|
2
|
+
def self.cp_R(src, dest, options = {})
|
3
|
+
return if options[:noop]
|
4
|
+
output = `cp -vR#{options[:preserve] ? 'p' : ''}#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}`
|
5
|
+
raise "cp failed: #{output}" unless $?.exitstatus.zero?
|
6
|
+
end
|
7
|
+
end
|
data/lib/mason/stacks.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "mason"
|
3
|
+
|
4
|
+
class Mason::Stacks
|
5
|
+
|
6
|
+
def self.load_vagrant!
|
7
|
+
require "vagrant"
|
8
|
+
if Gem::Version.new(Vagrant::VERSION) < Gem::Version.new("1.0.1")
|
9
|
+
raise "mason requires vagrant 1.0.1 or higher"
|
10
|
+
end
|
11
|
+
build_vagrantfile unless File.exists?(vagrantfile)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.vagrant_env(display=false)
|
15
|
+
load_vagrant!
|
16
|
+
ui = display ? Vagrant::UI::Basic : nil
|
17
|
+
Vagrant::Environment.new(:vagrantfile_name => vagrantfile, :ui_class => ui)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.vms
|
21
|
+
vagrant_env.vms
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.vagrant(args)
|
25
|
+
vagrant_env(true).cli(args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.stacks
|
29
|
+
@stacks ||= begin
|
30
|
+
vms.inject({}) do |hash, (name, vm)|
|
31
|
+
next(hash) if name == :default
|
32
|
+
hash.update(name => vm.box ? vm.box.name : "")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.create(name, box)
|
38
|
+
raise "stack already exists: #{name}" if stacks.keys.include?(name.to_sym)
|
39
|
+
raise "vagrant box does not exist: #{box}" unless vagrant_env.boxes.map(&:name).include?(box)
|
40
|
+
build_vagrantfile(stacks.update(name => box))
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.destroy(name)
|
44
|
+
raise "no such stack: #{name}" unless stacks.keys.include?(name.to_sym)
|
45
|
+
vm = vms[name.to_sym]
|
46
|
+
vm.halt rescue nil
|
47
|
+
vm.destroy rescue nil
|
48
|
+
s = stacks
|
49
|
+
s.delete(name.to_sym)
|
50
|
+
build_vagrantfile(s)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.state(name)
|
54
|
+
raise "no such stack: #{name}" unless stacks.keys.include?(name.to_sym)
|
55
|
+
case vms[name.to_sym].state.to_sym
|
56
|
+
when :running then :up
|
57
|
+
else :down
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.up(name)
|
62
|
+
raise "no such stack: #{name}" unless stacks.keys.include?(name.to_sym)
|
63
|
+
return if state(name) == :up
|
64
|
+
vms[name.to_sym].up
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.down(name)
|
68
|
+
raise "no such stack: #{name}" unless stacks.keys.include?(name.to_sym)
|
69
|
+
return if state(name) == :down
|
70
|
+
vms[name.to_sym].suspend
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.run(name, command)
|
74
|
+
raise "no suck stack: #{name}" unless stacks.keys.include?(name.to_sym)
|
75
|
+
vms[name.to_sym].channel.execute(command, :error_check => false) do |type, data|
|
76
|
+
print data
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.vagrantfile
|
81
|
+
File.expand_path("~/.mason/Vagrantfile")
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.vagrantfile_template
|
85
|
+
File.expand_path("../../../data/Vagrantfile.template", __FILE__)
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.share_dir(name)
|
89
|
+
dir = File.expand_path("~/.mason/share/#{name}")
|
90
|
+
FileUtils.mkdir_p dir unless File.exists?(dir)
|
91
|
+
dir
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.build_vagrantfile(stacks={})
|
95
|
+
data = File.read(vagrantfile_template)
|
96
|
+
ip_base = 3
|
97
|
+
data.gsub! "BOXES", (stacks.map do |name, box|
|
98
|
+
ip_base += 1
|
99
|
+
<<-BOX
|
100
|
+
config.vm.define :#{name} do |config|
|
101
|
+
config.vm.box = "#{box}"
|
102
|
+
config.vm.base_mac = "080027706AA#{ip_base}"
|
103
|
+
config.vm.network :hostonly, "33.33.33.#{ip_base}"
|
104
|
+
config.vm.share_folder "share", "/share", "#{share_dir(name)}"
|
105
|
+
end
|
106
|
+
BOX
|
107
|
+
end.join("\n").chomp)
|
108
|
+
File.open(vagrantfile, "w") do |file|
|
109
|
+
file.puts data
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: andys-mason
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- David Dollar
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thor
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: foreman
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: Build things
|
47
|
+
email: ddollar@gmail.com
|
48
|
+
executables:
|
49
|
+
- mason
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- README.md
|
54
|
+
- bin/mason
|
55
|
+
- data/Vagrantfile.template
|
56
|
+
- lib/mason.rb
|
57
|
+
- lib/mason/buildpack.rb
|
58
|
+
- lib/mason/buildpacks.rb
|
59
|
+
- lib/mason/cli.rb
|
60
|
+
- lib/mason/stacks.rb
|
61
|
+
- lib/mason/version.rb
|
62
|
+
- lib/mason/cp_r.rb
|
63
|
+
homepage: http://github.com/andys/mason
|
64
|
+
licenses: []
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ! '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
requirements: []
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 1.8.24
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: Build things
|
87
|
+
test_files: []
|