Skip to content
SEO Optimizer is live, Audit on page SEO and AI-search readiness. Try it free
Connexion
CI/CD Integration

Guides

Intégration CI/CD

Chaque endpoint de scanner accepte une clé API, si bien que chacun peut s'exécuter comme une étape de pipeline. Cette page explique ce que signifie la sortie de chaque outil, comment la transformer en gate réussite/échec, et fournit des exemples fonctionnels pour quatre plateformes CI.

Analyse de code avec la GitHub Action

Le moyen le plus rapide d'analyser le code en CI. Elle exécute @scanverra/cli sur le runner (votre code ne le quitte jamais), fait échouer le job à partir de la gravité choisie et envoie le SARIF à GitHub code scanning.

on: [push, pull_request]
jobs:
  scanverra:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v4
      - uses: scanverra/scan-action@v1
        with:
          api-key: ${{ secrets.SCANVERRA_API_KEY }}
          fail-on: high
Ajoutez votre clé API comme secret de dépôt SCANVERRA_API_KEY. Le forfait Free inclut une clé ; chaque scan envoyé compte dans vos scans Repo Scanner du mois. Les vérifications par URL ci-dessous utilisent l'API REST.

Base de notation par outil

Ce que renvoie chaque endpoint, et sur quoi fixer un seuil.

OutilSortieBaseGate intégré
Audit de site web4 scores 0-100 : performance, seo, accessibility, bestPracticesScores par catégorie de 0 à 100. Convention : ≥90 bon, 50-89 à améliorer, <50 mauvais.Aucun gate intégré — choisissez votre propre seuil par score.
Analyse de sécuritériskScore 0-100 (plus élevé = plus sûr)100 moins des déductions pondérées selon la gravité, plafonnées par catégorie. Tranches de notation : A+ ≥90 … F <30.Aucun gate intégré — choisissez votre propre seuil.
Audit navigateurTableaux : jsErrors, brokenLinks, formIssues, imgIssues (chacun avec severity: "error" | "warning")Aucun score global.Conditionnez sur le nombre de problèmes, par exemple toute severity === "error".
Analyseur de dépôtsscore 0-100 + qualityGate: "passed" | "failed"100 moins des déductions pondérées selon la gravité, plafonnées par catégorie.Intégré — échoue automatiquement en cas de résultat critique ou de score < 60.
Optimiseur SEOscore 0-100 (SEO on-page + signaux GEO/E-E-A-T)100 moins des déductions pondérées selon la gravité, plafonnées par catégorie, sur les constats on-page, GEO et E-E-A-T.Non intégré — définissez votre propre seuil.

Arrêter le pipeline

La mécanique CI est la même quelle que soit la plateforme : une étape qui se termine avec un code non nul fait échouer le job. Pour réellement empêcher un PR ou une merge request d'être fusionnée, ajoutez ce job comme vérification de statut obligatoire dans la protection de branche ou les paramètres d'approbation des merge requests de votre hébergeur de dépôt — une étape échouée affiche seulement du rouge, elle n'empêche pas la fusion à elle seule.

Exemples de pipelines

Les cinq mêmes vérifications — Audit, Security Scan, Browser Audit, Repo Scan, Optimiseur SEO — écrites pour quatre plateformes. Choisissez votre système CI ci-dessous.

name: Scanverra checks
on: [pull_request]

