Depuis un (long) moment, l’interface d’édition de Wordpress me déplait nettement. Mon utilisation plus qu’anecdotique de ce site n’était pas non plus vraiment compatible avec les mises à jours de sécurité régulières et nécessaires.

J’ai donc décidé de passer sur autre chose, en l’occurence Hugo. Ce post détaille la migration.

Pourquoi Hugo ?

Je souhaite que tout reste auto-hébergé, pas de recours à des services externes.

Après quelques recherches, je me suis tourné vers un générateur de site statique, ce qui implique à priori moins de failles de sécurité. Il y en a plusieurs, qui au vu de mon utilisation, auraient répondu au besoin. Les deux plus courants semblent être Hugo et Jekyll. J’ai choisi Hugo car il s’agit d’un unique binaire, tandis que Jekyll nécessite l’installation de Ruby.

On fournit les sources des pages au format Markdown et un thème, et Hugo génère le site au format HTML. Il ne reste plus qu’à déployer le site.

Précédemment, le site et le blog étaient sur des noms de domaines séparés. Ce n’est plus le cas, tout se retrouve maintenant sur le www. Cela implique quelques modifications au serveur web pour qu’il redirige les anciennes url du blog correctemente.

Modifier le site

Je voulais que la publication sur le site reste simple. Le process est le suivant :

  • Les sources du site sont dorénavant dans un dépôt git. Précédememnt, si le site était bien dans un dépôt git, le blog était sur Wordpress.
  • Je modifie le site sur ma machine, par exemple j’ajoute un article de blog comme celui-ci.
  • commit
  • push
  • C’est fini, le site est actualisé.

Migration

Récupérer les données de Wordpress

Cette étape était simple.

  • Installation du plugin Wordpress to Hugo Exporter
  • Execution du plugin et récupération d’un zip avec toutes les données. Je n’ai gardé que les posts.
  • Cleanup des fichiers Markdown récupérés à la main, n’ayant que quelques posts.
    • Renommage des fichiers, par défaut ils incluent la date et je n’en voulais pas. J’ai aussi renommé quelques aberration d’anciennes versions de Wordpress.
    • Ajout d’un alias dans le header de chaque fichier pour pointer vers l’ancienne url de WordPress (Exemple, ancienne url: https://blog.moomoocamp.net/index.php/build-boost-on-windows-with-mingw/ , alias: /index.php/build-boost-on-windows-with-mingw/)
    • Vérification des catégories et tags.
    • Supression de l’heure, je ne voulais que la date.
  • On se trouve avec un dossier contenant un fichier .md par post.

Création du site sur Hugo

Le plus long, c’est de choisir un thème…

  • Installation de Hugo en local.
  • hugo new site blog-hugo
  • cd blog-hugo
  • git init
  • git submodule add https://github.com/rhazdon/hugo-theme-hello-friend-ng.git themes/hello-friend-ng
  • Création du fichier config.toml à partir du fichier inclu dans le thème et modification pour correspondre au site
  • Très légères modifications du thème (le footer notamment)
  • Copie des fichiers .md récupéré de Wordpress dans le dossier content/posts/
  • Création d’une page about, modification du menu, …
  • Ajout du tout pour le prochain commit git
  • commit

Déploiement

Architecture

Il y a deux serveurs impliqués.

  • Le serveur web.
  • Le serveur git.

Ce que j’ai mis en place sur le serveur git :

  • Les sources du site sont dans un dépôt sur Gitea
  • A chaque push, Gitea appelle un webhook sur le serveur web

Ce que j’ai mis en place sur le serveur web :

  • Un service webhook qui peut lancer des scripts quand le hook est appelé.
  • Un script qui construit le site et le met dans le bon dossier.

Gitea

Je ne vais pas détailler l’installation de Gitea, juste la configuration dont on a besoin ici :

  • Dans le dépôt, ajouter un déclencheur web vers l’url du futur webhook, par exemple https://example.com:10123/hook/blog-deploy
  • Créer une clé SSH sur le serveur web avec l’utilisateur qui fera tourner le script du hook.
  • Dans le dépôt, ajouter cette clé dans les clés de déploiement. Comme ça, l’utilisateur du webhook aura accès à ce dépôt (et uniquement celui-ci) en lecture uniquement.

Webhook

Installation

Le serveur web tourne sur une Debian. L’installation n’est pas bien compliquée.

  • apt install webhook
  • adduser webhook

J’ai modifié le fichier de démarrage du service, par défaut il tourne en root sans SSL. Les certificats sont générés avec Let’s Encrypt je ne vais pas détailler ça ici, ce n’est pas le sujet.

[Unit]
Description=Small server for creating HTTP endpoints (hooks)
Documentation=https://github.com/adnanh/webhook/
ConditionPathExists=/etc/webhook/webhook.conf

[Service]
WorkingDirectory=/home/webhook/
User=webhook
Group=webhook
ExecStart=/usr/bin/webhook -secure -cert /etc/webhook/ssl/fullchain.pem -key /etc/webhook/ssl/privkey.pem -port 10123 -hotreload -nopanic -hooks /etc/webhook/webhook.conf

[Install]
WantedBy=multi-user.target

Configuration

Le script du hook contient simplement l’appel au script.

[
  {
    "id": "blog-deploy",
    "execute-command": "/home/webhook/scripts/blog-deploy.sh",
    "command-working-directory": "/home/webhook"
  }
]

Script

Le script est très basique pour l’instant.

#!/bin/bash

GIT_REPO_NAME=blog-hugo
GIT_REPO_URL="ssh://gitea@git.example.com/blog/blog-hugo.git"

TMP_BASE_DIR=/home/webhook/tmp
TMP_REPO_DIR=$TMP_BASE_DIR/$GIT_REPO_NAME

FINAL_DIR=/home/webhook/done/$GIT_REPO_NAME

if [ -e $TMP_REPO_DIR ]; then
  echo "Git repo already exists. Updating..."
  cd $TMP_REPO_DIR
  git pull
else
  echo "Git repo does not exist locally. Cloning..."
  cd $TMP_BASE_DIR
  git clone $GIT_REPO_URL --recursive
fi

if [ -e $TMP_REPO_DIR ]; then
  echo "Git repo exists. Building with Hugo."
  cd $TMP_REPO_DIR
  hugo -D

  if [ $? -ne 0 ]; then
    echo "Hugo build failed. Exiting."
    exit 1
  else
    rm -r $FINAL_DIR
    mv $TMP_REPO_DIR/public $FINAL_DIR
  fi
else
  echo "Git repo should exist at this point but does not. Exiting."
  exit 1
fi

Serveur web

Dans mon cas, le serveur web est simplement configuré avec un symlink vers le FINAL_DIR du script.

Pour ce qui est des redirections, j’ai ajouté ces deux lignes dans le VirtualHost qui concerne le domaine blog.moomoocamp.net, en attendant de le supprimer un jour :

    RedirectMatch permanent "^/index.php/(.*)" "https://www.moomoocamp.net/index.php/$1"
    RedirectMatch permanent "^/(.*)" "https://www.moomoocamp.net/"