summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--app.js116
-rw-r--r--build.sh26
-rw-r--r--default122
-rw-r--r--package.json15
-rw-r--r--readme.md48
6 files changed, 329 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..36420af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+config.json \ No newline at end of file
diff --git a/app.js b/app.js
new file mode 100644
index 0000000..f9a7f60
--- /dev/null
+++ b/app.js
@@ -0,0 +1,116 @@
+var _ = require('underscore');
+var fs = require('fs');
+var express = require('express');
+var app = express();
+var queue = require('queue-async');
+var exec = require('child_process').exec,
+ child,
+ cmd;
+var email = require('emailjs/email');
+var config = require('./config.json');
+var mailer = email.server.connect(config.email);
+var data;
+
+app.use(express.bodyParser());
+
+// Receive webhook post
+app.post('/hooks/jekyll', function(req, res){
+ var q = queue(1);
+ data = JSON.parse(req.body.payload);
+
+ // Close connection
+ res.send(200);
+
+ // 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, 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, cmd);
+
+ // Run jekyll
+ cmd = 'cd ' + data.source +
+ ' && jekyll ' + data.source + ' ' + data.dest +
+ ' --no-server --no-auto --base-url="' + data.baseurl + '"' +
+ ' && cd -';
+ q.defer(run, cmd);
+
+ // Sync files (remove old files, copy new ones)
+ cmd = 'rm -rf ' + data.site + ' && mv ' + data.dest + ' ' + data.site;
+ q.defer(run, 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(cmd, cb) {
+ console.log('Running: ' + cmd);
+ child = 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
new file mode 100644
index 0000000..9e267a7
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,26 @@
+#!/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/default b/default
new file mode 100644
index 0000000..b1afabb
--- /dev/null
+++ b/default
@@ -0,0 +1,122 @@
+# You may add here your
+# server {
+# ...
+# }
+# statements for each of your virtual hosts to this file
+
+##
+# You should look at the following URL's in order to grasp a solid understanding
+# of Nginx configuration files in order to fully unleash the power of Nginx.
+# http://wiki.nginx.org/Pitfalls
+# http://wiki.nginx.org/QuickStart
+# http://wiki.nginx.org/Configuration
+#
+# Generally, you will want to move this file somewhere, and start with a clean
+# file but keep this around for reference. Or just disable in sites-enabled.
+#
+# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
+##
+
+server {
+ #listen 80; ## listen for ipv4; this line is default and implied
+ #listen [::]:80 default ipv6only=on; ## listen for ipv6
+
+ 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;
+ # Uncomment to enable naxsi on this location
+ # include /etc/nginx/naxsi.rules
+ auth_basic "Restricted";
+ auth_basic_user_file /etc/nginx/.htpasswd;
+ }
+
+ # location /doc/ {
+ # alias /usr/share/doc/;
+ # autoindex on;
+ # allow 127.0.0.1;
+ # deny all;
+ # }
+
+ # Only for nginx-naxsi : process denied requests
+ #location /RequestDenied {
+ # For example, return an error code
+ #return 418;
+ #}
+
+ #error_page 404 /404.html;
+
+ # redirect server error pages to the static page /50x.html
+ #
+ #error_page 500 502 503 504 /50x.html;
+ #location = /50x.html {
+ # root /usr/share/nginx/www;
+ #}
+
+ # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
+ #
+ #location ~ \.php$ {
+ # fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ # # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
+ #
+ # # With php5-cgi alone:
+ # fastcgi_pass 127.0.0.1:9000;
+ # # With php5-fpm:
+ # fastcgi_pass unix:/var/run/php5-fpm.sock;
+ # fastcgi_index index.php;
+ # include fastcgi_params;
+ #}
+
+ # deny access to .htaccess files, if Apache's document root
+ # concurs with nginx's one
+ #
+ #location ~ /\.ht {
+ # deny all;
+ #}
+}
+
+
+# another virtual host using mix of IP-, name-, and port-based configuration
+#
+#server {
+# listen 8000;
+# listen somename:8080;
+# server_name somename alias another.alias;
+# root html;
+# index index.html index.htm;
+#
+# location / {
+# try_files $uri $uri/ /index.html;
+# }
+#}
+
+
+# HTTPS server
+#
+#server {
+# listen 443;
+# server_name localhost;
+#
+# root html;
+# index index.html index.htm;
+#
+# ssl on;
+# ssl_certificate cert.pem;
+# ssl_certificate_key cert.key;
+#
+# ssl_session_timeout 5m;
+#
+# ssl_protocols SSLv3 TLSv1;
+# ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;
+# ssl_prefer_server_ciphers on;
+#
+# location / {
+# try_files $uri $uri/ /index.html;
+# }
+#}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..6faa85e
--- /dev/null
+++ b/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "jekyll-hook",
+ "description": "A server that listens for GitHub webhook posts and renders a Jekyll site",
+ "author": "dhcole",
+ "version": "0.0.1",
+ "dependencies": {
+ "express": "3.x",
+ "queue-async": "1.0.1",
+ "underscore": "1.4.4",
+ "emailjs": "0.3.4",
+ "xml2js": "0.2.6",
+ "js-yaml": "2.0.3"
+ },
+ "private": true
+}
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..26a8960
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,48 @@
+# 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.
+
+## Installation
+
+- `build.sh` installs server dependencies for Ubuntu Linux
+- run `$ npm install` to install app dependencies
+
+## Configuration
+
+Copy the following JSON to `config.json` in the application's root directory.
+
+```json
+{
+ "gh_server": "github.com",
+ "branch": "master",
+ "temp_directory": "/home/ubuntu/jekyll-hook",
+ "site_directory": "/usr/share/nginx/www",
+ "email": {
+ "user": "",
+ "password": "",
+ "host": "",
+ "ssl": true
+ }
+}
+```
+
+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
+- `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
+ - `host` SMTP host for sending email account (e.g. `smtp.gmail.com`)
+ - `ssl` `true` or `false` for SSL
+
+## Usage
+
+- run once: `$ node app.js`
+- use [forever](https://github.com/nodejitsu/forever) to run as server: `$ forever app.js`
+
+## Web server
+
+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.