Als ich mich 2007 entschied einen Blog zu erstellen um meine Erfahrungen im IT Bereich teilen zu können war das Mittel der Wahl Wordpress. Dieses CMS abstrahiert komplexe Administration, so das ich mich mehrheitlich auf den Inhalt konzentrieren konnte. Ich habe die Installation allerdings von Anfang an selber auf meinem gemieteten Webspace durchgeführt, weil ich mich auch für die technischen Details interessiere. Später im Jahre 2018 habe ich dann vom Webspace auf einen Virtual Private Server (VPS) gewechselt und die Docker Images für Wordpress und MySQL selber gebaut.

Immer wieder mal habe ich auf anderen Blogs die Fusszeile “Powered by Hugo” bemerkt und kürzlich beschlossen mir mal genauer anzuschauen was GoHugo eigentlich ist. Es handelt sich um einen Static Site Generator (SSG) welcher aus Markdown Dateien eine Webseite (oder eben Blog) erstellen kann. GoHugo ist in Go geschrieben was mir bestens bekannt ist. Die Markdown Dateien werden mittels dem html template Package gerendert, welches ich ebenfalls in Microservices verwendet habe. Der Workflow, den Blog mittels Git als static Page auf einem Provider bereitzustellen, war mir sehr sympatisch. Dies bringt vor allem Vorteile in Punkto Security und Performance mit sich. Zudem wird der Wartungsaufwand wegen fehlender Datenbank und Plugins auf ein Minimum reduziert. Also habe ich beschlossen meinen Blog auf GoHugo zu migrieren, was in folgendem Abschnitt beschrieben wird.

Migration

GoHugo hat dokumentiert wie eine Wordpress Seite migriert werden kann, allerdings gibt es dennoch die eine oder andere Nachbearbeitung.

Erstellen der Hugo Seite

Ich habe mich am Quickstart Guide von GoHugo orientiert um eine neue Seite zu erstellen und das Theme PaperMod hinzu zu fügen.

1
2
3
4
hugo new site irbe_blog
cd irbe_blog
git init
git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod

und dann noch in der Datei hugo.yaml (ich habe von toml auf yaml gewechselt) das PaperMod Theme aktivieren durch hinzufügen der folgenden Zeile

theme: "PaperMod"

Wordpress Export

Der Export war der einfachste Schritt, da er lediglich aus dem Herunterladen und Ausführen des Plugins wordpress-to-hugo-exporter entweder in der Wordpress Administrator Oberfläche oder direkt via SSH, besteht. Die resultierende ZIP Datei hugo-export.zip habe ich ins Hauptverzeichnis der neuen GoHugo Seite abgelegt. Anschliessend habe ich hugo-export.zip in den content Ordner entpackt. Um das Script dem Export anpassen zu können lösche ich jeweils den Content Ordner um eine saubere Ausgangslage zu haben.

1
2
3
unzip hugo-export.zip
rm -rf content
mv -f hugo-export content

Page Bundles und Regex

Ich habe mich entschieden, die Posts in Page Bundles zu organisieren. Mit dieser Methode wird im Ordner content/pages pro Seite ein Ordner angelegt, welcher die Markdown Datei und dem Post zugehörige Ressourcen wie Bilder oder Dateien beinhaltet. Der Wordpress Export hat im Ordner posts eine flache Hierarchie der Beiträge erstellt und die Markdown Dateien mit dem Schema yyyy-MM-dd-PostNamen.md erstellt. Ich habe mir ein kleines Python Script erstellt, welches pro Wordpress Export Post ein Page Bundle erstellt und dabei auch gleich mit Hilfe von Regular Expressions (Regex) den Inhalt an GoHugo anpasst. Da ich in letzer Zeit nicht mehr viel Python geschrieben hatte, kam es mir sehr gelegen, dass ich über eine Github Copilot Subscription verfüge, welche mich tatkräftig beim Schreiben des Scripts unterstützt hat. Folgende Änderungen musste ich an den Posts vornehmen

  • Lesen der HTML <img> Tags, kopieren der Bilder aus wp-content/uploads/yyyy/MM/bild ins Page Bundle und anpassen der Referenz zum Bild auf Markdown
  • Ersetzten aller HTML <a> tags durch Markdown Code
  • Löschen aller <figure> und </figure> html Tags
  • Ersetzen der Code Blöcke mit dem GoHugo hightlight Shortcode
  • Ersetzen der HTML Entitäten durch das entsprechende Zeichen
  • anpassen des featured_image: /wp-content/uploads/2021/06/git_push.png durch cover:\n image: /posts/title/images/git_push.png\n relative: true\n alt: “git push” (für PaperMod)

