diff options
author | Dave Cole <dave@developmentseed.org> | 2013-04-09 18:26:13 -0400 |
---|---|---|
committer | Dave Cole <dave@developmentseed.org> | 2013-04-09 18:26:13 -0400 |
commit | 809a9b5204804ef36bae56f04eb640ab342fd381 (patch) | |
tree | a9a20765f2c84a1027a01a6713395bfb54ed6a8e | |
parent | e28985afe274e46086f73d20ac6cebc7710204d4 (diff) |
Reorganize app to use external scripts. Make main file executable. Move sample code to readme. Refs #40.
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | app.js | 114 | ||||
-rw-r--r-- | build.sh | 26 | ||||
-rw-r--r-- | config/nginx | 17 | ||||
-rwxr-xr-x | jekyll-hook.js | 94 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | readme.md | 77 | ||||
-rwxr-xr-x | scripts/build.sh | 25 | ||||
-rwxr-xr-x | scripts/publish.sh | 16 |
9 files changed, 205 insertions, 168 deletions
@@ -1,2 +1,3 @@ node_modules -config.json
\ No newline at end of file +config.json +test.json @@ -1,114 +0,0 @@ -var fs = require('fs'); -var express = require('express'); -var app = express(); -var queue = require('queue-async'); -var exec = require('child_process').exec; -var email = require('emailjs/email'); -var config = require('./config.json'); -var mailer = email.server.connect(config.email); - -app.use(express.bodyParser()); - -// Receive webhook post -app.post('/hooks/jekyll', function(req, res){ - var data = JSON.parse(req.body.payload); - var q = queue(1); - var cmd = ''; - - // Close connection - res.send(202); - - // Process webhook data - data.repo = data.repository.name; - data.branch = data.ref.split('/')[2]; - data.owner = data.repository.owner.name; - data.url = 'git@' + config.gh_server + ':' + data.owner + '/' + data.repo + '.git'; - data.base = config.temp_directory + '/' + data.owner + '/' + data.repo + '/' + data.branch; - data.source = data.base + '/' + 'code'; - data.dest = data.base + '/' + 'site'; - data.baseurl = '/' + data.repo; - data.site = config.site_directory + '/' + data.repo; - - // End early if not master branch - if (data.branch !== config.branch) { - console.log('Not ' + config.branch + ' branch.'); - return; - } - - // If repo doesn't exist locally, clone it - if (!fs.existsSync(data.source)) { - // Git clone repo from GitHub - cmd = 'git clone ' + data.url + ' ' + data.source; - q.defer(run, data, cmd); - } - - // Git checkout appropriate branch, pull latest code - cmd = 'cd ' + data.source + - ' && git checkout ' + data.branch + - ' && git pull origin ' + data.branch + - ' && cd -'; - q.defer(run, data, cmd); - - // Run jekyll - cmd = 'cd ' + data.source + - ' && jekyll ' + data.source + ' ' + data.dest + - ' --no-server --no-auto --base-url="' + data.baseurl + '"' + - ' && cd -'; - q.defer(run, data, cmd); - - // Sync files (remove old files, copy new ones) - cmd = 'rm -rf ' + data.site + ' && mv ' + data.dest + ' ' + data.site; - q.defer(run, data, cmd); - - // Done processing - q.await(function() { - // Log success message - console.log( - 'Successfully rendered: ' + data.owner + '/' + data.repo + - '@' + data.after + ' to ' + data.site - ); - - // Send success email - if (config.email && data.pusher.email) { - var message = { - text: 'Successfully rendered: ' + data.owner + '/' + data.repo + - '@' + data.after + ' to ' + data.site, - from: config.email.user, - to: data.pusher.email, - subject: 'Successfully built ' + data.repo + ' site' - }; - mailer.send(message, function(err, message) { console.log(err || message); }); - } - }); -}); - -// Start server -app.listen(8080); -console.log('Listening on port 8080'); - -function run(data, cmd, cb) { - - console.log('Running: ' + cmd); - exec(cmd, function (error, stdout, stderr) { - console.log('stdout: ' + stdout); - console.log('stderr: ' + stderr); - - if (error !== null) { - console.log('exec error: ' + error); - - // Send error email - if (config.email && data.pusher.email) { - var message = { - text: 'exec error: ' + error, - from: config.email.user, - to: data.pusher.email, - subject: '!! Failed to build ' + data.repo + ' site' - }; - mailer.send(message, function(err, message) { console.log(err || message); }); - } - - } else { - if (typeof cb === 'function') cb(); - } - }); -} diff --git a/build.sh b/build.sh deleted file mode 100644 index 9e267a7..0000000 --- a/build.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -# Install node and depencencies -sudo apt-get update -y -sudo apt-get install python-software-properties python g++ make -y -sudo add-apt-repository ppa:chris-lea/node.js -y -sudo apt-get update -y -sudo apt-get install nodejs npm -y - -# Forever to keep server running -sudo npm install -g forever - -# Git -sudo apt-get install git -y - -# Ruby -sudo apt-get install ruby1.8 -y -sudo apt-get install rubygems -y - -# Jekyll -sudo gem install jekyll --version "0.11.2" -sudo gem install rdiscount -- version "1.6.8" -sudo gem install json --version "1.6.1" - -# Nginx for static content -sudo apt-get install nginx -y
\ No newline at end of file diff --git a/config/nginx b/config/nginx deleted file mode 100644 index 07b5ece..0000000 --- a/config/nginx +++ /dev/null @@ -1,17 +0,0 @@ -server { - root /usr/share/nginx/www; - index index.html index.htm; - - # Make site accessible from http://localhost/ - server_name localhost; - - location / { - # First attempt to serve request as file, then - # as directory, then fall back to index.html - try_files $uri $uri/ /index.html; - - # Optional basic auth restriction - # auth_basic "Restricted"; - # auth_basic_user_file /etc/nginx/.htpasswd; - } -} diff --git a/jekyll-hook.js b/jekyll-hook.js new file mode 100755 index 0000000..e020836 --- /dev/null +++ b/jekyll-hook.js @@ -0,0 +1,94 @@ +#!/usr/bin/env node + +var config = require('./config.json'); +var fs = require('fs'); +var express = require('express'); +var app = express(); +var spawn = require('child_process').spawn; +var email = require('emailjs/email'); +var mailer = email.server.connect(config.email); + +app.use(express.bodyParser()); + +// Receive webhook post +app.post('/hooks/jekyll', function(req, res){ + var data = JSON.parse(req.body.payload); + var params = []; + + // Parse webhook data for internal variables + data.repo = data.repository.name; + data.branch = data.ref.split('/')[2]; + data.owner = data.repository.owner.name; + + // Close connection + res.send(202); + + // End early if not master branch + if (data.branch !== config.branch) { + console.log('Not ' + config.branch + ' branch.'); + return; + } + + // Process webhook data into params for scripts + /* repo */ params.push(data.repo); + /* branch */ params.push(data.branch); + /* owner */ params.push(data.owner); + /* giturl */ params.push('git@' + config.gh_server + ':' + data.owner + '/' + data.repo + '.git'); + /* source */ params.push(config.temp + '/' + data.owner + '/' + data.repo + '/' + data.branch + '/' + 'code'); + /* build */ params.push(config.temp + '/' + data.owner + '/' + data.repo + '/' + data.branch + '/' + 'site'); + + // Run build script + run(config.scripts.build, params, function(err) { + if (err) { + console.log('Failed to build: ' + data.owner + '/' + data.repo); + send('Your website at ' + data.owner + '/' + data.repo + ' failed to build.', 'Error building site', data); + return; + } + + // Run publish script + run(config.scripts.publish, params, function(err) { + if (err) { + console.log('Failed to publish: ' + data.owner + '/' + data.repo); + send('Your website at ' + data.owner + '/' + data.repo + ' failed to publish.', 'Error publishing site', data); + return; + } + + // Done running scripts + console.log('Successfully rendered: ' + data.owner + '/' + data.repo); + send('Your website at ' + data.owner + '/' + data.repo + ' was succesfully published.', 'Succesfully published site', data); + + }); + }); +}); + +// Start server +app.listen(8080); +console.log('Listening on port 8080'); + +function run(file, params, cb) { + var process = spawn(file, params); + + process.stdout.on('data', function (data) { + console.log('' + data); + }); + + process.stderr.on('data', function (data) { + console.warn('' + data); + }); + + process.on('exit', function (code) { + if (typeof cb === 'function') cb(code !== 0); + }); +} + +function send(body, subject, data) { + if (config.email && data.pusher.email) { + var message = { + text: body, + from: config.email.user, + to: data.pusher.email, + subject: subject + }; + mailer.send(message, function(err) { if (err) console.warn(err); }); + } +} diff --git a/package.json b/package.json index 51918c7..6348faf 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "version": "0.1.0", "dependencies": { "express": "3.x", - "queue-async": "1.0.1", "emailjs": "0.3.4" }, "private": true @@ -1,6 +1,6 @@ # jekyll-hook -A server that listens for webhook posts from GitHub, generates a site with Jekyll, and moves it somewhere to be served. Use this to run your own GitHub Pages-style web server. Great for when you need to serve your websites behind a firewall, need extra server-level features like HTTP basic auth (see `default` file for Nginx config with basic auth), or want to host your site directly on a CDN or file host like S3. +A server that listens for webhook posts from GitHub, generates a site with Jekyll, and moves it somewhere to be served. Use this to run your own GitHub Pages-style web server. Great for when you need to serve your websites behind a firewall, need extra server-level features like HTTP basic auth (see `default` file for Nginx config with basic auth), or want to host your site directly on a CDN or file host like S3. It's cutomizable with two user-configurable shell scripts and a config file. ## Installation @@ -15,8 +15,11 @@ Copy the following JSON to `config.json` in the application's root directory. { "gh_server": "github.com", "branch": "master", - "temp_directory": "/home/ubuntu/jekyll-hook", - "site_directory": "/usr/share/nginx/www", + "temp": "/home/ubuntu/jekyll-hook", + "scripts": { + "build": "./scripts/build.sh", + "publish": "./scripts/publish.sh" + }, "email": { "user": "", "password": "", @@ -30,8 +33,10 @@ Configuration attributes: - `gh_server` The GitHub server from which to pull code - `branch` The branch to watch for changes -- `temp_directory` A directory to store code and site files -- `site_directory` A directory to publish the site +- `temp` A directory to store code and site files +- `scripts` + - `build` A script to run to build the site + - `publish` A script to run to publish the site - `email` Optional. Settings for sending email alerts - `user` Sending email account's user name (e.g. `example@gmail.com`) - `password` Sending email account's password @@ -40,9 +45,63 @@ Configuration attributes: ## Usage -- run once: `$ node app.js` -- use [forever](https://github.com/nodejitsu/forever) to run as server: `$ forever app.js` +- run as executable: `$ ./jekyll-hook.js` -## Web server +## Publishing content -Serve content from a simple webserver link Nginx (the `default` file is a sample Nginx configuration with HTTP basic auth) or use s3cmd or rsync to mirror files on S3 or a CDN. +The stock `build.sh` copies rendered site files to subdirectories under a web server's `www` root directory. For instance, use this script and NGINX with the following configuration file to serve static content behind HTTP basic authentication: + +``` +server { + root /usr/share/nginx/www; + index index.html index.htm; + + # Make site accessible from http://localhost/ + server_name localhost; + + location / { + # First attempt to serve request as file, then + # as directory, then fall back to index.html + try_files $uri $uri/ /index.html; + + # Optional basic auth restriction + # auth_basic "Restricted"; + # auth_basic_user_file /etc/nginx/.htpasswd; + } +} +``` + +Replace this script with whatever you need for your particular hosting environment. + +## Dependencies + +Here's a sample script to install the approriate dependencies on an Ubunutu server: + +```sh +#!/bin/sh + +# Install node and depencencies +sudo apt-get update -y +sudo apt-get install python-software-properties python g++ make -y +sudo add-apt-repository ppa:chris-lea/node.js -y +sudo apt-get update -y +sudo apt-get install nodejs npm -y + +# Forever to keep server running +sudo npm install -g forever + +# Git +sudo apt-get install git -y + +# Ruby +sudo apt-get install ruby1.8 -y +sudo apt-get install rubygems -y + +# Jekyll +sudo gem install jekyll --version "0.12.0" +sudo gem install rdiscount -- version "1.6.8" +sudo gem install json --version "1.6.1" + +# Nginx for static content +sudo apt-get install nginx -y +``` diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..c4942ac --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -e + +repo=$1 +branch=$2 +owner=$3 +giturl=$4 +source=$5 +build=$6 + +# Check to see if repo exists. If not, git clone it +if [ ! -d $source ]; then + git clone $giturl $source +fi + +# Git checkout appropriate branch, pull latest code +cd $source +git checkout $branch +git pull origin $branch +cd - + +# Run jekyll +cd $source +jekyll $source $build --no-server --no-auto -base-url="/$repo" +cd - diff --git a/scripts/publish.sh b/scripts/publish.sh new file mode 100755 index 0000000..5e1d2f1 --- /dev/null +++ b/scripts/publish.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -e + +repo=$1 +branch=$2 +owner=$3 +giturl=$4 +source=$5 +build=$6 + +# Set the path of the hosted site +site="/usr/share/nginx/www/$repo" + +# Remove old site files, move new ones in place +rm -rf $site +mv $build $site |