ssg: static site generator

⚠️ 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 any unix-like system.

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
sitemap   sitemap.xml
sitemap   sitemap.xml.gz
sitemap   robots.txt
sitemap   robots.txt.gz
b32c4ec2134507da10e3139f6b42e2543dec5e44f9ca253093cf71477fcf356d
$

on the first run, index.md is converted to index.html and gzipped.

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.

the final line is the sha256 hash of the dst directory.

check files in dst:

$ find dst | sort
dst
dst/.ssg.dst
dst/.ssg.src
dst/index.html
dst/index.html.gz
dst/robots.txt
dst/robots.txt.gz
dst/sitemap.xml
dst/sitemap.xml.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
b32c4ec2134507da10e3139f6b42e2543dec5e44f9ca253093cf71477fcf356d
$

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

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

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 *.asc, *.css, *.js, *.json *.sh, *.svg, *.txt, and *.xml files get gzipped, unless a .gz file already exists alongside a file.

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.

publish

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

© roman zolotarevmastodonbsky