Skip to content
This repository has been archived by the owner on Aug 22, 2019. It is now read-only.

Commit

Permalink
Merge pull request #157 from mozilla/156-continuous-devployment
Browse files Browse the repository at this point in the history
156 continuous devployment
  • Loading branch information
stenington committed May 4, 2012
2 parents faf2f0d + 3c4d7f9 commit e1998fd
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 209 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -2,7 +2,7 @@ language: node_js
node_js:
- 0.6
notifications:
webhooks: http://bjb.io:9251
webhooks: http://bjb.io:7076
irc:
- "irc.mozilla.org#badges"
email:
Expand Down
7 changes: 0 additions & 7 deletions Vagrantfile
@@ -1,18 +1,11 @@
Vagrant::Config.run do |config|
config.vm.box = "lucid32"

config.vm.box_url = "http://files.vagrantup.com/lucid32.box"

config.vm.share_folder "srv", "/home/vagrant/openbadges", "."

config.vm.network :hostonly, '33.33.33.11'

config.vm.forward_port 22, 2229

config.vm.forward_port 80, 8888

config.vm.forward_port 8889, 8889

config.vm.provision :puppet do |puppet|
puppet.manifests_path = ".puppet-manifests"
puppet.manifest_file = "nodes.pp"
Expand Down
159 changes: 159 additions & 0 deletions bin/deployer
@@ -0,0 +1,159 @@
#! /usr/bin/env node

var util = require('util');
var http = require('http');
var program = require('commander');
var colors = require('colors');
var path = require('path');
var spawn = require('child_process').spawn;

function inspect (thing) {
return util.inspect(thing, undefined, undefined, true);
}
function debug() {
var args = [].slice.call(arguments);
args.unshift('debug'.grey);
console.log.apply(console, args);
}
function error() {
var args = [].slice.call(arguments);
args.unshift(' error'.red);
console.log();
console.log.apply(console, args);
console.log();
}
function accept() {
var args = [].slice.call(arguments);
args.unshift('accept'.green);
console.log.apply(console, args);
}
var log = console.log;

program
.version('0.0.1')
.option('-p, --port <n>', 'port the webhook endpoint should listen on')
.option('-a, --auth <value>', 'authorization key to expect in the header')
.option('-b, --branch <name>', 'branch to watch')
.option('-e, --script <file>', 'file to execute on a successful test run')
.parse(process.argv);

if (program.port === undefined) {
program.missingArgument('--port');
}
if (!program.auth) {
program.missingArgument('--auth');
}
if (!program.script) {
program.missingArgument('--script');
}
if (!program.branch) {
program.missingArgument('--branch');
}
if (!path.existsSync(program.script)) {
error('could not find file', program.script.bold);
process.exit(1);
}

debug('listening on port', program.port.toString().blue);
debug('authorization code', program.auth.toString().blue);

var lastRunData;
function requestHandler(request, response) {
var auth;
var length;
var headers = request.headers;

function reject() {
var args = [].slice.call(arguments);
// immediately end the response
response.end('rejected: ' + args.join(' '));
args.unshift('reject'.red);
console.log.apply(console, args);
}

if (request.method.match(/get/i)) {
response.setHeader('content-type', 'text/plain; charset=utf-8');
if (!lastRunData)
return response.end('nothing has been run yet');
return response.end(lastRunData);
}

if (!request.method.match(/post/i))
return reject('got request, but not a POST');

if (!(auth = headers['authorization']))
return reject('missing authorization code');

if (auth !== program.auth)
return reject('invalid authorization code, got', auth.bold);

if (!(length = headers['content-length']))
return reject('expects a `content-length` header');

function parseIncoming(data) {
try {
// data comes in as `payload=...`, so we want to slice that first part off and decode it.
var payload = decodeURIComponent(data.slice(8));
var hookData = JSON.parse(payload);
return hookData;
} catch (e) {
// well, just don't do anything I guess
return;
}
}

function executeScript(payload) {
var message = payload.status_message;
var status = payload.status;
var branch = payload.branch;
var lastChar = '\n';

if (!message || status === undefined || !branch) {
reject('the payload didn\'t have the expected fields');
return debug(inspect(payload))
}

if (branch !== program.branch)
return reject('ignoring, branch doesn\'t match');

if (!message.match(/passed/i) || status !== 0)
return reject('the tests failed, not deploying.');

lastRunData = '';
function echoScript(data) {
var dataString = data.toString();
if (lastChar === '\n') process.stdin.write(program.script.magenta + ' ');
process.stdin.write(data);
lastRunData += dataString;
lastChar = dataString[dataString.length - 1];
}

var script = spawn('./' + program.script);
script.stdout.on('data', echoScript);
script.stderr.on('data', echoScript);
script.on('exit', function (code) {
if (code === 0) return accept('script successfully completed');
reject('something went wrong the the deploy script');
});
return true;
}

var incoming = '';
// buffer incoming data;
// TODO: might be more memory friendly to use a real buffer instead?
request.on('data', function (data) { incoming += data });
request.on('end', function () {
var payload;
if (!(payload = parseIncoming(incoming))) {
reject('the payload was malformed');
return debug(inspect(incoming));
}
debug('successful payload recieved, executing script');
if (executeScript(payload))
response.end('ok');
});
};

