Produit
L'intégration CI/CD
Un job de plus dans votre pipeline, sans rien changer à vos habitudes
Sommaire
Trois temps dans votre pipeline
Grammage tourne dans un conteneur Docker, donc il prend sa place dans votre pipeline comme n'importe quelle autre étape, et personne n'a besoin d'y penser ensuite puisqu'il se lance tout seul à chaque demande de fusion.
Chaque demande de fusion est auditée
Grammage mesure l'aperçu de la demande, ou le site qu'il construit lui-même dans le job, et publie son rapport directement dans la demande, pour que chacun voie ce que le changement coûte avant de le valider.
Le pipeline échoue sous votre seuil
Vous fixez la note minimale, une lettre comme C ou un score sur 100, et une demande qui ferait passer le site en dessous le signale, ou ne part pas du tout en production si vous le décidez.
La production est mesurée et attestée
Après le déploiement, un second job mesure l'adresse publique, signe son rapport et l'envoie à Grammage, qui le vérifie puis fait vivre votre badge.
L'installer dans GitLab CI
Le modèle ci/grammage.gitlab-ci.yml porte trois jobs tout prêts, qu'on reprend par un include puis qu'on règle par quelques variables. Le premier audite l'aperçu de la demande de fusion, le second lit le code source du dépôt, et le troisième, .grammage-certify, fait vérifier la production plus bas dans cette page.
include:
- project: solyzon/products/grammage/grammage
ref: prod
file: ci/grammage.gitlab-ci.yml
grammage:
extends: .grammage
needs: [preview]
variables:
GRAMMAGE_CATEGORY: auto
GRAMMAGE_FAIL_UNDER: C
grammage-source:
extends: .grammage-sourceneeds: [preview] fait arriver PREVIEW_URL, l'adresse de l'aperçu publiée par le job qui le déploie. Pour auditer une autre adresse, GRAMMAGE_URL la remplace, et votre licence arrive par la variable masquée GRAMMAGE_LICENSE, qu'on pose une fois pour tout le groupe.
Sans attendre l'aperçu
Le job peut aussi construire votre site et le lancer lui-même, ce qui évite d'attendre l'aperçu et son mot de passe. GRAMMAGE_SERVE porte la commande qui construit puis démarre le site, et le job attend qu'il réponde avant de placer devant lui un petit proxy, qui compresse les réponses en zstd ou en gzip comme le ferait votre serveur de production. Sans ce proxy, un serveur Node qui ne compresse rien ferait paraître le site bien plus lourd qu'il ne l'est vraiment en ligne.
grammage:
extends: .grammage
variables:
GRAMMAGE_SERVE: pnpm install --frozen-lockfile && pnpm exec astro build && HOST=127.0.0.1 PORT=4321 node dist/server/entry.mjs
GRAMMAGE_SITE: '1'
GRAMMAGE_FAIL_UNDER: ASi le site ne répond jamais, le job échoue et affiche le journal de la commande, pour que vous voyiez tout de suite ce qui a coincé.
Le site entier et la durée du job
Avec GRAMMAGE_SITE, le job ne mesure plus une page mais tout le site, trois pages par gabarit et cinquante au plus. Un passage prend en moyenne une trentaine de secondes sur un vrai site, vingt-quatre passages en un peu plus de douze minutes sur nos essais de septembre 2026, donc la durée du job suit directement le nombre de pages, le nombre de passages et le nombre de pages mesurées en même temps. Les traductions d'une même page ne sont mesurées qu'une fois, et le mode fast saute en plus la visite de retour, ce qui fait gagner de 10 à 15 % par page sans rien changer à la note.
| Mode | Passages | Pour quoi |
|---|---|---|
fast | 1 | un premier coup d'œil, et le job de chaque demande de fusion |
normal | 2 | le réglage par défaut |
full | 3 | la mesure la plus stable, pour un job planifié ou un résultat qu'on publie |
Le calcul est donc simple, environ trente secondes fois le nombre de pages fois le nombre de passages, divisé par GRAMMAGE_PARALLEL, plus la découverte du site. Ce sont des estimations tirées de cette moyenne, et une page très lourde ou un serveur lent les allongent.
| Réglage | Pages | Passages | En même temps | Durée estimée |
|---|---|---|---|---|
| Chaque demande de fusion | 10 | 1 | 3 | ≈ 2 min |
| Réglage par défaut | 50 | 2 | 2 | ≈ 25 min |
| Job planifié complet | 50 | 3 | 4 | ≈ 20 min |
| Complet, page après page | 50 | 3 | 1 | ≈ 75 min |
grammage:
extends: .grammage
needs: [preview]
variables:
GRAMMAGE_SITE: '1'
GRAMMAGE_MODE: fast
GRAMMAGE_PARALLEL: '3'
GRAMMAGE_MAX_PAGES: '10'Le runner
L'image part de Node 24 et n'embarque que Chromium sans fenêtre, pour rester légère sur les runners. Chaque page mesurée en même temps ouvre son propre Chromium, donc GRAMMAGE_PARALLEL ne doit jamais dépasser le nombre de cœurs que le runner donne au job, et il plafonne de toute façon à 8. Au-delà, les navigateurs se disputent le processeur, ce qui ne change ni le poids, ni les requêtes, ni la note, mais fausse le temps de calcul affiché dans le rapport. Pour une mesure qu'on publie, le mode full avec peu de pages en même temps reste donc le plus fiable, et il se réserve plutôt à un job planifié.
Ce que montre la demande de fusion
Le job range ses résultats là où GitLab sait déjà les afficher, donc on lit l'audit sans quitter la demande de fusion, et chaque constat pointe vers le fichier du dépôt à corriger, une image public/images/hero.png servie sous une adresse transformée par le framework par exemple.
| Où | Fichier | Ce qu'on y lit |
|---|---|---|
| Onglet Tests | grammage-junit.xml | une règle par cas et une page par groupe |
| Widget Code Quality | gl-code-quality-report.json | chaque règle non tenue, avec son correctif, annotée sur le fichier du dépôt quand Grammage le retrouve |
| Widget Metrics | metrics.txt | le score, le poids, les requêtes, le DOM, le carbone et le code inutilisé, comparés à la branche cible |
| Artefacts | grammage/ | le rapport HTML et PDF, le JSON, le brief grammage-agent.md et l'aide RGESN |
Bloquer ou prévenir
Le job sort en 1 quand le seuil ou le budget n'est pas tenu, et en 2 quand une page n'a pas pu être mesurée. Par défaut, il prévient sans bloquer sur un seuil manqué, alors qu'une mesure impossible fait bien échouer le pipeline, puisqu'on ne sait alors plus rien du site. Pour qu'un audit raté empêche vraiment la mise en production, il suffit de le rendre obligatoire et de faire attendre le déploiement.
grammage:
allow_failure: false
deploy:
needs: [grammage]Les pages réservées
Un aperçu fermé par un mot de passe HTTP s'audite avec GRAMMAGE_HTTP_AUTH. Une page derrière un formulaire de connexion s'audite avec la session d'un compte de test sans droits particuliers, qu'on enregistre une fois sur son poste par npx playwright codegen --save-storage=session.json, puis qu'on colle dans une variable masquée de type fichier, GRAMMAGE_STORAGE_STATE. La session finit par expirer, et une page qui renvoie alors vers la connexion est mesurée comme telle, donc on la refait de la même façon dès que les pages auditées ne sont plus les bonnes.
Seul l'audit reçoit la session. L'attestation ne porte que sur des pages publiques, parce que Grammage les remesure ensuite sans aucune session pour contrôler votre rapport.
Attester la production
Le job .grammage-certify se lance juste après le déploiement. Il mesure l'adresse publique, signe le rapport avec la clé que porte votre licence et l'envoie à l'API de Grammage, puis il attend le verdict, vingt minutes au plus, pendant que Solyzon remesure la page d'entrée et deux pages tirées au hasard. Le domaine doit faire partie de votre licence, et la licence doit porter le badge.
grammage:certify:
extends: .grammage-certify
needs: [deploy]
rules:
- if: $CI_COMMIT_BRANCH == "prod"
variables:
GRAMMAGE_URL: https://exemple.fr/
GRAMMAGE_SITE: '1'
GRAMMAGE_CATEGORY: autoQuand les chiffres de Solyzon confirment votre rapport, le job affiche la date de fin de l'attestation et le badge se met à jour. Quand une page pèse plus de 20 % de plus que déclaré, ou qu'une page tirée n'a pas pu être remesurée, le job échoue en disant pourquoi, et le badge garde l'attestation précédente tant qu'elle est valable. Sans GRAMMAGE_FAIL_UNDER ni budget, le site est vérifié quelle que soit sa note, ce qui permet d'afficher un badge honnête même quand le site est encore lourd. Le détail de la vérification est sur la page de la attestation.
Les variables du modèle
Les jobs d'audit et d'attestation lisent les mêmes variables. La vérification porte toujours sur une page ou sur le site entier, jamais sur une liste, puisque l'attestation vaut pour une page ou pour un domaine, donc GRAMMAGE_URLS_FILE et GRAMMAGE_SERVE ne servent qu'à l'audit.
| Variable | Par défaut | Rôle |
|---|---|---|
GRAMMAGE_URL | PREVIEW_URL | la page de départ, l'aperçu de la demande de fusion par défaut |
GRAMMAGE_SITE | toute valeur non vide mesure le site entier à partir de l'adresse | |
GRAMMAGE_MAX_PAGES | 50 | le nombre de pages au plus quand GRAMMAGE_SITE est posée |
GRAMMAGE_CATEGORY | showcase | la catégorie de notation, ou auto pour la déduire de chaque page |
GRAMMAGE_FAIL_UNDER | la note minimale, une lettre de A à F ou un score sur 100 | |
GRAMMAGE_BUDGET | un fichier budget.json du dépôt, tenu comme le seuil | |
GRAMMAGE_DEVICE | mobile | mobile ou desktop |
GRAMMAGE_MODE | normal | fast, normal ou full, soit 1, 2 ou 3 passages par page |
GRAMMAGE_RUNS | un nombre de passages par page, à la place du mode | |
GRAMMAGE_PARALLEL | 2 | le nombre de pages mesurées en même temps, de 1 à 8 |
GRAMMAGE_SERVE | la commande qui construit et lance le site dans le job, audit seulement | |
GRAMMAGE_SERVE_URL | http://127.0.0.1:4321 | l'adresse où répond le site lancé par GRAMMAGE_SERVE |
GRAMMAGE_SERVE_TIMEOUT | 300 | les secondes laissées au site lancé pour répondre |
GRAMMAGE_URLS_FILE | un fichier du dépôt qui liste d'autres pages, une par ligne, audit seulement | |
GRAMMAGE_HTTP_AUTH | utilisateur:mot de passe d'un aperçu protégé, en variable masquée | |
GRAMMAGE_STORAGE_STATE | une session de Playwright, en variable de type fichier, pour les pages réservées | |
GRAMMAGE_COOKIES | des cookies au format de Playwright, à la place de la session | |
GRAMMAGE_LICENSE | votre licence, en variable masquée posée une fois pour tout le groupe | |
GRAMMAGE_REGISTRY | registry.solyzon.net | le registre de l'image |
GRAMMAGE_VERSION | celle du modèle | la version de l'image, ou le tag à votre nom qui suit les mises à jour |
La ligne de commande
Tout ce que font les jobs passe par la commande grammage, qu'on lance de la même façon sur un poste, dans l'image Docker. grammage audit mesure, grammage certify mesure puis enregistre le résultat, grammage source lit le code du dépôt, et grammage license verify dit ce que couvre votre licence, qui se lit dans GRAMMAGE_LICENSE. Enfin grammage proxy sert un site lancé dans la CI en le compressant en zstd ou en gzip, comme le ferait un vrai serveur, pour que la mesure voie les mêmes octets qu'en production, et c'est d'ailleurs ce que fait le modèle GitLab avec GRAMMAGE_SERVE.
grammage proxy http://127.0.0.1:4321 --port 8080| Option | Rôle |
|---|---|
--site <url> | le site entier, par son sitemap, ou en suivant ses liens à défaut |
--urls <fichier> | une adresse par ligne, # pour commenter |
--max-pages <n> | les pages du site au plus, 50 par défaut |
--per-template <n> | les pages mesurées par gabarit, 3 par défaut |
--exclude <motif> | les pages écartées, /admin/* par exemple, option répétable |
--device mobile|desktop | l'appareil simulé, mobile par défaut |
--mode <mode> | fast, normal ou full, full par défaut sur un poste |
--runs <n> | un nombre de passages par page, à la place du mode |
--parallel <n> | les pages mesurées en même temps, de 1 à 8 |
--category <catégorie> | presentation, documentation, showcase, editorial, ecommerce, application ou auto |
--page-type home|inner | le type de page, accueil ou page intérieure, déduit de l'adresse par défaut |
--audience <pays> | le pays du public pour l'estimation locale, FR par défaut |
--idle <secondes> | une veille après les relevés, pour voir ce que coûte une page laissée ouverte |
--format <liste> | json, html, pdf, agent, rgesn, markdown, junit, codequality, metrics ou gitlab |
--out <dossier> | où écrire les rapports, le dossier courant par défaut |
--source <dossier> | le dépôt du site, pour relier chaque fichier fautif à sa source |
--budget <fichier> | des plafonds par page en JSON, bytes, requests, dom, co2PerView, score et rules |
--fail-under <note> | la note minimale, de A à F, ou un score de 0 à 100 |
--http-user, --http-password | les identifiants d'un aperçu protégé |
--cookies, --storage-state | une session de Playwright, pour une page réservée |
| Code de sortie | Ce qu'il veut dire |
|---|---|
0 | tout va bien |
1 | le budget ou la note minimale n'est pas tenu |
2 | une page n'a pas pu être mesurée, ou un argument est invalide |
Les autres CI
Le modèle prêt à inclure est écrit pour GitLab CI, qui affiche en plus les rapports dans la demande de fusion. Ailleurs, Grammage reste une image Docker et une commande, donc le job se réécrit en quelques lignes, et les rapports se récupèrent dans le dossier de sortie.
- GitLab CImodèle prêt à inclure
- GitHub Actions
- Bitbucket Pipelines
- Jenkins
- Docker
jobs:
grammage:
runs-on: ubuntu-latest
container:
image: registry.solyzon.net/solyzon/products/grammage/grammage:<votre-tag>
credentials:
username: ${{ secrets.GRAMMAGE_USER }}
password: ${{ secrets.GRAMMAGE_LICENSE }}
env:
GRAMMAGE_LICENSE: ${{ secrets.GRAMMAGE_LICENSE }}
steps:
- run: grammage audit --site https://exemple.fr/ --mode fast --format json,html,agent --out grammagedocker login registry.solyzon.net
docker run --rm -e GRAMMAGE_LICENSE -v "$PWD/grammage:/out" \
registry.solyzon.net/solyzon/products/grammage/grammage:<votre-tag> \
grammage audit --site https://exemple.fr/ --format json,html --out /outL'image et la licence
L'image se tire sur registry.solyzon.net, avec votre identifiant et votre licence pour mot de passe, ce qui passe dans GitLab par la variable masquée DOCKER_AUTH_CONFIG de votre projet. Le registre ne vous sert que le tag à votre nom et les versions que couvre votre licence, et ce tag avance tout seul à chaque nouvelle version, sans jamais reculer, donc un projet qui le suit reçoit les mises à jour sans rien toucher.
{ "auths": { "registry.solyzon.net": { "auth": "<base64 de identifiant:licence>" } } }Sans licence valide, l'image audite quand même une page à la fois, en JSON et en HTML, ce qui exclut seulement le site entier, les rapports de GitLab et le badge.
Voyez ce que votre site pèse vraiment
Grammage réunit dans un seul outil l'audit dans un vrai navigateur, l'analyse du code dans la CI/CD et un résultat signé que chacun peut vérifier.
Essai gratuit, sans engagement.
Essayez gratuitement