Wintersmithing

Wintersmith

How to setup and use Wintersmith is covered pretty thoroughly elsewhere on the net, (namely the wintersmith homepage.

Instead I'll cover a few tweaks I had to do to get it running the way I wanted. To avoid being truly confusing, all the paths referenced here are relative to the site you create by running wintersmith new <your site dir here>

LiveReload plugin

There's a Wintersmith LiveReload plugin available which makes previewing your site with wintersmith preview very easy - it's great for editing or setting up CSS.

Installing the LiveReload plugin on Linux Mint (which I run) can be done with sudo npm install -g wintersmith-livereload

You need to then add the path to your config.json file under "plugins" e.g. for this blog:

{
  "locals": {
    "url": "http://localhost:8080",
    "name": "wrouesnel_blog",
    "owner": "Will Rouesnel",
    "description": "negating information entropy"
  },
  "plugins": [
    "./plugins/paginator.coffee",
    "wintersmith-stylus",
    "wintersmith-livereload"
  ],
  "require": {
    "moment": "moment",
    "_": "underscore",
    "typogr": "typogr"
  },
  "jade": {
    "pretty": true
  },
  "markdown": {
    "smartLists": true,
    "smartypants": true
  },
  "paginator": {
    "perPage": 20
  }
}

You then want to insert the line:

!{ env.helpers.livereload() }

into the templates/layout.jade file - giving you something like the following at the top of the file

!!! 5
block vars
  - var bodyclass = null;
html(lang='en')
  head
    block head
      meta(charset='utf-8')
      meta(http-equiv='X-UA-Compatible', content='IE=edge,chrome=1')
      meta(name='viewport', content='width=device-width')
      !{ env.helpers.livereload() }
      script(type='text/javascript').

I also add a script section with Google Analytics to layout.jade because I'm vain like that:

!!! 5
block vars
  - var bodyclass = null;
html(lang='en')
  head
    block head
      meta(charset='utf-8')
      meta(http-equiv='X-UA-Compatible', content='IE=edge,chrome=1')
      meta(name='viewport', content='width=device-width')
      !{ env.helpers.livereload() }
      script(type='text/javascript').
        // google analytics
        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
        })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

        ga('create', 'UA-43235370-1', 'wrouesnel.github.io');
        ga('send', 'pageview');

The glitch: Running wintersmith preview with these changes you'll find it doesn't work when trying to browse to the main index.html page with something like "env is undefined". This is a glitch in paginator which has been fixed upstream but not in wintersmith@2.0.5 in npm.

To fix it I just copied the commit patch manually into my local copy of plugins/paginator.coffee :

diff --git a/examples/blog/plugins/paginator.coffee b/examples/blog/plugins/paginator.coffee
index b8e9032..19098d5 100644
--- a/examples/blog/plugins/paginator.coffee
+++ b/examples/blog/plugins/paginator.coffee
@@ -43,7 +43,7 @@ module.exports = (env, callback) ->
         return callback new Error "unknown paginator template '#{ options.template }'"

       # setup the template context
-      ctx = {contents, @articles, @prevPage, @nextPage}
+      ctx = {env, contents, @articles, @prevPage, @nextPage}

       # extend the template context with the enviroment locals
       env.utils.extend ctx, locals

Show most recent article in the index

By default Wintersmith shows short summaries of articles on your index.html page. I can't decide whether or not I like this behavior yet, but until I do what I wanted was to always have my index show my most recent post.

To do this, we take advantage of Jade's iteration and if/then functionality to modify template/index.jade.

As of this article my index.jade looks as follows:

extends layout

block content
  include author
  each article, num in articles
    if num === 0
      // First article - render in full
      article.article.intro
      header
        h1.indexfullarticle
          a(href=article.url)= article.title
        div.date
          span= moment(article.date).format('DD. MMMM YYYY')
        p.author
            mixin author(article.metadata.author)
      section.content!= typogr(article.html).typogrify()
    else
      article.article.intro
      header
        h2
          a(href=article.url)= article.title
        div.date
          span= moment(article.date).format('DD. MMMM YYYY')
        p.author
            mixin author(article.metadata.author)
      section.content
        !{ typogr(article.intro).typogrify() }
        if article.hasMore
          p.more
            a(href=article.url) more

block prepend footer
  div.nav
    if prevPage
      a(href=prevPage.url) « Newer
    else
      a(href='/archive.html') « Archives
    if nextPage
      a(href=nextPage.url) Next page »

There might be a better way to do this, but for me, for now it works. Basically Jade's iterators will provide an iteration number if you add a variable name for it (num in this case) - and the articles are chronological by default. So 0 is always the most recent.

