Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
ddollar committed Sep 21, 2011
0 parents commit 60d4eee
Show file tree
Hide file tree
Showing 142 changed files with 14,709 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -0,0 +1,3 @@
.env
node_modules
pkg/*
3 changes: 3 additions & 0 deletions Gemfile
@@ -0,0 +1,3 @@
source "http://rubygems.org"

gemspec
22 changes: 22 additions & 0 deletions Gemfile.lock
@@ -0,0 +1,22 @@
PATH
remote: .
specs:
vulcan (0.0.1)
multipart-post (~> 1.1.3)
rest-client (~> 1.6.7)
thor (~> 0.14.6)

GEM
remote: http://rubygems.org/
specs:
mime-types (1.16)
multipart-post (1.1.3)
rest-client (1.6.7)
mime-types (>= 1.16)
thor (0.14.6)

PLATFORMS
ruby

DEPENDENCIES
vulcan!
9 changes: 9 additions & 0 deletions bin/vulcan
@@ -0,0 +1,9 @@
#!/usr/bin/env ruby

require "net/http/post/multipart"
require "tmpdir"
require "uri"
require "vulcan/cli"

Vulcan::CLI.start

2 changes: 2 additions & 0 deletions lib/vulcan.rb
@@ -0,0 +1,2 @@
module Vulcan
end
164 changes: 164 additions & 0 deletions lib/vulcan/cli.rb
@@ -0,0 +1,164 @@
require "digest/sha1"
require "net/http/post/multipart"
require "rest_client"
require "thor"
require "tmpdir"
require "uri"
require "vulcan"
require "yaml"

class Vulcan::CLI < Thor

desc "build [COMMAND]", <<-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 ot the directory name)"
method_option :output, :aliases => "-o", :desc => "output build artifacts to this file"
method_option :prefix, :aliases => "-p", :desc => "the build/install --prefix of the software"
method_option :source, :aliases => "-s", :desc => "the source directory to build from"
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"

command = options[:command] || "./configure --prefix #{prefix} && make install"
name = options[:name] || File.basename(Dir.pwd)
output = options[:output] || "/tmp/#{name}.tgz"
prefix = options[:prefix] || "/app/vendor/#{name}"
source = options[:source] || Dir.pwd
server = URI.parse(ENV["MAKE_SERVER"] || "http://#{app}.herokuapp.com")

Dir.mktmpdir do |dir|
puts ">> Packaging local directory"
%x{ cd #{source} && tar czvf #{dir}/input.tgz . 2>&1 }

puts ">> Uploading code for build"
File.open("#{dir}/input.tgz", "r") do |input|
request = Net::HTTP::Post::Multipart.new "/make",
"code" => UploadIO.new(input, "application/octet-stream", "input.tgz"),
"command" => command,
"prefix" => prefix

puts ">> Building with: #{command}"
response = Net::HTTP.start(server.host, server.port) do |http|
http.request(request) do |response|
response.read_body do |chunk|
print chunk if options[:verbose]
end
end
end

error "Unknown error, no build output given" unless response["X-Make-Id"]

puts ">> Downloading build artifacts to: #{output}"

File.open(output, "w") do |output|
begin
output.print RestClient.get("#{server}/output/#{response["X-Make-Id"]}")
rescue Exception => ex
puts ex.inspect
end
end
end
end
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 :ap=> name, :host => "#{name}.herokuapp.com", :secret => secret
update
end


desc "update", <<-DESC
update the build server
DESC

def update
error "no apyet, create first" unless config[:app]

FileUtils.mkdir_p File.expand_path("~/.heroku/plugins/heroku-credentials")

File.open(File.expand_path("~/.heroku/plugins/heroku-credentials/init.rb"), "w") do |file|
file.puts <<-CONTENTS
class Heroku::Command::Credentials < Heroku::Command::Base
def index
puts Heroku::Auth.password
end
end
CONTENTS
end

Dir.mktmpdir do |dir|
Dir.chdir(dir) do
api_key = %x{ env BUNDLE_GEMFILE= heroku credentials }

system "git init"
system "git remote add heroku git@heroku.com:#{config[:app]}.git"
FileUtils.cp_r "#{server_path}/.", "."
File.open(".gitignore", "w") do |file|
file.puts ".env"
file.puts "node_modules"
end
system "ls -la"
system "git add ."
system "git commit -m commit"
system "git push heroku -f master"

%x{ env BUNDLE_GEMFILE= heroku config:add SECRET=#{config[:secret]} SPAWN_ENV=heroku HEROKU_APP=#{config[:app]} HEROKU_API_KEY=#{api_key} 2>&1 }
%x{ env BUNDLE_GEMFILE= heroku addons:add cloudant:oxygen }
end
end
end

private

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

end
5 changes: 5 additions & 0 deletions lib/vulcan/version.rb
@@ -0,0 +1,5 @@
module Vulcan

VERSION = "0.0.1"

end
1 change: 1 addition & 0 deletions server/Procfile
@@ -0,0 +1 @@
web: node web.js
39 changes: 39 additions & 0 deletions server/bin/make
@@ -0,0 +1,39 @@
#!/usr/bin/env ruby

ENV["GEM_HOME"] = File.expand_path("../../vendor/gems", __FILE__)
ENV["GEM_PATH"] = File.expand_path("../../vendor/gems", __FILE__)

require "rubygems"

id = ARGV.shift
command = ARGV.shift
prefix = ARGV.shift

require "couchrest"
require "fileutils"
require "tmpdir"

db = CouchRest.database!(ENV["CLOUDANT_URL"] + ":5984/make")

Dir.mktmpdir do |dir|
Dir.chdir(dir) do
doc = db.get(id)

File.open("input.tgz", "wb") do |file|
file.print doc.fetch_attachment("input")
end

FileUtils.mkdir_p "input"

Dir.chdir("input") do
%x{ tar xzf ../input.tgz 2>&1 }
system command
end

Dir.chdir(prefix) do
%x{ tar czf #{dir}/output.tgz * 2>&1 }
end

doc.put_attachment("output", File.open("#{dir}/output.tgz"))
end
end
13 changes: 13 additions & 0 deletions server/lib/on.js
@@ -0,0 +1,13 @@
var events = require('events');

module.exports.inject = function(object) {
object.emitter = events.EventEmitter();

object.on = function(key, handler) {
object.emitter.on(key, handler);
}

object.emit = function() {
object.emitter.emit(arguments);
}
}
101 changes: 101 additions & 0 deletions server/lib/spawner.js
@@ -0,0 +1,101 @@
var events = require('events');
var https = require('https');
var net = require('net');
var qs = require('querystring');
var restler = require('restler');
var spawn = require('child_process').spawn;
var tls = require('tls');

var Spawner = function(env) {

require('on').inject(this);

this.env = env || process.env.SPAWN_ENV || 'local';

var cs = this;

this.spawn = function(command, callback) {
var spawner = cs['spawn_' + cs.env];

if (!spawner) {
callback('no spawner for env: ' + cs.env);
return(new events.EventEmitter());
} else {
console.log('spawning on %s: %s', cs.env, command);
return(spawner(command, callback));
}
}

this.spawn_local = function(command, callback) {
var args = command.match(/("[^"]*"|[^"]+)(\s+|$)/g);
var command = args.shift().replace(/\s+$/g, '');

args = args.map(function(arg) {
return(arg.match(/"?([^"]*)"?/)[1]);
});

var proc = spawn(command, args, { env: process.env });
var emitter = new events.EventEmitter();

proc.stdout.on('data', function(data) {
emitter.emit('data', data);
});

proc.stderr.on('data', function(data) {
emitter.emit('data', data);
});

proc.on('exit', function(code) {
emitter.emit('exit', code);
});

return(emitter);
}

this.spawn_heroku = function(command, callback) {
var api_key = process.env.HEROKU_API_KEY;
var app = process.env.HEROKU_APP;
var emitter = new events.EventEmitter();

var auth = new Buffer(':' + api_key).toString('base64');

restler.post('https://api.heroku.com/apps/' + app + '/ps', {
headers: {
'Authorization': auth,
'Accept': 'application/json'
},
data: {
attach: true,
command: command,
'ps_env[AMAZON_BUCKET]': ''
},
}).on('success', function(data) {

var url = require('url').parse(data.rendezvous_url);
var rendezvous = new net.Socket();

rendezvous.connect(url.port, url.hostname, function() {
rendezvous.write(url.pathname.substring(1) + '\n');
});

rendezvous.on('data', function(data) {
if (data != 'rendezvous\r\n') {
emitter.emit('data', data);
}
});

rendezvous.on('end', function() {
emitter.emit('exit', 0);
});

}).on('error', function(error) {
emitter.emit('error', error);
});

return(emitter);
}
}

module.exports.create = function(env) {
return new Spawner(env);
}
12 changes: 12 additions & 0 deletions server/package.json
@@ -0,0 +1,12 @@
{
"name": "heroku-make",
"version": "0.0.1",
"dependencies": [
"connect-form",
"cradle",
"express",
"knox",
"node-uuid",
"restler"
]
}

0 comments on commit 60d4eee

Please sign in to comment.