teqo-backup/docs/plans/ops104-disparo-automatico-do-deploy.md

102 lines
9.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 `main` virar 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 → `verify` full → staging publica automático → se `main` andou no meio, um novo run nasce no head novo → o humano aprova o environment `production` do run que quiser → produção publica.
- **Anti-goals de produto:** nunca auto-aprovar/auto-publicar produção; nunca atalhar o `verify` full; nunca um segundo pipeline/script gêmeo; nunca tocar `ci-pr.yml`, o `verify` ou os guards de banco local; nunca reintroduzir o stale-run guard.
### Esboço de fluxo
```text
[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 `main` sem deploy ativo → run do deploy nasce sozinho no SHA do head de `main`; 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 `verify` full 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: `push` na branch `main` no próprio `.github/workflows/deploy.yml`, mantendo `workflow_dispatch` (sem workflow gêmeo); dispatch/ref `main`; guard de "deploy ativo" pula o run.
- Run "ativo" (decidido no gate 2026-09-14): só `queued` + `in_progress` contam como ativo no guard do trigger 1; run `waiting` (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 grupo `deploy-homeserver`) compara o head de `main` com o SHA do run e re-dispatcha `deploy.yml` se houver commit novo; nunca no runner self-hosted.
- Permissões: novos jobs com `actions: read` (listar runs) e `actions: write` (dispatch); `contents: read` mantido; **nenhum secret novo** (o `GITHUB_TOKEN` nativo dispatcha — `workflow_dispatch` é a exceção documentada da anti-recursão).
- Environments intocados: `staging` sem reviewer; `production` com required reviewer (`fsolla`) — config do repo, não do YAML; `concurrency: deploy-homeserver` intocado (`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) e `tests/unit/ciSkipInvariants.unit.spec.ts` (o pin l.111–122 troca "sem `push:`" por "push em `main` + guard de run ativo + `workflow_dispatch` mantido"; 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 `verify` verde; `tests/unit/deployScript.unit.spec.ts` não muda.
## Dependências
- Nenhuma.
## Fora de escopo
- Auto-approve do environment `production` e qualquer forma de deploy automático do publish (destino: nunca) — só o início do run é automático.
- Retry/auto-heal de `verify` vermelho 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 job `verify`, 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 `production` destró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_progress` contam; `waiting` nã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** — `push` no próprio `deploy.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 o `push`.
- **Onde mora o trigger 2?** **Decidido no gate (2026-09-14): A** — job hosted de requeue após `deploy-staging` (lê head de `main` vs SHA do run); imediato e barato; o job **não** entra no `deploy-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: `verify` vermelho = 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