Retour au blog
Tests

PrestaFlow en CI hors GitHub Actions : GitLab, Bitbucket, CircleCI, Jenkins

PrestaEdit •
PrestaFlow en CI hors GitHub Actions : GitLab, Bitbucket, CircleCI, Jenkins

Décor

L’article 2 de la série a posé deux niveaux de CI :

  • Niveau 1 — la CLI PrestaFlow dans un workflow, sans compte prestaflow.io, avec Docker pour lancer Flashlight à la main.
  • Niveau 2 — l’intégration officielle qui gère Flashlight, le commentaire PR/MR, et la synchro plateforme (report URL, historique, régressions visuelles).

Le niveau 2 n’existe aujourd’hui que pour GitHub Actions. Trois portages sont en préparation, avec les mêmes inputs et le même endpoint côté plateforme, mais aucun n’est encore publié : leurs dépôts GitHub n’ont qu’un tag v0.1.0, et ni le registre GitLab, ni Docker Hub, ni le registre des orbs CircleCI ne les connaissent.

PlateformeNiveau 2Référence prévueÉtat
GitHubActionPrestaFlow/github-action@v2publiée
GitLabCI/CD Componentgitlab.com/prestaflow/ci/prestaflow@v0.1.0en préparation
BitbucketPipe Dockerdocker://prestaflow/pipe-pushen préparation
CircleCIOrbprestaflow/prestaflowen préparation
Jenkins—Jenkinsfile déclaratifniveau 1 uniquement

Cette annexe donne donc surtout le niveau 1 pour chaque plateforme, celui qui fonctionne aujourd’hui, et montre la syntaxe prévue du niveau 2 pour que vous sachiez à quoi vous attendre.

Ce qui doit tourner, quel que soit le CI

Le workflow niveau 1 fait six choses, dans l’ordre :

  1. git clone du projet (implicite dans tout CI)
  2. Installer PHP (typiquement 8.2), Composer et Chromium
  3. composer install
  4. Démarrer une base MariaDB puis Flashlight, en y montant le module
  5. Installer le module au démarrage de Flashlight (init-script), puis attendre que le back office réponde
  6. Lancer composer prestaflow -- run ./tests/prestaflow avec les variables d’environnement qui pointent sur la boutique

Le point 4 conditionne le reste. Pour que PrestaShop voie le module, il faut monter le dépôt cloné dans modules/psflowdemo du conteneur, puis l’installer : c’est l’init-script tests/flashlight-init/10-install-module.sh de l’article 2, qui lance php /var/www/html/bin/console prestashop:module install psflowdemo au premier démarrage.

Or les side-cars déclaratifs (services: de GitLab ou de Bitbucket) ne permettent pas de monter le checkout dans le conteneur. Flashlight sait aussi installer des zips placés dans le dossier INSTALL_MODULES_DIR, mais ce dossier doit exister dans le conteneur, et on n’y a pas accès non plus. La solution qui marche : piloter un démon Docker depuis le job et lancer MariaDB et Flashlight avec docker run, exactement comme dans l’article 2. Chaque plateforme fournit ce démon à sa façon.

Deux réglages valent pour les trois plateformes ci-dessous :

  • CHROME_NO_SANDBOX=1. Les images php:8.2-cli tournent en root, et Chromium refuse de démarrer en root sans --no-sandbox (« Running as root without —no-sandbox is not supported »). PrestaFlow v1.7.1 ne passe pas cette option, mais chrome-php la lit dans cette variable d’environnement et ajoute alors --no-sandbox.
  • Les variables PRESTAFLOW_* passent par l’environnement. PrestaFlow v1.7.1 les lit dans $_ENV, rempli seulement si variables_order contient E. L’image officielle php:8.2-cli n’embarque pas de php.ini : la valeur par défaut EGPCS s’applique, donc tout va bien. Sur une image qui charge le php.ini de production (GPCS), ajoutez -d variables_order=EGPCS ou un php.ini adapté.

GitLab CI

Niveau 1 — CLI seule

