Adding a Timestamp to Hugo Post Filenames
The DocSpring blog is powered by Hugo, which is an awesome static site generator.
Update from April 2020: We migrated our blog to Ghost! Hugo is great, but we wanted to have a proper CMS where we could edit and publish blog posts.
Hugo is written with Go, so it’s incredibly fast. Generating the static pages for our blog only takes 139 ms.
When you create a new blog post, the convention is to use <slug>.md
:
This means that your posts will be sorted by alphabetical order in your text editor (I use VS Code). I wanted to sort my posts by date so that it would be easier to find recent posts.
To do this, I’ve written a new_post
script that adds a timestamp to the beginning of the filename. (It also opens the URL in my browser, opens the file in my text editor, and starts the hugo server if it’s not already running.)
#!/bin/bash
set -e
cd blog
POST_SLUG="$1"
if [ -z "$POST_SLUG" ]; then
read -p "Post Name (e.g. your-new-post): " POST_SLUG
fi
TIMESTAMP=`date +%Y%m%d%H%M%S`
POST_FILENAME="${TIMESTAMP}-${POST_SLUG}.md"
hugo new "posts/${POST_FILENAME}"
(
sleep 0.2 &&
echo "Opening localhost:1313/blog/posts/${POST_SLUG}/" &&
open "http://localhost:1313/blog/posts/${POST_SLUG}/"
) &
(
sleep 0.5 &&
echo "Opening blog/content/posts/${POST_FILENAME}" &&
open "content/posts/${POST_FILENAME}" &
) &
pgrep -x hugo > /dev/null && echo "Hugo is already running!" || hugo server -D
sleep 1
Running ./new_post test-post
will generate a file at: ./blog/content/posts/20180924162015-test-post.md
.
This gets us most of the way, but the default post title will be “20180924162015 Test Post”, and the URL will be: /blog/posts/20180924162015-test-post
. I just want the timestamp in the filename, and I don’t want to include it in the title or the URL.
I updated my posts archetype (a.k.a. template) to strip the timestamp using the replace regex function (replaceRE
.) Here’s my new blog/archetypes/posts.md
:
---
title: "{{ .TranslationBaseName | replaceRE "^[0-9]{14}-" "" | replaceRE "-" " " | title }}"
slug: {{ .TranslationBaseName | replaceRE "^[0-9]{14}-" "" }}
date: {{ .Date }}
draft: false
---
Hugo will generate a new post with the following contents:
title: "Test Post"
slug: test-post
date: 2018-09-24T16:20:15+07:00
draft: false
---
Setting the slug
means that the post URL will be: /blog/posts/test-post
I also ran a Bash script to rename all of my existing posts and set the original slug
:
for POST in *.md; do
TIMESTAMP=$(ruby -r yaml -e \
"puts YAML.load_file('$POST')['date'].strftime('%Y%m%d%H%M%S')")
! [[ $POST =~ ^[0-9]{14} ]] \
&& perl -i -p0e "s/(---\n\n)/slug: ${POST/.md/}\n\1/s" $POST \
&& mv $POST $TIMESTAMP-$POST
done
Now my post filenames have timestamps, so they’re sorted by date and I can find the most recent post at the bottom:
Another little advantage of setting the slug is that it’s easier to copy/paste it. This helps when I’m creating links in different posts.
Note: I’m using the latest version of hugo (
v0.48
). Earlier versions of hugo may not have thereplaceRE
function.