Skip to content

Instantly share code, notes, and snippets.

@tbranyen
Last active January 27, 2017 11:50
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tbranyen/2481cfdb27424d60e528 to your computer and use it in GitHub Desktop.
Save tbranyen/2481cfdb27424d60e528 to your computer and use it in GitHub Desktop.
Automatic GitHub deployments
//
// README:
// - Listens for PUSH events
// - Fetches the ref pushed via the given remote
// - Sets the repositories HEAD to latest ref
// - Checks out the new HEAD (--force)
// - Install dependencies from package.json
// - Calls `npm run reload` (My app uses this)
// - Calls `nginx -s reload` (My app also uses this)
//
// Usage:
// export SECRET=YOUR_GITHUB_WEBHOOK_SECRET
// export CWD=/path/to/your/project
// export REMOTE=origin
// export SOCKET_PRIV="your_user:your_group"
// export GIT_AUTHOR="Your Name"
// export GIT_EMAIL="Your Email"
//
// As root:
// node auto-deploy
//
['SECRET', 'CWD', 'REMOTE', 'SOCKET_PRIV', 'GIT_AUTHOR', 'GIT_EMAIL']
.forEach(function(ENV) {
if (!(ENV in process.env)) {
console.error('Missing environment variable: ' + ENV);
process.exit();
}
});
var express = require('express');
var Git = require('nodegit');
var exec = require('child_process').execSync;
var createHandler = require('github-webhook-handler');
var GHWebHook = createHandler({ path: '/', secret: process.env.SECRET });
var me = Git.Signature.now(process.env.GIT_AUTHOR, process.env.GIT_EMAIL);
GHWebHook.on('push', function (event) {
var payload = event.payload;
if (payload.ref !== 'refs/heads/master') {
return;
}
function fetchRemote(repo) {
console.log('Fetching remote', process.env.REMOTE);
return repo.getRemote(process.env.REMOTE).then(function(remote) {
remote.setCallbacks({
credentials: function(url, userName) {
return Git.Cred.sshKeyFromAgent(userName);
}
});
return remote.fetch([payload.ref], me, 'Fetched latest deployment');
}).then(function() {
return repo;
});
}
function resetAndCheckout(repo) {
return repo.getReference(process.env.REMOTE).then(function(ref) {
console.log('Changing HEAD to', ref.target());
return repo.setHead(ref.name(), me, 'Set head to ' + ref.target());
}).then(function() {
console.log('Checking out HEAD');
return Git.Checkout.head(repo, {
checkoutStrategy: Git.Checkout.STRATEGY.FORCE
});
});
}
function reloadAndComplete() {
console.log('Ensure Node modules are up-to-date');
exec('npm install', { cwd: process.env.CWD });
console.log('Reloading application');
exec('npm run reload', { cwd: process.env.CWD });
console.log('Resetting NGINX');
exec('nginx -s reload');
console.log('Completed deployment');
}
// Async-flow.
Promise.resolve(process.env.CWD)
.then(function(path) {
console.log('Opened repository');
return Git.Repository.open(path);
})
.then(fetchRemote)
.then(resetAndCheckout)
.then(reloadAndComplete)
.catch(function(ex) {
console.log(ex.stack);
});
});
// Attempt to clean up previous socket and re-bind.
try { exec('rm ./socket'); } catch (ex) {}
express().use(GHWebHook).listen('./socket');
exec('chown ' + process.env.SOCKET_PRIV + ' ./socket');
@frederikbrinck
Copy link

@tbranyen Thanks for sharing this. A late question: I was wondering if there is any reason behind using execSync and not making asynchronous calls instead on line 76-87?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment