Tovább a tartalomhoz

Pipeline-ok és automatizálás

A pipeline a Control Center determinisztikus fele. Az ügynök eldönti, mit mondjon; a pipeline eldönti, mi történjen ezután, minden alkalommal ugyanúgy. A pipeline lépésekből álló DAG, amely ügynököket indíthat, szkripteket futtathat, feltétel alapján elágazhat, egy gyűjtemény elemein szétágazhat és adatot adhat át a lépések között — tartósan mentve, folytathatóan és költségelszámolással.

Három okból léteznek: hogy összefűzzenek egy ismétlődő, több lépésből álló folyamatot, hogy több ügynököt hangoljanak össze sorban vagy párhuzamosan, és hogy reagáljanak valamire, ami ember jelenléte nélkül történt.

A sablon (PipelineDefinition) a deklaratív DAG: lépések, deklarált bemenetek és konfiguráció. A munkaterület saját adatbázisában él. Egy futtatás (PipelineRun) a sablon egyetlen végrehajtása, amely egy módosítható állapottárat, költség- és tokenösszegeket, valamint lépésenkénti állapotot hordoz.

Egy sablonhoz sok futtatás tartozik. A futtatások túlélik az újraindítást: a motor újratölti a folyamatban lévő futtatásokat, és onnan folytatja őket, ahol abbamaradtak.

Két biztosíték szándékosan inkább megbuktatja a teljes futtatást, mintsem folytatna egy lépést, mert a folytatás rosszabb lenne a leállásnál:

  • Egy lépés, amely a suspendedStepTimeout-nál hosszabb ideig van suspended állapotban (alapértelmezés szerint 24 óra), olyan jegyekre vár, amelyek soha nem jutnak végállapotba, ezért a rendszer megbuktatja, hogy felszabadítsa a futtatást. A kivétel az extras['awaitApproval'] == true — ez az orchestration.awaitApproval részleges jóváhagyási kapuja. A human.gate nem kivétel.
  • Egy lépés, amely extras: {'idempotent': false} beállítással van deklarálva, és éppen futott, amikor a szerver leállt, már alkalmazhatta a mellékhatását (egy squash merge, egy worktree-takarítás). Az újrafuttatása kétszer alkalmazhatná, ezért a motor megbuktatja, és az újrapróbálkozást Önre hagyja.

Egy lépésnek két egymástól független része van, és éppen a szétválasztásuk teszi lehetővé, hogy a motor kicsi maradjon.

A fajta (StepKind) mondja meg a motornak, hogy egy lépés mikor válik futtathatóvá a felette lévő lépésekhez képest — az ütemezőnek csak ezt kell tudnia. Hat van belőle: trigger, listen, join, router, forEach és terminal. A törzs (bodyKey) az a munka, amelyet a lépés az ütemezés után elvégez. A törzsek a szerver indításakor kulcs szerint regisztrált closure-ök, így sok különböző fajtájú lépés osztozhat egy törzsön: például több listen lépés is ugyanazt a conversation.promptAgent törzset futtatja.

Mivel az ütemezési szemantika hat fajtában él, a viselkedések katalógusa bővülhet anélkül, hogy bárkinek hozzá kellene nyúlnia az ütemezőhöz. A fajták, törzsek és konfigurációjuk teljes katalógusát lásd a Pipeline-lépésfajták oldalon.

A regisztrérium kialakításának két következményét érdemes ismerni, mielőtt kézzel sablont ír:

  • Egy lépés, amelynek bodyKey-je nincs regisztrálva, figyelmeztetést ad a sablon betöltésekor, majd soha nem fut le. Futásidőben semmi nem jelez hangosan hibát. A trigger és terminal lépések mentesülnek ez alól a figyelmeztetés alól: a trigger továbbra is lefuttatja a semmittevő pipeline.trigger törzset, hogy a további figyelők elinduljanak; egy terminal lépésnek nincs törzse, és az ütemező őrszeme.
  • Egy csomópont extras leképezésében lévő kulcsok pontos egyezést igényelnek, és minden más esetben csendben figyelmen kívül maradnak. Csak az extras['spaceId'] teszi egy ügynöklépés munkáját egy létező beszélgetésbe. Az extras['mode'] (chat / plan / review) annál a csomópontnál olvasódik, amelyik megnyitja a szobát (messaging.createSpace, alapértelmezés: review) — egy későbbi ügynöklépés nem tudja újra jogosultságolni azt a beszélgetést, amelyhez csatlakozott. Mindkét kulcs bármely más írásmódja figyelmen kívül marad.

A lépések nem hívják egymást. Egy közös állapottárba írnak és abból olvasnak a futtatáson belül: egy lépés kulcs szerint olvassa be a bemeneteit, végrehajt, majd a kimenetét a saját outputKey-je alá írja vissza. Ha párhuzamos ágak ugyanazt a kulcsot célozzák, egy StateReducer fésüli össze az egyidejű írásokat, egy TemplateRenderer pedig ugyanabból az állapotból helyettesíti be a {{...}} helyőrzőket a promptokban és a szkriptekben.

Éppen ezért vizsgálható meg egy pipeline utólag — az állapottár a futtatás teljes története.

