Coding with Titans

so breaking things happens constantly, but never on purpose

Convert Miniblog to Hugo

For a very long time I was using the MiniBlog as a backend solution for my blogs. I liked its speed and the minimalistic style. But one thing that drove me crazy was the need of having Live Writer aka Open Live Writer installed to write a simplest post. 5 years ago this tool looked amazing. Although time has passed quickly, and meanwhile OLW seemed to be also abandoned by Microsoft. Even making it an open-source project didn’t help much.

Thankfully one friend suggested me recently a static-site generator called Hugo. And I agree it has an amazing set of features build-in. In particular two of them convinced me to give it a try:

  • I won’t have anymore problems with security patches of the custom .NET blog-engine as result of static-site generator is a bunch of HTMLs. No custom code running server-side!

  • Even better - posts can be written using Markdown with any editor I like (for example Visual Studio Code), easily on Windows or MacOS. More of that I can keep track of all my posts (MD files) in a Git repository! They are no more entries hosted somewhere only on my server in blog-engine’s known way.

  • Finally - among existing Hugo Themes I was able to find one looking exactly as the original Bootstrap from MiniBlog.

With this as a background I had few issues to solve in advance:

  1. handling migration of old posts (plus media files)
  2. making sure old links are still working
  3. making the contact form working
  4. making sure existing code snippets are respectively colorful
  5. making sure whole output is minimized
  6. making sure favicon is recognized
  7. reconfiguring the server approprietely (as I would love to have SSL working all the time and also move from codetitans.pl/blog to dedicated subdomain blog.codetitans.pl).

Here starts the story of how I accomplished those tasks and setup fully working statically generated blog. There are no hacks, no workarounds. Everything seemed to be so amazingly clean and natural.

Of course I recommend first taking a look at Hugo documentation and great introduction course from Make Dane.

Problem 1.

Turned-out I configured MiniBlog to store all posts as XML files on the server. This was a real blessing, as porting posts turned out to be much simpler task. Here is a small Python script convert.py that performs whole job. It assumes posts and additional media files are placed in dedicated folders next to this script, called respectively posts and files. Then it parses metadata to:

  • rename posts files and updates .md extension
  • set slug, title and publish date in .md front matter (aka header part)
  • update whole content formatting (convert from HTML tags to markdown)
  • copy related media files to new location based on post publish date and alternative image description set in <img> tag (<year>/<month>/<img_alt>.<ext>)

It relys on custom media shortcode to simplify links management inside Hugo. The shortcode should be placed in layouts/shortcodes/media.html and looks following:

{{ path.Join "/media" (.Get 0) | relURL }}

This script is almost perfect. I would still recommend going though all posts and check their content though. Visual Studio Code has a nice feature of visualizing rendered markdown as sometimes Open Live Writer could generate really strange data for <code> fragments and also tables are not handled at all.

Problem 2.

Thanks to setting up proper slug property inside each markdown post (and file name itself) I was able to keep the same structure as original blog.

The other required action was to instruct IIS to execute redirect from the old location (codetitans.pl/blog) to new one (blog.codetitans.pl). Since the links were matching the proper web.config is following:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <directoryBrowse enabled="false" />
        <httpRedirect enabled="true" destination="https://blog.codetitans.pl/" httpResponseStatus="Permanent" />
    </system.webServer>
</configuration>

Problem 3.

Instead of implementing custom solution I integrated formspree.io.

<form method="POST" action="https://formspree.io/<my_contact_email>">
 ... fields for email, name
</form>

Problem 4.

It was enough to define following 3 settings inside config.toml file. They respectively enable using emojis and cause typical code formatting used in markdown to be reflected in generated HTML. More pygments styles could be seen here.

enableEmoji = true
pygmentsCodeFences = true
pygmentsStyle = "friendly"

Problem 5.

Turned out there is a parameter --minify that could be passed to Hugo and it will do the whole job. Script to

hugo --minify -s <blog_directory> -d <output_directory>

Problem 6.

Having favicon.ico and favicon.png in not enough these days. Straighforward solution is to use sites like favicon generator to quickly create required bundle. Then all images should be directly extracted inside /static folder of the blog. Hugo will copy them to public root folder during publish process. Additionally inside /layouts/partials/head-custom.html should be placed HTML defining extra link and meta info. It will be picked-up by the theme used and rendered inside <head> tag though.

Problem 7.

My hosting has a way of enabling self-renewable Let’s Encrypt certificate. Since it’s IIS, I also had to upload there proper web.config file to force usage of SSL connection (HTTPS) and custom page for 404 (not found) error.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <directoryBrowse enabled="false" />
        <httpErrors errorMode="DetailedLocalOnly" existingResponse="Auto" />
        <rewrite>
            <rules>
                <rule name="HTTPS force" enabled="true" stopProcessing="true">
                    <match url="(.*)" />
                    <conditions>
                        <add input="{HTTPS}" pattern="^OFF$" />
                    </conditions>
                    <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" redirectType="Permanent" />
                </rule>
            </rules>
        </rewrite>
        <httpErrors errorMode="Custom" existingResponse="Auto">
            <remove statusCode="404" subStatusCode="-1" />
            <error statusCode="404" subStatusCode="-1" prefixLanguageFilePath="" path="404.html" responseMode="File" />
        </httpErrors>
    </system.webServer>
</configuration>

That’s all. A bit long, but hopefully full of useful materials.

👌 💪 👷‍♂️