How I set up my Gopher space

Documentation

tech unix hack cyberpunk

Setting up a Gopher site isn't terribly difficult, but I wanted a setup that allowed my to manage my posts using Git and to essentially mirror what I have on the web. I'm documenting my solution here mostly for my own future reference.

Here's the workflow.

  1. On the Gopher server, add a bare Git repo and create a Git hook to copy anything pushed to the repository into a separate staging directory. Also create the staging directory, and a blog directory (where posts are stored when they go live ).
  2. Locally, add the Gopher server as a Git remote.
  3. Create a date directory (where the files are linked to, by date).
  4. One time only: Add all historical posts to the blog directory.
  5. One time only: Sort all posts by date and generate a master gophermap in the date directory
  6. Create a cron job to check the blog directory for any post with a publish_date of today, and copy that post to the blog directory, and add it to the date gophermap

1. Setup Gopher directories on server

On the Gopher server, add a bare Git repo to receive posts you push through Git:

$ mkdir /my/gopher/dir/blog.git
$ cd !$
$ git init --bare .

Create a staging directory as a destination for posts received, but waiting to go live:

$ mkdir /my/gopher/dir/staging

Create a directory for live posts. This is the directory users actually get to see.

$ mkdir /my/gopher/dir/blog

Edit /my/gopher/dir/blog.git/hooks/post-receive to create a Git hook to copy received posts to the staging directory:

#!/usr/bin/bash
# GNU All-Permissive License
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.  This file is offered as-is,
# without any warranty.

WEB_DIR="/my/gopher/dir/staging"

while read oldrev newrev refname
do
    BR=`git rev-parse --symbolic --abbrev-ref $refname`
    if [ "$BR" == "master" ]; then
        export GIT_DIR="$WEB_DIR/.git"
        pushd $WEB_DIR > /dev/null
        git pull
        popd > /dev/null
    fi
done

Make it executable so that Git executes it whene a post is received:

$ chmod +x /my/gopher/dir/blog.git/hooks/post-receive

2. Configure local Git

Next, add the Gopher server as a Git remote.

$ git remote add gopher example.com:/my/gopher/dir

Now you can push to your remote Gopher server:

$ git push gopher HEAD

3. Create date directory

On the Gopher server, create a date directory as a location to link to all blog posts in order of date.

4. Add historical posts

Because this was my first time setting this up, I had to copy all extant posts to my Gopher server. I did this by pushing my Git HEAD to gopher, and then logging into the server and copying all item.md files in all subdirectories to new files named for the subdirectory. I can't recall the exact command I used for this, but it would have been something to rename each file based the dirname of the basename.

5. Generate a master gophermap in the date directory

Once again, only because I was starting from nothing I had to sort all posts by the publish_date listed in the header of each post, and generate a gophermap file in the date directory.

#!/usr/bin/bash
# GNU All-Permissive License
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.  This file is offered as-is,
# without any warranty.

DIR_BASE=/my/gopher
DIR_TOP=dir

DIR_DATE=date
DIR_LIVE=blog
DIR_STAGING=staging

# start fresh
rm "$DIR_BASE"/"$DIR_TOP"/"$DIR_DATE"/gophermap

for POST in `find "$DIR_BASE"/"$DIR_TOP"/"$DIR_STAGING" -type f -name "item.md"`;
do 
POSTDIR=`dirname "$POST"`
DATE=`grep '_date:' "$POST" | cut -f2 -d' '`
echo -e 0"$DATE" `basename $POSTDIR`'\t'"$DIR_TOP"/"$DIR_LIVE"/`basename $POSTDIR`.txt >> "$DIR_BASE"/"$DIR_TOP"/"$DIR_DATE"/gophermap
done

6. Create a cron job and publish script

Create the script to check for posts in staging that need to be published on the current date. This is the script that makes a post go live. It could be more robust than it is. For example, currently it only checks for a post with a publish_date of exactly today, but it would probably be better to post anything with a date of today or earlier.

#!/usr/bin/bash
# GNU All-Permissive License
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.  This file is offered as-is,
# without any warranty.

SED=/usr/bin/sed
DIR_BASE=/my/gopher
DIR_TOP=dir
DIR_LIVE=blog
DIR_STAGING=staging

DATE=${DATE:-`date --rfc-3339=date`}

for POST in `find "$DIR_BASE"/"$DIR_TOP"/"$DIR_STAGING" -type f -name "item.md" -exec grep -Hl "$DATE" {} \;`;
do 
POSTDIR=`dirname "$POST"`
cp "$POST" "$DIR_BASE"/"$DIR_TOP"/"$DIR_LIVE"/`basename $POSTDIR`.txt

echo Found $POST
echo On $DATE

echo -e 0Latest'\t'../"$DIR_LIVE"/`basename $POSTDIR`.txt > /tmp/klaatu-blog-updater.tmp || echo "tmp file creation failed"
echo -e 0"$DATE" `basename $POSTDIR`'\t'../"$DIR_LIVE"/`basename $POSTDIR`.txt >> /tmp/blog-updater.tmp || echo "tmp file creation failed"

"${SED}" -i "/0Latest/ r /tmp/blog-updater.tmp" "$DIR_BASE"/date/gophermap || echo "failed"
"${SED}" -i '0,/0Latest/{/0Latest/d;}' "$DIR_BASE"/"$DIR_TOP"/date/gophermap

rm /tmp/blog-updater.tmp
done

Now create a cron job to run the script at midnight every night. Open your crontab using crontab -e and then add a line like this:

0 0 * * * /home/tux/bin/copy-post-to-blog.sh

Test it out

You can create a fake blog entry and then run copy-post-to-blog.sh to test the process. Then check back regularly to ensure your script is updating the date gophermap.

As I've said in a previous post, I don't love Gopher for what it is, I appreciate that it is not www. Using some open source software and a little ingenuity (if it can be called that), Gopher is relatively easy to automate. It may not be anywhere near as robust as HTML and HTTP, but it's nice to contribute content to a place where it's appreciated.

Solarized GNUby Linux Pictures under the idgaf license.

Previous Post