A PipelineTrigger sorok deklarálják, mikor indul el automatikusan egy sablon. Négy fajtájuk van:

Eseményindító Akkor indít futtatást, amikor
Domainesemény Egy illeszkedő domainesemény elsül — például PullRequestPublished vagy TicketAssigned
Ütemezés Egy cron-/intervallumkifejezés esedékessé válik
Kézi Ön kézzel futtatja; az, hogy létezik egy engedélyezett kézi eseményindító, az teszi be a sablont a futtatásválasztóba
Webhook Egy külső rendszer POST kérést küld a szerver HTTP-portján a /webhooks/<token> végpontra

Az eseményindítók zárt halmazt alkotnak: a diszpécser 16 eseménytípust ismer, és ennyit kínál fel a választóban (EventPayloadMapper.knownEventTypes), más nem indíthat pipeline-t. Ebből a 16-ból tizennégyhez tartozik payload-leképezés. Kettőhöz — a TicketCreated és a TicketStatusChanged eseményhez — nem tartozik, így az ezekre állított eseményindító engedélyezve marad, de soha nem sül el. A teljes listát lásd a Domainesemények oldalon.

A webhook token egyben a HMAC titkos kulcs is: a kérés X-Hub-Signature-256: sha256=<hex> fejlécét a rendszer a HMAC-SHA256(body, token) értékkel ellenőrzi, az ellenőrizetlen kézbesítéseket elutasítja, és azok nem játszhatók újra, a duplikátumokat pedig a dedupe_key dobja el. Mivel az eseményindító-sorok egyediek (munkaterület, eseménytípus, sablon) szerint, egy sablonhoz legfeljebb egy webhook-eseményindító és eseménytípusonként egy eseményindító tartozhat.

A beépített sablonok kézi és eseményindítóit engedélyezve telepítik; az ütemezett eseményindítók letiltva kerülnek be, így az ismétlődő átvizsgálás mindig kifejezett bekapcsolást igényel.

Tizenhárom pipeline-sablont telepít a rendszer minden munkaterületre, és ezek közül nyolc letiltva érkezik: external_pr_welcome, cross_review, ticket_to_pr, pr_triage, pre_merge_gate, release_notes, dep_audit és pr_digest.

Ez az a rész, amely meglepi az embereket. Az eseményindítóik engedélyezve kerülnek be, a PipelineEngine.start viszont elutasítja a letiltott sablont, és null-t ad vissza — így az eseményindító a semmibe sül el, és nem jelenik meg futtatás. A kapcsoló a sablon, nem az eseményindító. Kapcsolja be a Beállítások → Munkaterület → Pipeline-sablonok alatt, mielőtt azt várná, hogy az a nyolc bármit is tegyen.

Egy munkaterület újratelepítése megőrzi, amit Ön választott: az isEnabled beállítása és a meglévő eseményindító-sorai megmaradnak. Pontosan egy szándékos kivétel van — a pr_merged_cleanup napi ütemezése akkor is kényszerítetten engedélyezett, ha korábban ki volt kapcsolva, mert ez szemétgyűjtés. Enélkül a worktree-sorok, a kódgráf-partíciók és a copy-on-write másolatok halmozódnak fel, és ez soha nem is volt felhasználói döntés tárgya.

Még egy aszimmetria, amit érdemes tudni: a beépített pr_review szándékosan már nem tesz közzé semmit a GitHubon. Egy átnézés közzététele külön, felhasználói jóváhagyáshoz kötött lépés, és egy közvetlenül posztoló pipeline kétszer tenné közzé. A cross_review, a ticket_to_pr és a pr_triage viszont futásukkor továbbra is egyenesen a GitHubra tesz közzé egy hozzászólást.

A PipelineEngine létrehozza a futtatás rekordját, ütemezi a futtatás eseménytípusához illeszkedő indítócsomópontot, majd bejárja a DAG-ot: ahogy a lépések befejeződnek, kiértékeli a további eseményindítókat, feloldja a router elágazásait, vár a join lépésekre, alkalmazza az újrapróbálkozási szabályokat és a hiba utáni folytatást, valamint minden lépésnél menti az állapotot, a költséget és a hibákat. Azokat az éleket, amelyek egy másik indítócsomópontból indulnak, erre a futtatásra nézve figyelmen kívül hagyja, így egy ütemezett indítás nem vár egy csak kézi feltételre.

Mit nem tud: semmit a jegyrendszerről, az ügynökindításról, a GitHubról vagy az üzenetküldésről. Egyetlen bővítési pontja a PipelineBodyRegistry. Az ott regisztrált törzsek azok, amelyek az AgentDispatchPort, a MessagingPort és testvéreik hordozói — ezért nem jelenti soha az ütemező módosítását, ha új képességet adunk a pipeline-okhoz.

Egy hibát adó lépés rögzíti a hibaüzenetét, és failed állapotba kerül. Ha a lépés continueOnFail beállítást deklarál, a további lépések folytatódnak, és a hiba a futtatás állapotában tárolódik; különben a futtatás megbukik. Egy StepRetryPolicy-val rendelkező lépést előbb újrapróbál a rendszer, lineáris vagy exponenciális backoff-fal, mielőtt bármelyik fenti eset érvénybe lépne.