BLASH - Bash static blog generator

Introducing BLASH to the world

By Francesco Bertolaccini

Introducing BLASH

Backstory

So I wanted to start blogging. The first thing I needed was a place to put all the stuff into — well, GitHub Pages does exactly that, I guess that’s one thing less to worry about. Now, GH Pages uses Jekyll, which is a static site generator, which is great because it means I do not have to worry about databases and plugins and all that kind of nonsense, if I ever need to backup the blog I can just rsync everything and call it a day.

But… I don’t like Jekyll. It’s written in Ruby, and that makes it a real pain in the proverbial to use under Windows, but I don’t like having to install the Ruby runtime on Linux either. Also, I don’t really like being forced to use Sass and CoffeeScript or the Liquid template language. This means I have to search for another generator.

Maybe Hugo? Too complicated, I don’t really want to spend that much time configuring it to make my blog look the way I want it to. (In hindsight, this is quite ironic: I could probably have spent less time configuring Hugo than writing BLASH but…)

Introducing BLASH

What I wanted was something really flexible yet simple to configure (or at least intuitive… for me) and with very few dependencies.

The last point made bash a very obvious choice, as it is available on basically any system nowadays - even Windows has it via WSL. The only other dependency is on Pandoc.

How does it work?

BLASH can be configured using shell files. Whereas Jekyll uses YAML front-matter, BLASH uses a separate shell script for each blog post, in which information like the title, tags and author are stored.

For example, let’s say you want to create a blog post titled Hello world: you’ll create two files:

2018-11-12-hello-world.sh
2018-11-12-hello-world.md

The shell file will contain something like this:

#!/bin/bash

title="Hello, world!"
author="Jane Doe"
excerpt="Lorem ipsum dolor sit amet"
tags=("hello-world" "foo")

While the Markdown file will be your standard content, ready to be converted to HTML by Pandoc.

Templates work in a similar way, as they are shell scripts too. Let’s take a look at a simple template (post.sh):

#!/bin/bash

year=${date:0:4}

source="<!DOCTYPE html>
<html lang=\"en\">
<head>
  <meta charset=\"UTF-8\">
  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
  <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">
  <link rel=\"stylesheet\" href=\"$base_url/assets/css/stylesheet.css\">
  <title>$title</title>
</head>
<body>
  <header>
    <h1>$title</h1>
    <h2>$excerpt</h2>
    <h3>By $author</h3>
  </header>

  <main>
    <article>$content</article>
  </main>

  <footer>
    <p>Copyright &copy; $year $author</p>
  </footer>
</body>
</html>"

This file will be read once for every post, each time with different values in the variables. Of course, it’s a shell script, so you’re not limited to string expansion.

BLASH will also take care of generating the various indices in your blog: the main one, one for every tag and category, and one for every date. It uses a single template for every kind of index, index.sh:

#!/bin/bash

content=""

for post in "${posts[@]}"
do
  post_title=${titles[$post]}
  excerpt=${excerpts[$post]}
  date=${dates[$post]}
  url=$(fileNameToUrl $post)
  content+="<li>$date - <a href=\"$base_url/$url\">$post_title</a> &#126; <em>$excerpt</em></li>"
done

subtitle=""

if [ -n "$tag_name" ]; then
  subtitle="Browsing posts by tag: <span class=\"tag\">$tag_name</span>"
fi

if [ -n "$category_name" ]; then
  subtitle="Browsing posts by category: <span class=\"category\">$category_name</span>"
fi

if [ -n "$date_span" ]; then
  date_span=$(sed 's/\//-/g' <<< "$date_span")
  subtitle="Browsing posts by date: <span class=\"date\">$date_span</span>"
fi

source="<!DOCTYPE html>
<html lang=\"en\">
<head>
  <meta charset=\"UTF-8\">
  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
  <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">
  <link rel=\"stylesheet\" href=\"$base_url/assets/css/stylesheet.css\">
  <title>$title</title>
</head>
<body>
  <header>
    <h1>$title</h1>
  </header>

  <main>
    <ul>$content</ul>
  </main>

  <footer>
    <p>Copyright &copy; $current_year $default_author</p>
  </footer>
</body>
</html>"

tag_name, category_name and date_span will have different values depending on the index type that’s being generated.

Finally, general configuration of the blog is done via the config.sh file:

#!/bin/bash

: ${blog_title:="Francesco Bertolaccini's blog"}
: ${default_author:="Francesco Bertolaccini"}
: ${base_url:=""}

configureAssets () {
  for css in assets/css/*.css
  do
    filename=$(basename -- "$css")
    cp "$css" "publish/assets/css/$filename"
  done
}

configureAssets will be called once every time the site is built. You can put your css/js minification in there, for example. The weird variable syntax is to allow override in the command line.

To summarize, this is the general directory tree:

website
|- assets
|  |- css, js, images...
|
|- contents
|  |-posts
|    |- 2018-11-12-hello-world.md
|    |- 2018-11-12-hello-world.sh
|
|- templates
|  |- index.sh
|  |- post.sh
|
|- config.sh
|- publish.sh

So, how do you build the site?

$ ./publish.sh

Done. The command will put the resulting artifacts into the publish subdirectory, and those files will be ready to be sent to a server.

You can also override variables if you want:

$ base_url=/foo ./publish.sh

Conclusion

This is working well for me so far — that is, one post — but I’m convinced it’s simple and flexible enough for me to keep using it, I hope you’ll like it too :) You can find it on github: BLASH!