GitHub Webhooks
Integrating GitHub Webhooks
We'll use a NodeJS package to help us securely listen for GitHub webhooks.
# as user deployer
cd ~/build-server
npm install --save github-webhook-handler
Once that's installed, we can build on our web listener!
Let's also make a "Secret", which GitHub and this NodeJS package use to ensure webhooks received are legimiately from GitHub.
On my local Mac, I ran the following, curtesy of GitHub's docs:
ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'
This gave me a long, random string, which I used as the application secret.
Within GitHub, in our repository, we can fill out what we need so GitHub sends webhooks to our listening NodeJS application. These are per-project settings under Settings > Webhooks and Services > Webhook.
- Payload URL: http://104.236.246.253:8080/webhook
- Content Type: application/json
- Secret: Long string we just generated
We can then add this to our NodeJS application as well. Note that we use /webhook as the URI end point and we add in the secret we created:
var express = require('express');
var app = express();
var exec = require('child_process').exec;
var createHandler = require('github-webhook-handler')
var gitHubHandler = createHandler({ path: '/webhook', secret: 'myhashsecret' });
/**
* Use middleware provided by github-webhook-handler
* @link https://github.com/rvagg/github-webhook-handler
* @link http://expressjs.com/guide/using-middleware.html
*/
app.post('/webhook', gitHubHandler, function (req, res)
{
// If we made it this far
res.statusCode = 404;
res.end('no such location');
});
/**
* A custom webhook for ourselves to call externally
* Perhaps from Slack or another chatroom
* Or from a CI server
*/
app.post('/deploy', function (req, res) {
var deployment = deploy_app();
deployment.on('exit', function(code, signal)
{
if( code > 0 )
{
console.log('Deployment ended in error', deployment.stderr)
} else {
console.log('Deployment succeeded')
}
});
res.send('Deploying');
});
/**
* Make ExpressJS application listen
*/
var server = app.listen(8080, '0.0.0.0', function () {
var host = server.address().address;
var port = server.address().port;
console.log('Deployer listening at http://%s:%s', host, port);
});
/**
* Handle Github Webhook events
*/
gitHubHandler.on('error', function (err) {
console.error('Github Webhook Error:', err.message)
});
gitHubHandler.on('push', function (event) {
console.log(
'Received a push event for %s to %s',
event.payload.repository.name,
event.payload.ref
)
// Test what our reference actually is!
if( event.payload.ref == 'refs/heads/master' )
{
deploy_app();
}
});
/**
* Do the actual application deploment
* @return null
*/
function deploy_app()
{
var options = {
cwd: '/home/deployer/build-server',
shell: '/bin/bash',
maxBuffer: 1048576 // One MB
// uid: //deployer
// gid: // deployer
};
var childProcess = exec(
'source .venv/bin/activate && python deploy.py',
options,
function(error, stdout, stderr)
{
// Possibly log stdout/stderr
// Do error checking
console.log(error, stdout, stderr)
});
return childProcess;
}
We can turn this on via node index.js. If we push to our GitHub repository, that should kick off a deployment!
Security
In order for GitHub to be able to reach our server, listening at port 880, we need to adjust Iptables to open that port to incoming traffic. That can be done with the following command:
# As user admin, who can use "sudo"
sudo iptables -I INPUT 5 -p tcp --dport 8080 -j ACCEPT
sudo service iptables save