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.sh new
will create a new blog post.blog.sh edit
will let you edit an existing blog post.blog.sh del
will let you delete blog posts.blog.sh build
will apply templates to all pages and put the result inout
. Add-u
to make it build unpublished pages for a preview!blog.sh deploy
will build the website and usersync
to deploy the website somewhere remote or not. No unpublished pages will be built.
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!