GitLab pour tester avec plein de versions de php le code d’un module dolibarr…

Dernière étape de notre série d'articles suite au devcamp de Lyon: aujourd'hui nous allons voir comment l'intégration continue peut lancer les tests sur différents environnements, par exemple pour vérifier que les tests passent en php de version 7.3 à 8.2 ...

Un exemple pour illustrer cet article : php 7.4 et php 8.2:

Exemple de pipeline GitLab avec le test php7.4 qui passe mais pas le test en php8.2
Exemple de pipeline GitLab avec le test php7.4 qui passe mais pas le test en php8.2
Détail de l'erreur en php 8.2
Détail de l'erreur en php 8.2 à propos d'une fonctionnalité dépréciée depuis php 8.0 ...

Comme vous pouvez le voir ce genre de test permet de s'assurer que votre module ne retournera pas d'erreur basique que votre utilisateur soit avec un serveur qui fait tourner php 7.4 ou php 8.2 ... dans notre cas le moteur php 8.2 refuse certaines bricoles que php 7.4 acceptait mais ça pourrait aussi vous permettre de vérifier que des fonctions dépréciées et supprimées (ou modifiées) dans php 8.2 n'impactent pas votre code.

Choix d'architecture

Dans ce qui suit nous avons fait des choix qui correspondent à nos contraintes, il n'est pas question pour nous de dire que c'est LA bonne solution et que tout le monde devrait faire pareil, ce n'est qu'une illustration de la souplesse offerte par GitLab.

Notre choix est donc de nous appuyer sur notre infrastructure technique: des serveurs "bare metal" loués chez des hébergeurs où hébergés dans nos locaux. Notre objectif à ce sujet est de maîtriser le budget alloué et de ne pas tomber dans le "paiement à l'usage" qui ne correspond pas à notre mode de fonctionnement.

GitLab - runner et étiquettes (tags)

Lorsque vous configurez un runner dans GitLab vous pouvez lui affecter des tags comme vous pouvez le voir sur la capture ci-dessous:

Tags associés à deux runner GitLab de notre infrastructure

Ces tags peuvent ensuite être utilisés de manière très simple dans votre fichier .gitlab-ci.yml avec le point d'entrée ... tags !

stages:
  - phpstan-7.4
  - phpstan-8.2

cache:
  paths:
    - .tmp/
    - vendor/

phpstan-7.4:
  stage: phpstan-7.4
  tags:
    - php7.4
  script:
    - /usr/local/bin/phpstan.sh

phpstan-8.2:
  stage: phpstan-8.2
  tags:
    - php8.2
  script:
    - /usr/local/bin/phpstan.sh

Ce fichier de configuration demande donc à gitlab de lancer l'intégration continue sur deux tâches: phpstan-7.4 liée au tag php7.4 et phpstan-8.2 liée au tag php8.2 ... automatiquement gitLab va orchestrer tout ceci dans son pipeline en lançant la tâche sur les runner dont les tags correspondent !

Charge à notre adminsys de faire le nécessaire pour que php7.4 soit disponible, il pourrait à sa convenance faire tout sur le même serveur en ayant recours à des lancements différents, des conteneurs, des chroot, etc. ... ou avoir un runner dédié php7.4 et un autre php 8.2.

Encore une fois il n'y a pas LA solution universelle meilleure que toutes les autres, mais vous pouvez mettre en place la solution adaptée à vos contraintes, choix, habitudes, compétences.

Tester plusieurs versions de dolibarr

Maintenant que nous avons compris la mécanique pour tester sur différentes versions de php notre souhait serait de vérifier que notre module ne "claque" pas d'erreur basique sur dolibarr-14 et dolibarr-18 ...

Première possibilité: le développeur du module souhaite indiquer pour quelle version de dolibarr il veut avoir un retour de tests. Ça semble le plus normal, je développe un module j'indique sur dolistore qu'il est compatible dolibarr-14 les tests doivent donc être lancés sur cette version.

