a warm space

Org-mode and Pandoc as a static site generator

2018-09-17 Mon

I've spent a lot of time looking for the "perfect" static site generator. I want something that's:

While there are some awesome site generators out there now (I really enjoyed hugo for it's 0 dependencies and speed) most were just too big for me. They had specific templating systems, or required set up and theming that I didn't want to get into.

I settled on using pandoc to convert a set of markdown files to html, and paired it with a very simple css file. This worked well! I had an ultra-minimal blog generator that I could run on Gitlab Pages super easily.

Eventually though I ran into the limitations. I wanted to generate an index page for the blog, as well as a fancier table of contents for a whitepaper. To handle this I ended up adding cases to what was a pretty small bash script. And of course that became a pain to maintain.

The main problem was a separation between the writing I wanted to do and the code I had to write to manage it. What if these could be tied together? I'd had some exposure to the ideas of Literate Programming from Donald Knuth, but it had never really clicked for me in a practical way.

This changed as I started using the wonder that is org mode. Bear with me, I'll get to blog generation soon.

What is Org Mode?

Org mode is for keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system.

Org is a joyful piece of software. It's a simple yet powerful combination of intuitive markup and ergonomic tooling that (most of the time) just works. The key is that the markup is readable at a glance, and the tools provide some pretty neat ways of interacting with and manipulating it.

It's core components are fairly simple:

Literate blog generation

You may have noticed the source blocks scattered around this site. They define how the content under them is generated. They're just code blocks, but with org-babel I can execute them right in my editor to generate those blocks on the fly! For the site I'm using bash, but babel (as the name implies) let's you write in pretty much anything.

Build the blog list with bash
Build the blog list with bash

Is this a little overkill? Yes. But it's way more maintainable than the monster bash script I was using previously.

It's probably not the static site generator for you if you just want to get to blogging, but it's incredibly fun to put together and a great learning experience.

Having the code in the same place as the writing you do encourages you to both maintain it and write more. This could turn into an endless bikeshed, but for me it opens up a ton of possibilities in producing the kind of work I want to.

Getting Started

Org-babel's documentation is somewhat scattered but digging through The Org Manual eventually got me where I needed be. This article on using it for Literate Dev-Ops by Howard Abrams also has a ton of useful information and techniques.

How this site is actually built

The rough idea is to have a bunch of code snippets distributed throughout the site in org-babel blocks. Then we use blocks that look something like this at each level up.

(find-file "./blog/index.org")
(org-babel-execute-buffer )
(save-buffer)

This will go into a specific file and execute all the org-babel blocks contained within. And if that file includes a similar block, it'll go down into others!

And at the top level (the index.org file) we have the code that starts it all.

Getting some HTML

Once all the files are set up, we have a small bash script right at the top to convert all of that markdown to html.

#!/bin/bash

rm -rf _public
root="$(pwd)"

#emacs --batch -l ./build-assets/build.el
for folder in $(find . -type d -not -path '*/\.*'); do
    (cd $folder
     for file in `find . -maxdepth 1 -name "*.org"`; do
         [ -e "$file" ] || continue
         mkdir -p "$root/_public/${folder#./}"

         pandoc -f org -t html "$file" \
                -o "$root/_public/${folder#./}/`basename "$file" .org`.html"\
                --lua-filter $root/scripts/changeLinks.lua \
                --lua-filter $root/scripts/task-list.lua \
                -B $root/static/html/header.html \
                -H $root/static/html/includes.html \
                -c "/static/styles.css" \
                -s
     done)
done

cp -r static _public/

It's pretty small as the heavy lifting is done by pandoc. This time however, this script can stay small as we're letting org-babel take the weight.

What's next?

Well actually writing for one. I've got the infrastructure in place and now it's time to start using it!

Well, there's also some additional infrastructure I'd like to get in of course :)