Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Naatan committed Apr 22, 2014
0 parents commit a473913
Show file tree
Hide file tree
Showing 7 changed files with 534 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules/
31 changes: 31 additions & 0 deletions daemon
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash
# Deployment daemon
# chkconfig: 345 20 80
# description: Manages the komodo-web-deploy instance, basically just wraps "forever"
# processname: ko-deploy

DAEMON_PATH="/home/deployment/komodo-web-deploy"
DAEMON="node server.js"
NAME="ko-deploy"
USER="nathanr"

case "$1" in
start)
su $USER -c "$DAEMON_PATH/node_modules/forever/bin/forever stop $DAEMON_PATH/server.js"
;;
status)
forever list
;;
stop)
su $USER -c "$DAEMON_PATH/node_modules/forever/bin/forever stop $DAEMON_PATH/server.js"
;;

restart)
$0 stop
$0 start
;;

*)
echo "Usage: $0 {status|start|stop|restart}"
exit 1
esac
265 changes: 265 additions & 0 deletions deployment.log

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions deployments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports =
{
"komodo-website": // repository
{
"master": // branch
{
path: __dirname + "/../komodo-website-master"
}
}
};
12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "komodo-web-deploy",
"description": "Minimal deployment system for Komodo web apps",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "3.x",
"cron": "~1.0.4",
"winston": "~0.7.3",
"forever": "~0.11.0"
}
}
18 changes: 18 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
This is a small and very simple deployment script that allows us to automatically
deploy eg. komodoide.com whenever we push to our deployment branches.

It is designed specifically with static sites in mind, and will only work if
both deployer and deployment configurations are on the same server, in the same
parent directory.

For a site to be valid for deployment it must have a deploy.js library in its
root which exposes the following methods:

* init(logger)
* run(params, done)
* schedule(branch, cron, deploy) - optional, if you want to schedule deployments
via cron

To learn more about how this works please look at [deploy.js] on the komodo-website repository

[deploy.js]: https://github.com/Komodo/komodo-website/blob/master/deploy.js
197 changes: 197 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
var server = new function()
{
var express = require('express');
var sys = require('sys');
var fs = require('fs');
var exec = require('child_process').exec;
var CronJob = require('cron').CronJob;
var winston = require('winston');

var deploying = {};
var queued = {};
var app;

// Run the logger, which dumps everything to stdout and deployment.log
var logger = new winston.Logger({
transports: [
new (winston.transports.Console)({ level: 'debug', colorize: true }),
new (winston.transports.File)({ filename: 'deployment.log', level: 'verbose', json: false })
]
});

var init = function ()
{
// Initialize App and define how we parse the request body
app = express();

app.configure(function () {
app.use(function (err, req, res, next)
{
logger.error(err.stack);
next(err);
});

app.use(express.json());
app.use(express.urlencoded());
app.use(express.multipart());
});

// Bind our routes
bindRoutes();

// Bind schedulers
bindSchedulers();

// Launch the server
var server = app.listen(8282, function()
{
logger.info('Listening on port %d', server.address().port);
});

// Handle server related errors
server.on("error", function (err)
{
logger.error('there was an error:', err.message);
});
};

var bindRoutes = function()
{
app.post('/hooks/push', routeHookPush);
};

/**
* Bind schedulers from sibling directories
*/
var bindSchedulers = function()
{
// Scan the parent dir
var files = fs.readdirSync(__dirname + "/..");
for (k in files)
{
var file = files[k];
if (file[0] == '.') continue;

// Validate whether the current file/folder contains deploy.js
var path = __dirname + "/../" + file
if ( ! fs.existsSync(path + "/deploy.js")) continue;

// Validate whether this deployerRunner has a schedule method
var deployerRunner = require(path + "/deploy.js");
if ( ! ("schedule" in deployerRunner)) continue;

deployerRunner.init(logger);

// Retrieve the branch name of the current repo
(function(path, deployerRunner)
{
exec('cd "'+path+'" && git rev-parse --abbrev-ref HEAD', function(err, stdo, stde)
{
if (err) return;

var branch = stdo.replace(/\s/g,'');

// Run the scheduler
logger.debug("Running scheduler for " + path);
deployerRunner.schedule(branch, CronJob, deploy);
});
})(path, deployerRunner);
}
};

/**
* Github event hook for push events
*/
var routeHookPush = function(req, res)
{
logger.info("Received push event");

// Parse relevant info
var payload = req.body;
var repo = payload.repository.name;
var branch = payload.ref.split("/").slice(-1).join("-");
var deployerName = [repo,branch].join("-");

// Prepare object to be passed to deployerRunner
var deployer = {
name: deployerName,
path: __dirname + "/../" + deployerName,
repository: payload.repository.name,
branch: payload.ref.split("/").slice(-1)[0]
};

logger.debug("Deployer data", deployer);

// Validate whether we have a deployment for the current repo and branch
fs.exists(deployer.path + "/deploy.js", function(exists)
{
logger.debug(deployer.path + " exists: " + exists);
if ( ! exists) return;
deploy(deployer);
})

res.send('');
};

/**
* Perform a deployment
*/
var deploy = function(deployer)
{
// Add to queue if a deployment is already in progress for this repo+branch
if (deployer.name in deploying)
{
logger.info("Queueing " + deployer.name);
queued[deployer.name] = true;
return;
}

logger.info("Deploying " + deployer.name);
deploying[deployer.name] = true;

// Perform a git pull on the targeted deployment so we can execute
// the latest version of deploy.js
exec('cd "'+deployer.path+'" && git pull', function(err, stdo, stde)
{
logger.debug(stdo);

if (err !== null)
{
logger.error("Git pull error ("+deployer.name+"): " + err);
return;
}

// Invoke the actual deployment script
var deployerRunner = require(deployer.path + "/deploy.js");
deployerRunner.init(logger);
deployerRunner.run(deployer, function(err)
{
if (err)
{
logger.error("Error while deploying "+deployer.name+": " + err);
}

// All done, unblock this deployer
delete deploying[deployer.name];
logger.info("Done deploying " + deployer.name);

// Run the job again if another deploy has
// been scheduled for this deployer
if (deployer.name in queued)
{
delete queued[deployer.name];
deploy(deployer)
}
});
});
};

// Catch uncaught exceptions and restart the server
process.on('uncaughtException', function (err) {
logger.error('uncaughtException: ' + err.message, err.stack);
process.exit(1); // Forever should restart our process (if we're running it through ./daemon)
});

init();

}

0 comments on commit a473913

Please sign in to comment.