Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

215 lines (173 sloc) 6.112 kb
require "digest/sha1"
require "heroku/auth"
require "heroku/command"
require "heroku/command/base"
require "heroku/command/help"
require "heroku/plugin"
require "net/http/post/multipart"
require "rest_client"
require "thor"
require "tmpdir"
require "uri"
require "vulcan"
require "yaml"
class Vulcan::CLI < Thor
desc "build", <<-DESC
build a piece of software for the heroku cloud using COMMAND as a build command
if no COMMAND is specified, a sensible default will be chosen for you
DESC
method_option :command, :aliases => "-c", :desc => "the command to run for compilation"
method_option :name, :aliases => "-n", :desc => "the name of the library (defaults to the directory name)"
method_option :output, :aliases => "-o", :desc => "output build artifacts to this file"
method_option :prefix, :aliases => "-p", :desc => "vulcan will look in this path for the compiled artifacts"
method_option :source, :aliases => "-s", :desc => "directory, tarball, or url containing the source"
method_option :deps, :aliases => "-d", :desc => "urls of vulcan compiled libraries to build with", :type=>:array
method_option :verbose, :aliases => "-v", :desc => "show the full build output", :type => :boolean
def build
app = read_config[:app] || "need a server first, use vulcan create"
source = options[:source] || Dir.pwd
name = options[:name] || File.basename(source, File.extname(source))
output = options[:output] || "/tmp/#{name}.tgz"
prefix = options[:prefix] || "/app/vendor/#{name}"
command = options[:command] || "./configure --prefix #{prefix} && make install"
deps = options[:deps] || []
server = URI.parse(ENV["VULCAN_HOST"] || "http://#{app}.herokuapp.com")
source_is_url = URI.parse(source).scheme
Dir.mktmpdir do |dir|
unless source_is_url
input_tgz = "#{dir}/input.tgz"
if File.directory?(source)
action "Packaging local directory" do
%x{ cd #{source} && tar czvf #{input_tgz} . 2>&1 }
end
else
input_tgz = source
end
input = File.open(input_tgz, "r")
end
make_options = {
"command" => command,
"prefix" => prefix,
"secret" => config[:secret],
"deps" => deps
}
if source_is_url
make_options["code_url"] = source
else
make_options["code"] = UploadIO.new(input, "application/octet-stream", "input.tgz")
end
request = Net::HTTP::Post::Multipart.new "/make", make_options
if source_is_url
print "Initializing build... "
else
print "Uploading source package... "
end
response = Net::HTTP.start(server.host, server.port) do |http|
http.request(request) do |response|
response.read_body do |chunk|
unless chunk == 0.chr + "\n"
print chunk if options[:verbose]
end
end
end
end
error "Unknown error, no build output given" unless response["X-Make-Id"]
puts ">> Downloading build artifacts to: #{output}"
output_url = "#{server}/output/#{response["X-Make-Id"]}"
puts " (available at #{output_url})"
File.open(output, "w") do |output|
begin
output.print RestClient.get(output_url)
rescue Exception => ex
puts ex.inspect
end
end
end
rescue Interrupt
error "Aborted by user"
rescue Errno::EPIPE
error "Could not connect to build server: #{server}"
end
desc "create APP_NAME", <<-DESC
create a build server on Heroku
DESC
def create(name)
secret = Digest::SHA1.hexdigest("--#{rand(10000)}--#{Time.now}--")
Dir.mktmpdir do |dir|
Dir.chdir(dir) do
system "env BUNDLE_GEMFILE= heroku create #{name} -s cedar"
end
end
write_config :app => name, :host => "#{name}.herokuapp.com", :secret => secret
update
end
desc "update", <<-DESC
update the build server
DESC
def update
error "no app yet, create first" unless config[:app]
# clean up old plugin, can use auth:token now
FileUtils.rm_rf(File.expand_path("~/.heroku/plugins/heroku-credentials"))
Dir.mktmpdir do |dir|
Dir.chdir(dir) do
Heroku::Plugin.load!
api_key = Heroku::Auth.api_key
error "invalid api key detected, try running `heroku auth:token`" if api_key =~ / /
system "git init"
system "git remote add heroku git@#{heroku_git_domain}:#{config[:app]}.git"
FileUtils.cp_r "#{server_path}/.", "."
File.open(".gitignore", "w") do |file|
file.puts ".env"
end
system "git add . >/dev/null"
system "git commit -m commit >/dev/null"
system "git push heroku -f master"
heroku "config:add SECRET=#{config[:secret]} SPAWN_ENV=heroku HEROKU_APP=#{config[:app]} HEROKU_API_KEY=#{api_key} NODE_PATH=lib NODE_ENV=production"
heroku "addons:add cloudant:oxygen"
end
end
end
private
def action(message)
print "#{message}... "
yield
puts "done"
end
def heroku(command)
%x{ env BUNDLE_GEMFILE= heroku #{command} 2>&1 }
end
def config_file
File.expand_path("~/.vulcan")
end
def config
read_config
end
def read_config
return {} unless File.exists?(config_file)
config = YAML.load_file(config_file)
config.is_a?(Hash) ? config : {}
end
def write_config(config)
full_config = read_config.merge(config)
File.open(config_file, "w") do |file|
file.puts YAML.dump(full_config)
end
end
def error(message)
puts "!! #{message}"
exit 1
end
def server_path
File.expand_path("../../../server", __FILE__)
end
#
# heroku_git_domain checks to see if the heroku-accounts plugin is present,
# and if so, it will set the domain to the one that matches the credentials
# for the currently set account
#
def heroku_git_domain
suffix = %x{ git config heroku.account }
suffix = "com" if suffix.nil? or suffix.strip == ""
"heroku.#{suffix.strip}"
end
end
Jump to Line
Something went wrong with that request. Please try again.