Créé le 03/03/2021
Dernière mise à jour le 06/11/2021
Voici presque un an que je me suis mis à Gitlab CI, depuis l’article à ce sujet (sans compter celui dédié aux tests PHPUnit), il y a eu pas mal de changements. Je profite de l’occasion d’un meetup où nous avons présenté avec Inès Wallon nos avancées sur ce sujet pour synthétiser cette progression.
Vue d’ensemble
En presque un an, j’ai ajouté de nombreux outils sur mon skeleton projet, même si certains étaient déjà mentionnés dans le dernier article, je vais les citer à nouveau pour avoir une liste exhaustive au moment de l’écriture de cet article.
À l’avenir, j’essaierai de trouver des occasions de faire des points d’avancement plus réguliers sur les évolutions faites pour avoir des articles plus digestes à lire (et à écrire :) ).
Après, c’est un équilibre à trouver car beaucoup de changements (surtout pris individuellement) n’ont pas suffisamment d’intérêt pour justifier d’un article dédié, de plus il m’arrive de mettre en place quelque chose et de revenir dessus car pas tout à fait au point ou finalement inutile. C’est en constante évolution / amélioration.
Certains outils ne sont pas encore prêts pour une utilisation avec Drupal, d’autres ne sont pas simples ou n’ont pas été simples à ajouter, cela sera détaillé dans les parties dédiées.
Pour rôder ces outils, j’ai mis en place des branches dédiées :
- ci-tests : comportant du code dédié à faire échouer les tests.
- ci-contrib : pour exécuter les tests sur les modules que je maintiens. Ce qui permet d’avoir plus d’outils que ce que propose actuellement drupalci. J’ai quand même par exemple ignorer les tests du module File Extractor car nécessitant un setup assez particulier qui est déjà géré sur drupalci.
- ci-contrib-smile : idem ci-contrib, pour exécuter les tests sur les modules que je maintiens via Smile.
- ci-tests-example : pour fournir un exemple de tests PHPUnit sur de la configuration projet existante.
Et depuis le weekend dernier, j’ai mis à jour l’architecture de mon site par rapport à tous ces changements pour pouvoir profiter de Gitlab CI et avoir un cas personnel de mise en application.
Améliorations de la CI
Stages
Auparavant avec 2 étapes, "build" et "tests", à mesure de l’ajout de jobs dans l’étape "tests" cela devenait mal réparti et illisible, j’ai donc séparé les tests en :
- sécurité,
- qualité du code,
- tests.
Pour bien faire la distinction entre certains jobs selon leur finalité.
Artifacts
Au début j’utilisais la fonctionnalité de cache prévue dans Gitlab CI pour passer des éléments de build d’une étape à l’autre. Afin de préparer une future gestion de déploiement, je suis passer aux artefacts qui permettent de télécharger, de parcourir le build du projet et sont la base d’un système de rapport dans Gitlab CI.
Visualisation des tests
Gitlab CI gère un système de rapports, de nombreux aspects sont couverts. Ceux qui nous intéressent sont :
- jUnit
- Code Quality : laissé de côté car :
- nécessite un export au format "Code Climate" que la plupart des outils utilisés n’ont pas, ou nécessitant des librairies supplémentaires,
- les résultats de ce type de rapport n’apparaissent que sur les Merge Requests, pas sur le détail d’une pipeline. Ce qui aurait contraint à définir des jobs différents que l’on soit sur une MR ou non afin de pouvoir avoir accès aux résultats de la commande si l’analyse échouait,
- il n’est possible d’avoir qu’un seul job exportant un rapport de ce type par pipeline, voir l’issue https://gitlab.com/gitlab-org/gitlab/-/issues/9014.
- Performance : laissé de côté car fonctionnalité Premium (voir également partie Sitespeed.io).
Ce qui nous laisse avec le format jUnit.
J’ai pu trouver un bug de Gitlab.com, en effet certains rapports (PHP CS, PHPStan) ne fonctionnaient pas, cela a été résolu depuis.
Bien que la plupart des outils ont un export jUnit, j’ai décidé d’utiliser ce type de rapport avec uniquement les jobs de tests à proprement parler, PHPUnit et Behat. Pa11y aussi s’il y avait le support de ce type de rapport (il existe via un plugin, mais pour Pa11y, pas Pa11y-ci que j’utilise). Nous avons par exemple un écart avec le skeleton d’Inès sur ce point, qui elle a choisit d’utiliser les rapport jUnit pour des jobs de code quality.
Améliorations à venir
-
Certains jobs nécessitent d’avoir le site Drupal d’installé, il s’agit pour l’instant de Configuration inspector, Behat et Pa11y.
Pour éviter d’avoir chaque job qui réinstalle le site, il faudrait que je rajoute une étape de build avec export d’un dump de la base de données et adaptation des scripts pour injecter ce dump directement.
- Le fichier .gitlab-ci.yml grandissant au fur et à mesure de l’ajout de fonctionnalité, il faudra que je songe à séparer des parties dans des fichiers dédiés et faire des inclusions. Déjà fait sur le skeleton d’Inès par exemple.
- Modifier les conditions d’exécution de certains jobs afin de pouvoir lancer des pipelines programmés ciblant uniquement les mises à jour de sécurité (voir partie suivante).
Les tâches de la CI
Scans de sécurité
Dans le fichier composer.json (section require-dev), j’utilise les métapaquets "drupal-composer/drupal-security-advisories" et "roave/security-advisories" afin d’empêcher l’ajout de "projets" Drupal et de librairies PHP ayant une mise à jour de sécurité disponible. Et l’exécution de la commande "composer update" permet de vite voir si des mises à jour de sécurité sont disponibles.
Seulement, je voulais intégrer cela dans la CI afin de s’assurer que ce soit exécuté régulièrement et automatiquement (même un projet dormant) et être notifié en cas de mise à jour disponible.
Pour la partie "projets" Drupal, je comptais faire un plugin Composer pour lancer la commande "composer update" en mode dry-run et si la commande échouée, envoyer un mail. Mais en fait, le principe existait déjà, via la commande "Drush pm:security" qui justement s’appuie sur le métapaquet "drupal-composer/drupal-security-advisories".
Tant pis, pas d’envoi d’email dédié et c’est pas plus mal, car géré des mails n’est jamais simple.
Pour la partie librairie PHP, le service Sensiolabs Security Check a été arrêté depuis quelques mois, j’ai également découvert l’existence de la commande "drush pm:security-php" qui utilisait ce service et ait donc signalé que cela ne fonctionnait plus dans l’issue https://github.com/drush-ops/drush/issues/4648. Ayant déjà mis en place la solution de remplacement https://github.com/fabpot/local-php-security-checker je suis resté dessus.
Il est ainsi possible de programmer une exécution de pipeline tous les mercredis soir et être informés de la sortie de mises à jour de sécurité sur un projet en particulier.
Composer
Application des commandes "composer validate" et "composer-normalize" pour vérifier l’état des fichiers composer.json et du fichier composer.lock.
J’ai pu découvrir une limite le weekend dernier via l’usage sur mon site. En effet, je gère les librairies front via Composer et le nom de dossier attendu pour 2 librairies utilisées par le module Slick n’est pas celui obtenu par défaut via Composer. Or je surcharge le nom de dossier de destination dans la configuration installer-paths et cette surcharge doit être placée après la règle générique ce qui s’oppose à un tri alphabétique.
J’ai pour l’instant signalé le problème dans l’issue https://github.com/ergebnis/composer-normalize/issues/699, selon la réponse, je ferai un patch sur le module Slick pour qu’il accepte les dossiers par défaut obtenus via Composer.
Edit 1 : J'ai étudié le code du module Slick pour faire un patch pour qu'il utilise les noms de dossier par défaut des librairies téléchargées. Il s'avère que le module a déjà dynamiquement du code pour cela. J'ai donc pu retirer cette gestion particulière des librairies dans le composer.json.
Shellcheck
Pas de changement depuis le dernier article.
PHP CS
Pas de changement depuis le dernier article.
PHP MD
J’ai juste rajouté des exclusions de règles par rapport à des noms de méthodes ou de variables trop court issue de hooks par exemple.
À noter cependant la désactivation de la détection "MissingImport", en effet PHP MD levait une alerte sur l’utilisation d’une classe globale, exemple "\Exception", et nécessitait l’utilisation d’un use même pour ce cas. Et alors c’est PHP CS qui râle, donc impossible de satisfaire les 2.
Comme PHP MD ne levait pas cette alerte dans d’autre cas où cela était nécessaire pour et où PHPCS lui détectait bien le problème, j’ai préféré désactiver dans PHP MD.
De plus cela évite des problèmes de coding standard sur Drupal.org qui n’est pas équipé de PHP MD.
PHP CPD
Pas de changement depuis le dernier article.
PHPStan
Auparavant j’utilisais drupal-check, mais suite à des problèmes de versions par rapport aux librairies téléchargées et à une discussion par rapport à une utilisation en PHAR ou non de la librairie, je suis passé sur PHPStan directement afin de pouvoir configurer la manière dont j’utilise l’outil et cela évite une couche d’abstraction.
J’ai ajouté quelques exclusions d’erreurs principalement dues au typage lié à l’utilisation de la fonction t() / à l’objet TranslatableMarkup qui fonctionnent comme des strings, mais niveau typage ce n’est pas bon.
À part ça, les problèmes détectés dans le code peuvent être réglés, il n’y a que de temps en temps l’ajout d’exclusions via un commentaire Phpstan pour des points intraitables car dans du code surchargeant du code du noyau Drupal ou de modules communautaires.
Rector
Outil de détection du code déprécié permettant de générer une correction du code en question. Mis en lumière l’an passé avec la préparation du passage à Drupal 9 par la communauté.
En règle général pas de souci, mais à noter une erreur que je n’ai pour l’instant pas réussi à expliquer sur le code de mon site :
Fatal error: require(): Failed opening required 'app/core/modules/migrate/src/Plugin/MigrationInterface.php'
Utiliser normalement dans le code du module ayant servi à la migration des contenus. J’ai pour l’instant exclu les classes en question. Il faudra que je signale le bug si pas déjà existant.
Edit 2 : l'issue a été créée : https://github.com/palantirnet/drupal-rector/issues/132
Configuration Inspector
À la base, j’avais fait uniquement une commande Make pour faciliter l’analyse sur l’environnement de développement. Et une fois que j’avais géré une tâche (Behat) qui nécessitait l’installation du site, je me suis dit que cela pouvait être facilement mis dans Gitlab CI.
Configuration Inspector est un module qui permet de vérifier que le schéma de configuration déclaré dans les fichiers schema.yml des extensions Drupal est bien respecté par la configuration active.
Le module fournit un rapport BO, mais surtout une commande Drush.
J’ai quand même indiqué que le job pouvait échoué car de nombreux modules communautaires Drupal n’ont pas leur schéma de configuration déclaré ou déclaré sans erreur.
Cspell
Cet outil permet de vérifier dans le code et les commentaires les fautes de frappe.
J’ai surchargé la configuration du noyau Drupal afin de pouvoir indiquer des dictionnaires de mots propres au projet.
Au début tant que les mots propres à un projet ou des modules, ou que certains fichiers (comme un PDF de test) ne sont pas exclus, il y a beaucoup d’erreurs, après les fautes de frappe ressortent bien.
YAML Lint
Utilisation de la librairie "grasmash/yaml-cli" que j’ai utilisé via un script custom car la commande fournie prend uniquement un fichier en argument, impossible de préciser un chemin. D’où la création d’un script pour trouver les fichiers YAML des dossiers à analyser.
Stylelint
Linter pour les fichiers CSS.
Utilisation du fichier de configuration du noyau avec uniquement la surcharge du fichier .stylelintignore pour préciser les fichiers à ignorer propres à un projet.
Le script fournit une option à la commande pour corriger automatiquement le code.
ESLint
Linter pour les fichiers JS.
J’ai pu discuter avec Théodore Biadala au sujet de l’utilisation d’ES6 dans le noyau Drupal. Il y a justement l’initiative d’amélioration du JS du noyau afin entre autre de pouvoir plus facilement utiliser ES6 dans les modules communautaires et le code spécifique. Actuellement la compilation ES6 du noyau est uniquement à destination du noyau. J’ai donc décidé de rester sur des fichiers JS, et d’utiliser la configuration ".eslintrc.legacy.json".
De la même manière qu’il y a près d’un an, j’ai rencontré des problèmes avec le plugin "eslint-config-prettier". Nous avons découvert que cela venait d’un fichier .eslintrc.json qui était géré via drupal-scaffold et pris en compte car nous lancions la commande eslint dans le dossier des sources Drupal pour prendre en compte le fichier .eslintignore qui s’y situe. Le fichier .eslintrc.json était pris en compte malgré le fait que dans les options de la commande on précisait un autre fichier de configuration. Il a suffit de le supprimer et de l’exclure du scaffolding.
Je n’ai pour l’instant pas fait de système pour avoir un fichier .eslintignore surchargeable dans un projet car celui fournit par le noyau est suffisant pour l’instant.
Le script fournit une option à la commande pour corriger automatiquement le code.
PHPUnit
Pas de changement depuis l’article précédent sur les tests PHPUnit, à part le fait de gérer si dans un contexte de CI de faire un export jUnit des résultats des tests.
Behat
Un des points les plus complexes à mettre en place. Les étapes ont été les suivantes :
- reprendre la configuration Behat et les scripts que j’avais fait il y a 3 ans pour lancer les tests Behat afin de l’adapter aux changements qui ont eu lieu depuis dans mon skeleton et vérifier que cela fonctionne sur un environnement de développement,
- faire en sorte d’avoir un site installé lors de l’exécution de la pipeline. Le script utilisé pour l’installation Drupal a quasi fonctionné immédiatement, le seul point a été de changer dynamiquement certaines variables utilisées dans les scripts et de définir un alias Drush dédié à Gitlab CI. Heureusement le chemin des sources est constant : /builds/namespace/nom_projet/
-
comme l’URL du site diffère de l’environnement de développement, il a fallu pouvoir changer cette valeur dans la configuration Behat, j’ai pour cela utilisé le système de profil Behat pour justement lui préciser des ensembles de paramètres à prendre via une option de la commande.
Un point difficile a été le paramètre drupal_root où j’ai utilisé le fait que Behat prend en compte une variable d’environnement BEHAT_PARAMS (un peu comme certaines variables PHPUnit) pour générer cette variable dynamiquement. Avec le recul comme le chemin vers les sources est finalement constant quelque soit la pipeline, cela aurait pu être mis directement dans le fichier de configuration Behat. Au moins le système pour injecter des variables dynamique à Behat est en place.
Extrait du fichier de configuration Behat pour la déclaration des profils :
docker-dev: extensions: Drupal\MinkExtension: base_url: 'http://web' gitlab-ci: extensions: Drupal\MinkExtension: base_url: 'http://localhost:8888'
Extrait du fichier de gestion des variables des scripts pour la génération dynamique de la variable BEHAT_PARAMS :
# Export BEHAT_PARAMS dynamically. BEHAT_PARAMS=$(jq -n \ --arg APP_PATH "$APP_PATH" \ '{"extensions":{"Drupal\\DrupalExtension":{"drupal":{"drupal_root":$APP_PATH}}}}') export BEHAT_PARAMS
J’ai rencontré des complications avec le cas concret de mon site, mais cela fera l’objet d’un prochain article.
Pa11y
Pa11y et sa version CI Pa11y-ci sont des outils de tests d’accessibilité. J’avais testé l’outil et créé des images Docker pour faciliter son usage en 2019 : voir l’article dédié.
Les étapes pour la mise en place des tests ont été :
- Reprendre ces images Docker car non utilisée, donc nécessitant des mises à jour et revoir la documentation sur l’utilisation.
- Faire en sorte de pouvoir s’en servir sur l’environnement de développement. Cela s’est fait via le fichier docker-compose.yml pour avoir un conteneur pouvant atteindre le site via l’alias du service (mais du coup pas d’URL en HTTPS par rapport à ma stack).
- Faire en sorte de pouvoir s’en servir dans Gitlab CI… :
- Avec Pa11y-ci pas de système de profil comme pour Behat, d’où la nécessité d’avoir un fichier de configuration propre à l’environnement Gitlab CI car l’alias web n’allait pas fonctionner.
-
Dans un job, jusqu’à présent c’est le conteneur PHP qui faisait les actions (section "script"). Dans le cas de PHPUnit, c’est toujours ce même conteneur qui pouvait piloter le conteneur "chrome" pour les tests Functional Javascript. Avec Pa11y-ci, la commande ne peut être lancé que depuis ce conteneur mais impossible de changer de conteneur car il faut bien lancer le serveur web et installer le site via le conteneur PHP.
D’où la décision de modifier l’image Docker utilisée dans la CI pour y intégrer directement Pa11y-CI.
Et quelle surprise de voir qu’un Drupal standard ne passe pas le standard "WCAG2AA" à cause de l’issue https://www.drupal.org/project/drupal/issues/1852090, dès qu’il y a 2 formulaires sur un page avec pour chacun une section "actions", on obtiens l’erreur "Duplicate id attribute value "edit-actions"".
D’où le fait que j’ai autorisé le job a échoué.
Sur mon site c’est beaucoup d’erreurs de contraste qu’il faudra que je regarde.
Les outils hors CI
Upgrade Status
Le module Upgrade Status permet de vérifier qu’un site Drupal 8 est prêt à être migré en Drupal 9. Upgrade Status n’a pas à être utilisé sur Drupal 9, j’ai cependant laissé la commande Make car je suppose qu’à la sortie de Drupal 10, Upgrade Status sera à réutiliser à ce moment là.
PHPCS Fixer
Ajouté avec une configuration s’appuyant sur "drupol/phpcsfixer-configs-drupal", afin de rajouter la surcharge de coding standard sur les strict type.
Cependant j’ai constaté que la commande ne détecte pas les fichiers avec une extension autre que .php.
GrumPHP
Beaucoup d’outils sont exécutés dans la CI, il y a également des commandes Make afin de faciliter leur exécution sur l’environnement de développement et de manière indépendante à l’IDE utilisé. Mais au plus vite le développeur est alerté des problèmes au mieux c’est, donc autant éviter de devoir attendre les résultats d’une merge request et c’est là que GrumPHP intervient.
C’est une librairie PHP permettant d’exécuter une batterie de vérifications avant le commit et de vérifier le commit en lui-même en utilisant les hooks Git.
Chose pratique, il ne lance les analyse que sur les fichiers qui vont être commités et non sur l’ensemble du code.
Je n’ai malgré cela pas mis tous les points possibles gérables via GrumPHP, car je n’ai pas envie d’attendre qu’un test PHPUnit de 3 minutes m’empêche de commiter, je n’ai mis que des outils de qualité de code et de vérification du message du commit.
À noter :
- je n’ai pas mis PHP MD car impossible de lui préciser un fichier de configuration PHP MD,
- pour PHP CS, idem, impossible de lui préciser un fichier de configuration PHP CS, comme j’utilise les standards avec des règles supplémentaires uniquement, j’ai listé les 2 ensembles de coding standard fournis par l’extension Coder,
- GrumPHP embarque un linter JSON,
- la liste de mot interdits est très pratique pour éviter un console.log qui traîne,
- il est possible de forcer aussi une convention de nom de branche Git, mais je ne suis pas allé à ce point là.
Au pire il est toujours possible d’utiliser l’option "--no-verify" pour éviter la vérification et la CI interceptera les problèmes normalement.
La prochaine étape sera une intégration directement dans l’IDE des outils pour accélérer la résolution des problèmes.
Psalm
Je n’ai pas trouvé de configuration pour Drupal, et ai plutôt trouvé au contraire des problèmes rencontrés par d’autres personnes ayant essayé l’outil sur du code Drupal.
Comme les solutions de contournements ne sont pas encore très claires, j’avais décidé de ne pas inclure l’outil, il y a déjà assez d’analyseurs PHP. Mais suite à une discussion avec Adrien Lucas à propos de PHPStan et d’une dépréciation Symfony impactant Drupal, je l’ai remis mais non présent dans la CI. Uniquement là pour être exécuté pour challenger un résultat PHPStan si besoin.
Outils non intégrés
Pour l’instant en tout cas.
Twig lint
Je voulais intégrer la librairie https://github.com/asm89/twig-lint, mais cette dernière n’est plus maintenue et impossible d’utiliser directement twigcs car il faut pouvoir préciser que l’on est dans un contexte Drupal pour que le linter puisse prendre en compte les spécificités Twig liées à Drupal.
En cherchant, j’ai trouvé que dans Acquia BLT il y avait une intégration fonctionnelle pour Drupal, seulement impossible de reprendre cela simplement en script Shell. En effet, c’est tout un ensemble de scripts PHP basé sur Robo qui ont été faits dans Acquia BLT et il est compliqué de reprendre qu’une partie.
C’est complexe, mais je comprends tout à fait la démarche, cela permet quelque soit l’outil de gérer sa configuration dans un fichier centralisé pour tous les outils et du coup d’encapsuler les spécificités de chaque outils pour qu’ils se comportent et s’utilisent tous de manière identiques.
Pour l’instant j’ai laissé ce point de côté, car réécrire tous les scripts faits jusqu’à présent n’est pas ma priorité :).
Sitespeed.io
Après la qualité du code, les tests, l’accessibilité, il y avait un aspect qui manquait d’automatisation, les performances !
Il existe de nombreux outils de mesure de performance utilisable dans une CI et conteneurisés. Par exemples, Lighthouse CI ou Sitespeed.io. Ce dernier bénéficie d’une intégration facilitée avec Gitlab CI.
Cependant le rapport de performance Gitlab CI étant une fonctionnalité premium et comme pour l’instant il n’y a pas de gestion du déploiement via Gitlab CI, la question de la pertinence des résultats se pose pour ce qui est des temps de réponses par exemple.
Des points comme l’accessibilité ou des détections de redirections seraient restés valables même si exécuté sur un environnement non contrôlé.
J’ai donc voulu tester uniquement la mise en place sur un environnement de développement. Mais vu que l’image Docker fait 2 Go, j’ai laissé cette idée de côté car si cela n’est pas utilisé régulièrement, cela ne sert pas à grand-chose.
J’ai documenté un usage manuel dans l’issue concernée.
Cypress
Cypress est un framework de tests end-to-end JS.
J’ai pour l’instant commencé à me renseigner à ce sujet et ai vu des présentations d’une utilisation avec Drupal impliquant le module du même nom.
Le module a l’air d’apporter des avantages intéressants avec entre autres :
- une intégration Drush pour faciliter l’utilisation de Cypress,
- une mise en cache de l’installation Drupal pour gagner du temps sur l’exécution des tests.
Mais cela oblige donc à ce que NodeJS et Cypress soient installés sur le même environnement que PHP pour que Drush puisse piloter Cypress.
Les premiers tests n’ont pas été très concluants :
- cela nécessiterait de nombreuses librairies à ajouter dans les images Docker nécessaires pour faire fonctionner Cypress,
- des fonctionnalités de Cypress comme Cypress Studio ne sont pas accessibles, alors qu’elles fournissent un avantage certain sur l’écriture de tests Behat ou PHPUnit Javascript par exemples,
- il existe déjà de nombreuses images Docker Cypress officielles pour faire fonctionner les navigateurs souhaités dans la version souhaitées, ce serait dommage de ne pas pouvoir s’en servir.
La prochaine fois que je retravaillerai sur l’utilisation de Cypress, je passerai donc plutôt par une approche native Cypress indépendante de Drupal.
Conclusion
Cela fait une bonne dose de changements depuis un an et cet article me permet de prendre du recul sur le chemin parcouru.
Il va continuer à y avoir du changement, la roadmap est toujours accessible publiquement et j’ai indiqué les prochains points dans l’article.
Ce qu’il faut retenir :
- Ce n’est qu’un exemple d’utilisation de Gitlab CI avec Drupal.
- Il faut prendre du recul sur les outils, ce ne sont que des outils. Ils sont là pour aider, si un outil pose problème il ne faut pas se forcer à s’en servir. Certains d’ailleurs scannent les mêmes règles de manière différentes et peuvent donner des résultats incompatibles entre eux.
- Il y a toujours des choses à améliorer, avec l’évolution des pratiques et des outils, des points laissés de côté, des adaptations à faire à chaque cas d’usage.
Prochaine étape, un article PHPUnit vs Behat en expérimentant directement sur mon site.
Remerciements
Merci à :
- Inès Wallon pour les échanges et l’entraide quasi continus sur ce sujet.
- Théodore Biadala pour la discussion à propos des outils NodeJS du noyau Drupal.
- Adrien Lucas pour la discussion sur les PHPStan et Psalm.
- Bastien Rigon pour les retours faits au fil de l’intégration de nouveaux éléments dans mon skeleton.
- Smile de m’avoir permis de passer du temps sur ce sujet.
Ajouter un commentaire