jobs:
  scanverra:
    runs-on: ubuntu-latest
    env:
      API_KEY: ${{ secrets.SCANVERRA_API_KEY }}
      TARGET_URL: https://staging.example.com   # your deployed preview URL
    steps:
      - name: Website Audit (perf/SEO/a11y)
        run: |
          RES=$(curl -sf -X POST https://www.scanverra.com/api/audit \
            -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
            -d "{\"url\":\"$TARGET_URL\"}")
          echo "$RES" | jq .
          PERF=$(echo "$RES" | jq '.performanceScore')
          A11Y=$(echo "$RES" | jq '.accessibilityScore')
          if (( $(echo "$PERF < 50" | bc -l) )) || (( $(echo "$A11Y < 90" | bc -l) )); then
            echo "::error::Audit failed thresholds (perf=$PERF, a11y=$A11Y)"
            exit 1
          fi

      - name: Security Scan
        run: |
          SCAN_ID=$(curl -sf -X POST https://www.scanverra.com/api/security-scan \
            -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
            -d "{\"url\":\"$TARGET_URL\"}" | jq -r .scanId)

          for i in $(seq 1 30); do
            RESULT=$(curl -sf "https://www.scanverra.com/api/security-scan?id=$SCAN_ID" -H "X-API-Key: $API_KEY")
            STATUS=$(echo "$RESULT" | jq -r .status)
            [ "$STATUS" != "running" ] && break
            sleep 10
          done

          RISK=$(echo "$RESULT" | jq '.riskScore')
          echo "riskScore=$RISK"
          [ "$STATUS" = "failed" ] && { echo "::error::Security scan failed to complete"; exit 1; }
          [ "$RISK" -lt 70 ] && { echo "::error::Security risk score too low ($RISK)"; exit 1; }
          true

      - name: Browser Audit (JS errors, broken links)
        run: |
          TEST_ID=$(curl -sf -X POST https://www.scanverra.com/api/browser-test \
            -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
            -d "{\"url\":\"$TARGET_URL\"}" | jq -r .testId)

          for i in $(seq 1 30); do
            RESULT=$(curl -sf "https://www.scanverra.com/api/browser-test?id=$TEST_ID" -H "X-API-Key: $API_KEY")
            STATUS=$(echo "$RESULT" | jq -r .status)
            [ "$STATUS" != "running" ] && break
            sleep 10
          done

          ERRORS=$(echo "$RESULT" | jq '[.jsErrors[]? | select(.severity=="error")] | length')
          [ "$STATUS" = "failed" ] && { echo "::error::Browser audit failed to complete"; exit 1; }
          [ "$ERRORS" -gt 0 ] && { echo "::error::$ERRORS JS errors found"; exit 1; }
          true

      - name: Repo Scan (SAST, secrets, deps, quality gate)
        run: |
          SCAN_ID=$(curl -sf -X POST https://www.scanverra.com/api/repo/scan \
            -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
            -d '{"owner":"your-org","repo":"your-repo","branch":"${{ github.head_ref }}"}' | jq -r .scanId)

          for i in $(seq 1 30); do
            RESULT=$(curl -sf "https://www.scanverra.com/api/repo/result?id=$SCAN_ID" -H "X-API-Key: $API_KEY")
            STATUS=$(echo "$RESULT" | jq -r .status)
            [ "$STATUS" != "running" ] && break
            sleep 10
          done

          echo "$RESULT" | jq '{score, qualityGate}'
          GATE=$(echo "$RESULT" | jq -r .qualityGate)
          [ "$GATE" = "failed" ] && { echo "::error::Repo quality gate failed"; exit 1; }
          true

      - name: SEO Optimizer (on-page, GEO, E-E-A-T)
        run: |
          SCAN_ID=$(curl -sf -X POST https://www.scanverra.com/api/seo-optimizer \
            -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
            -d "{\"url\":\"$TARGET_URL\"}" | jq -r .scanId)

          for i in $(seq 1 30); do
            RESULT=$(curl -sf "https://www.scanverra.com/api/seo-optimizer?id=$SCAN_ID" -H "X-API-Key: $API_KEY")
            STATUS=$(echo "$RESULT" | jq -r .status)
            [ "$STATUS" != "running" ] && break
            sleep 10
          done

          SCORE=$(echo "$RESULT" | jq '.score')
          echo "seoScore=$SCORE"
          [ "$STATUS" = "failed" ] && { echo "::error::SEO scan failed to complete"; exit 1; }
          [ "$SCORE" -lt 70 ] && { echo "::error::SEO score too low ($SCORE)"; exit 1; }
          true

Où stocker la clé API en tant que secret, selon la plateforme :

  • GitHub Actions : secret du dépôt — Settings → Secrets and variables → Actions.
  • GitLab CI : variable CI/CD masquée — Settings → CI/CD → Variables.
  • Azure Pipelines : variable de pipeline secrète (icône de cadenas) ou un Variable Group sous Pipelines → Library.
  • Bitbucket Pipelines : Paramètres du dépôt > Pipelines > Variables du dépôt, marquées comme sécurisées.
  • Jenkins : credential de type texte secret — Manage Jenkins → Credentials — référencé via credentials(). L'image de l'agent a également besoin de curl, jq et bc installés ; les runners ubuntu-latest hébergés par GitHub et Azure les incluent déjà, l'image alpine de GitLab non (d'où l'étape apk add).

L'étape de l'analyseur de dépôts nécessite au préalable la connexion de votre compte GitHub/Bitbucket/Azure dans les paramètres Scanverra — l'analyse s'exécute avec le jeton qui y est stocké, et non avec un jeton transmis à chaque requête.

Vous préférez ne pas connecter de compte ? @scanverra/cli analyse le code extrait directement dans le runner et n’a besoin que d’une clé API, sans connexion GitHub, Bitbucket ou Azure.

Sortie SARIF pour le code scanning

SARIF (Static Analysis Results Interchange Format) est le format JSON standard d'échange des résultats d'analyse de code. La CLI Scanverra produit du SARIF 2.1.0 avec --sarif, pour que les résultats apparaissent partout où SARIF est lu.

  • GitHub code scanning : activez sarif: true dans scanverra/scan-action et les résultats apparaissent dans l'onglet Security et en annotations de pull request.
  • SonarQube et les autres plateformes qui importent SARIF peuvent lire le même fichier.
  • Bitbucket et GitLab : conservez le fichier SARIF comme artefact du pipeline, ou utilisez --json pour votre propre étape de reporting.
- uses: actions/checkout@v4
- uses: scanverra/scan-action@v1
  with:
    api-key: ${{ secrets.SCANVERRA_API_KEY }}
    sarif: true          # uploads the report to GitHub code scanning
# The job needs: permissions: { security-events: write }
Remarque sur le quota : une clé API du forfait Free est soumise au même quota qu'un compte web gratuit — les exécutions CI continuent de compter dans votre limite mensuelle d'analyses et peuvent l'épuiser rapidement si vous analysez à chaque PR. Les clés API des forfaits Pro, Team et Enterprise ne sont pas mesurées. Voir Limites et comportement.

Seul l'analyseur de dépôts renvoie un champ qualityGate prêt à l'emploi. Pour les quatre autres outils, définissez vos propres seuils à l'avance et gardez-les cohérents entre les environnements (par exemple, ne comparez pas le score de sécurité d'une URL de staging à un seuil calibré pour la production).