var server = http.createServer();
server.on('request', requestHandler);
server.listen(program.port);
50 changes: 0 additions & 50 deletions bin/migrator.js

This file was deleted.

102 changes: 102 additions & 0 deletions bin/start
@@ -0,0 +1,102 @@
#!/usr/bin/env node
var spawn = require('child_process').spawn;
var colors = require('colors');
var http = require('http');
var program = require('commander');
var qs = require('querystring');
var fs = require('fs');
var util = require('util');

program
.version('0.5.0')
.option('-e, --env [env]', 'run under specified environment')
.option('-f, --pidfile [file]', 'save a pidfile')
.parse(process.argv);

var restarts = 0;
var runningServer;

function log () {
var args = [].slice.call(arguments);
args.unshift('runner'.magenta);
console.log.apply(console, args);
};

function inspect (thing) {
return util.inspect(thing, undefined, undefined, true);
}

var spawnServer = function () {
var app, fancypid;
if (++restarts > 5) {
log('too many failures, shut. down. everything.');
process.exit();
}

log('spawning new server');
app = spawn('node', ['app.js']);
fancypid = ('(' + app.pid + ') ').grey;

app.stdout.on('data', function (data) {
process.stdout.write(fancypid);
process.stdout.write(data);
});

app.stderr.on('data', function (data) {
process.stderr.write(fancypid);
process.stderr.write(data);
});

app.on('exit', function (code, sig) {
if (sig) log('server killed with signal ' + sig);
if (code) log('server exited with code ' + code);
runningServer = spawnServer();
});

return app;
};

process.on('SIGHUP', function () {
// crash the server
runningServer.kill();
})
;
process.on('SIGINT', (function (timeout) {
var seriously = 0;
return function () {
if (seriously) process.exit();
seriously = true;
setTimeout(function () {
seriously = false
}, timeout);
}
})(1000));

process.on('exit', function () {
if (pidfile) {
log('destroying pidfile', pidfile.bold);
try {
fs.unlinkSync(pidfile);
} catch (err) {
console.log('couldn\'t destroy pidfile:', inspect(err));
}
}
});

var pidfile = program.pidfile;
if (program.env)
process.env['NODE_ENV'] = program.env;

if (pidfile) {
log('saving pid to', pidfile.bold);
fs.writeFileSync(pidfile, process.pid.toString())
}

log('runner pid is', process.pid.toString().bold);
runningServer = spawnServer();

// reduce restart count every 5 seconds
setInterval(function () {
if (restarts > 0) restarts--;
}, 5000);

6 changes: 6 additions & 0 deletions bin/update.sh
@@ -0,0 +1,6 @@
#!/bin/bash
git pull origin development && \
npm install && \
npm test && \
kill -s SIGHUP `cat var/server.pid`

17 changes: 0 additions & 17 deletions bin/vm

This file was deleted.

0 comments on commit e1998fd

Please sign in to comment.