How to write a static site generator in 30 lines or less

29 March 2021

This weekend I played around with Gemini. I became aware of Gemini in the last six months or so, via various techie people talking about it on Mastodon. Even though the tech stack for Mastodon could barely be further from what Gemini is trying to do, there is a lot of overlap in the interests of people using each technology. I'm very attracted to the forced simplicity of Gemini: it has a lot in common with concepts I've written about before, like Brutalist web design, decentralisation, the Hundred Rabbits philosophy and even to an extent some of the things that drive the Rust community - it's not surprising that a lot of the Gemini server software is written in Rust (or that Hundred Rabbits have a gemini site.

I found Gemini a little confusing at first, since it's more a "souped up gopher" than a slimmed-down HTTPS, and gopher is something my mother once used to access files over the university network, rather than something I ever remember using myself. But with a few hours reading and tinkering I got a site up and running, where I'll post notes about what I'm reading and thinking, in the spirit of the Classic Era of blogging. The gemtext syntax is disarmingly simple — significantly simpler and cleaner than HTML, essentially it's a very tight and rigid subset of Markdown with one exception, being the way URLs are formatted. This means that the gemini file format is extremely close to plain text.

Even so, I don't really want to futz around manually adding a new listing to an index page every time I write a note in a new file. So while there's no need to transpile formatting like I do for this blog (writing in markdown and publishing in HTML), the whole point of Gemini for me is to focus exclusively on writing prose rather than code or markup.

Fortuitously, just as I was thinking about this, Ed Summers published a nice little note about a shell script he uses for journaling. Ed's post inspired me to create my own tiny "static site generator" for Gemini:

#!/bin/sh

DATE=`date +"%Y-%m-%d"`
YEAR=`date +"%Y"`
DIR="/Users/hugh/gemini/"
FILE="$DIR/$YEAR/$DATE.gmi"
REMOTE="projects:/srv/gemini/public"
LISTING="\n=> $DATE.gmi $DATE ($@)"

# push to server if run with argument `up`
if [ $1 = "up" ];
then
  rsync -aqz $DIR $REMOTE
  echo "🚀 capsule launched!"
else
  if [ ! -f $FILE ];
  then
    # Create new file with title as header
    echo "# $@\n" > $FILE
    if [ ! -f "$DIR/$YEAR/index.gmi" ];
    then
      # make new directory and index page for current year if there isn't one
      mkdir $DIR/$YEAR
      echo "# $YEAR Notes\n" > $DIR/$YEAR/index.gmi
    fi
    # add new file to the top of the index listing for current year
    sed -i "" "2 s/^/$LISTING/" $DIR/$YEAR/index.gmi
  fi
  code $FILE
fi

I saved this as an executable called "notes", so now I can run:

notes Title of today's post

And that will open a new file named with today's date, with a heading, "Title of today's post". It will also add a link to that page with that title, on the index page for the year's posts. If it's the first post for the year, it will create a new directory and index page for that year before saving the new file. If I already wrote a note today, instead of writing over the top of it, the script simply opens the file in VSCode.

Once I've written my note, I just notes up and the site is updated 🚀. Thanks for the inspiration, Ed!