Subject: Blog about my blog From: Reyk Floeter Date: 2017-07-19 Tags: blog misc httpd I really like Twitter because it allows me to share short messages, we have a great community, and 140 characters are enough for everybody. And this statement was exactly 140 characters, but sometimes I want to say more than that. And that's why I finally created this new blog. I was never really into blogging because I barely had time or the audience to write long articles. I sometimes wrote short stories for sites like [undeadly.org](http://undeadly.org/), I collected some of them here, but my own blog was hosted on tumblr and never saw any activity. I want to try it again, and this time I decided to create a self-hosted blog. Something that runs on my own server and with [httpd](https://man.openbsd.org/httpd.8), the web server that I wrote for OpenBSD. So I was looking for potential blogging tools that I could use to run my own blog. Besides the popular and heavyweight ones such as WordPress, there are countless other options: I looked at blogs from fellow developers, such as Ted Unangst's [flak](https://www.tedunangst.com/flak/) (I like the fact that it is written in Lua but the implementation is a bit over my head), or Pelican that is used by Peter Hessler for [bad.network](http://bad.network/) (but, sorry, I don't like Python), and finally Kristaps Dzonsons' [sblg](https://kristaps.bsd.lv/sblg/) that is used for all of his projects and blogs. I decided to use sblg. ---- Kristaps keeps on releasing very useful free software. Most well-known is [mandoc](http://mdocml.bsd.lv), at least everyone is using it for manpages these days, but there is is also his [BCHS](https://www.learnbchs.org) (*beaches*) web stack which strongly advertises OpenBSD's httpd. Great. I also use [kcgi](https://kristaps.bsd.lv/kcgi/) whenever I have to write small CGIs. So sblg seemed like the right choice to me. slbg, or "the simple off-line blog utility", generates a blog from XML pages. It is static because it generates the HTML pages without needing any dynamic content. This is exactly what I want as my simple blog doesn't need a database or any on-the-fly processing to generate the output. All it needs is to display articles and to take care about indexes, tags, links, and some page templates. Dynamic is *over*, static is cool! But there was still a problem: XML. I don't want to write my articles in XML. Using languages with markup to write longer texts doesn't work for me - the text is hard to read and I very quickly get lost in "optimizing" the "source code" of it. Have you ever procrastinated by tweaking your LaTeX macros? Did you ever get disgusted by DocBook's syntax? That's why I decided to write my articles, including [this](https://reykfloeter.com/posts/blog-about-my-blog.md) one, in Markdown and use another tool such as [lowdown](https://kristaps.bsd.lv/lowdown/) to generate the XML pages for sblg. ## The Makefile If you read Kristaps' instructions on the sblg page, you will first get a bit confused about all the steps to generate the sblg blog. But there is one important statement after the examples: *A Makefile makes this easy.* The example looks very easy indeed: XMLS = article1.xml article2.xml ARTICLES = article1.html article2.html all: index.html $(ARTICLES) index.html: index.xml $(ARTICLES) sblg -o $@ -t index.xml $(ARTICLES) .xml.html: sblg -o $@ -t article.xml $< I copied this version and used it as a starting point for my [Makefile](https://reykfloeter.com/Makefile). I'm not necessarily proud of it, because it quickly turned into more than 100 lines instead of the 7 lines example. Let me quickly iterate over my current Makefile. I keep on tweaking this file, so it might have been changed by the time you are reading this article. Please note that the Makefile is written for OpenBSD's [make](https://man.openbsd.org/make.1), a distant derivative of pmake which is not like GNU make. First there is the header: ``` .SUFFIXES: .xml .html .md ARTICLEMDS != ls posts/*.md ARTICLES = $(ARTICLEMDS:S/.md$/.html/g) ARTICLEXMLS = $(ARTICLEMDS:S/.md$/.xml/g) ATOM = atom.xml HTMLS = $(ARTICLES) index.html TEMPLATEXMLS = templates/index.xml templates/article.xml templates/tags.xml TEMPLATES = $(TEMPLATEXMLS:S/.xml$/.html/g) TAGS = tags/index.html posts/tag-cloud.html CLEANFILES = $(ATOM) $(ARTICLES) $(ARTICLEXMLS) $(HTMLS) $(TAGS) CLEANFILES += $(ARTICLES:S/.html//) $(TEMPLATES) posts/tag-cloud* CLEANFILES += tags/* *~ posts/*~ templates/*~ css/*~ ``` The "source" of my blog articles is a number of Markdown files in the `posts/` directory. Instead of listing each article by name, I decided to generate the `$(ARTICLEMDS)` lists with a simple `ls`. Variable modifiers are used to create the lists of the generated files, including `$(ARTICLEXMLS)` and `$(ARTICLES)`. ``` all: $(HTMLS) $(ATOM) tags install: # none $(ARTICLES): $(ARTICLEXMLS) templates/article.html $(TEMPLATEXMLS): tags/index.html index.html: templates/index.html $(ARTICLES) $(TAGS) sblg -o $@ -t templates/index.html $(ARTICLES) atom.xml index.html $(ARTICLES) tags: # Only include articles tagged with "blog" in the feed # (it should be possible to filter this with sblg) atom.xml: templates/atom.xml $(ARTICLES) BLOG=$$(grep -l "Tags:.*blog" $(ARTICLEMDS) | sed 's/\.md/.html/g'); \ sblg -o $@ -t templates/atom.xml -a $${BLOG} ``` After some general rules I generate the Atom file for the blog's RSS feed. I only want to include articles in the feed that are tagged with **blog**, to allow hidden or drafted articles without the tag that don't appear in any of the indexes. Unlike other modes, sblg's `-a` mode doesn't seem to provide a built-in way to filter articles based on their tags. So I filter the articles manually by grep'ing a list of files that include the "blog" tag in the Markdown metadata. I'm talking with Kristaps about all the possible improvements in sblg, so maybe he will help to reduce such scripting hacks in the future. ``` # Generate list of known tags... tags/index.txt: $(ARTICLEXMLS) mkdir -p tags sblg -o- -rl $(ARTICLEXMLS) | awk '{ print $$1 }' | sort | uniq > $@ # ...and a simple HTML "tag cloud" for the menu... tags/index.html: tags/index.txt { (for i in $$(cat tags/index.txt); do \ sblg -rl posts/*.xml | grep "^$$i" | wc -l | tr -d '\n'; \ echo " $$i $$i"; \ done) | sort -R | \ xargs printf '\ %s\n'; } > $@ # ...and a page for each tag tags: tags/index.txt templates/tags.html index.html for i in $$(cat tags/index.txt); do \ sed "s/@NAVTAG@/$$i/g" templates/tags.html > tags/$$i.xml; \ sblg -o tags/$$i.html -t tags/$$i.xml $(ARTICLES); \ ln -sf $$i.html tags/$$i; \ done # ...and a fancy full-page tag cloud posts/tag-cloud.html: $(TEMPLATES) sblg -o- -t templates/article.html templates/tag-cloud.xml | \ sed -e '/@TAGS@/r tags/index.html' -e '/@TAGS@/d' > $@ ln -sf tag-cloud.html posts/tag-cloud ``` The sblg `-l` option allows extracting all the tags from the articles. I use it to create a few files: [tags/index.txt](https://reykfloeter.com/tags/index.txt) is the sorted list of tags, [tags/index.html](https://reykfloeter.com/tags/index.html) is an HTML version that is used by the "tag cloud", and [posts/tag-cloud.html](https://reykfloeter.com/posts/tag-cloud.html) is an auto-generated post with a large version of the tag cloud. I think that any serious blog needs a tag cloud ;-), so I decided to generate one with a simple shell script that is included in the `tags/index.html` target above: for each tag in `tags/index.txt` it counts the number of occurrences in the articles, prints a line with tag name and the count, sorts the output list of tags randomly, and prints an HTML line with the CSS class `tag-cloud-%d` where `%d` is the count. The [css/style.css](https://reykfloeter.com/css/style.css) style sheet includes a number of different `tag-cloud` classes that adjust the opacity and scale the font-size up to 300%. It is all about the cloud. ``` $(TEMPLATEXMLS): templates/header.xml templates/footer.xml templates/navbar.xml # Generate templates including a menu on the right side # 1. Generate sidebar template from all articles # 2. Mark sidebar nav as processed (data-sblg-nav="0") # 3. Insert tag cloud # 4. Insert sidebar into template # 5. Insert other page elements (header, footer, navbar) $(TEMPLATES): $(TEMPLATEXMLS) $(ARTICLESXMLS) templates/sidebar.xml sblg -o- -t templates/sidebar.xml $(ARTICLEXMLS) | \ sed -e 's/data-sblg-nav="1"/data-sblg-nav="0"/g' \ -e '/@TAGS@/r tags/index.html' -e '/@TAGS@/d' | \ sed -e '/@SIDEBAR@/r /dev/stdin' -e '/@SIDEBAR@/d' \ -e '/@HEADER@/r templates/header.xml' -e '/@HEADER@/d' \ -e '/@FOOTER@/r templates/footer.xml' -e '/@FOOTER@/d' \ -e '/@NAVBAR@/r templates/navbar.xml' -e '/@NAVBAR@/d' \ -e '/@SOCIAL@/r templates/social.xml' -e '/@SOCIAL@/d' \ $(@:S/.html/.xml/) > $@ ``` I need different templates for various use cases: the index page ([index.html](https://reykfloeter.com/index.html)), tag index pages ([eg. tags/relayd.html](https://reykfloeter.com/tags/relayd.html)), and a template for the posted articles. All these templates include common parts such as header and footer so I use my own "templating" to create the actual templates from a number of files. I never knew about sed's `/r` function to include files before I used it here, but I also think that sblg should support an include directive in templates directly. ``` .xml.html: sblg -o $@ -t templates/article.html -c $< ln -sf $(@:S/posts\///) $(@:S/.html//) .md.xml: templates/social.xml TAGS=$$(lowdown -X Tags $< | tr -d '\t'); \ TITLE=$$(lowdown -X Subject $< | tr -d '\t'); \ AUTHOR=$$(lowdown -X From $< | tr -d '\t'); \ DATE=$$(lowdown -X Date $< | tr -d '\t'); \ { echo ""; \ printf "
\n\
\n\

%s

\n\
Posted by
%s
on
\n\
\n" "$$TAGS" "$(@:S/.xml//)" "$$TITLE" "$$AUTHOR" "$$DATE" "$$DATE"; \ lowdown $< ; \ cat templates/social.xml; \ echo "
"; } | \ sed -e '2,/

/s/

/