ssg.sh: static site generator written in posix shell

⚠️ ssg.sh reads from the source directory and reads from and writes to the destination directory. it will delete any unexpected files in the destination. use with caution!

ssg.sh builds static websites by converting markdown to html, applying templates, executing site scripts, honoring ignore files, zipping content, and copying other files unchanged. on subsequent runs, it only updates what has changed.

it's a 12 kb shell script with an optional dependency on lowdown.

ssg.png actual source code of ssg.sh. feel free to fork it

install

ssg.sh works on openbsd, macos, and with minor adjustments should work on most unix-like systems.

usage

create a source directory with a markdown file and run ssg.sh:

$ mkdir src
$ echo '# hello, world!' > src/index.md
$ ssg.sh src dst
md        index.md > index.html
md        index.md > index.html.gz
c0a431cc4f351f9135d001314c846fccf3f6eea0ee8b4c332e947fc5dd224321
$

on the first run, index.md is converted to index.html and gzipped. the final line is the sha256 hash of the dst directory.

check files in dst:

$ find dst
dst
dst/.ssg.dst
dst/.ssg.src
dst/index.html
dst/index.html.gz
$

ssg.sh created dst directory and generated four files:

the html file is simply the output generated by lowdown:

$ cat dst/index.html
<h1 id="hello-world">hello, world!</h1>
$

incremental updates

if nothing changed in src and you run it again:

$ ssg.sh src dst
c0a431cc4f351f9135d001314c846fccf3f6eea0ee8b4c332e947fc5dd224321
$

it does nothing but prints the same sha256 hash.

templates

each directory may have an .ssg.template file with simplified mustache:

create the first template and re-run ssg.sh:

$ echo '<html>
<title>{{#title}}{{title}}: {{/title}}{{site}}</title>
<body>{{content}}<body>
</html>' >src/.ssg.template
$ ssg.sh src dst
template  .ssg.template
md        index.md, .ssg.template > index.html
md        index.md, .ssg.template > index.html.gz
9216cbb74e6df54892539c1c80dd2f9ee17ce13ea0922f5aef6cc9aab6712a5f
$

if html file contains html tag then no template applied. otherwise, when an html page is rendered, ssg.sh locates the nearest template in the current directory or walks up the directory tree until it reaches the src directory.

now index.html should be wrapped with the template:

$ cat dst/index.html
<html>
<title>hello, world!: src</title>
<body><h1 id="hello-world">hello, world!</h1><body>
</html>
$

see also .ssg.template.

ignore files

each directory may have an .ssg.ignore file with list of file names to ignore. directory names should end with /. wildcards are allowed.

$ mkdir src/.git
$ echo >src/.git/index
$ echo '.git/' >src/.ssg.ignore
$ ssg.sh src dst
ignore    .ssg.ignore
9216cbb74e6df54892539c1c80dd2f9ee17ce13ea0922f5aef6cc9aab6712a5f
$

same sha256 hash, no change in dst.

site scripts

any directory may contain multiple .ssg.*.sh site scripts. each script must:

see also .ssg.example.sh, .ssg.logo.sh, txt2img.sh.

other files

all other files (unless ignored) are copied verbatim, then gzipped unless they are *.png, *.jpg, *.gif, *.mp4, *.zip, or *.gz.

if a .gz file already exists alongside a file, that file is not gzipped.

collisions

ssg.sh aborts on any collision. examples include index.html and index.md in the same directory, or a script generating dst/main.css when src/main.css already exists.

sitemap

after building all pages in dst, ssg.sh creates dst/sitemap.xml unless src/sitemap.xml exists. it then creates dst/robots.txt pointing to sitemap.xml, unless src/robots.txt exists. on later runs, sitemap.xml is regenerated when pages are added or removed.

publish

to copy dst directory to the server you can use rsync, for example, see my publish.sh.

© roman zolotarevmastodonbsky