Full Stack Blogging

I decided to revamp how this site is built and hosted - out of a desire to simplify the site, and control its entire stack from the content payload to the host itself. The site remains statically-generated, but the generation now happens in Middleman instead of Octopress. Additionally, repo-level GitHub Pages hosting has been dropped in favour of nginx serving the site directly from a Digital Ocean droplet. This is a few notes on the migration experience, and the ultimate configuration I've ended up with.

The Site Itself

I wanted to make this site as simple as possible. I don't post often, nor does anything Earth-shattering find its way here, but it's still my home on the web and I want it to be tidy. Octopress is great, but it ships with all manner of Jekyll partials, default styles, JavaScript libraries, support for comments, share buttons, etc. I decided to dispense with just about everything that might be considered surplus to the requirements of the site's role as a simple repository for words, code, and images that loads fast, and is around indefinitely in a place and from a source that I control1.

Dateless URLs

Matt Gemmell has a persuasive piece in which he argues against the ubiquitous date-embedded-URL of the form: http://foo.com/2015/12/31/article. I won't replicate his arguments here, but I was sufficiently convinced to get rid of them in my own site (while ensuring old dated links aren't broken).

Static Generation: Middleman

Middleman is a really nice Ruby-based static site generator. Whereas Octopress is a comprehensive blogging solution – albeit a wonderfully geeky, statically-generated one – Middleman is conceptually-pristine: your assets, layouts, and markdown content are transformed by a Ruby layer into a tree of HTML documents – nothing more. That being said, it can be extended in myriad ways with the various middleman-* gems that exist; e.g. middleman-blog provides several blogging primitives: layout templates, rake tasks, and the like.

Slim

slim is my preferred templating system, and as Middleman is a ruby application, it's trivial to enable it by adding the slim gem. Much nicer than angle brackets everywhere!

/ layout.html.slim

doctype html
html
  head
    meta charset="utf-8"
    meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"
    meta name="viewport" content="width=device-width, initial-scale=1.0"
    meta name="author" content="#{author}"
    - if current_page.data.description.present?
      meta name="description" content="#{current_page.data.description}"
    - else
      meta name="description" content="#{site_description}"

  body
    #wrapper
      #header
        = partial 'partials/header'
      section#content
        == yield
      #footer
        = partial 'partials/footer'

Redcarpet

I installed the redcarpet gem for markdown parsing2:

# config.rb
set :markdown, autolink:           true,
               gh_blockcode:       true,
               fenced_code_blocks: true,  # Win.
               footnotes:          true
set :markdown_engine, :redcarpet

Page-Specific CSS & JavaScript

Middleman makes it easy to add optional stylesheet and script keys to the frontmatter of any article for article-specific assets:

---
layout: article
title: "Efficient Currency Conversion"
stylesheet: efficient-currency-conversion
script: efficient-currency-conversion
---

These can be inserted into the document head by the containing layout:

/ layout.html.slim
- stylesheets = %w[application]
- stylesheets << current_page.data.stylesheet if current_page.data.stylesheet.present?
= stylesheet_link_tag *stylesheets

- scripts = %w[application]
- scripts << current_page.data.script if current_page.data.script.present?
= javascript_include_tag *scripts

The common application assets are handled by the asset pipeline, described below.

Front-end Assets

Middleman has an excellent asset pipeline implementation for stylesheets and JavaScript.

Bower

I'm a fan of bower for handling front-end dependencies. It's not perfect, but it does brings long-absent sanity to managing external front-end libraries. I'm using it to manage these dependencies3:

// bower.json
{
  "dependencies": {
    "jquery": "~2.1.3",
    "moment": "~2.10.2",
    "bigfoot": "~2.1.2",
    "normalize-css": "~3.0.3",
    "highlightjs": "~8.5.0",
    "featherlight": "~1.2.3"
  }
}

The bower-managed assets at the root directory's bower_components can be imported for asset compilation in Middleman's config.rb:

# config.rb
bower_directory = 'bower_components'
compass_config do |config|
  config.add_import_path "../#{bower_directory}"
  config.output_style = :compact
end
sprockets.append_path File.join root, bower_directory

Sass

Custom styling can be done in Sass with SCSS syntax, my preferred way to do CSS preprocessing:

// _custom.css.scss
#header {
  height: $header-height-large;
  text-align: center;
  margin-left: auto;
  margin-right: auto;
  h1, h2 {
    font-family: $header-font;
    font-weight: 400;
    margin-bottom: 0;
    color: $header-color;
    a, a:hover, a:visited {
      color: $header-color;
    }
  }
}

