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 ssg4 extracts its title from <H1> tag, wraps the page with _header.html, _footer.html.

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

ssg4 180 LoC. Enlarge, enhance, zoom!

Install

Download and chmod it:

$ mkdir -p bin
$ ftp -Vo bin/ssg4 https://www.romanzolotarev.com/bin/ssg4
ssg4       100% |*********************|    4916      00:00
$ chmod +x bin/ssg4
$ 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
$ ftp -Vo src/_header.html https://www.romanzolotarev.com/raw/_header.html
_header.html 100% |**************************|  3362       00:00
$ ftp -Vo src/_footer.html https://www.romanzolotarev.com/raw/_footer.html
_header.html 100% |**************************|   727       00:00
$ ftp -Vo src/favicon.png https://www.romanzolotarev.com/raw/favicon.png
favicon.png  100% |**************************|   408       00:00
$ bin/ssg4 src dst 'Test' 'http://www'
./index.md
./favicon.png
[ssg] 2 files, 1 url
$ find dst
dst
dst/.files
dst/index.html
dst/favicon.png
dst/sitemap.xml
$ open dst/index.html

Markdown and HTML files

ssg4 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

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.

Sitemap

ssg4 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 _header.html.
For example:

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

Incremental updates

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

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

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

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

Watch

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

$ cat $HOME/bin/sssg
#!/bin/sh
while :
do
    find . -type f ! -path '*/.*' |
    entr -d "$HOME/bin/ssg4" . "$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 <!DOCTYPE html>, <STYLE>...</STYLE> with your styles and an empty <TITLE></TITLE> tags to _header.html.

ssg4 captures page's title from the first <H1> tag of the page and inject it into <TITLE>, if it's present and empty.

Move _rss.html to _header.html, _styles.css to <STYLE> tag in _header.html, and _scripts.js to <SCRIPT> tag.

ssg3 ssg4
Builds 1,730 files in 8.54s in 5.43s
 
Contains basic HTML tags. Contains no HTML tags.
wc(1) is required. Doesn't use wc(1).
 
List of feeds read from _rss.html, _rss.html,
styles from _styles.css, and _styles.css, and
scripts from _scripts.js. _scripts.js have been removed.

Dependencies

ssg4 depends on few programs from OpenBSD base:

$ for f in $(which cat cpio date sh awk find grep printf readlink sort tee)
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/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

blog.solobsd.org
bloguslibrus.fr
bsdjobs.com
cryogenix.net
dethronedemperor.com
grosu.nl
h3artbl33d.nl
high5.nl
matthewgraybosch.com
mvidal.net
openbsd.amsterdam
openbsd.space
romanzolotarev.com — obviously ;)
runbsd.info
starbreakersaga.com
stockersolutions.com


Thanks to Devin Teske for helping with awk(1), Kristaps Dzonsons for lowdown(1), and Eric Radman for entr(1).


© 2008–2019 Roman Zolotarev  User Agreement  Privacy Policy  Newsletter