Files
scoreodonto.com/documentação/DEPLOY-PRODUCAO.md
T
VPS 4 Builder 0f50835114 docs: reconcilia deploy/versionamento com o estado real (pós v1.0.15)
- VERDADE-HOJE.md: reescrito p/ a verdade de 17/jun — banco DEV isolado (pgdev, não
  compartilha mais), produção roda imagem v1.0.15, versão em runtime via /api/version.
  Declarado documento principal (prevalece em divergência).
- deploy.md: §5 rollback  (existe); §6 corrigida — remove o "não há CI/registry/deploy"
  (era falso/contraditório); CI/CD por tag ATIVO, falta só o Passo 5.
- DEPLOY-PRODUCAO.md: status 🧪 dos Passos 1/3/4 (promovidos em v1.0.15).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 11:34:51 +02:00

7.6 KiB

Deploy & Versionamento — Runbook ScoreOdonto

Auditado contra o sistema em 17/jun/2026. Cada item carrega seu status real — o doc responde "o que existe hoje?", não "o que gostaríamos?".

Legenda de status (convenção do projeto):

  • EM PRODUÇÃO — está no ar em scoreodonto.com (VPS1), verificável agora.
  • 🧪 EM DEV — implementado no working tree da VPS4 / dev.scoreodonto.com; não commitado, não promovido.
  • 🎯 ALVO — desenhado/aprovado, ainda não implementado.