Sur GitLab, le démon Docker vient d’un service docker:dind. Il faut un runner qui accepte le mode privilégié : c’est le cas des runners Linux partagés de GitLab.com ; sur un runner auto-hébergé, activez privileged = true.

Créez .gitlab-ci.yml à la racine :

stages:
  - test

e2e:
  stage: test
  image: php:8.2-cli
  services:
    - name: docker:27-dind
      alias: docker
  parallel:
    matrix:
      - PS_VERSION: ["8.1.7", "9.0.0"]
  variables:
    DOCKER_HOST: tcp://docker:2375
    DOCKER_TLS_CERTDIR: ""
    PRESTAFLOW_PS_VERSION: $PS_VERSION
    PRESTAFLOW_LOCALE: en
    PRESTAFLOW_FO_URL: http://docker/
    PRESTAFLOW_BO_URL: http://docker/admin-dev/
    PRESTAFLOW_BO_EMAIL: admin@prestashop.com
    PRESTAFLOW_BO_PASSWD: prestashop
    PRESTAFLOW_HEADLESS: "true"
    CHROME_NO_SANDBOX: "1"
  before_script:
    - apt-get update && apt-get install -y --no-install-recommends curl chromium docker-cli libgd-dev unzip git
    - docker-php-ext-install gd
    - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
    - composer install --prefer-dist --no-progress
    - docker network create prestashop
    - >-
      docker run -d --name mysql --network prestashop
      -e MARIADB_ROOT_PASSWORD=prestashop -e MARIADB_DATABASE=prestashop
      -e MARIADB_USER=prestashop -e MARIADB_PASSWORD=prestashop
      mariadb:11
    - >-
      docker run -d --name ps --network prestashop -p 80:80
      -e PS_DOMAIN=docker -e MYSQL_HOST=mysql
      -v "$CI_PROJECT_DIR":/var/www/html/modules/psflowdemo
      -v "$CI_PROJECT_DIR/tests/flashlight-init":/tmp/init-scripts:ro
      prestashop/prestashop-flashlight:$PS_VERSION
    - timeout 300 sh -c 'until [ "$(curl -s -o /dev/null -w "%{http_code}" http://docker/admin-dev/)" = "302" ]; do sleep 2; done' || { docker logs ps; exit 1; }
  script:
    - composer prestaflow -- run ./tests/prestaflow
  artifacts:
    when: on_failure
    paths:
      - prestaflow/screens/errors/
    expire_in: 1 week

Quatre particularités GitLab :

  • Le service docker:dind fournit le démon, joignable sous l’alias docker. Les conteneurs lancés par docker run tournent dans ce service : un port publié avec -p 80:80 se joint donc sur http://docker/, et non sur localhost. D’où PS_DOMAIN=docker : PrestaShop redirige vers son domaine configuré, qui doit correspondre à l’URL de PRESTAFLOW_FO_URL.
  • Le montage du checkout fonctionne : le dossier du projet ($CI_PROJECT_DIR, sous /builds) est partagé entre le job et ses services, ce qui permet au démon dind de le monter dans Flashlight. C’est ce qui rend l’installation du module possible.
  • parallel:matrix: duplique le job pour chaque valeur de PS_VERSION : deux jobs, deux boutiques, deux runs. La variable sert à la fois au tag de l’image Flashlight et à PRESTAFLOW_PS_VERSION.
  • artifacts: ne s’attache qu’on_failure, comme le if: failure() de GitHub. Les captures d’erreur (prestaflow/screens/errors/, prises à l’échec d’une assertion) restent téléchargeables 7 jours.

L’attente sur /admin-dev/ échoue franchement au bout de 5 minutes (timeout renvoie un code non nul, et le || { …; exit 1; } affiche les logs de Flashlight avant d’arrêter le job) : une boucle for qui se contente de break laisserait le job continuer contre une boutique morte. La locale est en : l’image Flashlight n’installe que l’anglais.

Niveau 2 — CI/CD Component (en préparation)

Le portage GitLab de l’Action vit dans le dépôt GitHub PrestaFlow/gitlab-component. Il n’est pas encore publié : le projet gitlab.com/prestaflow/ci n’existe pas, donc la ligne include ci-dessous échoue aujourd’hui. Voici la syntaxe prévue par son README :