Beim Ersetzen der Code Blöcke habe ich die Herausforderung angetroffen, dass ich im Laufe der Zeit verschiedene Methoden für Code Blöcke benutzt habe.

⚠️ Achtung beim Übernehmen des Scripts muss der Wordpress Export überprüft und das Script gegebenenfalls angepasst werden, weil der Export sehr individuell sein kann und je nach Theme andere Front Matter Parameter benutzt.

Ich habe zudem die mainSections angepasst in hugo.yaml, dass alle Posts berücksichtigt werden.

1
2
3
params:
  mainSections:
    - post

Suchfunktion und Archiv

Auf einen Blog gehört meiner Meinung nach eine Suchfunktion und ein Archiv. Zum Glück bringt das PaperMod Theme eine integrierte Suchfunktion auf Basis von Fuse.js mit und ein Template für das Archiv ist ebenfalls vorhanden. In der hugo.yaml Konfiguration habe ich den benötigten JSON Ouput aktiviert und im Menu Links für die Kategorien, Tags, Archiv und Suche erstellt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
menu:
  main:
    - identifier: categories
      name: Categories
      url: /categories/
      weight: 1
    - identifier: tags
      name: Tags
      url: /tags/
      weight: 2
    - identifier: archive
      name: Archiv
      url: /archive/
      weight: 3
    - identifier: search
      name: Suche
      url: /search/
      weight: 4
outputs:
    home:
        - HTML
        - RSS
        - JSON # is necessary

und im Ordner content die Datei search.md mit folgendem Front Matter erstellt

1
2
3
4
5
6
7
---
title: "Beiträge suchen"
layout: "search"
url: "/search/"
summary: "search"
placeholder: "Suche"
---

Die Fuse.js optionen habe ich auf dem Standart belassen.

Für das Archiv habe ich ebenfalls im content Ordner die Datei archive.md erstellt

1
2
3
4
5
6
---
title: Archiv
layout: 'archives'
url: '/archive/'
summary: archives
---

Template anpassen

Auf der ersten Seite bietet PaperMod die Möglichkeit einer speziellen Gestaltung, entweder mit dem Home-info Mode oder dem Profile Mode. Ich entschied mich für den Home-info Mode, weil ich auf der Startseite ebenfalls Posts haben möchte. Das Template für den Home-info Mode ist so gestaltet, dass lediglich Text, welcher in hugo.yaml definiert wird, als erster Post dargestellt wird. Es gibt in GoHugo einen Mechanismus, dass Templates angepasst werden können, indem das gewünschte Template im Ordner layouts abgelegt wird. Das habe ich für den Home-info Mode gemacht, in dem ich folgendes Template erstellt habe layouts/partials/home_info.html. Mein Ziel ist es nebst dem Text auch ein Profilbild (konfigurierbar über hugo.yaml) angezeigt wird. Das Bild schicke ich durch das Image Processing um ein webp Profilbild mit 180x180 Pixeln zu erhalten und mache es Rund mithilfe von CSS.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{{- with site.Params.homeInfoParams }}
<article class="first-entry home-info">
    <header class="entry-header">
        <h1>{{ .Title | markdownify }}</h1>
    </header>
    <div class="entry-content">
        {{- if .Image -}}
        <div style="width: 75%; display: inline-block;">
            {{ .Content | markdownify }}
        </div>
        {{- else -}}
            {{ .Content | markdownify }}
        {{- end -}}
        {{- if .Image -}}
        <div style="width:25%; display: inline-block; vertical-align: top;">
            {{- with resources.Get .Image -}}
            {{- with .Resize " 180x webp q100" -}}
                <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="profile picture" style="border-radius:50%;height:auto;">
            {{- end -}}
            {{- end -}}
        </div>
        {{- end -}}
    </div>
    <footer class="entry-footer">
        {{ partial "social_icons.html" site.Params.socialIcons }}
    </footer>
</article>
{{- end -}}

Das Profilbild lege ich unter assets/images/profilbild.jpg ab und referenziere es in hugo.yaml

