9.8 KiB
OPS104 — Disparo automático do deploy pós-merge (staging automático, produção com review)
Status: rascunho Atualizado em: 2026-09-14 Issue: #980 Priority: P2 Impeccable: A — N/A sem UI Rascunho UI: N/A — sem UI Appetite: ~1 dia eng — um outcome verificável Responsável: —
Intenção
"lets improve our CI pipeline to autimatically initiate the deploy action, which will run verify and deploy staging automatically btu wait review for production deploy, every time that: - A PR merges to main and there's no deploy action already running. So it should automatically fire a deploy action from main's head. - A deploy action has juust finished the staging deploy step and there's newer commits to main (head was updated). So it should automatically fire a new deploy run from main new head."
Hoje publicar é ato manual (OPS71/OPS102): mergear em main não dispara nada, e se ninguém lembrar do dispatch o site fica no SHA antigo enquanto o merge parece entregue. O pedido tira o trabalho braçal mantendo os gates: o merge em main passa a ser o gatilho, o staging se mantém atualizado sozinho, e produção continua esperando o reviewer humano. A OPS102 escreveu "não voltar a ser deploy automático" como anti-goal; esta entrega supersede esse anti-goal com evidência de produto nova — automatiza-se o início do run, nunca a aprovação de produção nem o atalho do verify. Prioridade P2 (recomendação no gate): é mudança de ops/CI, sem incidente em produção ou bloqueio de produto; o ganho é o pipeline se manter sozinho.
Persona e fluxo
- Persona / contexto: o operador do deploy — hoje depende de lembrar do dispatch manual a cada merge; e quem mergeia (humanos e agentes) não vê que o site não foi publicado.
- Job principal: todo merge em
mainvirar staging atualizado sem intervenção, e o humano decidir com calma quando promover a produção. - Fluxo desejado: merge em
main→ run do deploy nasce sozinho no head →verifyfull → staging publica automático → semainandou no meio, um novo run nasce no head novo → o humano aprova o environmentproductiondo run que quiser → produção publica. - Anti-goals de produto: nunca auto-aprovar/auto-publicar produção; nunca atalhar o
verifyfull; nunca um segundo pipeline/script gêmeo; nunca tocarci-pr.yml, overifyou os guards de banco local; nunca reintroduzir o stale-run guard.
Esboço de fluxo
[merge em main] ── deploy ativo? ── sim → nada nasce (o run ativo decide)
│ não
▼
run nasce no head de main → verify full → deploy-staging (automático)
│ └─ main andou? → run novo nasce no head novo
▼
produção aguarda reviewer → aprovado → deploy-production = publicado
Objetivo e aceite
- Merge em
mainsem deploy ativo → run do deploy nasce sozinho no SHA do head demain; ninguém precisa lembrar do dispatch (que segue disponível como escape manual). - Merge durante verify/staging → ao terminar o staging, um run novo nasce no head novo de
main; staging não fica velho em silêncio. - Merge enquanto produção aguarda review → um run novo nasce no head (o pendente aguardando aprovação não bloqueia); os dois ficam pendentes e o humano aprova qual promover — produção continua só com aprovação humana.
- Verde falso não existe: nada publica sem
verifyfull verde; run vermelho = humano resolve (sem retry/auto-heal).
Sobreposição com trabalho entregue
- OPS102 (removeu o stale-run guard) e OPS103 (staging + aprovações separadas) permanecem válidos: esta entrega muda quem inicia o run, não o fluxo dentro do
deploy.yml(verify→ staging → review → produção, script, flock e "already deployed" intocados); o que nunca volta é o publish sem reviewer.
Dados (intenção)
- Vou apresentar dados? Não — N/A: sem superfície de dados para usuário; a evidência são os logs do Actions (run que nasce sozinho, ordem dos jobs, approval pendente).
- Decisões desbloqueadas: operador decide qual run promover a produção; ninguém decide "quando lembrar de disparar".
- Forma: N/A — nada a apresentar.
Dados da decisão (literais)
- ID reservado OPS104; kind chore; sem UI (Impeccable A).
- Trigger 1:
pushna branchmainno próprio.github/workflows/deploy.yml, mantendoworkflow_dispatch(sem workflow gêmeo); dispatch/refmain; guard de "deploy ativo" pula o run. - Run "ativo" (decidido no gate 2026-09-14): só
queued+in_progresscontam como ativo no guard do trigger 1; runwaiting(aguardando review de produção) não bloqueia — um merge nessa janela dispara run novo e os dois ficam pendentes, cabendo ao humano aprovar qual promover. - Trigger 2: job hosted após
deploy-staging(fora do grupodeploy-homeserver) compara o head demaincom o SHA do run e re-dispatchadeploy.ymlse houver commit novo; nunca no runner self-hosted. - Permissões: novos jobs com
actions: read(listar runs) eactions: write(dispatch);contents: readmantido; nenhum secret novo (oGITHUB_TOKENnativo dispatcha —workflow_dispatché a exceção documentada da anti-recursão). - Environments intocados:
stagingsem reviewer;productioncom required reviewer (fsolla) — config do repo, não do YAML;concurrency: deploy-homeserverintocado (cancel-in-progress: false,queue: max) e só os jobs de deploy entram nele.
Direção no codebase (hipótese)
- Áreas prováveis:
.github/workflows/deploy.yml(trigger + job de preflight + job de requeue) etests/unit/ciSkipInvariants.unit.spec.ts(o pin l.111–122 troca "sempush:" por "push emmain+ guard de run ativo +workflow_dispatchmantido"; l.124–150 segue additive-safe); docs vivos a realinhar:docs/ops/teqo-1313-deploy.md(l.3–8, l.12, l.225),docs/AGENT-OPS.md(l.3, l.19, l.82, l.111),AGENTS-infra.md,AGENTS.md(l.15),README.md(l.12, l.17),.agents/rules/agent-pr-workflow.mdc(l.64),scripts/issue-transition-on-merge.mjs(l.74). - Precedente a olhar:
issue-done-on-main-merge.yml(evento + recuperação manual),agent-pr-ready-automerge.yml(nota de anti-recursão do token),scripts/lib/github-api.mjs(workflowDispatch),scripts/deploy-homeserver.sh(idempotência "already deployed" — re-dispatch do mesmo SHA sai verde sem rebuild). - Risco de acoplamento: o fluxo verify→staging→review→produção, o script e o flock não mudam; self-hosted continua exclusivo dos jobs de deploy; nenhum run automático alcança o homeserver sem
verifyverde;tests/unit/deployScript.unit.spec.tsnão muda.
Dependências
- Nenhuma.
Fora de escopo
- Auto-approve do environment
productione qualquer forma de deploy automático do publish (destino: nunca) — só o início do run é automático. - Retry/auto-heal de
verifyvermelho e cancelamento automático de runs antigos (decidido no gate: ambos pendentes; nada publica sem merge novo ou dispatch). - Mexer em
ci-pr.yml, no jobverify, nos guards de banco local, em secrets ou na proteção de branch.
Rabbit holes de produto
- "Já que automatiza o start, automatiza o approve." Atalho no
productiondestrói o único gate que valida o SHA em produção. Corte: produção só publica com reviewer; approve automático não existe neste item. - "Se o verify falhar, tenta de novo." Auto-retry mascara regressão e pode publicar SHA que não passou. Corte: vermelho = nada publica; o escape é merge novo ou dispatch manual.
- "Um dispatcher separado é mais limpo." Workflow gêmeo cria drift e um lugar a mais onde o run "ativo" escapa do guard. Corte: o dono é o
deploy.yml; no máximo um job hosted de requeue dentro dele.
Questões em aberto (produto)
- Run parado no review de produção conta como "ativo"? Decidido no gate (2026-09-14): B — só
queued/in_progresscontam;waitingnão bloqueia, um merge na janela dispara run novo (dois pendentes, humano aprova qual promover). - Run novo nasce enquanto um antigo aguarda review: e o antigo? Decidido no gate (2026-09-14): A — ambos ficam pendentes, o operador aprova o mais novo e ignora/cancela o antigo; nenhuma automação destrutiva; o risco fora-de-ordem fica no runbook (OPS102 já o aceitou documentado).
- Onde mora o trigger 1? Decidido no gate (2026-09-14): A —
pushno própriodeploy.yml+ job de preflight que pula se já há deploy ativo; um dono só; o pin unitário passa a testar o guard em vez de proibir opush. - Onde mora o trigger 2? Decidido no gate (2026-09-14): A — job hosted de requeue após
deploy-staging(lê head demainvs SHA do run); imediato e barato; o job não entra nodeploy-homeserver(um job aguardando approval não pode bloquear o requeue). - Guardrail de falha: Decidido no gate (2026-09-14): A — sem retry/auto-heal:
verifyvermelho = nada publica até merge novo ou dispatch manual.
Referências
- GitHub Issue #— (a registrar via
pnpm agent:register) docs/plans/ops102-deploy-manual-sem-stale-guard.md— anti-goal superseded;docs/plans/ops103-staging-homeserver.md— fluxo atual (verify único, aprovações separadas).github/workflows/deploy.yml— trigger (l.44–47), jobs e concurrency (l.161–212)tests/unit/ciSkipInvariants.unit.spec.ts— pins do manual-only (l.111–122) e do chain (l.124–150);tests/unit/deployScript.unit.spec.ts— script (não muda)docs/ops/teqo-1313-deploy.md— "Gatilho e fluxo" (l.10–51) e "Falhas conhecidas" (l.225, coluna de gatilho de revisita)docs/AGENT-OPS.md,AGENTS-infra.md,AGENTS.md,README.md,.agents/rules/agent-pr-workflow.mdc,scripts/issue-transition-on-merge.mjs— citações do deploy manual a realinhar