Souvenez vous dans notre fichier phpstan nous indiquions "en dur" le chemin où se trouvent les sources de dolibarr:

fichier de configuration de phpstan qui indique en dur le chemin d'accès à dolibarr-18

Et la version améliorée avec les stubs:

Configuration qui fait appel aux stubs de dolibarr

Comme CAP-REL a eu la bonne idée (oui c'est bien de se lancer des fleurs) de mettre tous les stubs dans un dépôt git il suffirait que le fichier de configuration puisse faire appel à une variable d'environnement ... ce que gitLab permet par l'ajout du mot clé variables:

variables:
  DOLIBARR_VERSION: "18"

stages:
  - phpstan-7.4
  - phpstan-8.2

cache:
  paths:
    - .tmp/
    - vendor/

phpstan-7.4:
  stage: phpstan-7.4
  tags:
    - php7.4
  script:
    - /usr/local/bin/phpstan.sh

Et cette variable est passée au script shell lancé sur le runner comme on peut le voir en demandant d'afficher le détail (via l'ajour de "export" dans script:) ...

Nous pourrions donc utiliser cette variable d'environnement pour influer sur le script phpstan.sh ... approche "système", nous pourrions même nous imaginer un lanceur qui pourrait même prendre un tableau (des valeurs séparées par des espaces) en guise de DOLIBARR_VERSION ...

variables:
  TARGETDOLVERSIONS: "14 15 16"

Ce qui nous permettrait d'avoir

#!/bin/bash
if [ -n "${TARGETDOLVERSIONS}" ]; then
  for DOLV in ${TARGETDOLVERSIONS}
  do
    DOLIBARR_VERSION=${DOLV} phpstan analyse --error-format gitlab --no-progress -c .phpstan.gitlab.neon --memory-limit 6G
  done
fi

Comme nous savons que le support de certaines versions de PHP est lié aux versions de dolibarr il faudrait prendre en compte le fait que php8 n'est officiellement possible que depuis dolibarr-16 par exemple (voir le ChangeLog) etc. Nous pouvons donc imaginer une clé de configuration de ce genre vu que gitLab nous propose de créer des variables

variables:
  DOLIBARR_VERSION_PHP73: "14 15 16 17 18"
  DOLIBARR_VERSION_PHP74: "14 15 16 17 18"
  DOLIBARR_VERSION_PHP80: "17 18"
  DOLIBARR_VERSION_PHP81: "18 19"
  DOLIBARR_VERSION_PHP82: "20"

Pourquoi mettre ça dans le .gitlab-ci.yml ? pour laisser la possibilité au développeur de gérer lui-même la compatibilité de son module avec les versions visées de dolibarr et de php ... mais nous pourrions avoir une politique plus "massive" ou "automatisée" (si nous avions des centaines de modules par exemple) en déplaçant tout ceci sur nos serveurs ...

Le problème qui se pose alors est le suivant: la tâche qui consiste à lancer le test sur toutes les variantes de dolibarr prendra beaucoup de temps et ne sera pas parallélisable !

Tester sur chaque version de dolibarr demande beaucoup de temps surtout le 1er coup lorsque phpstan n'a pas encore mis à jour ou créé son cache !

Après des heures passées à analyser le fonctionnement de phpstan nous avons compris que son mécanisme de cache était empoisonné par notre approche : il faut cloisonner le cache correspondant à chaque version de dolibarr ! Une fois qu'on a compris c'est évident: les fichiers et les classes de dolibarr-14 portent le même nom que dolibarr-15 (et toutes les autres versions) de ce fait le mécanisme de cache détecte que les fichiers sont différents d'une version à l'autre et reconstruit son cache à chaque fois annulant ainsi tout le gain possible lié au mécanisme de cache !

Modification du fichier .phpstan.gitlab.neon pour lui indiquer que le répertoire de stockage du cache est différent pour chaque build selon la version de dolibarr :

includes:
	- .phpstan.dist.neon
