---
title: How I set up my Gopher space
subtitle: "Documentation"
author: Seth
date: 2025-05-06 20:25
publish_date: 2025-05-06 20:25
hero_classes: text-light title-h1h2 overlay-dark-gradient hero-large parallax
hero_image: tech_gnu-solarized.png
show_sidebar: true
show_breadcrumbs: true
show_pagination: true
taxonomy:
   category: tech
   tag: [ 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](http://mixedsignals.ml/games/blog/tech_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.

<div class="mxs_attribution">
<p><a href="https://linux.pictures/" target="_blank">Solarized GNU</a>by Linux Pictures under the
<a href="https://linux.pictures/about" target="_blank">idgaf</a> license.</p>
</div>