Produção AGORA: v1.0.15 · commit f2eb90c · 2026-06-17 (curl https://scoreodonto.com/api/version). Os Passos 1/3/4 (versão em runtime) foram promovidos a produção em 17/jun (tag v1.0.15).


1. O modelo

Push na main produz imagem por commit no registry; a tag vX.Y.Z promove essa imagem para produção. Produção nunca builda — só executa imagens.

 PUSH main (commit C)                 TAG vX.Y.Z (no commit C)
        │                                     │
        ▼                                     ▼
   CI: build                           CI: deploy
   publica :C, :<versão>, :latest      ✅ hoje: REBUILDA + promove + deploya
        ▼                              🎯 alvo (Passo 5): só re-tag :C→:vX.Y.Z, SEM build
   registry                                  ▼
                                       VPS1: pull + up --no-build   ✅
                                       APP_VERSION=tag em runtime    ✅ (Passo 3, em prod)

2. Estado por componente (verificado)

Componente Status Detalhe
Infra VPS1/3/4 + Docker rootless Mapa na §3
Registry (git.clube67.com/ruicesar/...) Ver REGISTRY.md
CI: act_runner "vps4-builder" builda+publica em push .gitea/workflows/build.yml; runner systemd na VPS4
Deploy por tag (job deploy SSH VPS4→VPS1) deploy-prod.sh na VPS1
Produção não builda (image: + pull + up --no-build) docker-compose.prod.yml
Banco DEV isolado em pgdev; prod no Postgres da VPS3 Ver BANCO-DEV-ESTRATEGIA.md
GET /api/version responde (de process.env) No ar: v1.0.15 · f2eb90c · PROD
Versão exibida em Configurações → "Sobre o sistema" Passo 1 (promovido em v1.0.15; saiu do Sidebar)
Front/telas consomem /api/version (useAppVersion); app não usa APP_VERSION Passos 1/4 (em prod desde v1.0.15)
Deploy injeta APP_VERSION/APP_ENV em runtime (environment) Passo 3 (compose.prod/deploy-prod.sh em prod)
Tag promove sem rebuild + validação de integridade 🎯 Passo 5 (§7) — único item pendente

3. Mapa de máquinas

Onde Host Papel Docker
VPS1 10.99.0.1 Produção (Traefik + apps) rootless (unix:///run/user/1000/docker.sock)
VPS3 10.99.0.3 Postgres (prod) + Gitea + registry
VPS4 10.99.0.4 Dev + Builder (código; runner CI; pgdev) rootless
  • Repo (fonte da verdade): ssh://git@10.99.0.3/ruicesar/scoreodonto.com.git.
  • Project Compose: scoreodontocom. Diretório nas duas máquinas: /home/deploy/stack/scoreodonto.com.

4. De onde vem a versão (contrato /api/version)

{ "name": "ScoreOdonto API", "version": "v1.0.15", "commit": "f2eb90c",
  "build": "2026-06-17 03:37 UTC", "environment": "PROD" }
  • commit / build = carimbados no build da imagem (imutáveis — identidade do artefato).
  • version / environment = injetados no deploy em runtime (a partir da tag) via environment do compose.prod (Passo 3, em prod) — a mesma imagem pode ser promovida sob qualquer tag sem rebuild.
  • Frontend = lê tudo de /api/version via useAppVersion(); não embute mais a versão (Passos 1/4, em prod). Fonte única = backend.

5. Runbook — subir um release (fluxo ATUAL )

⚠️ Regra de ouro: confirme DEV ou PRODUÇÃO antes de agir. dev.scoreodonto.com = VPS4 (banco pgdev, descartável). scoreodonto.com = VPS1 (real). Nunca git push --force na main. Push/tag exigem autorização explícita.

# 1) VPS4: validar em DEV (build local do container de dev)
cd /home/deploy/stack/scoreodonto.com
node --check backend/server.js
docker compose build scoreodonto-backend scoreodonto-frontend
docker compose up -d --force-recreate scoreodonto-backend scoreodonto-frontend
# conferir em https://dev.scoreodonto.com

# 2) ⚠️ transitório (até Passo 5): a TAG precisa casar com frontend/constants.ts
node update-version.js          # bump (ex. V1.0.14 → V1.0.15)

# 3) commit + push → CI builda e publica :<commit> + :<versão> + :latest (NÃO deploya)
git add -A && git commit -m "feat: <descrição> (vX.Y.Z)"
git push origin main

# 4) tag = "aprovado p/ produção" → CI (hoje REBUILDA ✅) promove + deploya na VPS1
git tag v1.0.15 && git push origin v1.0.15

# 5) verificar SEM SSH:
curl -s https://scoreodonto.com/api/version     # → version/commit/environment

Backup do banco de PRODUÇÃO antes de releases sensíveis (migrações rodam no boot; aditivas/idempotentes):

ssh deploy@10.99.0.3 'pg_dump -Fc scoreodonto -f ~/backups/scoreodonto_$(date +%Y%m%d_%H%M).dump'

⚠️ Fase transitória — por que APP_VERSION/constants.ts ainda existem

Estamos entre o modelo antigo (versão escrita à mão em constants.ts) e o novo (identidade pelo commit; tags só promovem). Até o Passo 5, o CI ainda lê APP_VERSION para nomear a imagem → o bump manual continua necessário para evitar colisão de tags no registry (republicar :vX.Y.Z com outro código quebraria "uma tag = um artefato"). Após o Passo 5: constants.ts e update-version.js são removidos, a identidade passa a ser o commit, e tags apenas promovem artefatos já construídos.


6. Rollback

Reverter o código é seguro (migrações só adicionam). Por imagem, sem rebuild:

ssh deploy@10.99.0.1
/home/deploy/stack/scoreodonto.com/deploy-prod.sh v1.0.13   # tag anterior → pull + up --no-build

Restaurar o dump do banco só em caso extremo (pg_restore --clean).


7. Passo 5 — "tag promove, não builda" 🎯

Desenho aprovado (17/jun/2026), não aplicado. Refina o CI existente para Build Once → Promote → Deploy:

  • build.yml → 2 jobs por ref: build (só push; publica :<commit>+:latest, sem APP_VERSION); promote (só tag; re-tag :<commit>→:vX.Y.Z via docker buildx imagetools create, sem rebuild) → deploy.
  • Validação de integridade antes do deploy: existe backend:<C> e frontend:<C>? digests válidos? alias :vX.Y.Z == digest de :<C>? front e back do mesmo commit? Só então deploy (evita deploy parcial).
  • Decisões fechadas: manter :<commit> (sem prefixo); falhar se o commit não foi pushado (nunca buildar na tag); :latest só conveniência.
  • Eliminar update-version.js + frontend/constants.ts (a versão passa a nascer da tag git).
  • Follow-ups: GET /api/health; política de retenção do registry.

Racional completo em deploy.md e na memória project-cicd-tag-promove-nao-rebuilda.


Sequência acordada para fechar o drift: (1) doc honesta com status /🧪/🎯 [este doc] → (2) commitar Passos 1/3/4 → (3) promover → (4) confirmar /api/version → (5) elevar VERDADE-HOJE.md a doc principal (com esta convenção e precedência) → (6) Passo 5 → (7) atualizar docs. À medida que cada item 🧪 for promovido, vira aqui.