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 celý postup sestavit a kde se obvykle zadrhává.
Celý postup má čtyři kroky: spustit, počkat, vyhodnotit bránu a případně vydat SARIF nebo komentář.
API klíče, dotazování na výsledek a export podkladů máte od tarifu Starter. Přehrávání cest proti náhledovým URL vyžaduje Professional.
GitHub App nebo vlastní volání
Máte dvě možnosti a obě používají stejné 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 v datové vrstvě webu (
window.dataLayer,window.adobeDataLayer) a blokuje jen kritické nálezy. - Vlastní volání API funguje v jakémkoli CI a pravidlo brány si určíte sami. Recepty pro CI pro GitHub, GitLab a Bitbucket už čekání i vyhodnocení obsahují.
Stačí vám výchozí pravidlo na GitHubu? Vezměte App. Pro jiné CI nebo jiné pravidlo použijte API.
Klíč
Klíč vytvoří editor nebo vyšší role v Nastavení projektu › API klíče. Klíč má jedno oprávnění: Čtení, Úpravy nebo Admin. CI scany spouští i čte, takže potřebuje Úpravy. 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. Klíč funguje od tarifu Starter. Volání, které tarif nezahrnuje, dostane 403 plan_required a error.meta.minTier uvádí, jaký tarif potřebuje: náhledový běh, o kterém je tenhle návod, potřebuje Professional. Když projekt přejde na Free, klíče zůstanou uložené a po přechodu na vyšší tarif zase fungují. 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_journey" a volitelně s journeyId. Bez něj se použije první cesta testu, kterou tarif dovolí spustit. Odpověď pak obsahuje jeden sken: jeho id a pollUrl.
S "journeys": "all" se přehrají všechny cesty testu, které tarif dovolí spustit, a maxJourneys jejich počet omezí. Odpověď má pak jiný tvar: nemá id na nejvyšší úrovni, jen pole scans s id a pollUrl pro každou cestu a pole failed s cestami, které se nepodařilo spustit, každá s error.code. Skript musí počkat na každé scans[].pollUrl. 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: commit, běh CI (na GitHubu běh a jeho pokus, v GitLabu job), test, výběr cest a adresu náhledu. Při opakování téhož požadavku ho neměňte. Dokud se první požadavek zpracovává, dostane opakovaný pokus odpověď409 idempotent_in_progresss hlavičkouRetry-After; počkejte a pošlete ho znovu. Klíč dokončeného požadavku vrací stejné skeny, dokud existují, i po 24 hodinách, a adresa náhledu bývá pro celý pull request stejná, takže se klíč musí změnit s každým novým pushem. Proto do něj patří commit. Běh CI navíc zajistí nový sken i pro znovu spuštěný job. Bez adresy by stejný commit nasazený na druhý náhled dostal422 idempotency_conflict. Místo samotné adresy do klíče dejte její hash: klíč delší než 200 znaků API odmítne s chybou 400. - 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ě.
Všechna pole těla požadavku popisuje specifikace OpenAPI.
Č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. Nikdy nečeká déle než 90 sekund; dotaz opakujte, dokud sken neskončí.
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 doporučujeme začít tímto nastavením:
{ "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: mezi rozdíly se neobjeví a bránu nikdy neshodí. Posun o víc než jeden krok je rozdíl
event_driftedse závažnostíwarning, takže shodí bránu sfailOnSeverity: "warning"nebofailOnNewDataLayerRegressions. - 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í chybějícígate.passedbrát jako neúspěch. minComplianceScorehodnotí compliance skóre, takže patří k compliance testu. Cestu hodnotí rozdíly.- Stránka bez měřicích nástrojů a bez lišty se souhlasem neprojde bránou, kód je
no_tracking_observed. - Měření aplikace, která nenačetla GTM kontejner, neprojde bránou na rozdíly, kód je
vendors_not_evaluated. - Neúspěšný sken, který se přerušil na straně AnalyticsProof (například restartem, vypnutím nebo selháním naší databáze), neprojde bránou s kódem
infrastructure_interruptedmístostatus_faileda kód ke skóre k němu nepřibude. Výsledek o stránce nic neříká, proto sken spusťte znovu.
Parametr scope=direct_datalayer zúží rozdíly na to, co web posílá přímo do window.dataLayer nebo window.adobeDataLayer. Požadavky, které z toho dál odesílají GA4, Adobe Analytics a další nástroje, zůstanou v dashboardu, ale bránu neovlivní. Stejné zúžení používá App. 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 s uloženými nálezy dokument SARIF 2.1.0. Do té doby je odpovědí běžný JSON: u skenu, který ještě neskončil, i u skončeného skenu, jehož nálezy se ještě ukládají, s settled: false. Neúplný SARIF tak nedostanete, jen místo něj můžete dostat JSON. Nejjednodušší je stáhnout SARIF až po odpovědi JSON, ve které je status konečný a zároveň settled rovno true. SARIF obsahuje stejné nálezy, které posuzuje brána (kritické jako error, varování jako warning, informace jako note). Události posunuté o jeden krok v něm nejsou. 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 commitu, běhu CI, ID testu, výběru cest a hashe adresy náhledu. Uložte si všechnapollUrlzescansa neprázdnéfailedukončete chybou. - Každý sken čtěte s
wait=90, 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. Proti náhledu merge requestu přehraje jednu cestu s tělem {"url": "…", "scanType": "web_journey"}, s Idempotency-Key z commitu, jobu a hashe adresy náhledu a s bránou pro cestu z části výše. Čte jedno id. Pro "journeys": "all" potřebuje smyčku přes scans[], protože odpověď nemá id na nejvyšší úrovni.
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 |
plan_required |
Tarif volání nezahrnuje (error.meta.minTier) |
Klíč potřebuje Starter, náhledový běh Professional |
insufficient_permission |
Oprávnění klíče nestačí | Ke spouštění je potřeba oprávnění Úpravy, ke čtení stačí Čtení |
invalid_request s details[].code journey_required |
Test nemá cestu, kterou by přehrál | Nahrajte cestu |
journey_sleeping |
Cesta je nad limitem tarifu | Změňte pořadí cest nebo tarif |
step_cap_exceeded |
Cesta má víc kroků, než tarif dovoluje | Zkraťte cestu nebo změňte tarif |
scan_in_progress |
Běžný sken testu už běží | Počkejte, nebo pošlete url pro dry-run |
idempotency_conflict |
Stejný klíč pro jiný požadavek | 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.