link to home page with author's photo as a thumbnail/avatar

Building a Website with a Shell Script

I’ve built a lot of websites over the years, and every time I’ve been dissatisfied because of the complexity of the tools I used at the time. It didn’t matter if it was a content management system like WordPress, a static site generator like Jekyll, or even something like Org mode’s publishing functionality.

Nothing was ever quite good enough.

This latest incarnation of my website puts me in a rather difficult position. I built it using shell scripts developed by Roman Zolotarev:

If I’m not satisfied with these, the only way to simplify further is to do everything by hand. I don’t really have time for that.

ssg4 is easy enough to use, but it’s a simple script for creating simple websites. If your directory structure looks something like this, you shouldn’t have a lot of trouble.

index.md
_header.html
_footer.html
favicon.png
pages/
    about.md
    contact.md
blog/
    i-want-my-bsd.md
    matthew-graybosch-cant-write.md
    iron-maiden-rules-ok.md

You can link to your posts from your main index.md page using a format like the following:

- [Iron Maiden Rules, OK?](blog/iron-maiden-rules-ok.html "2019-01-03")
- [Matthew Graybosch Can't Write Worth a Damn](blog/matthew-graybosch-cant-write.html "2019-01-02")
- [I Want My BSD](blog/i-want-my-bsd.html "2019-01-01")

As long as your directory structure is relatively simple, you only want to use one set of _header.html and _footer.html files, and you have an absolute link to your RSS feed in your index.md file, ssg4 and rssg should do the right thing. You would run rssg first, and then ssg4.

However, my site is a little more complex. I want blog posts to have a “more like this” link, but that link doesn’t make sense on a category index, or the master blog index. That means needing multiple sets of _header.html and _footer.html files. But since ssg4 is a relatively simple script, it can’t tell which set of HTML wrapper files it should use. To get around this, I need to put index pages under a separate subdirectory and use multiple ssg4 calls. And since I have multiple categories, that would require multiple rssg calls. And I haven’t even gotten to uploading a site yet. In my case that’s a third command: rsync.

Basically, I need a shell script for my shell scripts. I’ve got one, of course, and it isn’t terribly elegant.

#!/usr/bin/env sh

# Copyright 2019 Matthew Graybosch <howdy@matthewgraybosch.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

set -e

# Common variables for all ssg4 calls.
SITE_TITLE='matthewgraybosch.com'
SITE_PREFIX='https://'

# The reason I have four different source directories is that I can't
# do custom navigation with a single ssg4 call. So I'll make multiple
# calls. It doesn't take that much longer, even though I have to force
# ssg4 to build everything from scratch.
HOME_SRC_DIR=$HOME/sr.ht/website/home/
PAGES_SRC_DIR=$HOME/sr.ht/website/pages/
ERRORS_SRC_DIR=$HOME/sr.ht/website/errors/
INDEXES_SRC_DIR=$HOME/sr.ht/website/indexes/
DEST_DIR=$HOME/public_html/matthewgraybosch.com/

# We need to copy index files over to the indexes directory so that
# they get the right navigation headers.
cp $HOME_SRC_DIR/.htaccess $DEST_DIR/
cp $PAGES_SRC_DIR/blog/index.md $INDEXES_SRC_DIR/blog/index.md
cp $PAGES_SRC_DIR/blog/books/index.md $INDEXES_SRC_DIR/blog/books/index.md
cp $PAGES_SRC_DIR/blog/bullshit/index.md $INDEXES_SRC_DIR/blog/bullshit/index.md
cp $PAGES_SRC_DIR/blog/games/index.md $INDEXES_SRC_DIR/blog/games/index.md
cp $PAGES_SRC_DIR/blog/music/index.md $INDEXES_SRC_DIR/blog/music/index.md
cp $PAGES_SRC_DIR/blog/tech/index.md $INDEXES_SRC_DIR/blog/tech/index.md
cp $PAGES_SRC_DIR/blog/travel/index.md $INDEXES_SRC_DIR/blog/travel/index.md
cp $PAGES_SRC_DIR/blog/video/index.md $INDEXES_SRC_DIR/blog/video/index.md
cp $PAGES_SRC_DIR/blog/writing/index.md $INDEXES_SRC_DIR/blog/writing/index.md
cp $PAGES_SRC_DIR/blog/web/index.md $INDEXES_SRC_DIR/blog/web/index.md
cp $PAGES_SRC_DIR/newsletter/index.md $INDEXES_SRC_DIR/newsletter/index.md
cp $PAGES_SRC_DIR/novels/index.md $INDEXES_SRC_DIR/novels/index.md
cp $PAGES_SRC_DIR/stories/index.md $INDEXES_SRC_DIR/stories/index.md

# Defining these so I can use them with rssg.
MASTER_DIR=$PAGES_SRC_DIR/blog/
BOOKS_DIR=$PAGES_SRC_DIR/blog/books/
BULLSHIT_DIR=$PAGES_SRC_DIR/blog/bullshit/
GAMES_DIR=$PAGES_SRC_DIR/blog/games/
MUSIC_DIR=$PAGES_SRC_DIR/blog/music/
TECH_DIR=$PAGES_SRC_DIR/blog/tech/
TRAVEL_DIR=$PAGES_SRC_DIR/blog/travel/
VIDEO_DIR=$PAGES_SRC_DIR/blog/video/
WRITING_DIR=$PAGES_SRC_DIR/blog/writing/
WEB_DIR=$PAGES_SRC_DIR/blog/web/
NEWSLETTER_DIR=$PAGES_SRC_DIR/newsletter/

# One feed is never enough. We gotta have per-category feeds.
rssg $MASTER_DIR/index.md > $MASTER_DIR/rss.xml
rssg $BOOKS_DIR/index.md > $BOOKS_DIR/rss.xmlr
rssg $BULLSHIT_DIR/index.md > $BULLSHIT_DIR/rss.xml
rssg $GAMES_DIR/index.md > $GAMES_DIR/rss.xml
rssg $MUSIC_DIR/index.md > $MUSIC_DIR/rss.xml
rssg $TECH_DIR/index.md > $TECH_DIR/rss.xml
rssg $TRAVEL_DIR/index.md > $TRAVEL_DIR/rss.xml
rssg $VIDEO_DIR/index.md > $VIDEO_DIR/rss.xml
rssg $WRITING_DIR/index.md > $WRITING_DIR/rss.xml
rssg $WEB_DIR/index.md > $WEB_DIR/rss.xml
rssg $NEWSLETTER_DIR/index.md > $NEWSLETTER_DIR/rss.xml

# We've built the feeds. Now we'll build the site.
rm $DEST_DIR/.files; ssg4 $HOME_SRC_DIR $DEST_DIR $SITE_TITLE $SITE_PREFIX
rm $DEST_DIR/.files; ssg4 $PAGES_SRC_DIR $DEST_DIR $SITE_TITLE $SITE_PREFIX
rm $DEST_DIR/.files; ssg4 $ERRORS_SRC_DIR $DEST_DIR $SITE_TITLE $SITE_PREFIX
rm $DEST_DIR/.files; ssg4 $INDEXES_SRC_DIR $DEST_DIR $SITE_TITLE $SITE_PREFIX

# Time to upload 
SSH_HOST=redacted
SSH_PORT=22
SSH_USER=redacted
SSH_TARGET_DIR=redacted
rsync -e "ssh -p ${SSH_PORT}" -P -rvzc --delete $DEST_DIR/ $SSH_USER@$SSH_HOST:$SSH_TARGET_DIR --cvs-exclude

exit 0

This was a bit long, but I thought it might be useful to reproduce my build script in its entirety. You’re welcome to copy it and use it yourself. You’re also welcome to right-click on this page and select “view source”. If you do so, you should be able to figure out how to build a website for yourself. There’s also the git repository for my site, but that doesn’t include the “sitegen” shell script I reproduced above.

Above all, have fun.