Skip to content
This repository has been archived by the owner on Oct 26, 2018. It is now read-only.

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
ddollar committed May 25, 2012
0 parents commit 899a474
Show file tree
Hide file tree
Showing 502 changed files with 68,165 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
.env
1 change: 1 addition & 0 deletions Procfile
@@ -0,0 +1 @@
web: bin/web
25 changes: 25 additions & 0 deletions README.md
@@ -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
...
42 changes: 42 additions & 0 deletions bin/compile
@@ -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
10 changes: 10 additions & 0 deletions bin/web
@@ -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
92 changes: 92 additions & 0 deletions lib/manifest.coffee
@@ -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)))
10 changes: 10 additions & 0 deletions lib/on.coffee
@@ -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
61 changes: 61 additions & 0 deletions lib/spawner.coffee
@@ -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)
1 change: 1 addition & 0 deletions node_modules/.bin/cake

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions node_modules/.bin/coffee

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions node_modules/.bin/express

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions node_modules/.bin/nodemon

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions node_modules/.bin/stylus

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions node_modules/async/.gitmodules

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions node_modules/async/.npmignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions node_modules/async/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions node_modules/async/Makefile

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 899a474

Please sign in to comment.