-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.env |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
web: bin/web |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# anvil | ||
|
||
Alternate Heroku push/deploy workflow. | ||
|
||
## Installation | ||
|
||
$ heroku plugins:install https://github.com/ddollar/heroku-anvil | ||
|
||
## Usage | ||
|
||
#### Push your app | ||
|
||
$ heroku push | ||
Generating application manifest... done | ||
Computing diff for upload... done | ||
Uploading new files... done | ||
Compiling... | ||
Launching build slave | ||
Buildpack: Buildkit+Node.js | ||
-----> Compiling for Node.js | ||
-----> Resolving engine versions | ||
Using Node.js version: 0.6.12 | ||
Using npm version: 1.1.4 | ||
-----> Fetching Node.js binaries | ||
... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
#!/bin/sh | ||
|
||
id=$1 | ||
buildpack=$2 | ||
|
||
# create a temporary compile directory | ||
compile_dir=$(mktemp -t compile_XXXXX) | ||
rm -rf $compile_dir | ||
mkdir -p $compile_dir | ||
|
||
# create a cache dir | ||
cache_dir=$(mktemp -t cache_XXXXX) | ||
rm -rf $cache_dir | ||
mkdir -p $cache_dir | ||
|
||
# fetching app | ||
cd $compile_dir | ||
curl -s $ANVIL_HOST/manifest/$id.tgz -o app.tgz | ||
tar xzf app.tgz | ||
rm app.tgz | ||
|
||
# fetch buildpack | ||
rm -rf .buildpack | ||
mkdir -p .buildpack | ||
cd .buildpack | ||
curl -s $buildpack -o- | tar xzf - | ||
cd .. | ||
|
||
# get buildpack name | ||
buildpack_name=$(.buildpack/bin/detect "$compile_dir") | ||
|
||
# abort if detect failes | ||
if [ $? -eq 0 ]; then | ||
echo "Buildpack: ${buildpack_name}" | ||
else | ||
echo "compile failed" | ||
exit 1 | ||
fi | ||
|
||
# compile | ||
cd $compile_dir | ||
.buildpack/bin/compile $compile_dir $cache_dir |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#!/bin/bash | ||
|
||
ROOT=$(dirname $(dirname $0)) | ||
PATH="node_modules/.bin:$PATH" | ||
|
||
if [ "${NODE_ENV}" == "production" ]; then | ||
exec coffee web.coffee | ||
else | ||
exec nodemon -w . web.coffee | ||
fi |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
async = require("async") | ||
fs = require("fs") | ||
knox = require("knox") | ||
mkdirp = require("mkdirp") | ||
path = require("path") | ||
spawn = require("child_process").spawn | ||
spawner = require("spawner").init() | ||
temp = require("temp") | ||
uuid = require("node-uuid") | ||
|
||
class Manifest | ||
|
||
constructor: (@manifest) -> | ||
@knox = knox.createClient | ||
key: process.env.AWS_ACCESS, | ||
secret: process.env.AWS_SECRET, | ||
bucket: process.env.AWS_BUCKET | ||
|
||
build: (cb) -> | ||
id = uuid.v1() | ||
buffer = new Buffer(JSON.stringify(@manifest), "binary") | ||
|
||
buildpack = "https://buildkit.herokuapp.com/buildkit/example.tgz" | ||
|
||
put = @knox.put "/manifest/#{id}", { "Content-Length":buffer.length, "Content-Type":"text/plain" } | ||
put.on "response", (res) -> cb(spawner.spawn("bin/compile \"#{id}\" \"#{buildpack}\"")) | ||
put.end(buffer) | ||
|
||
create_hash: (hash, stream, cb) -> | ||
@knox.putStream stream, "/hash/#{hash}", (err, knox_res) -> | ||
cb null | ||
|
||
generate_tarball: (cb) -> | ||
temp.mkdir "compile", (err, path) => | ||
async.parallel @datastore_fetchers(path), (err, results) -> | ||
cb spawn("tar", ["czf", "-", "."], cwd:path) | ||
|
||
missing_hashes: (cb) -> | ||
async.parallel @datastore_testers(), (err, results) -> | ||
missing = [] | ||
for hash, exists of results | ||
missing.push(hash) unless exists | ||
cb missing | ||
|
||
test_datastore_presence: (hash, cb) -> | ||
@knox.headFile "/hash/#{hash}", (err, res) -> | ||
cb(res.statusCode != 404) | ||
|
||
datastore_fetchers: (dir) -> | ||
fetchers = {} | ||
for name, file_manifest of @manifest | ||
do (name, file_manifest) => | ||
fetchers[file_manifest.hash] = (async_cb) => | ||
filename = "#{dir}/#{name}" | ||
mkdirp path.dirname(filename), => | ||
fs.open filename, "w", (err, fd) => | ||
@knox.getFile "/hash/#{file_manifest["hash"]}", (err, get) => | ||
console.log "writing:#{filename}" | ||
get.setEncoding "binary" | ||
get.on "data", (chunk) -> fs.write fd, chunk | ||
get.on "end", -> | ||
fs.fchmod fd, file_manifest.mode | ||
fs.close fd | ||
async_cb null, true | ||
|
||
datastore_testers: -> | ||
@hashes(@manifest).reduce (ax, hash) => | ||
ax[hash] = (async_cb) => | ||
@test_datastore_presence hash, (exists) -> | ||
async_cb(null, exists) | ||
ax | ||
,{} | ||
|
||
hashes: (manifest) -> | ||
object.hash for name, object of manifest | ||
|
||
module.exports.init = (manifest) -> | ||
new Manifest(manifest) | ||
|
||
module.exports.init_with_id = (id, cb) -> | ||
manifest = "" | ||
|
||
subknox = knox.createClient | ||
key: process.env.AWS_ACCESS, | ||
secret: process.env.AWS_SECRET, | ||
bucket: process.env.AWS_BUCKET | ||
|
||
subknox.getFile "/manifest/#{id}", (err, get) => | ||
get.setEncoding "binary" | ||
get.on "data", (chunk) -> manifest += chunk | ||
get.on "end", (success) -> | ||
cb(new Manifest(JSON.parse(manifest))) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
events = require("events") | ||
|
||
module.exports.inject = (object) -> | ||
object.emitter = events.EventEmitter() | ||
|
||
object.on = (key, handler) -> | ||
object.emitter.on key, handler | ||
|
||
object.emit = -> | ||
object.emitter.emit arguments |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
events = require("events") | ||
https = require("https") | ||
net = require("net") | ||
qs = require("querystring") | ||
restler = require("restler") | ||
spawn = require("child_process").spawn | ||
tls = require("tls") | ||
|
||
class Spawner | ||
|
||
constructor: (env) -> | ||
@env = env or process.env.SPAWN_ENV or "local" | ||
|
||
spawn: (command, options, cb) -> | ||
spawner = this["spawn_#{@env}"] | ||
spawner command, options, cb | ||
|
||
spawn_local: (command, options, cb) -> | ||
args = command.match(/("[^"]*"|[^"]+)(\s+|$)/g) | ||
command = args.shift().replace(/\s+$/g, "") | ||
args = args.map (arg) -> arg.match(/"?([^"]*)"?/)[1] | ||
proc = spawn command, args, env:process.env | ||
emitter = new events.EventEmitter() | ||
|
||
proc.stdout.on "data", (data) -> emitter.emit("data", data) | ||
proc.stderr.on "data", (data) -> emitter.emit("data", data) | ||
proc.on "exit", (code) -> emitter.emit("end", code) | ||
emitter | ||
|
||
spawn_heroku: (command, cb) -> | ||
api_key = process.env.HEROKU_API_KEY | ||
app = process.env.HEROKU_APP | ||
emitter = new events.EventEmitter() | ||
|
||
request = restler.post "https://api.heroku.com/apps/#{app}/ps", | ||
headers: | ||
"Authorization": new Buffer(":" + api_key).toString("base64") | ||
"Accept": "application/json" | ||
"User-Agent": "heroku-gem/2.5" | ||
data: | ||
attach: true | ||
command: command | ||
|
||
request.on "success", (data) -> | ||
url = require("url").parse(data.rendezvous_url) | ||
rendezvous = tls.connect url.port, url.hostname, -> | ||
if rendezvous.authorized | ||
console.log "valid socket" | ||
rendezvous.write url.pathname.substring(1) + "\n" | ||
else | ||
console.log "invalid socket" | ||
rendezvous.on "data", (data) -> console.log "data", data; emitter.emit("data", data) unless data.toString() is "rendezvous\r\n" | ||
rendezvous.on "end", -> emitter.emit "end" | ||
|
||
request.on "error", (error) -> | ||
emitter.emit "error", error | ||
|
||
emitter | ||
|
||
module.exports.init = (env) -> | ||
new Spawner(env) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.