⚠️ 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.
actual source code of ssg.sh. feel free to
fork it
ssg.sh works on openbsd, macos, and with minor
adjustments should work on any unix-like system.
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:
.ssg.dst contains hashes for all files in dst,.ssg.src—for files in src,index.html generated from index.md,index.html.gz—its gzipped version, useful for
httpd.sitemap.xml with one url /robots.txt with the link to the sitemapthe html file is simply the output generated by lowdown:
$ cat dst/index.html <h1 id="hello-world">hello, world!</h1> $
if nothing changed in src and you run it again:
$ ssg.sh src dst b32c4ec2134507da10e3139f6b42e2543dec5e44f9ca253093cf71477fcf356d $
it does nothing but prints the same sha256 hash.
each directory may have an .ssg.template file with simplified mustache:
{{title}} extracted from <h1> tag of a page,{{site}} is dirname of src,{{content}} is the content of a page,{{#title}}...{{/title}} renders its content only when title is defined.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.
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.
any directory may contain multiple .ssg.*.sh site scripts. each script must:
src and the second as dst,see also .ssg.example.sh, .ssg.logo.sh, txt2img.sh.
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.
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.
to copy dst directory to the server you can use rsync, for example, see my
publish.sh.