Skip to main content

This blog is now brought to you by Hugo

·630 words·3 mins
Cool Tools Fun Meta

Welp! It was time! Time for a big website refresh. Let’s talk through it.

2017 - 2024: Tech debt 😱
#

As a reminder, the previous version of this website was:

  • originally written in HTML + CSS + a touch of Javascript, with just a few html pages (about.html, scifi.html, etc.)
  • a static blog generated by Pelican
    • using a special Pelican theme on an outdated fork
    • modified to match the CSS of my original, manually-written HTML + CSS…

So I would use Pelican to generate the entire website, but I basically had everything spread out over four (4!!) directories/repos, and it was an awful pain to maintain. This was how things had been going since 2017. And it was just very very tech debty.

2022: Thinking about Hugo 🤔
#

I had heard good things about Hugo, and thought it might be fun to try rewriting the blog in Hugo. This was back in 2022. I tinkered a bit, but then life got busy again and I dropped it.

2024: Moving to Hugo ⛵
#

I picked it up again, found a theme I liked (Blowfish), and decided to commit! This was a couple weekends’ worth of work.

1. Quickstarting Blowfish with a bare Hugo site 🐡
#

As a first step, I followed the Blowfish tutorial to get a basic site up. Nothing special here.

2. Converting my Pelican blog to Hugo 🐦
#

I had about ~100 blog posts that needed to get Hugo-fied. I wrote a short Python script to rewrite their Markdown just a touch, mostly the frontmatter and relative references.

def process_file(
    filepath: str,
    read_dir: str = INPUT_DIRECTORY,
    write_dir: str = THIS_DIR,
    verbose: bool = False,
) -> None:
    if verbose:
        print(f"Now doing {filepath}")

    with open(f"{read_dir}/{filename}", "r") as f:
        lines = f.readlines()

    frontmatter = []
    body = []
    in_frontmatter = True
    subdir = None

    for line in lines:
        if in_frontmatter and re.match(r"^\w+:\s.*", line):
            key, value = map(str.strip, line.split(":", 1))
            key = key.lower()
            value = value.replace('"', "'")
            value = value.replace(":", "-")

            if key not in VALID_PELICAN_KEYS:
                continue

            if key == "date":
                value = format_date(value)

            elif key == "tags":
                value = [str(tag.strip()) for tag in value.split(",")]

            elif key == "category":
                if len(value) > 0:
                    value = [str(category.strip()) for category in value.split(",")]

            elif key == "slug":
                fpath = f"{write_dir}/{value}"
                if not os.path.exists(fpath):
                    os.mkdir(fpath)
                subdir = value

            elif key == "summary":
                value = f'"{value}"'

            frontmatter.append(f"{key}: {value}\n")

        else:
            in_frontmatter = False
            if "```{" in line:
                line = line.replace("```{", "```")
                line = line.replace("}", "")

            if "{static}" in line:
                line = line.replace("{static}", "")

            if "img" in line:
                img_filename = re.findall(r"\((.*)\)", line)[0]

                if "https" in img_filename:
                    if verbose:
                        print(img_filename)
                    continue

                # kinda hacky
                img_filename = img_filename.split("/")[-1]
                fpath = f"{write_dir}/{subdir}"

                if img_filename in os.listdir(fpath):
                    print("Already here")
                else:
                    os.rename(
                        f"{IMGS_DIRECTORY}/{img_filename}", f"{fpath}/{img_filename}"
                    )

                line = line.replace("imgs/", "")

            body.append(line)

In short, tons of string parsing - including some dreaded regexing. My workflow was mostly:

  • Ask ChatGPT for boilerplate
  • Try out regex in regexr
  • Tons of print statements to debug {chefs kiss}
  • Sit at cafe and think about the state of the world.

3. Just do the whole website in Hugo actually 🐡
#

Namely, the static, non-blog pages, like scifi. This just took some understanding of how Hugo configs work.

4. Deploying 🚀
#

I deploy this site manually, just SCPing the files over to the server where it’s being hosted.

scp -r public/* $WWW_USERNAME@$WWW_IP_ADDRESS:~/public_html/

Tadaaaaa! 🎉

80/20 it 😅
#

You may have noticed that some of the writing doesn’t show up, and the colors are a bit weird, and I think I still have draft posts sometimes showing up. Sigh.

Anyway, I kept telling myself: 80/20, 80/20, 80/20. So I’m keeping a running list of things I’d like to fix or polish, but the main website is up and running - and that’s what matters!

Related

One bash script to blog them all
·660 words·4 mins
Python Fun Meta
And one Python script to bind them!
Workflow 2024
·355 words·2 mins
Cool Tools Work Linux
It’s been a while!
Split ergo keyboard
·570 words·3 mins
Cool Tools
I did something crazy.