- 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>
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
mainproduz imagem por commit no registry; a tagvX.Y.Zpromove 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 / environment — hoje (✅) 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/versionviauseAppVersion(); deixa de embutir versão. Em produção ainda embuteAPP_VERSIONno bundle.
5. Runbook — subir um release (fluxo ATUAL ✅)
⚠️ Regra de ouro: confirme DEV ou PRODUÇÃO antes de agir.
dev.scoreodonto.com= VPS4 (bancopgdev, descartável).scoreodonto.com= VPS1 (real). Nuncagit push --forcenamain. 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.tsainda existemEstamos 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_VERSIONpara nomear a imagem → o bump manual continua necessário para evitar colisão de tags no registry (republicar:vX.Y.Zcom outro código quebraria "uma tag = um artefato"). Após o Passo 5:constants.tseupdate-version.jssã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, semAPP_VERSION); promote (só tag; re-tag:<commit>→:vX.Y.Zviadocker buildx imagetools create, sem rebuild) → deploy.- Validação de integridade antes do deploy: existe
backend:<C>efrontend:<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);:latestsó 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) elevarVERDADE-HOJE.mda doc principal (com esta convenção e precedência) → (6) Passo 5 → (7) atualizar docs. À medida que cada item 🧪 for promovido, vira ✅ aqui.