Pipelines e automatização
Os pipelines são a metade determinística do Control Center. Um agente decide o que dizer; um pipeline decide o que acontece a seguir, sempre da mesma forma. Um pipeline é um DAG de passos que podem despachar agentes, executar scripts, ramificar com base numa condição, distribuir-se em leque sobre uma coleção e passar dados entre passos — persistido, retomável e com custos calculados.
Existem por três razões: encadear um processo repetitivo de vários passos, coordenar vários agentes em sequência ou em paralelo e reagir a algo que aconteceu sem que estivesse presente uma pessoa.
Modelos e execuções
Secção intitulada “Modelos e execuções”Um modelo (PipelineDefinition) é o DAG declarativo: passos, entradas
declaradas e configuração. Vive na base de dados do próprio espaço de trabalho.
Uma execução (PipelineRun) é uma execução desse modelo, que transporta um
saco de estado mutável, totais de custo e de tokens e o estado de cada passo.
Um modelo tem muitas execuções. As execuções sobrevivem a um reinício: o motor volta a carregar as execuções em curso e retoma-as onde pararam.
Duas redes de segurança fazem falhar deliberadamente toda a execução em vez de retomar um passo, porque retomar seria pior do que parar:
- Um passo que está
suspendedhá mais tempo do quesuspendedStepTimeout(24 horas por predefinição) está à espera de tickets que nunca chegarão a um estado terminal, pelo que é marcado como falhado para libertar a execução. A exceção éextras['awaitApproval'] == true— esse é o portão de aprovação parcial deorchestration.awaitApproval.human.gatenão está isento. - Um passo declarado
extras: {'idempotent': false}que estava em curso quando o servidor parou pode já ter aplicado o seu efeito secundário (um squash merge, uma limpeza de worktree). Voltar a executá-lo poderia aplicá-lo em duplicado, pelo que o motor marca-o como falhado e deixa-lhe a nova tentativa.
O tipo agenda, o corpo trabalha
Secção intitulada “O tipo agenda, o corpo trabalha”Um passo tem duas partes ortogonais e mantê-las separadas é o que permite que o motor se mantenha pequeno.
O tipo (StepKind) indica ao motor quando um passo passa a ser executável
em relação aos seus passos a montante — é a única coisa que o agendador precisa
de saber. Existem seis: trigger, listen, join, router, forEach e
terminal. O corpo (bodyKey) é o trabalho que o passo realiza depois de
agendado. Os corpos são closures registadas por chave no arranque do servidor,
pelo que muitos passos de tipos diferentes podem partilhar um corpo: por exemplo,
vários passos listen a executar todos conversation.promptAgent.
Uma vez que a semântica de agendamento assenta em seis tipos, o catálogo de comportamentos pode crescer sem que ninguém toque no agendador. Consulte Tipos de passo de pipeline para o catálogo completo de tipos, corpos e respetiva configuração.
Vale a pena conhecer duas consequências do desenho do registo antes de criar um modelo à mão:
- Um passo cujo
bodyKeynão está registado produz um aviso na carga do modelo e depois nunca é executado. Em tempo de execução, nada falha de forma ruidosa. Os passostriggereterminalestão isentos desse aviso: o disparador continua a executar o corpo no-oppipeline.triggerpara que os listeners a jusante sejam acionados; um passoterminalnão tem corpo e é um sentinela do agendador. - As chaves do mapa
extrasde um nó têm de corresponder exatamente; caso contrário, são ignoradas em silêncio. Sóextras['spaceId']coloca o trabalho de um passo de agente numa conversa existente.extras['mode'](chat/plan/review) é lido no nó que abre a sala (messaging.createSpace, predefiniçãoreview) — um passo de agente posterior não pode redefinir as permissões de uma conversa a que se juntou. Qualquer outra grafia de qualquer das chaves é ignorada.
Estado e fluxo de dados
Secção intitulada “Estado e fluxo de dados”Os passos não se chamam uns aos outros. Leem e escrevem num saco de estado
partilhado da execução: um passo lê as suas entradas por chave, executa e volta a
escrever a sua saída sob o seu próprio outputKey. Um StateReducer funde as
escritas simultâneas quando ramos paralelos visam a mesma chave, e um
TemplateRenderer substitui os marcadores {{...}} de prompts e scripts a
partir desse mesmo estado.
É por isso que um pipeline é inspecionável a posteriori — o saco de estado é toda a história da execução.
O que inicia uma execução
Secção intitulada “O que inicia uma execução”As linhas de PipelineTrigger declaram quando um modelo arranca automaticamente.
Existem quatro tipos:
| Disparador | Inicia uma execução quando |
|---|---|
| Evento de domínio | Dispara um evento de domínio correspondente — por exemplo PullRequestPublished ou TicketAssigned |
| Agendamento | Vence uma expressão cron/de intervalo |
| Manual | Executa-o à mão; a presença de um disparador manual ativo é o que coloca o modelo no seletor de execuções |
| Webhook | Um sistema externo faz um POST para /webhooks/<token> na porta HTTP do servidor |
Os disparadores de eventos são um conjunto fechado: o despachante conhece 16
tipos de evento e oferece-os no seletor (EventPayloadMapper.knownEventTypes), e
nada mais pode iniciar um pipeline. Catorze desses 16 têm um mapeamento de
payload. Dois — TicketCreated e TicketStatusChanged — não têm nenhum, pelo
que um disparador sobre qualquer um deles fica ativo e nunca dispara. Consulte
Eventos de domínio para a lista
completa.
O token do webhook funciona também como segredo HMAC: o cabeçalho
X-Hub-Signature-256: sha256=<hex> do pedido é verificado em relação a
HMAC-SHA256(body, token), as entregas não verificadas são rejeitadas e não
podem ser reproduzidas, e os duplicados são descartados por dedupe_key. Uma vez
que as linhas de disparador são únicas por (espaço de trabalho, tipo de evento,
modelo), um modelo pode ter no máximo um disparador de webhook e um disparador
por cada tipo de evento de domínio.
Os modelos incorporados são criados com os seus disparadores manuais e de eventos ativados; os disparadores agendados são criados desativados, pelo que uma varredura recorrente é sempre opcional.
Os modelos que vêm desativados
Secção intitulada “Os modelos que vêm desativados”São criados treze modelos de pipeline em cada espaço de trabalho e oito deles
vêm desativados: external_pr_welcome, cross_review, ticket_to_pr,
pr_triage, pre_merge_gate, release_notes, dep_audit e pr_digest.
Esta é a parte que surpreende as pessoas. Os seus disparadores são criados
ativados, mas PipelineEngine.start recusa um modelo desativado e devolve
null — pelo que o disparador dispara para o vazio e não aparece nenhuma execução.
O interruptor é o modelo, e não o disparador. Ative-o em Definições → Espaço
de trabalho → Modelos de pipeline antes de esperar que qualquer desses oito faça
seja o que for.
Voltar a criar os dados iniciais de um espaço de trabalho preserva o que
escolheu: a sua definição isEnabled e as suas linhas de disparador existentes
sobrevivem. Há exatamente uma exceção deliberada — o agendamento diário de
pr_merged_cleanup é ativado à força mesmo que anteriormente estivesse
desligado, porque se trata de recolha de lixo. Sem ele acumulam-se linhas de
worktree, partições do grafo de código e cópias copy-on-write, e isso nunca
pretendeu ser uma decisão do utilizador.
Vale a pena conhecer mais uma assimetria: o modelo incorporado pr_review já não
publica deliberadamente no GitHub. Publicar uma revisão é um passo à parte,
controlado pelo utilizador, e um pipeline que publicasse diretamente estaria a
publicar em duplicado. Já cross_review, ticket_to_pr e pr_triage continuam
a publicar um comentário diretamente no GitHub quando são executados.
O que o motor sabe
Secção intitulada “O que o motor sabe”O PipelineEngine cria o registo da execução, agenda o nó de início que
corresponde ao tipo de evento da execução e depois percorre o DAG: à medida que
os passos terminam, avalia os disparadores a jusante, resolve os ramos do router,
aguarda os joins, aplica políticas de nova tentativa e continue-on-fail, e
persiste o estado, o custo e os erros em cada passo. As arestas que partem de
outro nó de início são ignoradas nessa execução, pelo que um disparo por
agendamento não espera por uma condição exclusiva da execução manual.
Aquilo que não sabe é tudo o que diga respeito a tickets, despacho, GitHub ou
mensagens. O seu único ponto de extensão é o PipelineBodyRegistry. Os corpos
aí registados são os que contêm AgentDispatchPort, MessagingPort e
congéneres — e é por isso que acrescentar uma capacidade aos pipelines nunca
implica alterar o agendador.
Tratamento de erros
Secção intitulada “Tratamento de erros”Um passo que falha regista a sua mensagem de erro e passa a failed. Se o passo
declarar continueOnFail, os passos a jusante prosseguem e o erro é guardado no
estado da execução; caso contrário, a execução falha. Um passo com uma
StepRetryPolicy é tentado novamente primeiro, com recuo linear ou exponencial,
antes de se aplicar qualquer uma dessas situações.
Ver também
Secção intitulada “Ver também”- Eventos de domínio: o que um disparador de eventos escuta
- Orquestração: um plano que aprova, compilado num destes pipelines
- Tickets e delegação: o que os passos de pipeline criam e concluem
- Tipos de passo de pipeline: o catálogo completo de tipos, corpos e configuração
- Crie o seu primeiro pipeline
- Criar um modelo de pipeline
- Executar um pipeline manualmente
- Monitorizar execuções de pipeline
- Configurar disparadores de pipeline