Files
scoreodonto.com/documentação/DEPLOY-PRODUCAO.md
T
VPS 4 Builder a991f1fe2c feat(version): versão em runtime via /api/version (Passos 1/3/4)
- buildInfo.ts: hook useAppVersion() consome GET /api/version (fonte única = backend),
  com fallback ao carimbo do bundle; deixa de depender do APP_VERSION manual.
- Versão exibida em Configurações ("Sobre o sistema") + telas públicas (Login/WaitingInvite);
  removida do rodapé do Sidebar.
- compose.prod/deploy-prod.sh: injetam APP_VERSION (=tag) e APP_ENV=PROD em runtime,
  desacoplando a versão semântica do build (commit/build seguem da imagem).
- constants.ts marcado legado + bump V1.0.15 (transitório até o Passo 5; evita colisão de tag).
- DEPLOY-PRODUCAO.md reescrito com status /🧪/🎯 + nota de fase transitória.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 05:19:27 +02:00

7.7 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.14 · commit 081985c · 2026-06-16 (curl https://scoreodonto.com/api/version). Todo o trabalho de versionamento abaixo marcado 🧪 está no working tree da VPS4 (não commitado).


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)

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) Em prod, valores vêm do build da imagem
Versão exibida em Configurações → "Sobre o sistema" 🧪 Passo 1. Em produção a versão está no Sidebar
Front/telas consomem /api/version (useAppVersion); app não usa APP_VERSION 🧪 Passos 1/4. Em prod o front usa APP_VERSION do bundle
Deploy injeta APP_VERSION/APP_ENV em runtime (environment) 🧪 Passo 3 (compose.prod/deploy-prod.sh no working tree)
Tag promove sem rebuild + validação de integridade 🎯 Passo 5 (§7)

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.14", "commit": "081985c",
  "build": "2026-06-16 08:06 UTC", "environment": "PROD" }
  • commit / build = carimbados no build da imagem (imutáveis — identidade do artefato).
  • version / environmenthoje () também vêm do build da imagem (build arg no CI). 🧪 (Passo 3): passam a ser injetados no deploy em runtime (a partir da tag), liberando a mesma imagem para qualquer tag sem rebuild.
  • Frontend🧪 (Passos 1/4): lê tudo de /api/version via useAppVersion(); deixa de embutir versão. Em produção ainda embute APP_VERSION no bundle.

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.