1
2
3
4
5
homeInfoParams:
    Title: Hallo 👋
    Image: /images/philipp-ritter.jpg
    Content: >
      ...      

Kommentare

Was meiner Meinung nach auf einem Blog ebenfalls nicht fehlen darf ist eine Kommentar Funktion. Diese ermöglicht einen Kanal für Feedbacks an den Autoren und kann als Diskussions- und Austauschplatform für andere Leser:innen genutzt werden. GoHugo beinhaltet ein Template für den bekannten Disqus Service, dieser hat mir allerdings nicht zugesagt, weil in der Free Version vom Hersteller Anzeigen geschaltet werden. Als Alternative wird ein Open Source Projekt Remark42 aufgelistet welches mir sympathisch war. Für das Backend habe ich mir ein docker-compose.yml aus den vielen Konfigurationsmöglichkeiten zusammengestellt und auf meinem VPS gestartet

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
version: '3'
services:
    remark:
        image: umputun/remark42:latest
        container_name: remark42
        restart: unless-stopped
        logging:
            driver: json-file
            options:
                max-size: 10m
                max-file: '5'
        environment:
        - REMARK_URL=https://comment.irbe.ch
        - APP_UID=1000
        - SITE=wwwirbech
        - AUTH_ANON=true
        - SECRET=*****
        - DEBUG=false
        - AUTH_GITHUB_CID=*****
        - AUTH_GITHUB_CSEC=*****
        - AUTH_GOOGLE_CID=*****
        - AUTH_GOOGLE_CSEC=*****
        - AUTH_MICROSOFT_CID=*****
        - AUTH_MICROSOFT_CSEC=*****
        - ADMIN_SHARED_ID=*****
        - TELEGRAM_TOKEN=*****
        - AUTH_TELEGRAM=true
        - NOTIFY_ADMINS=telegram
        - NOTIFY_USERS=telegram
        - NOTIFY_TELEGRAM_CHAN=*****
        volumes:
        - /srv/docker/comment.irbe.ch:/srv/var
        networks:
        - cloudflare
networks:
    cloudflare:
        external:
            name: cloudflare

Für das Frontend habe ich das Template für Kommentare (welche im PaperMod Theme ohnehin leer ist) überschrieben, in dem ich in die Datei layouts/partials/comments.html folgenden Code geschrieben habe

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{{ if .Site.Params.comments }}
<div id="remark42"></div>
<script>
    var remark_config = {
      host: 'https://comment.irbe.ch',
      site_id: 'wwwirbech',
      components: ['embed', 'last-comments'],
      max_shown_comments: 100,
      theme: 'dark',
      page_title: 'IRBE Blog',
      locale: 'de',
      show_email_subscription: false,
      simple_view: true,
      no_footer: false
    }
  </script>
  <script>!function(e,n){for(var o=0;o<e.length;o++){var r=n.createElement("script"),c=".js",d=n.head||n.body;"noModule"in r?(r.type="module",c=".mjs"):r.async=!0,r.defer=!0,r.src=remark_config.host+"/web/"+e[o]+c,d.appendChild(r)}}(remark_config.components||["embed"],document);</script>
{{ end }}

In der hugo.yaml Konfiguration habe ich die Kommentarfunktion für alle Posts aktiviert, habe aber die Möglichkeit via Front Matter bei individuellen Beiträgen die Funktion zu deaktivieren.

params:
  comments: true

Hosting

Da ich bereits einen Cloudflare Account besitze und Dienste wie Cloudflare Zero Trust, DNS und Pages benutze war es naheliegend, dass ich meinen neuen Blog ebenfalls auf Cloudflare Pages bereitstelle. Cloudflare stellt ein ausführliche Anleitung zur Verfügung, welche das Deployment direkt von einem Git Repository beschreibt.

Fazit

Die Migration von Wordpress zu GoHugo war eine interessante Herausforderung, da sie nicht einfach ein 1 Klick Setup bzw. Export/Import war. Durch die Anpassungen habe ich viel über die interne Funktionsweise von GoHugo lernen können. Ein Vorteil war natürlich, dass ich das html template Package von Go bereits bekannt war.

Das Erstellen von Beiträgen als Code, die Integration mit Git und die Möglichkeit die Seiten lokal als Live Preview anzuschauen finde ich einfach toll. Hinzu kommen noch die erhöhte Sicherheit und die Performance da es sich um statische Seiten handelt anstelle einer Datenbank.