PrestaFlow : cacher le binaire Chrome en CI
Décor
Sous le capot, PrestaFlow parle à Chrome via chrome-php/chrome, un client CDP en PHP pur. Contrairement à Playwright ou Puppeteer, chrome-php n’embarque pas de binaire : il lance celui qu’on lui désigne. Son ordre de recherche, sous Linux :
- la variable d’environnement
CHROME_PATH, si elle est définie ; - sinon, le premier binaire trouvé sur le
PATHparmigoogle-chrome,chromium-browser,chromeetchromium; - sinon,
chrome, qui a toutes les chances de ne pas exister.
C’est donc au workflow CI de garantir qu’un Chrome ou un Chromium est disponible avant le premier scénario, et de le désigner par CHROME_PATH quand il n’est pas celui que le PATH trouverait.
Cas où c’est gratuit :
runs-on: ubuntu-latest: l’image des runners GitHub embarque Google Chrome (google-chrome). Rien à faire.
Cas où c’est douloureux :
- Conteneur CI (
container: php:8.2-clisur GitHub, exécuteur Docker GitLab, Bitbucket Pipelines) : l’image PHP officielle n’a pas Chrome. Sans installation, chaque run échoue au démarrage du navigateur avec « Chrome process stopped before startup completed. Additional info: sh: 1: exec: chrome: not found ». - Runner auto-hébergé minimal : souvent une VM Debian slim où Chrome n’a jamais été installé.
Dans ces cas, l’installation (téléchargement, dépendances système, extraction) se paie à chaque job, souvent plusieurs dizaines de secondes. Faites le calcul pour votre projet : à 45 s par job et 40 jobs par jour, c’est une demi-heure de runner perdue chaque jour, et autant d’attente sur les PR.
Trois patterns d’installation
1. apt-get install : simple, non épinglé
Le plus court, dans un conteneur Debian comme php:8.2-cli, quand la version exacte de Chrome ne pèse sur rien (pas de tests visuels) :
jobs:
e2e:
runs-on: ubuntu-latest
container: php:8.2-cli
env:
CHROME_NO_SANDBOX: '1'
steps:
- name: Install Chromium
run: |
apt-get update
apt-get install -y --no-install-recommends chromium
Le paquet Debian installe /usr/bin/chromium, que chrome-php trouve seul sur le PATH. Le conteneur tourne en root, et Chromium refuse de démarrer en root sans --no-sandbox (« Running as root without —no-sandbox is not supported ») : CHROME_NO_SANDBOX=1 demande à chrome-php d’ajouter ce flag.
La version est celle de la distribution : elle bougera à chaque mise à jour de l’image de base.
2. browser-actions/setup-chrome : épinglé, mais pas mis en cache
L’action communautaire browser-actions/setup-chrome télécharge un canal (stable, beta, dev, canary) ou une version exacte de Chrome for Testing, et expose le chemin du binaire dans son output chrome-path.
- name: Setup Chrome
id: setup-chrome
uses: browser-actions/setup-chrome@v2
with:
chrome-version: '129.0.6668.100'
install-dependencies: true
Deux points à connaître :
- Le binaire installé n’est pas celui que chrome-php choisirait. Sur
ubuntu-latest,google-chromeest déjà sur lePATH, et c’est lui que chrome-php lancerait. Pour utiliser la version épinglée, passezCHROME_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}dans l’environnement de l’étape qui lance PrestaFlow. - Rien n’est conservé d’un job à l’autre. L’action range Chrome dans le tool cache du runner (
RUNNER_TOOL_CACHE), qu’elle consulte avant de télécharger. Mais sur un runner hébergé par GitHub, ce dossier repart vide à chaque job : le téléchargement se paie à chaque fois. Pour le conserver, ajoutez unactions/cachesur ce dossier (voir la recette plus bas).
3. Chrome for Testing dans un cache manuel
Utile hors de GitHub Actions ou pour maîtriser entièrement le chemin et la version. Chrome for Testing publie une archive par version exacte, ce qui rend l’épinglage réel : la clé de cache et l’URL désignent la même version.
- name: Cache Chrome
id: cache-chrome
uses: actions/cache@v4
with:
path: ~/.cache/chrome-for-testing
key: chrome-${{ runner.os }}-129.0.6668.100
- name: Download Chrome if not cached
if: steps.cache-chrome.outputs.cache-hit != 'true'
run: |
mkdir -p ~/.cache/chrome-for-testing
curl -sSLo /tmp/chrome.zip \
https://storage.googleapis.com/chrome-for-testing-public/129.0.6668.100/linux64/chrome-linux64.zip
unzip -q /tmp/chrome.zip -d ~/.cache/chrome-for-testing
- name: Point chrome-php to the cached Chrome
run: echo "CHROME_PATH=$HOME/.cache/chrome-for-testing/chrome-linux64/chrome" >> "$GITHUB_ENV"
Le cache ne contient que Chrome, pas les bibliothèques système dont il dépend (libnss3, libgbm1…). Sur ubuntu-latest, elles sont déjà là, puisque Google Chrome est préinstallé. Sur une image minimale, installez-les à chaque job, en dehors du if : après un cache hit, Chrome serait présent mais ne démarrerait pas.
Verbeux, mais la version ne dépend plus de l’image du runner.
La recette recommandée (GitHub Actions + Flashlight)
Sur un projet à tests visuels, la version de Chrome fait partie de la définition du build : une montée de version silencieuse casse les baselines. On épingle la version dans le workflow, on la désigne à chrome-php par CHROME_PATH, et on la fait monter volontairement (comme composer.lock ou package-lock.json).
name: PrestaFlow
on:
pull_request:
push:
branches: [main]
env:
CHROME_VERSION: '129.0.6668.100'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: gd
tools: composer:v2
ini-values: variables_order=EGPCS
- name: Restore Chrome from cache
uses: actions/cache@v4
with:
path: ${{ runner.tool_cache }}/setup-chrome
key: setup-chrome-${{ runner.os }}-${{ env.CHROME_VERSION }}
- name: Setup Chrome (pinned)
id: setup-chrome
uses: browser-actions/setup-chrome@v2
with:
chrome-version: ${{ env.CHROME_VERSION }}
install-dependencies: true
- name: Install PHP dependencies
run: composer install --prefer-dist --no-progress
- uses: PrestaFlow/github-action@v2
env:
CHROME_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
with:
token: ${{ secrets.PRESTAFLOW_TOKEN }}
projectId: 'pk_01ABC...'
flashlight: true
ps-version: '9.0.0'
flashlight-init-scripts: tests/flashlight-init
actions/cache restaure le tool cache de setup-chrome avant que l’action ne s’exécute : elle y trouve la version demandée et ne télécharge plus rien. Au premier run après une montée de version, la clé change, le téléchargement se paie une fois, et le cache est sauvegardé en fin de job.
CHROME_PATH est lu par chrome-php dans l’environnement du processus, quelle que soit la configuration PHP. Les variables PRESTAFLOW_*, elles, sont lues par PrestaFlow v1.7.1 dans $_ENV, que le php.ini de production installé par setup-php ne remplit pas (variables_order=GPCS) : ini-values: variables_order=EGPCS rétablit ce comportement, pour le jour où vous passez des identifiants BO par env:.
Mesurer le gain
GitHub Actions affiche la durée de chaque étape dans le détail du run : comparez l’étape d’installation de Chrome sur un premier run (cache vide) et sur le suivant. Ce qui se paie, selon la configuration :
| Configuration | Premier job | Jobs suivants |
|---|---|---|
ubuntu-latest, Chrome préinstallé | rien | rien |
container: php:8.2-cli, apt-get install chromium | installation apt | installation apt (pas de cache) |
ubuntu-latest, setup-chrome seul | téléchargement | téléchargement (pas de cache entre jobs) |
ubuntu-latest, setup-chrome + actions/cache | téléchargement | restauration du cache |
ubuntu-latest, cache manuel Chrome for Testing | téléchargement | restauration du cache |
Les ordres de grandeur dépendent du runner, du miroir et de la taille de l’archive (environ 160 Mo pour Chrome for Testing 129 sous Linux) : mesurez chez vous avant d’optimiser.
GitLab CI
L’image composer:2 est basée sur Alpine : pas d’apt-get, et ses paquets n’ont rien à voir avec ceux de Debian. On part donc de php:8.2-cli (Debian), comme dans l’annexe CI hors GitHub Actions, et on met en cache les paquets .deb téléchargés par apt :
test:
image: php:8.2-cli
variables:
CHROME_NO_SANDBOX: "1"
cache:
key: apt-chromium
paths:
- .cache/apt
before_script:
- mkdir -p .cache/apt/partial
- apt-get update
- apt-get install -y --no-install-recommends -o dir::cache::archives="$CI_PROJECT_DIR/.cache/apt" 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
script:
- composer prestaflow -- run ./tests/prestaflow
GitLab n’accepte en cache que des chemins situés dans le dossier du projet, d’où .cache/apt et l’option dir::cache::archives qui y redirige apt. Ce cache évite les téléchargements, pas l’installation elle-même : copier seulement /usr/bin/chromium ne suffirait pas, car sous Debian ce n’est qu’un script de lancement, qui s’appuie sur /usr/lib/chromium/ et sur des dizaines de bibliothèques.
Pour supprimer tout le coût, la vraie solution sur GitLab est une image : un Dockerfile FROM php:8.2-cli qui installe Chromium, GD et Composer, poussé dans le registre de conteneurs du projet et référencé dans image:. Chromium est alors installé une fois, à la construction de l’image, et sa version ne bouge que quand vous reconstruisez.
Le nom du binaire varie selon la base : chromium sous Debian et Alpine, chromium-browser sous Ubuntu (où le paquet apt passe désormais par snap). Vérifiez avec command -v chromium dans un premier run, ou fixez CHROME_PATH.
Le piège des baselines visuelles
Chrome rend le texte, les ombres CSS et l’anticrénelage légèrement différemment d’une version majeure à l’autre. Une baseline capturée avec la 128 et comparée avec la 129 peut tomber sous le seuil de similitude et échouer, sans qu’aucune ligne de code ait bougé.
Trois disciplines qui verrouillent ça :
- Épinglez la version de Chrome dans le workflow (numéro exact, pas
stable), et vérifiez parCHROME_PATHque c’est bien elle que chrome-php lance. - Documentez la version de Chrome dans le dépôt : un
.chrome-versionà côté ducomposer.json, ou un commentaire au-dessus de la ligneCHROME_VERSIONdu workflow. - Faites monter Chrome comme
composer.lock: PR dédiée, baselines régénérées si besoin (cf. versionner ses baselines visuelles).
Interaction avec les autres annexes
- Automatiser ses scénarios PrestaFlow en CI : l’article de base ne parle pas du binaire Chrome parce que
ubuntu-latestle fournit. Cette annexe couvre les cas où ce n’est plus vrai. - Régressions visuelles et versionner ses baselines visuelles : l’épinglage de Chrome est un préalable pour des baselines qui ne dérivent pas.
- CI hors GitHub Actions : GitLab, Bitbucket et Jenkins n’ont pas les images
ubuntu-latestde GitHub, l’installation de Chrome y est presque toujours nécessaire. - Runner auto-hébergé : sur un runner auto-hébergé, Chrome n’est plus une affaire de workflow mais de provisionnement de la machine. Un Chrome installé une fois pour toutes vaut mieux qu’un cache téléchargé à chaque job.
Notes
Dans la Série PrestaFlow — article 34 sur 36