mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-12 22:13:05 +00:00
Job deploy em ci.yml (runs-on host na workstation) streama scripts/deploy-homeserver.sh para o homeserver via SSH: build dos stages runner+migrator com secrets do teqo-1313.env, push no registry local, migrate pelo serviço de maintenance antes do rollout, up + smoke. Zero secrets novos no Forgejo; HEAD guard + flock; rollback automático com backup do compose; runbook docs/ops/teqo-1313-deploy.md; invariantes unit do script (bash -n + estrutura).
12 KiB
12 KiB
Impl: Etapa de deploy no CI para o homeserver (container teqo-1313)
Status: aprovado Atualizado em: 2026-08-17 Issue: #8 Intenção: docs/plans/ops53-ci-deploy-homeserver.md Appetite restante: herdado (~1–2 dias eng)
Leitura da intenção
- Outcome:
git pushemmaincom CI verde ⇒ site 1313 atualizado (container novo com o SHA do commit), sem passo manual; migrations aplicadas contra oteqo_1313antes do rollout (ordem migrate → rollout); rollback documentado; falha de deploy deixa o commit vermelho e nada meio-publicado. - O que NÃO negociar: build nunca no laptop do dev; ordem migrate → rollout; nada de schema migration contra produção fora do passo de migrate; imagem só publicada com full suite verde; rollback executável.
- O que reavaliar (fato novo crítico): a intenção assume "o runner Forgejo roda no próprio homeserver (tem Docker + registry + compose)". Não é verdade (verificado em
infra-solla/STATE.mde ao vivo): o runner éworkstation-runner(act_runner v0.2.11) na workstation (16c/61GB, Pop!_OS); o homeserver (8c/16GB, laptop) é que roda o stack inteiro (~/stack/docker-compose.yml: postgres comteqo_1313, forgejo, registry :5000 com htpasswd, cloudflared,teqo-1313).ssh homeserver(tailnet) funciona da workstation (chave existente, verificada). A arquitetura doplano-infra-final.md§"Arquitetura de deploy" (runner → build → registry :5000 → migrate + pull + up no homeserver) permanece o norte — só que o "runner" fica na workstation e a orquestração do deploy cruza tailnet por SSH.
Abordagem recomendada
flowchart LR
P[push main / dispatch] --> W[ci.yml full suite]
W --> C[checks]
C --> D[deploy job: runs-on host na workstation]
D --> S[ssh homeserver bash -s < scripts/deploy-homeserver.sh SHA]
S --> G[HEAD guard: main remoto == SHA?]
G --> B[build no homeserver: runner + migrator stages<br/>secrets do teqo-1313.env, --network stack_default]
B --> R[push localhost:5000 + tag teqo-1313:SHA]
R --> M[migrate: compose --profile maintenance run --rm teqo-1313-migrate]
M --> U[compose up -d teqo-1313 + healthcheck]
U --> K[smoke: / , /campanha/login, /admin, 307, webauthn, revalidate]
Opções consideradas:
- A) Deploy job
runs-on: hostna workstation; TODO o deploy executa no homeserver via SSH (bash -scom o script do repo), build lá mesmo com secrets lidos do~/stack/teqo-1313.envlocal. Recomendada — porque: o build precisa (1) do DB de produção para a geração estática —postgres:5432só existe dentro da rede do compose (stack_default; o postgres não publica porta); (2) dos secrets (DATABASE_URL,PAYLOAD_SECRET,NEXT_PUBLIC_SITE_URL) que já vivem chmod-600 no homeserver; (3) do registry emlocalhost:5000(loopback HTTP funciona — verificado: 401 de auth, não erro de transporte). Com tudo no homeserver, zero secrets novos no Forgejo e zero mudança de infra no compose.runs-on: hostusa a chave SSH existente da workstation (verificada) e mantém o job sem segredos. - B) Build na workstation com DB via túnel SSH + secrets no Forgejo. Exige publicar o postgres do homeserver (mudança de infra no compose), colocar
DATABASE_URL/PAYLOAD_SECRETde produção como secrets do Forgejo (pior superfície de vazamento), túnel SSH no job e BuildKit com--network hostpara alcançar o túnel. Rejeitada — pior em todos os eixos exceto CPU (e a build no laptop cabe: 8c, ~12GB livres medidos; o stack ocioso usa ~3GB; o manual de21b3c00djá provou o Dockerfile lá). - C) Job no container (ubuntu-latest) com chave SSH nova em secret do Forgejo. Mais isolamento formal, mas: gestão de chave/known_hosts, alcance tailnet do container do runner a verificar, e o mesmo SSH remote de qualquer forma. Rejeitada — o ator é o dono único da instância (só repos fsolla/amana), o runner já roda como
fsollano host, e a mitigação real é não expor o job a eventos não confiáveis (abaixo).
Decisões de engenharia (com rejeitadas):
- Job
deployapenas emci.yml(push emmain+workflow_dispatch),needs: [checks],timeout-minutes: 60,runs-on: host. Nunca emci-pr.yml. Guard de segurança: verificar na implementação que o Forgejo não executa workflows de PRs de fork (default off; se estiver ligado, desligar é passo humano no runbook). Sem isso, um PR de fork malicioso poderia rodar código no host da workstation. - Guard de HEAD remoto (análogo ao cooldown do Vercel): o script aborta (exit 0 "stale") se
git ls-remote <repo> refs/heads/main≠ SHA do job — um push mais novo vence; o deploy antigo não sobrepõe. Serialização:flockem/tmp/teqo-1313-deploy.lockno homeserver (capacity 1 + sem concurrency no Forgejo já serializam; o flock cobre dispatch+push simultâneos e re-deploys manuais). - Script único POSIX-ish
scripts/deploy-homeserver.shversionado neste repo, executado remotamente (ssh homeserver "bash -s -- <sha>" < scripts/deploy-homeserver.sh). O homeserver não ganha dependência nenhuma (só bash + git + docker, tudo presente). Lógica: HEAD guard → flock → source dos envs (~/stack/teqo-1313.env+~/stack/.env, sem eco) → workspace~/teqo-deploy(clone/fetch do Forgejo localhttp://localhost:3000/fsolla/teqo.git, público) →docker login localhost:5000(REGISTRY_USER/PASSWORD do.env) → build dos dois stages (--network stack_default,--secret id=database_url,env=DATABASE_URL+payload_secret,--build-arg NEXT_PUBLIC_SITE_URL) → push → backup do compose (docker-compose.yml.pre-$SHA) + swap dos tags de imagem viased→ migrate (docker compose --profile maintenance run --rm teqo-1313-migrate— o mesmo caminho do OPS51) →docker compose up -d teqo-1313→ aguarda healthcheck healthy → smoke. - Migrate: serviço de maintenance do compose (
teqo-1313-migrate, imagemteqo-1313-migrator:<sha>após o swap) — é o caminho que o compose já prevê e que o OPS51 usou. Opayload migrateembutido nopnpm buildnão roda no Dockerfile (ele chamanext builddireto — verificado), então a ordem migrate→rollout é garantida pelo script. - Smoke pós-deploy (no homeserver, contra
localhost:1313):GET /200;GET /campanha/login200;GET /admin200;GET /campanha→ 307 (barreira sem sessão);POST /campanha/webauthn/login-options200 (anônimo por design);POST /api/revalidatecomx-revalidate-secretdo env →{revalidated:true}. Login com credenciais reais fica como validação manual (precedente OPS51). - Falha → rollback automático best-effort: restore do backup do compose +
docker compose up -d teqo-1313(imagem anterior continua local e no registry; registry nunca deleta) + exit 1 (job vermelho). Caveat documentado: migration de schema aplicada no passo de migrate não é desfeita pelo rollback (Payload é append-only) — o código velho sobre schema novo é risco residual aceito; o caminho primário protege (migrate só roda após build+push ok; rollout só após migrate ok). - Testes: spec unit nova (
tests/unit/deployScript.unit.spec.ts) combash -nno script + greps estruturais que pinam as propriedades de segurança (HEAD guard,--network stack_default,--secret id=database_url, serviço de maintenance, endpoints do smoke) — mesmo padrão dociSkipInvariantsque pina os workflows. Os invariantes existentes deci.yml(.next-e2eetc.) permanecem.
Componentes / mudanças
.forgejo/workflows/ci.yml: jobdeploynovo (runs-onhost,needs: [checks], checkout@v5 +ssh homeserver "bash -s -- $GITHUB_SHA" < scripts/deploy-homeserver.sh); comentário de cabeçalho atualizado ("No deploy step" → o passo existe agora).scripts/deploy-homeserver.sh(novo): o script remoto descrito acima; args = SHA do commit; env de configuração com default (TEQO_REPO_URL,STACK_DIR,WORKSPACE,LOCK); nunca ecoa valores de env;set -euo pipefail.tests/unit/deployScript.unit.spec.ts(novo):bash -n+ invariantes estruturais.docs/ops/teqo-1313-deploy.md(novo, dir novo): runbook — gatilho, fluxo, onde roda cada passo, rollback (restore do backup do compose +up -d, imagens anteriores no registry), re-deploy manual (dispatch), falhas conhecidas, quem é o humano (secrets ficam no homeserver; nada novo no Forgejo).AGENTS.md(parágrafo de produção/CI) edocs/AGENT-OPS.md(linha do fluxo + tabela de ambientes): deploy deixou de ser "runbook fora do repo".docs/changelog/2026-08-17-ops53.md+pnpm changelog:build(OPS44).- infra-solla (fora do repo, anotação no fechamento):
STATE.mde runbook com a correção da topologia (runner na workstation, não no homeserver) e o passo único de setup humano — nada de daemon.json (loopback HTTP OK), nada de secrets novos. - Migration: sem migration (não toca schema).
- Access / Consent: N/A.
- UI: Impeccable A — sem UI.
Dados → forma
N/A (sem dados novos na UI).
Fases verificáveis
- Tracer — script + workflow + tests + docs — escrever
deploy-homeserver.sh, job noci.yml, spec unit, runbook, atualizações de AGENTS/AGENT-OPS, changelog. Gates:pnpm gate:fast,pnpm format:check,pnpm knip,pnpm check:cycles. - Rehearsal read-only — rodar os comandos do smoke contra o site atual (a partir do homeserver) para validar a lógica do smoke sem deploy;
bash -n; verificar o setting de fork-PRs no Forgejo (mitigação doruns-on: host). - First real deploy = o merge do próprio OPS53 — o pipeline publica o SHA do merge; acompanhar o job; se falhar, rollback pelo runbook (restore do backup +
up -d). Este é o tracer bullet do aceite ("merge == publicar").
Rabbit holes / Não escopo (engenharia)
- Blue-green / zero-downtime obrigatório — aceite é a janela do recreate; evolução futura.
- Deploy button / UI de rollback — runbook basta.
- Build na workstation com túnel para ganhar CPU — vira item futuro se a build no laptop incomodar (gatilho: OOM ou >30 min).
- Registry com
latest/tags rolantes — tags por SHA + retenção bastam. - Backup de DB no deploy — o plano infra já aponta pg_dump diário como pendência (débito registrado no fechamento, não neste item).
- Otimizar o job para "só deploy se o diff tocar código" — idempotente, custo aceito.
Riscos e mitigação
- OOM na build do laptop (8c/16GB, ~12GB livres). O manual de
21b3c00djá buildou lá; teqo-1313 tem mem_limit 3g; mitigação: monitorar o primeiro deploy; gatilho de revisitação = build na workstation via túnel (registrado acima). runs-on: host= código executando como fsolla na workstation. Mitigação: job só em ci.yml (push main/dispatch — main é protegida, só entra via PR com checks verdes); verificar fork-PRs desligado no Forgejo (passo 2); instance é do dono único (repos fsolla/amana).actions/checkout@v5em jobhost— se o checkout falhar no host label, fallback documentado: baixar o script via raw do Forgejo (git.solla.dev/fsolla/teqo/raw/branch/main/scripts/deploy-homeserver.sh).- Rollback pós-migrate — código velho + schema novo pode não ser 100% funcional; best-effort + vermelho no job + runbook; caminho primário minimiza a janela.
- Vazamento de secrets nos logs — script sem
set -x, valores só via env do~/stack/*.env, build com BuildKit--secret(sem camadas); smoke do revalidate sem ecoar o secret. - Deploy antigo sobrescrevendo push novo — HEAD guard remoto (stale → skip verde).
Aceite de engenharia
- Aceite de produto da intenção ainda coberto (merge == publicar; migrate → rollout; rollback documentado; falha = vermelho; sem build no laptop do dev)
- Invariantes AGENTS/engineering-standards (sem segredos no repo; guards de dev/test intocados; nada de Neon)
- Testes previstos: spec unit de
bash -n+ invariantes estruturais do script; invariantes existentes dos workflows intactos - Docs: runbook de deploy/rollback + AGENTS/AGENT-OPS + changelog
Self-score decision-quality: 1) decisões caras com rejeitadas ✓ 2) cabe no appetite ✓ 3) rabbit holes nomeados ✓ 4) reusa padrões existentes (Dockerfile/migrator, serviço de maintenance, smoke do OPS51) ✓ 5) aceite da intenção preservado ✓ — 5/5.