đź’» Scheduling and automating posts in a Hugo site
By goz
I’ve been working on a new little geeking site over at GozGeek.com as a way to share little geeky things I find on the internet and also write about my own geekspirations. Previously I would have created the site in Wordpress, but I wanted to learn Hugo so that’s what I used. One feature I use a lot in Wordpress is the scheduling of publishing posts, so I had to figure out how to do this in Hugo.
When I share, I don’t like to do a massive dump at once. With GozGeek.com I use two scripts to schedule posts, autoschedgg
and schedgg
. These two scripts add the posts to my website directory and sets the date and time in the future. The publish.sh
script runs every 30 minutes, rebuilding the site and publishing an articles that are scheduled.
The scripts work for me and my setup. If they work out of the box for you then we must be kindred spirits and I’m afraid for you.
Publish script - publish.sh
First task to complete is to write a publish script. This is the script that will build the site and then upload it to my web host. I’m using Dreamhost shared hosting, so I’m able to use rsync to upload the new site. The publish.sh
script assumes you already have Hugo and your site set up and published.
#!/bin/bash
if ! command -v /snap/bin/hugo &> /dev/null
then
echo "Hugo not found. Install with:"
echo "sudo snap install hugo"
exit
fi
# Go to the directory with the website
cd /home/goz/Web/gozgeek.com/
# We're building the site from scratch, clear out the old site
# If this is the first time we're building the site, then make
# public directory to store the site
echo "Clear out public.."
[[ -d "public" ]] || mkdir "public"
rm -r public/*
echo "Building site..."
HUGO_ENV=production /snap/bin/hugo
# I use the build date for troubleshooting, makes it easy to see when the site was last built and from what host
echo "Adding build date..."
date > public/built.txt
hostname >> public/built.txt
# Upload the site. I already have ssh keys set up and an alias for the host in ~/.ssh/config
echo "Uploading site..."
rsync -e ssh -avp --progress --delete public/ gozgeek:gozgeek.com/
No big surprises there.
Post format
The Hugo front matter I use is yaml, and the top of a post may look like this:
---
Title: My Great Title
Author: goz
date: 2021-04-16T13:56:00-04:00
Category:
Tags:
- Asides
---
When I schedule a post, the script will re-write the date:
line with the scheduled date and time.
Schedule publish.sh
The site is published every 30 minutes from in a cron job from 8am until 5pm.
01,31 8-17 * * * /home/goz/Web/gozgeek.com/publish.sh >> /home/goz/log/gozgeek.com.log 2>&1
Output is saved in the gozgeek.com.log file for troubleshooting issues.
We don’t have to worry about dates and times of future posts because Hugo won’t publish a post until the post date is in the past. This means I can add all of my posts in advance, and when the publish script is ran, Hugo won’t add posts dated in the future.
autoschedgg
The autoschedgg takes a filename as a parameter. It will then look in the posts directory for the very last dated file. After grabbing the date, it will increment it by one and then schedule the post for this new date. The script also picks a random hour and minute to post.
#!/bin/bash
# Where the posts lie, in a folder named after the current year
postsdir="${HOME}/Web/gozgeek.com/content/posts/$(date +"%Y")"
# After scheduling the post, where should I stash the file
archive="${HOME}/notes/Archive/$(date +"%Y")"
# Pick a random time
RANDOM=$(date +%s%N | cut -b10-19)
h=$(( $RANDOM % 8 + 8 ))
m=$(( $RANDOM % 59 + 0 ))
# All filenames of posts start with the date in YYYY-MM-DD format
# Get a directory listing, sort it, and get the first entry
d=$(find ${postsdir} | xargs -I {} basename "{}" | sort -r | head -1)
# Take the first entry, and use the date command to add a day
day=$(date -d "${d:0:10} + 1 day" +"%Y-%m-%d")
# Set the variable filename to the 1st parameter passed to the script
filename="${1}"
# If the filename starts with a date (sometimes they do, depending on how I named it)
# remove the date. This is a pretty dumb way of doing it because if the file starts
# with 2 for any reason, it's going to remove the first 11 characters of the filename.
# Since this is for myself, I can make sure I never do that. :-)
if [[ ${filename:0:1} == "2" ]]; then
outfilename="${day}-${filename:11}"
else
outfilename="${day}-${filename}"
fi
# This is the date and time in a format that works for Hugo
schedule="${day}T$(printf "%02d" ${h}):$(printf "%02d" ${m}):00-04:00"
# Put the file in the right place
cp "${filename}" "${postsdir}/${outfilename}"
# Add new schedule date and time
# Replaces the current Date: line with a Date: line and the scheduled date and time
sed -i "s/^Date: .*$/date: ${schedule}/" "${postsdir}/${outfilename}"
# Remove Status
# Depending on the file, it may have a Status: line
sed -i "/^Status: .*$/d" "${postsdir}/${outfilename}"
# Archive post
# My posts are in a git repo with my notes
git add "${filename}"
git mv "${filename}" "${archive}/${outfilename}"
echo "${filename} scheduled for ${day}!"
This script will break if you auto schedule January posts in December. Well, it won’t quite break, it will just put the posts in the folder for the wrong year. I’m not too worried, that’s 8 months from now.
schedgg
Sometimes I want to add another post to a day that’s already scheduled. That’s where schedgg
comes in to play. It takes two parameters. The first is the date to schedule and the second is the filename of the post. At somepoint I’ll merge autoschedgg
with this script so I only have to maintain one script.
#!/bin/bash
# Where the posts lie, in a folder named after the current year
postsdir="${HOME}/Web/gozgeek.com/content/posts/$(date +"%Y")"
# After scheduling the post, where should I stash the file
archive="${HOME}/notes/Archive/$(date +"%Y")"
# Pick a random time
RANDOM=$(date +%s%N | cut -b10-19)
h=$(( $RANDOM % 8 + 8 ))
m=$(( $RANDOM % 59 + 0 ))
# Get the post date from the first argument
day="${1}"
# Get the filename as the second argument
filename="${2}"
# If the filename starts with a date (sometimes they do, depending on how I named it)
# remove the date. This is a pretty dumb way of doing it because if the file starts
# with 2 for any reason, it's going to remove the first 11 characters of the filename.
# Since this is for myself, I can make sure I never do that. :-)
if [[ ${filename:0:1} == "2" ]]; then
outfilename="${day}-${filename:11}"
else
outfilename="${day}-${filename}"
fi
# This is the date and time in a format that works for Hugo
schedule="${day}T$(printf "%02d" ${h}):$(printf "%02d" ${m}):00-04:00"
# Put the file in the right place
cp "${filename}" "${postsdir}/${outfilename}"
# Add new schedule date and time
# Replaces the current Date: line with a Date: line and the scheduled date and time
sed -i "s/^Date: .*$/date: ${schedule}/" "${postsdir}/${outfilename}"
# Remove Status
# Depending on the file, it may have a Status: line
sed -i "/^Status: .*$/d" "${postsdir}/${outfilename}"
# Archive post
# My posts are in a git repo with my notes
git add "${filename}"
git mv "${filename}" "${archive}/${outfilename}"
echo "${filename} scheduled!"