Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

stacks, and building on stacks

  • Loading branch information...
commit 3bc3ce03a86425450699b0b244d12f6496ce8317 1 parent 2256f1d
@ddollar authored
View
2  Gemfile
@@ -1,3 +1,5 @@
source :rubygems
+gemspec
+
gem "thor"
View
7 Gemfile.lock
@@ -1,3 +1,9 @@
+PATH
+ remote: .
+ specs:
+ mason (0.0.1)
+ thor
+
GEM
remote: http://rubygems.org/
specs:
@@ -7,4 +13,5 @@ PLATFORMS
ruby
DEPENDENCIES
+ mason!
thor
View
5 data/Vagrantfile.template
@@ -0,0 +1,5 @@
+Vagrant::Config.run do |config|
+ config.vagrant.dotfile_name = File.expand_path("~/.mason/vagrant")
+
+BOXES
+end
View
16 lib/mason/buildpack.rb
@@ -30,15 +30,13 @@ def compile(app)
FileUtils.rm_rf compile_dir
FileUtils.cp_r app, compile_dir
Dir.chdir(compile_dir) do
- Bundler.with_clean_env do
- IO.popen(%{ #{script("compile")} "#{compile_dir}" "#{cache_dir}" }) do |io|
- until io.eof?
- data = io.gets
- data.gsub!(/^-----> /, " + ")
- data.gsub!(/^ /, " ")
- data.gsub!(/^\s+$/, "")
- print data
- end
+ IO.popen(%{ #{script("compile")} "#{compile_dir}" "#{cache_dir}" }) do |io|
+ until io.eof?
+ data = io.gets
+ data.gsub!(/^-----> /, " + ")
+ data.gsub!(/^ /, " ")
+ data.gsub!(/^\s+$/, "")
+ print data
end
end
end
View
180 lib/mason/cli.rb
@@ -1,5 +1,6 @@
require "mason"
require "mason/buildpacks"
+require "mason/stacks"
require "mason/version"
require "thor"
require "thor/shell/basic"
@@ -20,6 +21,8 @@ def version
method_option :buildpack, :type => :string, :aliases => "-b", :desc => "use a custom buildpack"
method_option :output, :type => :string, :aliases => "-o", :desc => "output location"
+ method_option :quiet, :type => :boolean, :aliases => "-q", :desc => "quiet packaging output"
+ method_option :stack, :type => :string, :aliases => "-s", :desc => "use a stack for building"
method_option :type, :type => :string, :aliases => "-t", :desc => "output type (dir, img, tgz)"
def build(app)
@@ -34,36 +37,78 @@ def build(app)
raise "no such output format: #{type}" unless %w( dir img tgz ).include?(type)
- print "* detecting buildpack... "
+ if stack = options[:stack]
+ print "* booting stack #{stack} (this may take a while)... "
+ Mason::Stacks.up(stack)
+ puts "done"
- buildpack, ret = Mason::Buildpacks.detect(app)
- raise "no valid buildpack detected" unless buildpack
+ buildpacks_dir = File.expand_path("~/.mason/share/#{stack}/buildpacks")
+ compile_dir = File.expand_path("~/.mason/share/#{stack}/app")
+ mason_dir = File.expand_path("~/.mason/share/#{stack}/mason")
- puts "done"
- puts " = name: #{buildpack.name}"
- puts " = url: #{buildpack.url}"
- puts " = display: #{ret}"
+ FileUtils.rm_rf buildpacks_dir
+ FileUtils.rm_rf compile_dir
+ FileUtils.rm_rf mason_dir
- puts "* compiling..."
- compile_dir = buildpack.compile(app)
+ FileUtils.cp_r File.expand_path("~/.mason/buildpacks"), buildpacks_dir
+ FileUtils.cp_r app, compile_dir
+ FileUtils.cp_r File.expand_path("../../../", __FILE__), mason_dir
- print "* packaging... "
- case type.to_sym
- when :tgz then
- Dir.chdir(compile_dir) do
- system %{ tar czf "#{output}" . }
- end
- when :img then
- puts "not yet"
- when :dir then
- FileUtils.rm_rf output
- FileUtils.cp_r compile_dir, output
+ mason_args = %{ /share/app -q -o /share/output -t #{type} }
+ mason_args += %{ -b "#{options[:buildpack]}" } if options[:buildpack]
+
+ Mason::Stacks.run(stack, <<-COMMAND)
+ gem spec thor 2>&1 >/dev/null || sudo gem install thor
+ /usr/bin/env ruby -rubygems /share/mason/bin/mason build #{mason_args}
+ COMMAND
+
+ FileUtils.cp_r File.expand_path("~/.mason/share/#{stack}/output"), output
+
+ puts "* packaging"
+ puts " = type: #{type}"
+ puts " = location: #{output}"
else
- raise "no such output type: #{type}"
+ print "* detecting buildpack... "
+
+ buildpack, ret = Mason::Buildpacks.detect(app)
+ raise "no valid buildpack detected" unless buildpack
+
+ puts "done"
+ puts " = name: #{buildpack.name}"
+ puts " = url: #{buildpack.url}"
+ puts " = display: #{ret}"
+
+ puts "* compiling..."
+ compile_dir = buildpack.compile(app)
+
+ print "* packaging... " unless options[:quiet]
+ case type.to_sym
+ when :tgz then
+ Dir.chdir(compile_dir) do
+ system %{ tar czf "#{output}" . }
+ end
+ when :img then
+ puts "not yet"
+ when :dir then
+ FileUtils.rm_rf output
+ FileUtils.cp_r compile_dir, output
+ else
+ raise "no such output type: #{type}"
+ end
+
+ unless options[:quiet]
+ puts "done"
+ puts " = type: #{type}"
+ puts " = location: #{output}"
+ end
end
- puts "done"
- puts " = type: #{type}"
- puts " = location: #{output}"
+
+ end
+
+ desc "vagrant COMMAND", "run a vagrant command in the mason environment"
+
+ def vagrant(*args)
+ Mason::Stacks.vagrant(args)
end
desc "buildpacks", "list installed buildpacks"
@@ -97,6 +142,69 @@ def uninstall(name)
end
+ desc "stacks", "list available stacks"
+
+ def stacks
+ puts "* available stacks"
+ Mason::Stacks.vms.each do |name, vm|
+ next if name == :default
+ puts " - #{name} [#{Mason::Stacks.state(name)}]"
+ end
+ end
+
+ class Stacks < Thor
+
+ # Hackery. Take the run method away from Thor so that we can redefine it.
+ class << self
+ def is_thor_reserved_word?(word, type)
+ return false if word == 'run'
+ super
+ end
+ end
+
+ desc "stacks:create name", "create a new stack"
+
+ method_option :box, :type => :string, :aliases => "-b", :desc => "vagrant box name"
+
+ def create(name)
+ box = options[:box] || name
+ print "* creating stack #{name}... "
+ Mason::Stacks.create(name, box)
+ puts "done"
+ end
+
+ desc "stacks:destroy STACK", "destroy a stack"
+
+ def destroy(name)
+ print "* destroying stack #{name}... "
+ Mason::Stacks.destroy(name)
+ puts "done"
+ end
+
+ desc "stacks:up STACK", "boot a stack"
+
+ def up(name)
+ print "* booting stack #{name} (this will take a while)..."
+ Mason::Stacks.up(name)
+ puts "done"
+ end
+
+ desc "stacks:down STACK", "boot a stack"
+
+ def down(name)
+ print "* stopping stack #{name}..."
+ Mason::Stacks.down(name)
+ puts "done"
+ end
+
+ desc "stacks:run STACK COMMAND", "run a command on a stack"
+
+ def run(name, *args)
+ Mason::Stacks.run(name, args.join(" "))
+ end
+
+ end
+
# hack thor
def self.run
args = ARGV.dup
@@ -108,6 +216,7 @@ def self.run
klass = case ns
when "buildpacks" then Buildpacks
+ when "stacks" then Stacks
else self
end
@@ -121,4 +230,27 @@ def self.run
# raise Mason::CommandFailed, ex.message
end
+private
+
+ def vagrantfile
+ FileUtils.mkdir_p File.expand_path("~/.mason")
+ file = File.expand_path("~/.mason/Vagrantfile")
+ build_vagrantfile unless File.exists?(file)
+ file
+ end
+
+ def build_vagrantfile(boxes={})
+ data = File.read(File.expand_path("../../../data/Vagrantfile.template", __FILE__))
+ data.gsub! "BOXES", (boxes.map do |name, box|
+ <<-BOX
+ config.vm.define :#{name} do |config|
+ config.vm.box = "#{box}"
+ end
+ BOX
+ end.join("\n"))
+ File.open(File.expand_path("~/.mason/Vagrantfile"), "w") do |file|
+ file.puts data
+ end
+ end
+
end
View
110 lib/mason/stacks.rb
@@ -0,0 +1,110 @@
+require "fileutils"
+require "mason"
+
+class Mason::Stacks
+
+ def self.load_vagrant!
+ require "vagrant"
+ build_vagrantfile unless File.exists?(vagrantfile)
+ end
+
+ def self.vagrant_env(display=false)
+ ui = display ? Vagrant::UI::Basic : nil
+ Vagrant::Environment.new(:vagrantfile_name => vagrantfile, :ui_class => ui)
+ end
+
+ def self.vms
+ load_vagrant!
+ vagrant_env.vms
+ end
+
+ def self.vagrant(args)
+ vagrant_env(true).cli(args)
+ end
+
+ def self.stacks
+ @stacks ||= begin
+ vms.inject({}) do |hash, (name, vm)|
+ next(hash) if name == :default
+ hash.update(name => vm.box ? vm.box.name : "")
+ end
+ end
+ end
+
+ def self.create(name, box)
+ raise "stack already exists: #{name}" if stacks.keys.include?(name.to_sym)
+ raise "vagrant box does not exist: #{box}" unless vagrant_env.boxes.map(&:name).include?(box)
+ build_vagrantfile(stacks.update(name => box))
+ end
+
+ def self.destroy(name)
+ raise "no such stack: #{name}" unless stacks.keys.include?(name.to_sym)
+ vm = vms[name.to_sym]
+ vm.halt rescue Vagrant::Errors::VBoxManagerError
+ vm.destroy rescue Vagrant::Errors::VBoxManageError
+ s = stacks
+ s.delete(name.to_sym)
+ build_vagrantfile(s)
+ end
+
+ def self.state(name)
+ raise "no such stack: #{name}" unless stacks.keys.include?(name.to_sym)
+ case vms[name.to_sym].state.to_sym
+ when :running then :up
+ else :down
+ end
+ end
+
+ def self.up(name)
+ raise "no such stack: #{name}" unless stacks.keys.include?(name.to_sym)
+ return if state(name) == :up
+ vms[name.to_sym].up
+ end
+
+ def self.down(name)
+ raise "no such stack: #{name}" unless stacks.keys.include?(name.to_sym)
+ return if state(name) == :down
+ vms[name.to_sym].suspend
+ end
+
+ def self.run(name, command)
+ raise "no suck stack: #{name}" unless stacks.keys.include?(name.to_sym)
+ vms[name.to_sym].channel.execute(command, :error_check => false) do |type, data|
+ print data
+ end
+ end
+
+ def self.vagrantfile
+ File.expand_path("~/.mason/Vagrantfile")
+ end
+
+ def self.vagrantfile_template
+ File.expand_path("../../../data/Vagrantfile.template", __FILE__)
+ end
+
+ def self.share_dir(name)
+ dir = File.expand_path("~/.mason/share/#{name}")
+ FileUtils.mkdir_p dir unless File.exists?(dir)
+ dir
+ end
+
+ def self.build_vagrantfile(stacks={})
+ data = File.read(vagrantfile_template)
+ ip_base = 3
+ data.gsub! "BOXES", (stacks.map do |name, box|
+ ip_base += 1
+ <<-BOX
+ config.vm.define :#{name} do |config|
+ config.vm.box = "#{box}"
+ config.vm.base_mac = "080027706AA#{ip_base}"
+ config.vm.network :hostonly, "33.33.33.#{ip_base}"
+ config.vm.share_folder "share", "/share", "#{share_dir(name)}"
+ end
+ BOX
+ end.join("\n").chomp)
+ File.open(vagrantfile, "w") do |file|
+ file.puts data
+ end
+ end
+
+end
View
21 mason.gemspec
@@ -0,0 +1,21 @@
+$:.unshift File.expand_path("../lib", __FILE__)
+require "mason/version"
+
+Gem::Specification.new do |gem|
+ gem.name = "mason"
+ gem.version = Mason::VERSION
+
+ gem.author = "David Dollar"
+ gem.email = "ddollar@gmail.com"
+ gem.homepage = "http://github.com/ddollar/mason"
+ gem.summary = "Build things"
+
+ gem.description = gem.summary
+
+ gem.executables = "mason"
+ gem.files = Dir["**/*"].select { |d| d =~ %r{^(README|bin/|data/|ext/|lib/|spec/|test/)} }
+ #gem.files << "man/mason.1"
+
+ gem.add_dependency 'thor'
+
+end
Please sign in to comment.
Something went wrong with that request. Please try again.