summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Cole <dave@developmentseed.org>2013-04-09 18:26:13 -0400
committerDave Cole <dave@developmentseed.org>2013-04-09 18:26:13 -0400
commit809a9b5204804ef36bae56f04eb640ab342fd381 (patch)
treea9a20765f2c84a1027a01a6713395bfb54ed6a8e
parente28985afe274e46086f73d20ac6cebc7710204d4 (diff)
Reorganize app to use external scripts. Make main file executable. Move sample code to readme. Refs #40.
-rw-r--r--.gitignore3
-rw-r--r--app.js114
-rw-r--r--build.sh26
-rw-r--r--config/nginx17
-rwxr-xr-xjekyll-hook.js94
-rw-r--r--package.json1
-rw-r--r--readme.md77
-rwxr-xr-xscripts/build.sh25
-rwxr-xr-xscripts/publish.sh16
9 files changed, 205 insertions, 168 deletions
diff --git a/.gitignore b/.gitignore
index 36420af..a8bc574 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
node_modules
-config.json \ No newline at end of file
+config.json
+test.json
diff --git a/app.js b/app.js
deleted file mode 100644
index fdfcd09..0000000
--- a/app.js
+++ /dev/null
@@ -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
diff --git a/readme.md b/readme.md
index 26a8960..fec9b3a 100644
--- a/readme.md
+++ b/readme.md
@@ -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