Publishing a Website from Emacs and Hugo
Introduction
After 5 years, it’s time to give the site a bit of a refresh, now with fewer images and more words. Previously I used bootstrap plus a bit of manual editing. This time I’ll be using a pipeline of Emacs org-mode -> ox-hugo -> hugo -> nearlyfreespeech.net. This post will self-document my steps to get all that up and running. The last time I did any web-related things was over 5 years ago, and I wasn’t an expert then, so these steps should be taken with a grain of salt.
Hugo Setup
sudo snap install hugo
mkdir petercheng && cd petercheng
hugo new site petercheng
Emacs init:
(use-package ox-hugo
:ensure t
:after ox)
Set up a theme (I’m using the hyde-hyde theme)
git submodule add https://github.com/htr3n/hyde-hyde.git themes/hyde-hyde
config.toml
For my intended setup, there are only 2 files I’ll be working with. The first one is config.toml
, which stores global hugo settings, as well as parameters for my chosen theme. I’m not really sure how to find all the toggle-able parameters for a given theme besides digging through the theme code or looking at example sites.
As an early example of why I’m using org-mode, I can directly insert a live copy of my config.toml
file below, simply by including the line:
#+INCLUDE: "config.toml" src ini
baseURL = "https://petercheng.net/"
title = "Peter Cheng"
theme = "etch"
languageCode = "en-US"
enableInlineShortcodes = true
pygmentsCodeFences = true
pygmentsUseClasses = true
[params]
description = "Peter Cheng's Website"
dark = "auto"
highlight = true
[menu]
[[menu.main]]
identifier = "posts"
name = "posts"
url = "/"
weight = 10
[[menu.main]]
identifier = "about"
name = "about"
url = "/about/"
weight = 20
[permalinks]
posts = "/:title/"
[markup.goldmark.render]
# Allows HTML in Markdown
unsafe = true
One early roadblock I hit was that hyde-hyde uses highlight.js for syntax highlighting, which does not contain emacs-lisp
as a language option, unlike org-mode and chroma (hugo’s default syntax highlighter). I’m currently using lisp
as a compromise, and it took me a while to realize that highlightjslanguages needed to be set to include non-default languages in highlight.js. If an unsupported (or empty!) language is passed to highlight.js, at least with hyde-hyde, it results in poorly formatted output, which led to much confusion for a while.
petercheng.org
The other file I need to create is the org file that generates all this content, on every page, following ox-hugo’s single-page architecture. In normal Hugo, individual pages written in markdown (or now in org-mode) are placed inside the content
directory inside the project root. With ox-hugo, a single org-mode file can be used to generate all pages, posts, and any other content. This has some advantages in allowing usage of org-mode functionality, as well as re-use of content or property settings across pages.
There’s a number of hugo properties that can be set within the file, but the only required one is HUGO_BASE_DIR
, which specifies the root directory of the hugo website, relative to the org file.
#+HUGO_BASE_DIR: ./
Afterwards, I have 2 top-level sections in my org file, Pages
, and Posts
. Any properties set under a section will be applied to subsections, so I have the following properties set for each, to place pages at the top level of my exported files, and posts within a subdirectory.
* Pages
:PROPERTIES:
:EXPORT_HUGO_SECTION: ./
:END:
* Posts
:PROPERTIES:
:EXPORT_HUGO_SECTION: posts
:END:
I can then create pages or posts by creating subsections within the relevant section. The EXPORT_FILE_NAME
property is required to be set for each, which determines the exported filename. Here’s an example of the properties setting for this current post.
** Publishing a Website from Emacs and Hugo
:PROPERTIES:
:EXPORT_FILE_NAME: website-v2-setup
:EXPORT_DATE: 2018-06-04
:END:
Exporting
Ox-hugo adds a new export option to the org-mode export menu. (C-c C-e)
by default. There’s a few options for exporting, but currently I find it simplest just to always export all content, with (C-c C-e H A)
. One setting I’ve seen used a lot is #+HUGO_AUTO_SET_LASTMOD: t
, and that doesn’t play nicely if always updating all files. But I don’t feel a need to track and update dates on every edit.
After exporting, markdown files should be created in the content directory, and hugo will auto-reload pages if already running (to start hugo, run hugo server
from the base directory).
Getting Online
There are some fancy options for deploying, such as this guide, which demonstrates hugo publishing on a remote server, triggered by git post-receive. For the time being I’m going to keep thing simple, and simply use a script to generate a static site, which I’ll keep synced up via rsync. A final example of showing a live code view of my publishing script:
#!/usr/bin/env bash
set -eux
username=$1
server=ssh.phx.nearlyfreespeech.net
rm -rf public
hugo
# rsync is problematic on WSL (https://github.com/microsoft/WSL/issues/2138)
# --whole-file is one workaround
# Not being able to use -z is another limitation
rsync -avh --whole-file --progress --delete public/ "$username@$server:"