--- 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.

Solarized GNUby Linux Pictures under the idgaf license.