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 10 kb shell script with an optional dependency on lowdown.

ssg.png actual source code of ssg.sh

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>
$

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.sitemap.sh, .ssg.logo.sh, .ssg.example.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.

© roman zolotarev