Pular para conteúdo

0027 — Adota o smoke de produção pós-deploy e a identidade de build

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-15 · Decidido em: 2026-09-08

Contexto

Esta não é uma decisão nova: é o registro local de que o central-backend adotou uma norma da casa. O porquê está no ADR central 0031 — Smoke de produção pós-deploy e na página engenharia/smoke-producao (constituição §9, versão 1.3.0). Este documento existe para o rastro do quando e do como neste repo — não re-litiga a escolha da casa.

O que a norma corrige é um ponto cego que o central-backend tem em dose dupla:

  • O POST /api/ci/deploy é assíncrono — retorna sucesso e o pipeline fica verde antes de o container novo servir. "Deployado" sem nada ter verificado o resultado.
  • O app deploya a si mesmo por esse endpoint (0022). Um deploy ruim aqui não derruba só este app: derruba o control-plane que toda a frota usa para deployar. O incidente que escreveu a norma da URL flavor-agnóstica foi exatamente esse.
  • O app tem dois deploys da mesma versão (jar e native, 0018). Sem discriminador, os dois /health eram indistinguíveis.

Decisão

Adotar as quatro peças da norma, no mesmo PR.

  1. XADM_COMMIT atravessa o pipeline inteiro. O CI passa --build-arg XADM_COMMIT=<sha> nos dois builds; Dockerfile e Dockerfile.native declaram ARG/ENV tarde (depois do COPY, no estágio final) — no topo, o ARG invalidaria o cache de tudo abaixo e o nativeCompile (~13 min) rodaria de novo a cada commit.

    A env se chamava SOURCE_COMMIT — e o nome estava errado

    Corrigido em 2026-09-09, depois da v0.8.0. SOURCE_COMMIT é variável predefinida do Coolify: em recurso pull-only ele a injeta em runtime com o literal HEAD, por cima do ENV da imagem. Este app foi onde o defeito apareceu — o build passou --build-arg SOURCE_COMMIT=81cbb619…, a imagem no registry carregava o sha, e produção respondia "commit":"HEAD". A camada 1 do smoke passou por liveness sem provar nada. Cura: XADM_COMMIT (namespace da casa) + xadm-comum-web 0.9.0, que trata HEAD como ausente. Norma: smoke-producao §6 e coolify §Carimbar o commit.

  2. xadm-comum-web sobe para 0.9.0 (era 0.8.0 no corte original), que lê essa env e emite commit no /health (e usa o mesmo sha como release do Sentry/GlitchTip quando SENTRY_RELEASE não está setada). O mesmo bump traz flavor (native|jvm), que responde "qual dos dois deploys está no ar".

  3. Job smoke no pipeline.yml, depois do deploy, rodando o smoke.py da casa contra as rotas declaradas em docs/app.json → smoke.routes. Reprovou → reverte para a tag imutável <imagem>:<sha anterior>-<target>, confirma a reversão e fica vermelho.
  4. CENTRAL_DEPLOY_URL deixa de carregar flavor de build — passa a https://central-backend.xadm.biz/api/ci/deploy. Ver "Consequências".

Correção (2026-09-15): a reversão do item 3 não é automática. O smoke.py pede ao control-plane a tag imutável, e o POST /api/ci/deploy só aceita a tag móvel <target>-amd64 — responde 400 invalid_image. O smoke reprova e reporta o alvo; o operador reverte à mão (Deploy, Reversão). A norma da casa registra o rollback automático como pendente no control-plane.

Alternativas descartadas

  • Seguir só com o /health como gate. /health verde é liveness, não "as rotas respondem": o JWKS pode estar vazio, o banco pode ter subido sem a chave, e o /health deste app é 200-fixo de propósito (não flapa por indicador JDBC — 0016). Liveness verde com JWKS quebrado derruba o login de toda a frota em silêncio.
  • versao como discriminador de entrega. Não distingue: dois deploys da mesma versão (um workflow_dispatch sem bump) servem a mesma string. Sem commit, o smoke não tem nem critério de identidade nem alvo de rollback.
  • Declarar a camada 3 (log-guarantee) agora. Declarar smoke.glitchtip sem cadastrar o GLITCHTIP_API_TOKEN reprova por desenho — é defeito de configuração, não aviso. O bloco fica de fora até o secret existir; é o self-skip previsto na norma, não omissão.

Consequências

  • O CENTRAL_DEPLOY_URL mudou de host. Era central-backend-jar.xadm.biz — flavor de build numa URL que outro sistema consome, exatamente o que a constituição §9 proíbe desde o incidente em que parar o jar numa promoção a native derrubou o POST /api/ci/deploy da frota (503). Pré-condição operacional: central-backend.xadm.biz tem de rotear para um recurso no ar que sirva /api/ci/deploy. Enquanto não roteia, o deploy da frota inteira falha no curl -fsS.
  • Rotas do smoke começam públicas. /health, /.well-known/jwks.json e /api/auth/jwks — as três que a frota e o PowerSync consomem sem credencial. Rota m2m entra quando houver SMOKE_M2M_TOKEN; rota session não se aplica (este app roda micronaut.security.enabled=false, postura do hub — 0016 — logo não há seam de sessão a expor).
  • A mesma lista serve dois gates: o e2e native afirma ≠404 no binário antes do deploy; o smoke afirma o status esperado depois.
  • 404 autenticado passou a glitchar (efeito do bump da lib, 0.7.1). Rota morta batida por cliente com credencial vira evento no GlitchTip — é o sinal desejado, e a rede de runtime para a classe de defeito "AOT podou rota registrada" que motivou a mudança na lib.