include:
  - component: gitlab.com/prestaflow/ci/prestaflow@v0.1.0
    inputs:
      token: $PRESTAFLOW_TOKEN
      project_id: pk_01ABCDEF
      flashlight: "true"
      ps_version: "9.0.0"
      flashlight_init_scripts: tests/flashlight-init
      mr_comment: "true"

Les inputs du template : token, project_id (Product Key pk_…), execute (lance composer run prestaflow:json:file, le script Composer de l’article 2), suites, flashlight, ps_version, flashlight_mount (auto/root/modules/themes), flashlight_init_scripts, mr_comment, upload_artifacts, visual, ainsi que image (par défaut composer:2) et stage. Le job démarre lui-même un service docker:24-dind pour Flashlight.

Deux variables CI/CD, masquées, à définir côté projet :

  • PRESTAFLOW_TOKEN — pour l’API.
  • GITLAB_TOKEN — token GitLab avec le scope api, pour poster la note sur la MR. Le CI_JOB_TOKEN par défaut ne peut pas commenter les MR.

Le template publie un artifact prestaflow.env (format dotenv) avec PRESTAFLOW_REPORT_ID, PRESTAFLOW_REPORT_URL, PRESTAFLOW_PASSED, PRESTAFLOW_FAILED, PRESTAFLOW_SKIPPED, PRESTAFLOW_TOTAL, PRESTAFLOW_DURATION_MS et PRESTAFLOW_STATUS. Un job en aval y accède via needs: [{job: prestaflow, artifacts: true}].

Bitbucket Pipelines

Niveau 1 — CLI seule

Sur Bitbucket, le démon vient du service docker intégré. Quand il est activé sur un step, la CLI docker est disponible dans le conteneur du build, et les ports publiés par docker run -p se joignent sur localhost, puisque les services partagent le réseau du step.

Créez bitbucket-pipelines.yml :

image: php:8.2-cli

definitions:
  services:
    docker:
      memory: 3072

