Automatiser la vérification des liens morts avec linkchecker, docker et gitlab

tbowan

17 juin 2019

Laisser des liens morts, c'est mal. Les vérifier, c'est bien, mais si c'est manuel, on oubliera et ça sera mal. Alors pourquoi pas l'automatiser à chaque déploiement ?

Lorsqu’on s’occupe d’un site web, il n’est jamais bon de laisser traîner des liens morts. Au delà de l’impression que ça donne aux internautes, c’est pour moi une histoire de respect. Bon d’accord, c’est aussi une histoire de SEO.

Pour les vérifier, il y a bien sûr plein d’outils, mais quid de l’automatisation ? Soyons honnêtes, c’est comme pour tous les tests ; si on doit les lancer à la main, c’est contraignant, on oublie et comme chaque fois qu’on ne teste pas, il y a des erreurs.

Alors aujourd’hui, pour votre plus grand bonheur, nous allons vous montrer comment nous avons automatisé cette vérification sur le site des arsouyes. Divulgachage : en fait, tout est dans le titre ;-)

Linkchecker

Linkchecker est un outil python en ligne de commande qui permet de parcourir un site en suivant les liens. Il a le bon goût de fournir un résumé (nombre de warning, nombre d’erreur) et d’être configurable pour correspondre à nos besoins.

⚠️ Le projet principal n’as plus eu de commit depuis trois ans mais un fork existe avec des correctifs.

Plutôt qu’utiliser les dépôts, qui pointent vers l’ancienne version, nous allons donc suivre la documentation du nouveau projet et utiliser pip.

pip install git+https://github.com/linkchecker/linkchecker.git

L’utilisation de base n’est pas compliquée, dans notre cas, nous avons ajouté deux options :

linkchecker https://www.arsouyes.org/      \
    --check-extern                         \
    --ignore-url=https://mafreebox.free.fr
  1. Nous activons la vérification des liens externes, pour s’assurer que les pages existent toujours et pouvoir adapter notre contenu en conséquent.
  2. Nous écartons les liens de configuration de la freebox, puisqu’ils seront bloqué par son contrôle d’accès.

Docker

On pourrait installer une machine dédiée, mais comme le but est de tester différentes versions du site... éventuellement dans des branches, voir en parallèle, lorsqu'on écrit comme des possédés, il est bien plus pratique d’utiliser un conteneur docker qui contiendra les deux outils1 :

On commence donc par le système de base. Arbitrairement Ubuntu mais techniquement, ça pourrait marcher avec n'importe quel autre dérivé de Debian (ou n'importe quelle autre distribution en adaptant la récupération des paquets).

FROM ubuntu:latest

Par contre, comme ce conteneur sera utilisé par gitlab pour lancer des scripts de tests, il y a quelques subtilités à prendre en compte par rapport aux conteneurs dockers habituels.

Serveur web apache

L'utilisation d'apache n'est fondamentalement pas compliquée. Il suffit d'installer le paquet, d'activer des modules et de copier les fichiers de configuration. Comme la prod utilise HTTPS, nous l'activons aussi, avec une PKI créée pour l'occasion.

RUN apt-get update && apt-get install -y    \
    apache2
RUN a2enmod rewrite
RUN a2enmod ssl

ADD fake_www.pem /etc/ssl/private/
ADD fake_www.crt /etc/ssl/certs/
ADD fake_root.crt /etc/ssl/certs/

ADD apache.conf /etc/apache2/sites-enabled/000-default.conf

C'est ici qu'intervient la différence avec un conteneur apache2 classique. Comme apache2 ne sera lancé que si on le demande, et que docker n'aime pas s'il tourne en fond, il est d'usage d'utiliser le point d'entrée suivant :

CMD /usr/sbin/apache2ctl -D FOREGROUND

Pour nous, c'est inutile de le mentionner car Gitlab remplacera le point d'entrée du conteneur par ses propres scripts. Par contre, il ne faudra pas oublier de lancer apache2 lors des tests.

Linkchecker

En lui-même, il n'est pas compliqué à installer, on ajoute python, pip et git, puis on lance l'installation comme déjà mentionné :

RUN apt-get update && apt-get install -y    \
    python                                  \
    python-pip                              \
    git

RUN pip install git+https://github.com/linkchecker/linkchecker.git

Si on lance linkchecker sur localhost maintenant, outre le manque de fichier à servir, on obtiendra des erreurs de connexion TLS puisque notre PKI n'est pas installée. On va donc forcer l'ajout de notre CA aux autorités déjà dans le système :