Thanks to Middleman's Compass support, all the custom Sass plus everything from bower gets built into a single, minified, asset-hashed application.css from this manifest file:

// application.css.scss
@charset "utf-8";

// Import bower stylesheets.
@import "normalize-css/normalize";
@import "bigfoot/src/scss/foundation/bigfoot-variables";
@import "bigfoot/src/scss/foundation/bigfoot-mixins";
@import "bigfoot/src/scss/base/bigfoot-button";
@import "bigfoot/src/scss/base/bigfoot-popover";
@import "highlightjs/styles/railscasts";
@import "featherlight/src/featherlight";

// Import custom partials.
@import "_custom";

Autoprefixer

As a final build step, the output is run through the npm tool autoprefixer, via the gem middleman-autoprefixer. This is a new tool to me, and it verges on the magical: it takes existing CSS and applies all necessary vendor prefixes based on the state of browsers today as reported by caniuse.com (!). That means you can write W3C compliant CSS and dispense with any -moz-transform etc nonsense, as well as kludgey approaches like Compass prefix generation. This is now an essential piece of any front-end workflow, I'd argue.

JavaScript

Similarly to the stylesheets, sprockets produces a single, minified, asset-hashed application.js from this manifest file:

// application.js
//= require jquery
//= require moment
//= require bigfoot
//= require highlightjs
//= require featherlight

//= require ./_init_site

Hosting

Middleman's build step produces a tree of HTML documents, alongside included images, and minified CSS and JavaScript files. This needs to be statically-hosted; I typically use various aspects of Amazon Web Services on a day-to-day basis, but I thought this would be a good opportunity to take Digital Ocean for a spin.

Ubuntu+Nginx Droplet

It's wonderfully uncomplicated to spin up a new Ubuntu Digital Ocean droplet and install nginx. I noticed that the HTML5 Boilerplate project has a great set of template nginx configuration files, with sensible defaults around asset cache expiry, etc. I forked this and used it as the basis for my own instance.

Redirecting Dated URLs

All existing URLs at this domain have contained dates prior to this switch. I don't want to break those, so it's a matter of 301 Moved Permanently redirecting them to their newly-canonical dateless form with a redirect rule in nginx:

# liggat.org.conf
server {
  # ...
  rewrite "/([0-9]{4})/([0-9]{2})/([0-9]{2})/(.*)" /$4 permanent;
  # ...
}

Deployment

Deployment is a rake task that invokes rsync to copy over any changes to the site:

# Rakefile
task :build do
  status = system("bundle exec middleman build --clean")
end

task :sync do
  command = "rsync -avze '#{SSH}' --delete build/ #{USER}@#{HOST}:#{DIR}"
  status  = system command
end

task build_deploy: [:build, :sync]

Still Outstanding: SSL

The final outstanding piece of this stack switchover is to enable SSL, and moreover, turn it on by default. I won't enumerate the arguments in favour here, but with Mozilla recently telegraphing its intent to deprecate HTTP amidst a wider push around the Internet to get HTTPS deployed by default, it's clear how the prevailing winds are blowing. This is a task for another post, and I intend to get to it shortly.

Stack Future

A common macro-trend I've noticed in both my day-to-day work as well as this little project is just how thoroughly capable web tooling (e.g. npm tools like autoprefixer) as well as client environments (e.g. JavaScript runtimes) have become. I happen to be doing my markdown processing, CSS/JavaScript compilation, etc in Ruby, but this can all easily be done entirely in web-native tooling.

Taken together, environments like Ruby, PHP, etc must work ever harder to justify their existence, especially in simpler contexts such as static-site generation. Were I not a huge fan of Ruby already, I'm not sure I would've bothered including it (i.e. Middleman) in this particular solution, and might have opted for a pure web stack – perhaps by replacing Middleman with orangejuice. I'm reasonably confident that's how this site's next redesign will go.

JavaScript detractors are often cantankerous about this trend, but like it or not, it's an unstoppable force, and if you work in web stuff in 2015, you ignore Atwood's Law at your peril.


  1. Oh, and I'll probably use it as a testbed for interesting technical experiments, on both the front-end and back-end. A wise man once told me: computer science is not a spectator sport

  2. Easily the best part of this is the ability to use Github-style, three-backtick code blocks. 

  3. I'm especially impressed with highlightjs. As shown on this page, it makes it effortless to syntax highlight all manner of source code.