Integrationen
CI/CD mit der API
Ein Deployment, das die Messung zerstört, fängt man am besten ab, bevor es in Produktion geht. Die API spielt Ihre gespeicherten Journeys gegen die Vorschau einer konkreten Änderung ab, vergleicht das Ergebnis mit dem erwarteten Zustand und liefert ein eindeutiges „bestanden“ oder „nicht bestanden“, an dem der CI-Job endet. Diese Anleitung zeigt, wie Sie diesen Ablauf aufbauen und wo er üblicherweise hängen bleibt.
Der Ablauf hat vier Schritte: starten, warten, das Gate auswerten und bei Bedarf SARIF oder einen Kommentar ausgeben.
GitHub App, Action oder eigene Aufrufe
Auf GitHub haben Sie drei Möglichkeiten, alle auf derselben API:
- Die GitHub App (Pull-Request-Prüfung) richten Sie im Dashboard ein; ins Repository kommt nichts. Ihre Regel ist fest: Sie meldet nur direkte dataLayer-Änderungen und blockiert nur kritische Befunde.
- Die Composite Action
analyticsproofläuft in Ihrem Workflow. Sie kopieren sie als.github/actions/analyticsproofin Ihr Repository und rufen sie mit eineruses:-Zeile auf; den passenden Workflow finden Sie in den CI-Rezepten. Sie spielt alle Journeys, die Ihr Plan ausführen lässt (pausierte ausgenommen), gegen die Vorschau ab, wendet dieselbe feste Regel an, schreibt eine SARIF-Datei und kommentiert den Pull Request. - Eigene API-Aufrufe funktionieren in jeder CI, und die Gate-Regel bestimmen Sie selbst. Das Warten und die Auswertung schreiben Sie dann aber auch selbst.
Reicht Ihnen die Standardregel auf GitHub? Nehmen Sie die App. Soll die Prüfung in Ihrem Workflow stehen oder in Code Scanning landen? Die Action. Eine andere CI oder eine andere Regel heißt API.
Der Schlüssel
Den Schlüssel legt ein Editor oder eine höhere Rolle unter Projekteinstellungen › API-Schlüssel an. Für CI genügen zwei Bereiche: „Scans auslösen“ und „Scans & Ergebnisse lesen“. Mehr braucht der Schlüssel nicht. Der Ablauf ist optional, und den vollständigen Schlüssel sehen Sie nur einmal, direkt nach dem Anlegen.
Der Schlüssel beginnt mit ap_live_ und gehört in einen Header Authorization: Bearer … oder X-API-Key: …. Speichern Sie ihn als maskierte Variable oder Secret, nie im Repository. Die API ist Teil des Enterprise-Plans; ohne ihn bekommt selbst ein gültiger Schlüssel 403 tier_forbidden. Details stehen in der API-Referenz.
Start gegen eine Vorschau
Einen Scan starten Sie mit POST /api/v1/targets/{id}/scans; die ID des Tests finden Sie über GET /api/v1/targets. Enthält der Body eine url mit der Vorschau-Adresse, läuft der Scan als Dry-Run: Er erzeugt Abweichungen und ein Ergebnis für das Gate, in die Journey wird aber nichts zurückgeschrieben. Erwarteter Zustand, überwachte Tools und Regressionsverlauf bleiben unverändert. Ohne url startet derselbe Aufruf einen normalen Scan des gespeicherten Tests.
Bei einem Website-Test reicht eine url allein für eine Journey nicht. Ohne Scan-Typ startet die Anfrage den Standard-Scan des Tests, ein Compliance-Audit der Vorschau. Um eine Journey abzuspielen, senden Sie "scanType": "web_datalayer" und optional eine journeyId; ohne sie wird die erste Journey des Tests genommen, die Ihr Plan ausführen lässt. Die Antwort enthält dann eine scanId und ihre pollUrl.
Mit "journeys": "all" werden alle Journeys des Tests abgespielt, die Ihr Plan ausführen lässt, und maxJourneys begrenzt ihre Anzahl. Die Antwort hat dann eine andere Form: Es gibt keine scanId auf oberster Ebene, nur ein Feld scans mit scanId und pollUrl je Journey und ein Feld failed mit Journeys, die sich nicht starten ließen. Ihr Skript muss auf jede scans[].pollUrl warten, so wie es die Composite Action tut. Ein nicht leeres failed sollte den Job scheitern lassen.
Zwei Dinge lohnt es sich gleich einzustellen:
- Einen Header
Idempotency-Key. Ein erneuter Versuch mit demselben Schlüssel startet keinen zweiten Scan, sondern liefert den ersten. Der Schlüssel steht für genau eine Anfrage: den Test, die Auswahl der Journeys und die Vorschau-URL. Lassen Sie ihn bei Wiederholungen dieser Anfrage unverändert, und leiten Sie ihn nicht allein aus dem Commit-Hash ab. Derselbe Commit kann auf eine zweite Vorschau deployt oder mit anderen Journeys geprüft werden, und mit dem Commit allein als Schlüssel bekämen Sie den alten Scan zurück. - Das Scan-Land. Die Vorschau wird aus dem Land gescannt, das beim Anlegen des Tests gewählt wurde. Liegt die Vorschau hinter einer IP-Allowlist, geben Sie die Adresse dieses Landes frei.
Siehe Dry-Run in der Referenz.
Auf das Ergebnis warten
Den Zustand des Scans lesen Sie mit GET /api/v1/scans/{id}?wait=N. Der Server hält die Anfrage offen, bis der Scan beendet und seine Befunde gespeichert sind, höchstens N Sekunden. Die Obergrenze liegt bei 300 Sekunden. Praktischer ist es, etwa 120 Sekunden zu warten und die Anfrage zu wiederholen, weil Proxys und Runner lange Verbindungen gern abbrechen.
Der Scan ist beendet, wenn sein status einer dieser Werte ist:
| Wert in der API | Im Dashboard |
|---|---|
completed |
Abgeschlossen |
failed |
Fehlgeschlagen |
cancelled |
Abgebrochen |
broken_step |
Abgebrochener Schritt |
no_data |
Keine Daten |
Wiederholen Sie, bis status endgültig und settled gleich true ist. settled: false heißt, dass Ihre Wartezeit abgelaufen ist, während die Befunde noch gespeichert wurden. Fragen Sie einfach erneut.
Ein Schlüssel darf höchstens fünf wartende Anfragen gleichzeitig halten; die sechste bekommt 429 too_many_long_polls. Wenn Sie mehrere Journeys auf einmal starten, lesen Sie sie nacheinander, nicht parallel.
Das Gate entwerfen
Das Gate übergeben Sie beim Lesen des Scans als ?gate= mit URL-kodiertem JSON. Prädikate werden mit ODER verknüpft: Das Gate schlägt fehl, sobald eines zutrifft. Alle Prädikate stehen in der Referenz. Für eine Vorschau und den dataLayer ist das ein vernünftiger Anfang:
{ "failOnSeverity": "critical", "failOnBrokenStep": true }
Regeln, die Sie der Prädikat-Tabelle nicht ansehen:
- Ein Scan mit dem Ergebnis
failed,cancelledoderno_databesteht nie ein Gate, egal was Sie einstellen. Ein Scan ohne Daten bekommt auch kein falsches „Score unter Schwelle“, sondern einen eigenen Code. - Ein abgebrochener Schritt lässt das Gate standardmäßig nicht scheitern. Er endet als Warnung, und die Abweichungen dieses Scans werden nicht ausgewertet. Zerstört eine Änderung den Warenkorb-Button, besteht ein Gate ohne
failOnBrokenStep. Deshalb empfehlen wir, es einzuschalten. - Ein Ereignis, das um einen Schritt verrutscht, liegt in der Toleranz und lässt das Gate nie scheitern.
- Solange eine Journey noch keinen erwarteten Zustand hat, haben die dataLayer-Prädikate nichts zum Vergleichen. Sie können nicht scheitern, und in
warningsstehtdatalayer_gate_no_baseline. Es lohnt sich, in der CI auf diese Warnung zu reagieren. - Ohne
?gate=enthält die Antwort gar kein Gate. Ihr Skript muss ein fehlendesgate.passeddeshalb als Fehlschlag werten, nicht als Erfolg. minComplianceScorebewertet den Compliance-Score und gehört daher zu einem Compliance-Test. Eine Journey wird an ihren Abweichungen gemessen.
Der Parameter scope=direct-datalayer beschränkt die Abweichungen auf das, was die Website direkt in window.dataLayer schreibt. Anfragen, die GA4 und andere Tools daraus erzeugen, bleiben im Dashboard, beeinflussen das Gate aber nicht. App und Action nutzen dieselbe Einschränkung. Verglichen werden das Vorhandensein von Ereignissen und Parametern und ihr JavaScript-Typ, nicht ihre Werte. Die Ausnahme sind Consent-Signale: Dort ist ein geänderter Wert ein eigener Befund.
SARIF und der Kommentar
Mit ?format=sarif liefert ein beendeter Scan ein SARIF-2.1.0-Dokument. Ein noch laufender Scan liefert das normale JSON, holen Sie SARIF also erst nach einer JSON-Antwort, deren status final ist und deren settled true ist. Solange Befunde noch gespeichert werden, könnte SARIF unvollständig sein. SARIF enthält dieselben Befunde, die das Gate bewertet, dazu die um einen Schritt verschobenen Ereignisse, die das Gate toleriert (kritisch als error, Warnung als warning, Information als note). Das Urteil steht nicht darin; über den Job entscheidet weiterhin gate.passed. Geben Sie denselben scope mit, damit die Datei zum Gate passt.
Einen Kommentar für den Pull oder Merge Request liefert POST /api/v1/scans/{id}/explain: eine Erklärung der Abweichungen in Alltagssprache, ohne Parameterwerte. Vor dem Ende des Scans antwortet der Aufruf mit 409 scan_not_terminal.
Beispiel: GitLab CI und andere CIs
Die Schritte sind in jeder CI gleich, die curl und jq hat:
- Schlüssel und Test-ID als maskierte CI-Variablen speichern.
- Den Job nach dem Vorschau-Deployment einplanen, damit er die Vorschau-Adresse kennt.
- Einen Dry-Run mit
url,"journeys": "all"und einemIdempotency-Keyaus Test-ID, Journey-Auswahl und Vorschau-URL starten. AllepollUrlausscansbehalten und bei nicht leeremfailedabbrechen. - Jeden Scan mit
wait=120, dem Gate und demscopelesen, bis er endgültig und abgeschlossen ist. Das gesamte Warten über das Job-Timeout begrenzen. - Den Job scheitern lassen, wenn
gate.passedbei irgendeiner Journey nichttrueist.gate.failuresundwarningsins Log schreiben. - Zeigt Ihre CI kein SARIF an, die Datei als Job-Artefakt ablegen.
Ein vollständiges GitLab-Skript steht in den CI-Rezepten. Es startet einen normalen Scan des gespeicherten Tests und liest eine einzelne scanId. Für die Vorschau einer Journey senden Sie den Body {"url": "…", "scanType": "web_datalayer"}, nehmen die Vorschau-URL in den Idempotency-Key auf und ersetzen minComplianceScore durch das Journey-Gate von oben; der Rest des Skripts bleibt, wie er ist. Für "journeys": "all" braucht es eine Schleife über scans[], weil die Antwort keine scanId hat.
Fehler und was zu tun ist
| Code | Übliche Ursache | Was tun |
|---|---|---|
missing_credentials, invalid_key |
Das Secret kam nicht im Job an oder ist abgeschnitten | Variable und Header prüfen |
key_revoked, key_expired |
Der Schlüssel ist widerrufen oder abgelaufen | Neuen anlegen und das Secret tauschen |
tier_forbidden |
Das Projekt hat kein Enterprise | Die API gibt es nur in Enterprise |
insufficient_scope |
Dem Schlüssel fehlt ein Bereich | Starten braucht „Scans auslösen“, Lesen „Scans & Ergebnisse lesen“ |
journey_required |
Der Test hat keine Journey zum Abspielen | Eine Journey aufzeichnen |
journey_sleeping |
Die Journey liegt über dem Limit des Plans | Reihenfolge der Journeys oder Plan ändern |
scan_in_progress |
Ein normaler Scan des Tests läuft bereits | Warten oder eine url für einen Dry-Run senden |
idempotency_conflict |
Derselbe Schlüssel für eine andere Art von Anfrage | Für einen anderen Body einen neuen Schlüssel verwenden |
rate_limited, too_many_long_polls |
Mehr als 60 Anfragen pro Minute oder fünf Wartende gleichzeitig | So lange warten, wie Retry-After angibt |
Codes in gate.failures sind keine API-Fehler, sondern die Gründe, warum das Gate nicht bestanden hat. Alle Codes sind in der Fehlerübersicht beschrieben.
Verwandte Anleitungen
Haben Sie in dieser Anleitung einen Fehler gefunden, oder fehlt etwas? Schreiben Sie uns