ADD fake_root.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates

La petite blague est que ces commande vont générer un avertissement car notre CA est détectée en double (l'autre copie étant dans /etc/ssl/certs/, pour apache) mais lors de nos tests, nous avions des erreurs tant que nous n'avions pas copié la CA...

Giglab

Le reste se passe donc dans la configuration de l'intégration continue, le fichier .gitlab-ci.yml.

Phase de préparation

La première phase de la construction du site est, chez nous, la construction des conteneurs. Nous pourrions le faire dans un projet à part mais nous avons trouvé plus pratique de regrouper tout dans un seul projet.

La configuration en elle-même n'est pas compliquée et suit la documentation officielle :

variables:
    IMG_CHECK : docker.arsouyes.org/$CI_PROJECT_PATH/check:$CI_COMMIT_REF_NAME

prepare:deploy:
    stage: prepare
    tags:
    - docker-build
    script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN docker.arsouyes.org
    - docker pull $IMG_CHECK || true
    - docker build --cache-from $IMG_CHECK --tag $IMG_CHECK docker/check
    - docker push $IMG_CHECK

Phase de test

C'est ici que toutes les briques sont mises ensemble, les derniers ajustements fait avant de lancer le parcours du site.

Résolution DNS

Comme nous voulons tester comme en prod, nous avons produit le site avec les références de prod (i.e. www.arsouyes.org plutôt qu'un sous-domaine spécifique). Au risque de parcourir la prod, il est donc nécessaire de rediriger ce domaine vers la machine locale.

echo "127.0.0.1 www.arsouyes.org" >> /etc/hosts

On pourrait vouloir mettre cette ligne dans la configuration du conteneur docker (et c'est ce que nous avions fait). Après tout, c'est une configuration d'environnement non ? Le problème, c'est que ce fichier sera écrasé lors du chargement du conteneur par gitlab et la redirection sera donc oubliée. On doit donc garder cette étape lors du test, dans le yaml.

Configuration apache

Comme promis, c'est maintenant que nous allons lancer apache2.

/usr/sbin/apache2ctl start

Il faut aussi déployer le site. Plutôt que de faire une copie, vu que nous sommes sur la même machine, nous allons faire un lien symbolique. Si vous n'êtes pas sous ubuntu, vous devrez peut être adapter le répertoire.

rm -rf /var/www/html
ln -s $(pwd)/public /var/www/html

Job complet

La tâche complète consiste donc à enchaîner ces étapes pour finir par le parcours du site à la recherche des liens morts.

check:links:
    stage: check
    image: $IMG_CHECK
    script:
    - echo "127.0.0.1 www.arsouyes.org" >> /etc/hosts
    - rm -rf /var/www/html
    - ln -s $(pwd)/public /var/www/html
    - /usr/sbin/apache2ctl start
    - linkchecker https://www.arsouyes.org/ --check-extern --ignore-url=https://mafreebox.free.fr

Conclusion

Grâce à cette étape, on peut maintenant vérifier le site avant même qu'il parte en production et éviter les liens morts, par construction.

Enchaînement pour la mise en production

Enchaînement pour la mise en production

Effet secondaire, cette vérification se fait également dans n'importe quelle branche et évite donc les mauvaises surprises après les merges.

Et après ?

Site statique
27 Septembre 2017 - tbowan. Après avoir expérimenté le PHP, nous revenons aux sources avec une génération statique du site des arsouyes. Pour les curieux, voici comment nous nous y sommes pris.
Déployer un site web via sftp et Gitlab
10 juin 2019 Avec notre nouvel hébergement, nous avons du revoir nos scripts de déploiement continu. Remplacer rsync par lftp n'est pas compliqué mais nécessite de fournir les données d'authentifications à l'environnement de déploiement. Heureusement, gitlab fournit une méthode sécurisée pour protéger ces informations sensibles.

  1. On pourrait utiliser deux conteneur, apache2 étant configuré comme service. Mais ça pose le problème de copier les fichiers du conteneur de test vers celui du service, ce qui n'est pas documenté et s'annonce bien plus compliqué à faire que de tout mettre dans une seule machine.

  2. Docker utilisant ce cache pour éviter de lancer les commandes (i.e. apt-get update), le conteneur n'est donc pas à jours. Sur certains projets, ça peut être génant mais pour notre cas d'usage, c'est acceptable.