Rebuilding the Site

July 27, 2020

The last time I updated this site was sometime in 2012. I put up a placeholder page, meaning to do far more, started using Blogger for my blog rather than a home-grown solution (again, meaning to find a way to import the old content), and then… and then… and then life happened, and I never seemed to do anything about it.

Then came the pandemic, and eventually, encouraged by my wife, I decided to refresh the whole thing.

The old site had been hosted on Google App Engine and I was pretty happy with it: SSL was zero-hassle, setup was simple, and it seemed like a safe bet. Eight years later, and App Engine isn’t the shinest, spiffiest thing on the block right now, but it has shown itself to be reliable and unfussy. I like reliable and unfussy in my tech. I like boring. So it seemed like a good idea to continue using GAE.

However, I wanted to move off Blogger, and I wanted the site design to be a little more interesting, to work on mobile, and to be as fast and light as possible. So I had a look around, and chose the following:

That last point is particularly nice: I just need to push, and the site gets updated automatically at some not-too-distant point in the future. So, how did I do it?


Hugo is pretty easy to set up. The docs are excellent, and after browsing through the Hugo Themes site, it looked like the “Minimal” theme hit many of my requirements and was also pretty light. Getting the skeleton set up was an absolute breeze.

Rather than using a git submodule, I used GitHub’s SubVersion support and grabbed the theme using svn export: I knew that I’d be making tweaks, and I really do want to understand CSS and modern HTML better, so being able to do an update wasn’t so important. This also meant that I avoided the full horror of git submodules, which have always seemed an inelegant way of expressing modularity to me.

Migrating from Blogger

I had thought that this would be a nightmare. I wanted the old URLs to continue to work, and it was this that had stopped me dead in my tracks. I need not have worried. A little hunting found blog2md, which is a useful tool to take a Blogger (or, it turns out, Wordpress) backup, and convert that into MarkDown with front-matter that Hugo can consume.

My blog on Blogger was pretty minimal, and there were no images, so running node index.js b blogger-exoport.xml out did what I wanted. The only problem were the comments, but since I’d done a pretty poor job keeping spammers out of them, I felt it okay (though a little sad) to delete them.

The hardest bit was navigating the Blogger admin console to find out how to do the backups. If you’re following in my footsteps, that’s currently hidden under Settings -> Manage blog -> Back up content. It’s quite a long way down the settings, and I managed to overlook it at first. D’oh!

Hosting on App Engine

The nice thing with a static site is that it’s really simple to host. Anywhere with a working web server will do. However, I’ve used App Engine for a while now and I quite like it, so I don’t see a need to jump on to the Fastly of Netlify trains yet. Besides, I’ve already spent part of my “interesting” budget on Hugo (which we also use for the Selenium website), and I want to spend the rest on the deployment pipeline. App Engine it is, then.

The app.yaml is remarkably simple:

runtime: python27
api_version: 1
threadsafe: true


- url: /
  static_files: public/index.html
  upload: public/*
  secure: always

# Standard index.html check
- url: /(.*)/
  static_files: public/\1/index.html
  upload: public/*
  secure: always

- url: /(.*\..*)
  static_files: public/\1
  upload: public/(.*)
  secure: always

Most of this is deeply unexciting (yay!) We’re using a lightweight runtime (python), but we could have used anything. We force all the handlers to use TLS where possible, and we check for an index.html file every time you visit a directory (which has been standard practice since the dawn of the Web)

One thing to note is that the content is all served from public. That’s the directory generated by Hugo when it’s run.

Having set up the basic app, it was possible to deploy from the command line, and everything looked pretty good, except I needed to configure TLS in order to have everything work on a custom domain. To do this, I needed to prove that I was the owner of the domain, and then the App Engine console has a handy option in Settings -> Custom Domains to Enable Managed Security. This will take care of creating and renewing SSL certs for you, so it becomes a no-brainer to just flip the switch.

Deploying via GitHub Actions

The final piece of the puzzle was to use GitHub Actions to deploy the site when I push to the repo hosting the content. I didn’t want to have to check in generated content (because “whhhhyyyy!?”). In the end I ended up with:

name: CI

    branches: [ trunk ]
    branches: [ trunk ]

    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    - uses: actions/checkout@v2

    - name: Setup Python
      uses: actions/setup-python@v2
        python-version: "3.8"

    - name: Hugo setup
      uses: peaceiris/actions-hugo@v2.4.12
        hugo-version: "0.71.1"

    - name: Build site
      run: hugo 

    - id: Login
      uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
        version: '290.0.1'
        project_id: "mysite"
        service_account_key: ${{ secrets.GCP_SA_KEY }}
        export_default_credentials: true
    - run: gcloud info

    - id: Deploy
      uses: GoogleCloudPlatform/github-actions/appengine-deploy@master
        project_id: "mysite"

The hardest bit was figuring out that I needed to install Python into the pipeline, or the Google tools would all get very grumpy. That, and remembering to set up a service account to do the deployments for me, remembering to store the secrets in GitHub Secrets.

Actually, I lied. The hardest bit was figuring out what IAM roles were needed for the service account to use. I’ve ended up with:

After creating those and manual reruns (which explains the workflow_dispatch section at the top) I finally got everything up and running.

In the end, the actual effort was a pleasant evening’s worth of mucking around with Hugo, App Engine (it’s been a while), and GitHub Actions. I’m pretty happy with the result :)

More recently Regular and Infrequent Performance Reviews     Writing Again Less recently

A New Approach to CI

September 5, 2023
bazel monorepo tech

There's No Such Thing as a Free Lunch

June 12, 2023
bazel monorepo tech

Your Roots Are Showing

May 10, 2023
bazel tech