I wrote my blog engine

At first it didn’t look like a good idea but I got afraid when I tried hugo.
on 2024-10-17, at 04:57 CEST, it was a Thursday

Back to postsHiro Lynx by Muzz, facing you and pressing his handpaws beans on the inside of your screen

It’s written in shell-script.

In the end I managed to make it work so all-in-all this was a success! I didn’t need many features and wanted easy templating and extensibility.

I’m still saying it’s a bad idea because shell-script is terrible but I really like calling executables, processing lines of text and escaping strings with sed. Also it runs everywhere.

No Hugo?

I want to self-host and use a tool that generates a static website that I can push somewhere. That excludes WordPress and the likes, and Medium and co. A friend of mine tried Hugo and seemed happy with it so I had a look.

And it’s bigger and more complicated than I need or want. I just wanted a few markdown files that I can easily edit and build as a static website that I can push to my host (who incidentally is myself). And simple templates.

So what is it that I’m doing

I’m using pandoc, but sometimes in a twisted way. Also sed, grep, and a little rsync. The script is written in shell-script, for dash. It can run on busybox! I just like good old dash. Well I hate some of its drawbacks but bash is too big and dash is everywhere.

pandoc is big, yeah. But I like its versatility.

File hierarchy

Everything is pretty straightforward. During the build process, static files get copied as-is, posts are converted to HTML using the provided template. A list of posts is generated as an HTML page and as a RSS feed. An index template is there to add some introductory text, link to other pages and stuff on the website, and contains links to the latest blog posts.

my-blog
 | blog.sh
 | blog.vars
 | static
 |  | style.css
 |  \ (images, media…)
 | templates
 |  | index.tpl.html
 |  | posts.tpl.html
 |  | post.tpl.html
 |  \ feed.tpl.rss
 | posts
 |  | 00001 the-post-s-title
 |  |  | post.md
 |  |  \ (images, media…)
 |  | 00002 the-other-post-s-title
 |  |  | post.md
 |  |  \ (images, media…)
 |  \ (more posts…)
 \ out
    | index.html
    | static
    |  | style.css
    |  \ …
    \ posts
       | index.html
       | feed.rss
       | the-posts-s-title.1
       |  | index.html
       |  \ …
       \ the-other-posts-s-title.2
          | index.html
          \ …

The command

Things happen when one calls blog.sh. It is the only file needed to bootstrap a blog as it will create the file hierarchy, provide a basic stylesheet and sample templates.

Blog posts have some additional metadata that let the script know if the post is published or not. Here is the header in the post.md for this blog post:

---
title: I wrote my blog engine
date: 2024-10-16 21:55
author: Hiro Lynx

# Document number: 00003
created: 2024-10-16 21:55
comment: Obviously this was not a good idea but I got afraid when I tried hugo.
published: false #TODO: Change to true whenever the article is ready
---

The templates

There is the post.tpl.html template, which does things the normal way.

And then, there are the other templates. They do not use the $body$ of their source documents.

For those, blog.sh creates “mardown” files with metadata only written in YAML blocks. It uses pandoc to generate the index and list of blog posts this way.

It also uses pandoc to extract metadata from mardown files using a template that only puts some metadata besides variable names that I then extract using sed in the script. The template looks like this:

post_title:$title/nowrap$
post_comment:$comment/nowrap$
post_published:$published/nowrap$
post_author:$author/nowrap$
post_created:$created/nowrap$
post_date:$date/nowrap$

You might notice that the names between the dollars are the same as in the metadata of the post header above.

pandoc will also be used to generate the RSS feed! XML and HTML aren’t that different, so I made a template for a generated source markdown file that only contains metadata. The template looks like this:

<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
 <title>$title$</title>
 <description>$desc$</description>
 <link>$url$</link>
$if(blog-items)$
$for(blog-items)$
 <item>
  <title>$blog-items.title$</title>
  <description>$blog-items.comment$</description>
  <link>$url$posts/$blog-items.link$/</link>
  <pubDate>$blog-items.date$</pubDate>
 </item>
$endfor$
$endif$
</channel>
</rss>

It works similarly for the index and the list of all blog posts.

All in all

I’m happy with blog.sh for now. I plan on adding pages in addition to blog posts so I can build the rest of my website with it. I may have to rename it…

I also want it to generate gemtext for my future presence on the Gemini Space. I made a new pandoc writer for that, because the ones I found weren’t to my taste. But it’s a story for another blog post!

Back to posts