Integrace

CI/CD přes API

Aktualizováno: 3. listopadu 2026 Pro koho: vývojář Tarif: Professional a vyšší

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.

POST · 202 /targets/{id}/scans {"url": náhled} GET · ČEKÁNÍ /scans/{id}?wait=90 status není konečný ∨ settled = false BRÁNA · NEBO gate.passed ?gate={…} false → exit 1 SARIF 2.1.0 ?format=sarif POST · KOMENTÁŘ …/explain
Spuštění vrátí id skenu. Čekání se opakuje, dokud sken neskončí a jeho nálezy nejsou uložené. Brána rozhodne o výsledku jobu, SARIF a komentář jsou jen výstupy.

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_progress s hlavičkou Retry-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 dostal 422 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, cancelled nebo no_data brá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 failOnBrokenStep projde. 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_drifted se závažností warning, takže shodí bránu s failOnSeverity: "warning" nebo failOnNewDataLayerRegressions.
  • Když cesta ještě nemá očekávaný stav, predikáty nad dataLayer nemají s čím porovnávat. Nemůžou selhat a ve warnings přijde datalayer_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.passed brát jako neúspěch.
  • minComplianceScore hodnotí 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_interrupted místo status_failed a 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:

  1. Klíč a id testu uložte jako maskované proměnné CI.
  2. Job zařaďte až za nasazení náhledu, aby znal jeho adresu.
  3. Spusťte dry-run s url, "journeys": "all" a Idempotency-Key složeným z commitu, běhu CI, ID testu, výběru cest a hashe adresy náhledu. Uložte si všechna pollUrl ze scans a neprázdné failed ukončete chybou.
  4. Každý sken čtěte s wait=90, bránou a scope, dokud nebude konečný a zároveň settled rovno true. Celé čekání omezte timeoutem jobu.
  5. Job selže, když gate.passed u kterékoli cesty není true. Do logu vypište gate.failures a warnings.
  6. 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.



Našli jste v návodu chybu, nebo vám v něm něco chybí? Napište nám

Všechny návody