Using Hugo, Atom, GitHub and Render

12 Jan 2021

Table of Contents


Introduction

I used to use Blogger, but recently moved over to Hugo, so I could use Markdown for writing posts. This is much easier for me, in terms of writing notes, formatting code, embedding links and images, etc.

But this process does not have much built-in support for drafting and publishing posts, so there is some extra work needed, which I used to get out-of-the-box with Blogger.

Here is the process:

Hugo Archetype

In Hugo, I have the following post.md archetype:

---
title: "{{ getenv "POST_TITLE" }}"
date: {{ .Date }}
lastmod: {{ .Date }}
draft: true
toc: false
tags: [tbc]
summary: "tbc"
---

tbc

The title is loaded from the POST_TITLE environment variable (more about that below).

By default, documents created from this archetype are set to draft. These draft documents are not included in the public site. I have to edit the headers to change a document from draft to public.

New Document

To create a new document I have a very basic Python script. This allows me to type in a new title for the new post. It assigns the title string to the POST_TITLE environment variable, generates a file name from the title, and then creates a new Markdown document.

Finally, it opens the document in Atom.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import string
import os
import re

title = input("\nEnter Title: ")
title = title.strip()
name = title
# remove punctuation and replace spaces with dashes:
name = name.translate(str.maketrans('', '', string.punctuation)).replace(" ", "-").lower()
# check name length - trim if needed:
name = (name[:35]) if len(name) > 35 else name
name = name.strip()
# replace 2 or more dashes in a row with one dash and make sure the string
# does not start or end with dashes. Add markdown file extension:
name = re.sub(r"--+", "-", name).lstrip("-").rstrip("-") + ".md"

title = "[draft] " + title
print("Title: " + title)
print("Name:  " + name)
input("\nHit enter to continue...")

os.chdir('Hugo/Sites/northcoder.org')
os.environ["POST_TITLE"] = title
os.system('hugo new post/' + name)
input("\nHit enter to continue...")
atom = 'my/path/to/AppData/Local/atom/atom.exe'
docDir = 'my/path/to/Hugo/Sites/northcoder.org/content/post/'
os.system(atom + ' ' + docDir + name)

In Atom, I hit Ctrl-Shift-M to activate the Markdown Preview side-by-side view of the document. For some reason I have to do this twice (or just start typing something) as I always get an error message, the first time I try to open the Markdown Preview window:

Previewing Markdown failed - html.trim is not a function

I think there is some cross-package interference happening here…

Search Data

My site includes a custom search page. To support this, I generate two JSON files by scanning the Markdown files after each content change (i.e. an edited page or any new page).

I use a small Java program for this.

The two files are saved into the Hugo static folder. They are accessed by the search function to show matching pages based on the provided search text. Stop-words are removed from the indexing process.

This is a fairly crude approach, but results in fast - if basic - searching.

The two JSON files are shown below.

Page Index

One entry per page:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "1": {
    "date": "2013-11-15T19:39:03-04:00",
    "draft": "false",
    "title": "AdFind - the Command Line Active Directory Query Tool",
    "pageName": "adfind-the-command-line-active-directo"
  },
  "2": {
    "date": "2019-12-07T14:08:00.000-05:00",
    "draft": "false",
    "title": "AWS Cognito - Signing Up and Signing In",
    "pageName": "aws-cognito-signing-up-and-signing-in"
  },
  ...
}

Word Index

An array of page IDs for each indexed word:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "inspect": [
    48
  ],
  "half": [
    84,
    21
  ],
  "upload": [
    94
  ],
  "tokenization": [
    74
  ],
  ...
}

GitHub and Render

Changes to content are pushed to a GitHub repository.

I then use Render as my static site host. My Render account has a watcher to detect changes to the GitHub repository, which then triggers a rebuild of the Hugo site. For managing one static site, Render does not charge me any fee.

The overall process has more steps than the old Blogger approach, but I find the ability to use Markdown is worth those extra steps.