From there I just duplicate some code fro template/article.jade to have it render the full article in section.content - which is article.html, rather then just the intro section - which is article.intro.

An important note here is that the default CSS selectors require some modification to get things to look right. I'm not sure I've nailed it yet, so editing those is an exercise left to the reader (or just a matter of downloading the stylesheet from this site).

Deploy Makefile

This site is hosted on Github pages, but they have no support for Wintersmith - so it's necessary to manually build the static content and upload that. make is more then capable of handling this task, and while we're at it it's a decent tool to automate housekeeping - in particular I wanted my article metadata to be automatically tagged with a date if the date field was blank.

Automatic date tagging

After banging my head with awk or sed one-liners (which probably can be done) I came to my senses and wrote a bash script to do this for me.

#!/bin/bash

find contents -name '*.md' | while read markdownfile; do
    datemeta=$(cat $markdownfile | grep -m1 date: )
    datestamp=$(cat $markdownfile | grep -m1 date: | cut -d' ' -f2)

    if [ ! -z "$datemeta" ]; then
        if [ -z "$datestamp" ]; then
            # generate a datestamp entry and replace the field with sed
            echo "Date stamping unstamped article $markdownfile"
            datestamp=$(date '+%Y-%m-%d %H:%M GMT%z')
            sed -i "s/date:\ .*/date: $datestamp/" "$markdownfile"
        fi
    fi
done

Git Submodules

Since I use Git to manage the blog, but GitHub Pages uses a git repo to represent the finished blog, it's necessary on my local machine to somehow have two repositories - one representing the Wintersmith site in source form, and one representing the GitHub Pages site after it's rendered.

I do this by treating the build/ directory of my Wintersmith site as a Git submodule. Git won't checkout an empty repo, so you need to create a full repo somewhere and then push it to your normal storage (in my case my private server, but it could be somewhere else on GitHub):

$ mkdir build
$ cd build
$ git init
$ git remote add origin ssh://will@myserver/~/wrouesnel.github.io~build.git
$ touch .gitignore
$ git add *
$ git commit
$ git push master

At this point you can delete the build/ directory you just created. It's not needed any more. Then it can be imported as a submodule to the main Wintersmith repo. We also need to add a remote for pushing output to Github:

$ cd your_wintersmith_repo
$ git submodule add ssh://will@myserver/~/wrouesnel.github.io~build.git build
$ cd build
$ git remote add github git@github.com:wrouesnel/wrouesnel.github.io.git

And after all that effort your module is imported and ready to participate in the build process.

Putting the makefile together

The final makefile looks something like this:

# Makefile to deploy the blog

# Search article markdown for "date" metadata that is unset and set it.
date: 
    ./add-date-stamps.bsh

# Draft's are pushed to my private server
draft: date
    wintersmith build
    cd build; git add *; git commit -m "draft" ; \
    git push origin

# Publish makes a draft, but then pushes to GitHub.
publish: draft
    cd build; git commit -m "published to github"; \
    git push github master

.PHONY: date draft publish

The workflow is that I can call make draft which builds the site and commits the build to my private repo which just tracks draft sites elsewhere, and then make publish for when I want things to go live.

There are obviously other ways this could work - for example, I could use post-commit hooks on the server to push to Github Pages, but the idea here is that provided I can access the the wintersmith repository, everything else can be rebuilt.

Personal thoughts

I've been meaning to blog for sometime to have somewhere to put the things I do or random bits of knowledge I pick up so they might help someone else, but for one reason or another most blogging engines never did it for me.

I've never been much of a fan of managed services they lead to sprawling personal "infrastructure" - I'll be happy when my entire digital life can be backed up by just making a copy of my home directory.

So for blogging I've not much cared for the services out there or their focus. I don't particularly want to manage a heavyweight WordPress or other type of CMS installation on a web-server just for a personal blog, since that requires a lot of careful attention to security, patching, updates and I simply don't need the features.

At the same time services like tumblr never quite seemed for me - it skirts the line between microblogging and blogging and it's relationship with markdown and code didn't gel for me. A deluge of social networking features is also not what I wanted.

With Github pages offering free static site hosting, I initially looked at Jekyll as an SSG for putting something together. But Jekyll is written in Ruby, and at the moment I'm on a node.js kick so I really wanted something in that direction. Hence Wintersmith - simple, easy to use, and written in something that I'm inclinded to hack-on but with enough features out of the box (code highlighting in particular) to not feel onerous.

So far I'm really liking the static site model - it's simple, secure and easy to store, manage and keep in a nice neat git repository. Guess I'll see how it goes.