Integrace
CI/CD přes API
Nasazení, které rozbije měření, se nejlíp chytá dřív, než se dostane na produkci. API umí přehrát uložené cesty proti náhledu konkrétní změny, porovnat výsledek s očekávaným stavem a vrátit jednoznačné „prošlo / neprošlo“, podle kterého CI job skončí. Tenhle návod popisuje, jak ten tok postavit a na čem se obvykle zasekne.
Celý postup má čtyři kroky: spustit, počkat, vyhodnotit bránu a případně vydat SARIF nebo komentář.
GitHub App, akce, nebo vlastní volání
Na GitHubu máte tři cesty a všechny stojí na stejném API:
- GitHub App (kontrola pull requestu) se nastaví v dashboardu a do repozitáře nic nepřidáváte. Pravidlo je pevné: hlásí jen přímé změny dataLayer a blokuje jen kritické nálezy.
- Kompozitní akce
analyticsproofběží ve vašem workflow. Zkopírujete ji do repozitáře jako.github/actions/analyticsproofa zavoláte řádkemuses:. Workflow, které k ní patří, je v receptech pro CI. Proti náhledu přehraje všechny cesty, které plán dovolí spustit (pozastavené vynechá), použije stejné pevné pravidlo, zapíše soubor SARIF a komentář k pull requestu. - Vlastní volání API funguje v jakémkoli CI a pravidlo brány si určíte sami. Čekání a vyhodnocení si ale napíšete sami.
Stačí vám výchozí pravidlo na GitHubu? Vezměte App. Chcete mít kontrolu ve workflow nebo výsledky v code scanningu? Akci. Jiné CI nebo jiné pravidlo znamená API.
Klíč
Klíč vytvoří editor nebo vyšší role v Nastavení projektu › API klíče. Pro CI stačí dvě oprávnění: „Spouštět scany“ a „Číst scany a výsledky“. Víc klíč nepotřebuje. Vypršení je volitelné a celý klíč uvidíte jen jednou, hned po vytvoření.
Klíč začíná ap_live_ a posílá se v hlavičce Authorization: Bearer … nebo X-API-Key: …. Uložte ho jako maskovanou proměnnou nebo secret, nikdy do repozitáře. API je v plánu Enterprise. Když projekt Enterprise nemá, platný klíč dostane 403 tier_forbidden. Podrobnosti jsou v referenci API.
Spuštění proti náhledu
Sken se spouští přes POST /api/v1/targets/{id}/scans. Id testu najdete přes GET /api/v1/targets. Když do těla pošlete url s adresou náhledu, běží sken jako dry-run: vzniknou rozdíly a výsledek pro bránu, ale do cesty se nic nezapíše. Očekávaný stav, hlídané nástroje ani historie regresí se nezmění. Bez url spustí stejné volání běžný sken uloženého testu.
U testu webu samotné url na přehrání cesty nestačí. Bez typu skenu spustí výchozí sken testu, tedy compliance audit náhledu. Jednu cestu přehrajete s "scanType": "web_datalayer" a volitelně s journeyId. Bez něj se použije první cesta testu, kterou plán dovolí spustit. Odpověď pak obsahuje jedno scanId a jeho pollUrl.
S "journeys": "all" se přehrají všechny cesty testu, které plán dovolí spustit, a maxJourneys jejich počet omezí. Odpověď má pak jiný tvar: nemá scanId na nejvyšší úrovni, jen pole scans se scanId a pollUrl pro každou cestu a pole failed s cestami, které se nepodařilo spustit. Skript musí počkat na každé scans[].pollUrl, stejně jako to dělá kompozitní akce. Neprázdné failed by mělo shodit job.
Dvě věci, které se vyplatí nastavit hned:
- Hlavička
Idempotency-Key. Opakovaný pokus se stejným klíčem nespustí druhý sken, ale vrátí ten první. Klíč označuje jeden konkrétní požadavek: test, výběr cest a adresu náhledu. Při opakování téhož požadavku ho neměňte a neodvozujte ho jen z hashe commitu. Stejný commit se může nasadit na druhý náhled nebo zkontrolovat s jinými cestami a s klíčem jen z commitu byste dostali zpátky starý sken. - Země skenu. Náhled se skenuje ze země, kterou jste zvolili při vytvoření testu. Pokud je náhled za IP allowlistem, povolte adresu této země.
Viz dry-run v referenci.
Čekání na výsledek
Stav skenu čtete přes GET /api/v1/scans/{id}?wait=N. Server drží požadavek otevřený, dokud sken neskončí a nejsou uložené jeho nálezy, nejdéle N sekund. Strop je 300 sekund. Praktičtější je čekat kolem 120 sekund a dotaz opakovat, protože dlouhá spojení ráda přeruší proxy nebo runner.
Sken skončil, když je jeho status jeden z těchto:
| Hodnota v API | V dashboardu |
|---|---|
completed |
Dokončeno |
failed |
Selhalo |
cancelled |
Zrušeno |
broken_step |
Přerušený krok |
no_data |
Bez dat |
Opakujte, dokud nebude status konečný a zároveň settled rovno true. settled: false znamená, že vám vypršelo čekání ve chvíli, kdy se nálezy teprve ukládaly. Stačí se zeptat znovu.
Jeden klíč smí mít najednou nejvýš pět čekajících dotazů. Šestý dostane 429 too_many_long_polls. Pokud spouštíte víc cest najednou, čtěte je postupně, ne paralelně.
Návrh brány
Bránu předáte při čtení skenu jako ?gate= s URL-kódovaným JSONem. Predikáty se sčítají přes „nebo“: brána selže, když platí kterýkoli z nich. Všechny predikáty jsou v referenci. Pro náhled a dataLayer je rozumný začátek tohle:
{ "failOnSeverity": "critical", "failOnBrokenStep": true }
Pravidla, která z tabulky predikátů nevyčtete:
- Sken ve stavu
failed,cancellednebono_databránou neprojde nikdy, ať nastavíte cokoli. Sken bez dat nedostane ani falešné „skóre pod limitem“, má vlastní kód. - Přerušený krok ve výchozím stavu bránu neshodí. Skončí jen jako varování a rozdíly toho skenu se nevyhodnotí. Pokud změna rozbije tlačítko v košíku, brána bez
failOnBrokenStepprojde. Proto ho doporučujeme zapnout. - Posun události o jeden krok je v toleranci a bránu nikdy neshodí.
- Když cesta ještě nemá očekávaný stav, predikáty nad dataLayer nemají s čím porovnávat. Nemůžou selhat a ve
warningspřijdedatalayer_gate_no_baseline. Vyplatí se na tohle varování v CI reagovat. - Bez
?gate=odpověď žádnou bránu neobsahuje. Skript proto musí brát chybějícígate.passedjako neúspěch, ne jako úspěch. minComplianceScorehodnotí compliance skóre, takže patří k compliance testu. Cestu hodnotí rozdíly.
Parametr scope=direct-datalayer zúží rozdíly na to, co web posílá přímo do window.dataLayer. Požadavky, které z toho vyrábějí GA4 a další nástroje, zůstanou v dashboardu, ale bránu neovlivní. Stejné zúžení používá App i akce. Porovnává se přítomnost událostí a parametrů a jejich JavaScriptový typ, ne hodnoty. Výjimkou jsou signály souhlasu, u kterých je změna hodnoty samostatný nález.
SARIF a komentář
S ?format=sarif vrátí skončený sken dokument SARIF 2.1.0. Sken, který ještě neskončil, vrátí běžný JSON, takže SARIF si stahujte až po odpovědi JSON, ve které je status konečný a zároveň settled rovno true. Dokud se nálezy ukládají, SARIF by mohl být neúplný. SARIF obsahuje stejné nálezy, které posuzuje brána, a navíc události posunuté o jeden krok, které brána toleruje (kritické jako error, varování jako warning, informace jako note). Verdikt v něm není. O výsledku jobu dál rozhoduje gate.passed. Aby soubor odpovídal bráně, přidejte stejný scope.
Komentář k pull requestu nebo merge requestu vám připraví POST /api/v1/scans/{id}/explain. Vrátí vysvětlení rozdílů v běžné řeči bez hodnot parametrů. Před koncem skenu odpoví 409 scan_not_terminal.
Příklad: GitLab CI a jiná CI
Postup je stejný v každém CI, které umí curl a jq:
- Klíč a id testu uložte jako maskované proměnné CI.
- Job zařaďte až za nasazení náhledu, aby znal jeho adresu.
- Spusťte dry-run s
url,"journeys": "all"aIdempotency-Keysloženým z ID testu, výběru cest a adresy náhledu. Uložte si všechnapollUrlzescansa neprázdnéfailedukončete chybou. - Každý sken čtěte s
wait=120, bránou ascope, dokud nebude konečný a zároveňsettledrovnotrue. Celé čekání omezte timeoutem jobu. - Job selže, když
gate.passedu kterékoli cesty nenítrue. Do logu vypištegate.failuresawarnings. - Pokud CI SARIF nezobrazuje, uložte ho jako artefakt jobu.
Hotový skript pro GitLab je v receptech pro CI. Spouští běžný sken uloženého testu a čte jedno scanId. Pro náhled jedné cesty pošlete tělo {"url": "…", "scanType": "web_datalayer"}, do Idempotency-Key přidejte adresu náhledu a minComplianceScore vyměňte za bránu pro cestu z části výše. Zbytek skriptu zůstane, jak je. Pro "journeys": "all" potřebuje smyčku přes scans[], protože odpověď scanId nemá.
Chyby a co s nimi
| Kód | Obvyklá příčina | Co udělat |
|---|---|---|
missing_credentials, invalid_key |
Secret se do jobu nedostal nebo je oříznutý | Zkontrolujte proměnnou a hlavičku |
key_revoked, key_expired |
Klíč je zrušený nebo prošlý | Vytvořte nový a vyměňte secret |
tier_forbidden |
Projekt nemá Enterprise | API je jen v Enterprise |
insufficient_scope |
Klíči chybí oprávnění | Spouštění potřebuje „Spouštět scany“, čtení „Číst scany a výsledky“ |
journey_required |
Test nemá cestu, kterou by přehrál | Nahrajte cestu |
journey_sleeping |
Cesta je nad limitem plánu | Změňte pořadí cest nebo plán |
scan_in_progress |
Běžný sken testu už běží | Počkejte, nebo pošlete url pro dry-run |
idempotency_conflict |
Stejný klíč pro jiný druh požadavku | Pro jiné tělo použijte nový klíč |
rate_limited, too_many_long_polls |
Víc než 60 požadavků za minutu nebo pět čekání najednou | Počkejte podle Retry-After |
Kódy v gate.failures nejsou chyby API, ale důvody, proč brána neprošla. Popis všech kódů najdete v přehledu chyb.