Blog Migration Notes

Hopefully you can’t notice it much, but I recently ported this blog from Pelican to Hugo.

There were a few reasons driving this:

Switching themes

The old blog was using the Pelican flex theme.

At first, I tried using the Hugo flex theme, but it didn’t support that sweet left sidebar, so I went with the Hugo hyde theme instead and overrode the layout and CSS until it looked as close as possible to the old blog.

This is another area where if I had spent more time looking for themes I probably would have found one closer to what I was already using. But tweaking the hyde theme gave me a excuse to realize how much I still dislike working with CSS.

Go templates are so ugly

Tweaking the theme layouts was my first exposure to Go templates. I get that they’re popular since they’re in the Go standard library, and they’re designed for speed, but after working with Jinja for many years I didn’t find them easy to work worth. Some weirdness I ran into:

Actual post migration

I wrote a quick and dirty script to port the Pelican content over to Hugo. The script extracted the Pelican-style meta data, then called pandoc to convert the reStructuredText to markdown, and then added Hugo-style metadata back to the post.

This got the content about 90% converted. One problem I ran into was that Hugo uses draft: true to signify draft posts, where Pelican uses status: Draft. I fixed these manually since I have so few posts.

The second issue was that pandoc’s choice of syntax tags on the code blocks was often wrong, and again I fixed these blocks manually.

Click here to see the full migration script in all it's glory.
# convert the pelican rst files to Hugo md

import json
import os
import re
import subprocess
import sys
import yaml

re_title = re.compile(r'^\#*$')
re_meta = re.compile(r'^:([^:]+):\s+(.*)$')

def convert_meta(filename):
    meta_data = {}
    regular_data = ""
    title = ''
    with open(filename,"r") as a_file:

        # Going to do this quick and dirty.
        # Read the file line by line and look ones that match ':something:
        for a_line in a_file:

            if title == "":
                title_match = re_title.match(a_line)
                if not title_match:
                    title = a_line.rstrip()
                    meta_data['title'] = title

            a_match = re_meta.match(a_line)
            if a_match:
                value = a_match.group(2)
                if ',' in value:
                    rhs = value.split(',')
                else:
                    rhs = value

                meta_data[a_match.group(1)] = rhs
            else:
                regular_data += a_line

    meta_dump = yaml.safe_dump(meta_data, default_flow_style=False)

    temp_filename = "/tmp/temp.rst"
    with open(temp_filename, "w") as temp_file:
        temp_file.write(regular_data)

    name, extension = os.path.splitext(filename)
    new_name = name + ".md"

    # Have pandoc convert the file and read the output to a string
    subprocess.run('pandoc -f rst -t markdown -o /tmp/temp.md /tmp/temp.rst', shell=True)
    with open("/tmp/temp.md", "r") as temp_out:
        temp_contents = temp_out.read()

    with open(new_name, "w") as out_file:
        out_file.write('---\n')
        out_file.write(meta_dump)
        out_file.write('---\n\n')
        out_file.write(temp_contents)

# Walk through all the rst files under the directory.
for  root, dirs, files in os.walk('.'):
    for a_file in files:
        if a_file.endswith('.rst'):
            convert_meta(root + "/" + a_file)

Preprocessing of diagrams

I had one post in the old blog that used the Pelican PlantUML plugin, but Hugo doesn’t have something comparable. I poked around with converting that diagram to Mermaid as per https://gohugo.io/content-management/diagrams/, but pulling in a mermaid.js file that was over two megs to render a single static diagram seemed like overkill.

Instead I switched to preprocessing the diagram with PLantUML and updating the post to show the SVG image instead.

Here’s my Makefile for running plantuml on any of the diagrams that need updating. I went with make so I won’t have to re-render unchanged diagrams on each blog post. Yes, I spent more than five seconds remembering how to use pattern rules even though I currently have only one PlantUML diagram.

PUMLS := $(wildcard diagrams/*.puml)
SVGS  := $(PUMLS:diagrams/%.puml=static/images/%.svg)

.PHONY: plantuml
plantuml: ${SVGS}

static/images/%.svg: diagrams/%.puml
	plantuml -tsvg $< -o ../static/images

It didn’t consider using GoAT diagrams since I wanted the diagrams to be in GraphViz style so I didn’t have to worry about layout issues.

Vale is pretty nice

I also started using Vale as a prose linter and it’s working out OK after I turned off the ’no first person’ and ’no use of the verb to-be’ rules. Here’s the .vale.ini I’m currently using:

IgnoredScopes = code
StylesPath = .styles

MinAlertLevel = suggestion
Vocab = Base

Packages = Google, write-good

[*]
BasedOnStyles = Vale, Google, write-good

[*.md]
Google.FirstPerson = off
Google.Parens = off
write-good.E-Prime = off
Vale.Terms = off