pipelines:
  default:
    - step:
        name: E2E — PrestaShop 8.1.7
        size: 2x
        services:
          - docker
        script:
          - apt-get update && apt-get install -y --no-install-recommends curl chromium libgd-dev unzip git
          - docker-php-ext-install gd
          - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
          - composer install --prefer-dist --no-progress
          - docker network create prestashop
          - >-
            docker run -d --name mysql --network prestashop
            -e MARIADB_ROOT_PASSWORD=prestashop -e MARIADB_DATABASE=prestashop
            -e MARIADB_USER=prestashop -e MARIADB_PASSWORD=prestashop
            mariadb:11
          - >-
            docker run -d --name ps --network prestashop -p 80:80
            -e PS_DOMAIN=localhost -e MYSQL_HOST=mysql
            -v "$BITBUCKET_CLONE_DIR":/var/www/html/modules/psflowdemo
            -v "$BITBUCKET_CLONE_DIR/tests/flashlight-init":/tmp/init-scripts:ro
            prestashop/prestashop-flashlight:8.1.7
          - timeout 300 sh -c 'until [ "$(curl -s -o /dev/null -w "%{http_code}" http://localhost/admin-dev/)" = "302" ]; do sleep 2; done' || { docker logs ps; exit 1; }
          - export PRESTAFLOW_PS_VERSION=8.1.7
          - export PRESTAFLOW_LOCALE=en
          - export PRESTAFLOW_FO_URL=http://localhost/
          - export PRESTAFLOW_BO_URL=http://localhost/admin-dev/
          - export PRESTAFLOW_BO_EMAIL=admin@prestashop.com
          - export PRESTAFLOW_BO_PASSWD=prestashop
          - export PRESTAFLOW_HEADLESS=true
          - export CHROME_NO_SANDBOX=1
          - composer prestaflow -- run ./tests/prestaflow
        artifacts:
          - prestaflow/screens/errors/**

Trois particularités Bitbucket :

  • Les montages sont limités au dossier du clone. Bitbucket n’accepte un -v que sous $BITBUCKET_CLONE_DIR : c’est justement là que se trouvent le module et ses init-scripts.
  • La mémoire se règle sur le service et sur le step. Le service docker dispose par défaut de 1024 Mo, trop peu pour MariaDB et Flashlight réunis. On lui en donne 3072 dans definitions, et size: 2x sur le step double la mémoire totale disponible, pour que Chromium et Composer gardent de la place dans le conteneur du build.
  • Les artefacts ne sont pas conditionnels : ils sont capturés à la fin du step, qu’il passe ou non. Quand tout passe, prestaflow/screens/errors/ est simplement vide.

Niveau 2 — Pipe (en préparation)

Le pipe vit dans le dépôt GitHub PrestaFlow/bitbucket-pipe. Son image n’est pas encore sur Docker Hub : la référence ci-dessous, tirée de son README, ne se télécharge pas aujourd’hui.

image: php:8.3-cli

definitions:
  services:
    docker:
      memory: 3072

pipelines:
  pull-requests:
    '**':
      - step:
          name: PrestaFlow
          size: 2x
          services: [docker]
          artifacts: [prestaflow.env]
          script:
            - pipe: docker://prestaflow/pipe-push:1.0.0
              variables:
                TOKEN: $PRESTAFLOW_TOKEN
                PROJECT_ID: pk_01ABC...
                FLASHLIGHT: 'true'
                PS_VERSION: '9.0.0'
                FLASHLIGHT_INIT_SCRIPTS: 'tests/flashlight-init'
                BITBUCKET_ACCESS_TOKEN: $PRESTAFLOW_BITBUCKET_TOKEN

      - step:
          name: Notify
          script:
            - . ./prestaflow.env
            - echo "Report → $PRESTAFLOW_REPORT_URL"

Variables prévues : TOKEN, PROJECT_ID, API_URL, EXECUTE, SUITES (sans effet avec PrestaFlow v1.7.1, comme sur GitLab), FLASHLIGHT, PS_VERSION, FLASHLIGHT_MOUNT (auto/root/modules/themes), FLASHLIGHT_INIT_SCRIPTS, PR_COMMENT (automatique sur les builds de pull request), UPLOAD_ARTIFACTS, VISUAL (défaut true) et BITBUCKET_ACCESS_TOKEN, un token d’accès au dépôt avec pullrequest:write pour les commentaires.

Deux points à noter :

  • services: [docker] est obligatoire dès que FLASHLIGHT=true : le pipe démarre MariaDB et Flashlight avec docker compose, via le démon Docker du service.
  • Commentaires PR idempotents : le pipe insère un marqueur <!-- prestaflow-run:<project-key> --> dans son commentaire, ce qui lui permet de mettre à jour le commentaire existant plutôt que d’en ajouter un à chaque run.

Le prestaflow.env produit contient les mêmes variables que celui du component GitLab (PRESTAFLOW_REPORT_URL, PRESTAFLOW_STATUS, etc.) et se source dans un step suivant.

CircleCI

Niveau 2 — Orb (en préparation)

Le portage CircleCI vit dans le dépôt GitHub PrestaFlow/circleci-orb. L’orb prestaflow/prestaflow n’est pas encore publié dans le registre CircleCI : la configuration ci-dessous, tirée de son README, sera refusée tant qu’il ne l’est pas.

L’orb prévoit un job clé en main, prestaflow/test, et des commandes à composer soi-même : push (qui enchaîne tout), flashlight, install-deps, run-tests, visual-download, visual-upload, upload, comment-pr et set-outputs.

.circleci/config.yml minimal :

version: 2.1
orbs:
  prestaflow: prestaflow/prestaflow@1.0.0
workflows:
  test:
    jobs:
      - prestaflow/test:
          project_id: pk_01ABCDEFGHIJKLMNOPQR10
          flashlight: true
          ps_version: "9.0.0"
          flashlight_init_scripts: tests/flashlight-init
          context: prestaflow

Trois particularités CircleCI :

  • Exécuteur machine : le job prestaflow/test l’utilise par défaut, car Flashlight doit monter le checkout, ce que setup_remote_docker ne permet pas. Gardez-le en tête pour votre consommation de crédits.
  • GITHUB_TOKEN ou BITBUCKET_ACCESS_TOKEN à créer à la main : CircleCI n’injecte pas de token VCS. Sans lui, pas de commentaire PR, mais le run remonte quand même sur la plateforme.
  • Sorties via BASH_ENV : les variables (PRESTAFLOW_REPORT_URL, etc.) sont exportées dans BASH_ENV et écrites dans un fichier prestaflow.env, plutôt qu’en outputs de step comme sur GitHub.

Niveau 1

Jenkins (déclaratif)

Jenkins n’a pas d’intégration officielle, même en préparation. Le niveau 2 y passe par un curl vers l’API prestaflow.io (voir la fin de l’article) ; le niveau 1 reste la voie normale.

Créez Jenkinsfile à la racine :

pipeline {
    agent {
        docker {
            image 'php:8.2-cli'
            args '-u root --network host -v /var/run/docker.sock:/var/run/docker.sock'
        }
    }

    environment {
        PRESTAFLOW_PS_VERSION = '8.1.7'
        PRESTAFLOW_LOCALE = 'en'
        PRESTAFLOW_FO_URL = 'http://localhost/'
        PRESTAFLOW_BO_URL = 'http://localhost/admin-dev/'
        PRESTAFLOW_BO_EMAIL = 'admin@prestashop.com'
        PRESTAFLOW_BO_PASSWD = credentials('prestashop-admin-passwd')
        PRESTAFLOW_HEADLESS = 'true'
        CHROME_NO_SANDBOX = '1'
    }

    stages {
        stage('Prepare') {
            steps {
                sh 'apt-get update && apt-get install -y --no-install-recommends curl chromium docker-cli libgd-dev unzip git'
                sh 'docker-php-ext-install gd'
                sh 'curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer'
                sh 'composer install --prefer-dist --no-progress'
            }
        }

        stage('Start Flashlight') {
            steps {
                sh 'docker run -d --rm --name mysql --network host -e MARIADB_ROOT_PASSWORD=prestashop -e MARIADB_DATABASE=prestashop -e MARIADB_USER=prestashop -e MARIADB_PASSWORD=prestashop mariadb:11'
                sh 'docker run -d --rm --name ps --network host -e PS_DOMAIN=localhost -e MYSQL_HOST=127.0.0.1 -v "$WORKSPACE":/var/www/html/modules/psflowdemo -v "$WORKSPACE/tests/flashlight-init":/tmp/init-scripts:ro prestashop/prestashop-flashlight:8.1.7'
                sh '''timeout 300 sh -c 'until [ "$(curl -s -o /dev/null -w "%{http_code}" http://localhost/admin-dev/)" = "302" ]; do sleep 2; done' || { docker logs ps; exit 1; }'''
            }
        }

        stage('Run PrestaFlow') {
            steps {
                sh 'composer prestaflow -- run ./tests/prestaflow'
            }
        }
    }

    post {
        failure {
            archiveArtifacts artifacts: 'prestaflow/screens/errors/**', allowEmptyArchive: true
        }
        always {
            sh 'docker rm -f ps mysql 2>/dev/null || true'
        }
    }
}

Quatre particularités Jenkins :

  • Le socket Docker de l’hôte, pas du Docker-in-Docker. L’agent est un conteneur PHP ; pour qu’il lance Flashlight, on lui monte /var/run/docker.sock et on installe la CLI (docker-cli) dans le stage Prepare. Les conteneurs démarrés tournent alors sur le démon de l’hôte, à côté de l’agent. Donner ce socket revient à donner à l’agent le contrôle du Docker de l’hôte : réservez-le à des nœuds dédiés à la CI.
  • -u root : par défaut, Jenkins lance le conteneur avec l’uid de l’utilisateur de l’agent, qui n’a pas le droit de faire apt-get install ni d’accéder au socket. En root, CHROME_NO_SANDBOX devient nécessaire. Revers : les fichiers créés dans le workspace appartiennent à root, prévoyez un cleanWs() ou un nettoyage équivalent.
  • Le montage du workspace : $WORKSPACE est un chemin de l’hôte, que le plugin Docker Pipeline monte au même chemin dans l’agent. Le -v "$WORKSPACE":… fonctionne donc tant que l’agent Jenkins tourne directement sur l’hôte Docker ; si l’agent est lui-même conteneurisé, ce chemin n’existe pas côté démon.
  • credentials() et post { always } : le Credentials Store fournit le mot de passe sous forme de variable d’environnement, et post { always } supprime les conteneurs Flashlight et MariaDB même en cas d’échec, pour éviter les zombies sur le nœud.

Ce qui reste identique partout

Peu importe le CI et le niveau, les mêmes règles s’appliquent :

  • Une base à côté de Flashlight, et PS_DOMAIN — l’image embarque nginx et php-fpm mais pas MySQL. Sans base, la boutique réessaie la connexion en boucle et ne répond jamais ; sans PS_DOMAIN, elle s’arrête aussitôt.
  • Le module monté ET installé — monter le dépôt dans modules/ ne suffit pas : l’init-script prestashop:module install s’en charge au premier démarrage.
  • Attente explicite après le boot Flashlight — sur /admin-dev/, jusqu’à obtenir un 302 (redirection vers la page de connexion), plutôt qu’un 200 sur / qui prouve seulement que quelque chose écoute sur le port. Et une attente qui échoue franchement au bout du délai. Détails dans l’annexe PrestaShop Flashlight : des boutiques jetables pour tester.
  • Variables PRESTAFLOW_* — mêmes noms, mêmes rôles, quelle que soit la plateforme. Le code de la lib ne connaît rien du CI qui l’exécute ; il faut seulement qu’elles arrivent jusqu’à $_ENV (variables_order avec E).
  • Artefacts sur échec — les captures d’erreur dans prestaflow/screens/errors/, prises à l’échec d’une assertion. PrestaFlow ne produit pas de rapport HTML de run ; pour un fichier exploitable par le CI, les options --junit (écrit prestaflow/junit.xml) et -o json --file (écrit prestaflow/results.json) existent.
  • Code de sortie non nul de la CLI = job rouge. Ne pas le court-circuiter avec un || true bien intentionné. Attention toutefois : en v1.7.1, si une exception interrompt la CLI (Chrome qui ne démarre pas, par exemple), le code de sortie reste 0. Relisez la sortie du job quand un run est vert en quelques secondes.

Au niveau 2, les intégrations remontent les mêmes informations (identifiant et URL du rapport, compteurs, durée, statut), via le mécanisme natif de chaque plateforme. L’Action GitHub les expose en outputs de step, nommés id, report-url, passed, failed, skipped, total, duration-ms et status. Les portages GitLab, Bitbucket et CircleCI les écriront sous la forme PRESTAFLOW_REPORT_ID, PRESTAFLOW_REPORT_URL, PRESTAFLOW_PASSED… dans un prestaflow.env (artifact dotenv, fichier à sourcer ou BASH_ENV).

Le cas Jenkins : niveau 2 fait maison

Sans intégration officielle, deux options si vous êtes sur Jenkins et voulez la synchro plateforme :

  • Rester au niveau 1 — vous avez le CI qui rougit sur les régressions fonctionnelles. Vous n’avez ni le commentaire PR, ni l’historique côté plateforme, ni les régressions visuelles synchronisées. C’est déjà utile.
  • Faire soi-même l’upload vers l’API prestaflow.io — c’est ce que font l’Action et les portages : un POST https://api.prestaflow.io/ci/github-action, authentifié par l’en-tête X-Api-Token (votre PRESTAFLOW_TOKEN), en multipart, avec le champ projectId (la Product Key) et le results.json dans file[]. Ce fichier n’existe qu’avec -o json --file, d’où le script Composer prestaflow:json:file de l’article 2. Malgré son nom, l’endpoint accepte n’importe quel CI, mais il classe pour l’instant tous les runs reçus comme venant de GitHub Actions.

Ce second cas justifierait à lui seul un article dédié (idempotence, retries, captures d’écran jointes). Pour l’instant, on note l’option.

Notes

Dans la Série PrestaFlow — article 5 sur 23