logo

Tested on OpenBSD 6.3 and 6.4

Make a static site with find(1), grep(1), and lowdown(1)

ssg is a static site generator written in shell. Optionally it converts Markdown files to HTML with lowdown(1).

Unless a page has <HTML> tag ssg3 extracts its title, wraps it with _header.html, _footer.html, and injects _styles.css, _scripts.js, _rss.html into <HEAD>.

Then copies everything (excluding .*, CVS, and _*) from src to dst directory.

ssg3 214 LoC. Enlarge, enhance, zoom!

Install

Download and chmod it:

$ mkdir -p bin
$ ftp -Vo bin/ssg3 https://www.romanzolotarev.com/bin/ssg3
ssg3       100% |*********************|    5025      00:00
$ chmod +x bin/ssg3
$ doas pkg_add lowdown
quirks-2.414 signed on 2018-03-28T14:24:37Z
lowdown-0.3.1: ok
$

lowdown(1) is optional. It’s required only if there are any *.md files.

Usage

$ mkdir src dst
$ echo '# Hello, World!' > src/index.md
$ echo '<p><a href="/">Home</a></p>' > src/_header.html
$ echo '<p>2018 Roman Zolotarev</p>' > src/_footer.html
$ ftp -Vo src/_styles.css https://www.romanzolotarev.com/style.css
style.css    100% |**************************|  1020       00:00
$ bin/ssg3 src dst 'Test' 'https://www'
./index.md
[ssg] 1 file, 1 url
$ find dst
dst
dst/.files
dst/index.html
dst/sitemap.xml
$ open dst/index.html

Markdown and HTML files

ssg3 renders Markdown files first and then HTML files. In the following example src/a.html wins:

src/a.md   -> dst/a.html
src/a.html -> dst/a.html

Favicon

Every page wrapped by ssg3 has this HTML.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="/favicon.png">

So make sure you have /favicon.png in place.

Some browsers fetch /favicon.ico despite what you specified in the <LINK> tag, so you can use an empty one (180 bytes) as a placeholder.

JavaScript

To inject JS to all pages add it to _scripts.js. I recommend to avoid JS whenever possible, though.

CSS

To inject CSS to all pages add it to _styles.css.
Use my _styles.css as a starting point.

Sitemap

ssg3 generates sitemap.xml with the list of all page. Don’t forget to add absolute URL of the sitemap to your robot.txt.
For example:

user-agent: *
sitemap: https://www.romanzolotarev.com/sitemap.xml

RSS

To generate RSS feeds use rssg, then add their URLs to _rss.xml.
For example:

<link rel="alternate" type="application/atom+xml" href="/rss.xml">

Incremental updates

On every run ssg3 saves a list of files in dst/.files and updates only newer files. If no files were modified after that, ssg3 does nothing.

$ bin/ssg3 src dst 'Test' 'https://www'
[ssg] no files, 1 url
$

To force the update delete dst/.files and re-run ssg3.

$ rm dst/.files
$ bin/ssg3 src dst 'Test' 'https://www'
index.md
[ssg] 1 file, 1 url
$

Watch

Save this helper to ~/bin/sssg. It re-runs ssg3 with entr(1) on every file change.

$ cat $HOME/bin/sssg
#!/bin/sh
while :
do
    find . -type f ! -path '*/.*' |
    entr -d "$HOME/bin/ssg3" . "$1" "$(date)" '//www'
done
$

Install entr(1):

$ doas pkg_add entr
quirks-2.414 signed on 2018-03-28T14:24:37Z
entr-4.0: ok
$

Start the helper and keep it running:

$ ~/bin/s /var/www/htdocs/www
[ssg] 1 file, 1 url

Upgrade

Previous version of ssg has been retired.

Add <HTML> tag for pages your want to be excluded from parsing. If you don’t use *.md files you can uninstall lowdown(1).

ssg2 ssg3
Wraps pages with <H1> tag. Doesn’t wrap pages with <HTML> tag.
lowdown(1) is required. lowdown(1) is optional.

Dependencies

ssg3 depends on few programs from OpenBSD base:

$ for f in $(which cat cpio date sh awk find grep printf readlink sort tee wc)
do ldd "$f"
done | awk '/\//{print$7}' | grep '.' | sort -u
/bin/cat
/bin/cpio
/bin/date
/bin/sh
/usr/bin/awk
/usr/bin/find
/usr/bin/grep
/usr/bin/printf
/usr/bin/readlink
/usr/bin/sort
/usr/bin/tee
/usr/bin/wc
/usr/lib/libc.so.92.5
/usr/lib/libm.so.10.1
/usr/lib/libutil.so.13.0
/usr/lib/libz.so.5.0
/usr/libexec/ld.so

┌─┐┌─┐┌─┐
└─┐└─┐│ ┬
└─┘└─┘└─┘

Users and forks

blog.solobsd.org
cryogenix.net
grosu.nl
high5.nl
matthewgraybosch.com
mvidal.net
openbsd.amsterdam
openbsd.space
romanzolotarev.com — obviously ;)
runbsd.info
stockersolutions.com


Thanks to Kristaps Dzonsons for lowdown(1) and Eric Radman for entr(1).