parameters:
	scanDirectories:
		- /home/gitlab-runner/dolibarr-stubs-all/dolibarr-%env.DOLIBARR_VERSION%/
	excludePaths:
		analyse:
			- /home/gitlab-runner/dolibarr-stubs-all/dolibarr-%env.DOLIBARR_VERSION%/
	bootstrapFiles:
		- .phpstan-bootstrap.php
	tmpDir: /tmp/dolibarr-%env.DOLIBARR_VERSION%

Cette solution nous permet de retrouver un niveau de perf tout à fait intéressant :

Premier lancement : durée 6m37 ...
Second lancement : le cache joue son rôle la réponse est apportée en moins de 2 secondes ...
Le cache dolibarr-14 est opérationnel mais pas les autres ...
Cette fois tous les cache sont disponibles et le job complet se passe rapidement !

Néanmoins nous aimerions pouvoir lancer chaque test séparément, pour des questions de durée initiale (ou quand le cache n'est plus bon) mais aussi pour pouvoir bénéficier de rapports différentiés comme par exemple:

  • build ok php7.4 dolibarr 14
  • build ok php7.4 dolibarr 15
  • build err php7.4 dolibarr 16
  • build err php7.4 dolibarr 17
  • build err php7.4 dolibarr 18

Or avec notre solution actuelle il suffirait que le build en php7.4 pour une seule version de dolibarr retourne une erreur pour que tout le test soit considéré comme un échec.

Une autre approche consiste alors à consulter la doc de gitLab à la recherche d'une solution de plus haut niveau. On peut y lire qu'un tag peut être constitué à partir d'une variable ou qu'un script peut faire appel à une variable ... reste à trouver une astuce pour mettre tout ça en musique. Un peu plus loin il est question de pouvoir "inclure" un fichier de configuration depuis une adresse web ce qui nous laisse imaginer une grande possibilité de factorisation de la configuration ... et encore un peu plus loin se trouve ce qui sera la clé de notre problème: la possibilité de mettre des "rules" sur des jobs.

Résultat souhaité : chaque composante version-php / version-dolibarr est un job indépendant et a son propre rapport d'erreur ...

Attention c'est un peu complexe ...

Ça nous a demandé quelques heures de réflexions, tests, échecs, tentatives, café, mais finalement voici la solution que nous avons mis en place.

Un dépot git pour stocker / factoriser la configuration

Il est ici : https://inligit.fr/cap-rel/gitlab-caprel-settings

Vous y trouverez un script qui génère le fichier yml qui l'accompagne, vous comprendrez rapidement pourquoi en le lisant. Le fichier yml sera utilisé par tous nos modules dolibarr.

Un fichier gitlab-ci.yml relativement simple pour le développeur

Et ensuite dans notre module dolibarr voici le fichier .gitlab-ci.yml final : in fait un include du fichier commun et ensuite fait appel au stages phpstan après avoir déclaré la variable DOLIBARR_VERSION_PHP74 ... si vous voulez aussi tester sur php 8.2 il suffirait de rajouter une ligne DOLIBARR_VERSION_PHP82 avec la liste des versions dolibarr souhaitées

include:
  - remote: 'https://inligit.fr/cap-rel/gitlab-caprel-settings/-/raw/master/dolibarr-common.yml'

variables:
  DOLIBARR_VERSION_PHP74: "14 15 16 17 18"

stages:
  - phpstan

cache:
  paths:
    - .tmp/
    - vendor/
résultat avec des tests php7.4 et php 8.2 ...
Version "avancée" avec filiation des jobs php -> dolibarr -> phpstan ...

En résumé

Nous avons maintenant une infrastructure qui permet d'automatiser les tests sur nos deux contraintes: la version de php d'un côté et la version de dolibarr de l'autre tout en ajoutant la possibilité de ne pas tester certaines combinaisons php-dolibarr dont on sait qu'elles ne sont pas compatibles.

Avec cette solutions nous devrions maintenant pouvoir débusquer automatiquement un certain nombre de problèmes ...

Vous pourriez également aimer...

Articles populaires