Source code for this blog
Updated 2024-10-26
Tags: blog, github, hakyll, haskell, html, css, javascript, git, browsers
This site is built with Hakyll. I’ve had a great experience with that so far! It uses Haskell to generate a static HTML + CSS site, and doesn’t require the user to enable JS. Here I’ll do a quick overview of how I manage it in case you want to try something similar. Most of it is based on this tutorial, but I switched to self-hosting on a VPS rather than via Github Pages.
Update: 3.5 years later, I would do it the same way again if starting over. Added some details on my current workflow.
Branches
The master
branch holds the production source code.
I make a new branch like cssfixes
or newidea
when
starting any task that has a chance of failing, then merge back into master
once I know it works. I tried a branch per post, but it quickly became unwieldy.
Now all my draft posts live on one drafts
branch.
Draft posts
Each post is a folder with an index.md
like this and possibly some
other files too: drawings, standalone scripts, etc. The post should contain
links and instructions whenever you can do something non-obvious with the other
files. I mainly write in Pandoc markdown, but you can use anything
supported by Pandoc. Posting dates are based on the folder structure, and the
rest is pulled from the markdown header.
I date draft posts 2099/XX/XX
, which pushes them to the top of the recent
posts list and reminds me to fill in the actual posting date later.
I tend to group them by topic too. For example 2099/01/*
might be about
Haskell and 2099/02/*
about prediction markets.
Continuous build
To write I checkout the drafts
branch, rebase -Xtheirs master
if needed, and run
build.sh. It builds a local copy of the site, serves it at
http://localhost:8000, and auto-updates it as I change things. The tag cloud,
RSS feed, CSS, and recent posts list auto-update along with the post contents.
The only thing that doesn’t auto-update is the Haskell code; if I
edit that I have to kill and re-run the script.
Browsers
I use the Tab Reloader plugin in both FireFox and Chrome, and set it to reload every 10 seconds while writing.
One other gotcha is that I have to disable caching (Developer Tools → Network → “Disable cache”) in each browser to make sure I’m not looking at old versions of the CSS. Chrome sometimes does it anyway and needs to have its history cleared.
Publishing a post
I save the drafts often, naming commits by the post(s) they edit. Then when a post is done I:
git mv
the2099/XX/XX
date folder to the current date- Check it out onto
master
- Commit and push
master
, leaving a clean git repo - Run build.sh again, which creates the
.site
folder - Run publish.sh in another terminal to
rsync
it to the server - Checkout
drafts
again andgit rebase -Xtheirs master
Guardrails
To ensure that I don’t accidentally publish drafts before they’re ready, I have a pre-push hook as suggested here:
# .git/hooks/pre-push
url="$2"
if [[ `grep 'draft'`&& "$url" =~ github ]]; then
echo "Don't push the drafts branch to github! Aborting."
exit 1
fi
I also remove them in publish.sh
and .gitignore
:
# publish.sh
# Just in case, remove accidentally-added draft posts before publishing
rm -rf .site/posts/2099
# .gitignore
# Ignore draft posts
# (This should be commented out of .gitinore on the drafts branch)
src/posts/2099