> ⚠️ EM DESUSO desde 2026-06-17 — consolidado em /home/deploy/stack/documentos-unicos/ (ver LEIA-PRIMEIRO.md). Mantido só como histórico; NÃO usar como referência. --- # Deploy & Versionamento — Runbook ScoreOdonto > Auditado contra o sistema em **17/jun/2026**. Cada item carrega seu **status real**. > > **Legenda:** ✅ EM PRODUÇÃO · 🧪 EM DEV (não promovido) · 🎯 ALVO (não implementado). > > **Produção AGORA:** `v1.0.16 · commit 471c2c2 · 2026-06-17` (`curl https://scoreodonto.com/api/version`). > Modelo **Build Once → Promote → Deploy** completo — Passo 5 validado em v1.0.16. Tudo abaixo ✅. --- ## 1. O modelo ✅ > **Push** na `main` builda **uma vez** e publica por **commit**; a **tag `vX.Y.Z`** **re-tagueia** esse mesmo artefato (sem rebuild) e deploya. **Produção nunca builda — só executa imagens.** ``` PUSH main (commit C) TAG vX.Y.Z (no commit C) │ │ ▼ ▼ CI: build (1x) CI: promote (SEM build) ✅ publica :C + :latest re-tag :C → :vX.Y.Z (MESMO digest) + deploy ▼ ▼ registry VPS1: pull :vX.Y.Z + up --no-build APP_VERSION=tag injetada em runtime ``` Prova (v1.0.16): `:v1.0.16` tem **digest idêntico** a `:471c2c2` — a tag é só um rótulo sobre a imagem já testada. --- ## 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: job `build` publica por **commit** em push (sem `APP_VERSION`) | ✅ | `.gitea/workflows/build.yml` (runner `vps4-builder`) | | Job `promote` por tag: re-tag `:→:vX.Y.Z` (sem rebuild) + deploy | ✅ | validado em v1.0.16 (`:v1.0.16` == `:471c2c2`) | | Validação de integridade no promote (commit existe? digest igual? front+back mesmo commit) | ✅ | falha se o commit não foi pushado | | 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` | | Versão em runtime (`/api/version`) → Configurações + telas públicas | ✅ | `useAppVersion()`; sem `APP_VERSION` manual | | `APP_VERSION`/`APP_ENV` injetados no deploy em runtime | ✅ | `compose.prod` `environment` | | Versão **nasce da tag git** (`constants.ts`/`update-version.js` removidos) | ✅ | Passo 5 | --- ## 3. Mapa de máquinas ✅ | Onde | Host | Papel | Docker | |---|---|---|---| | VPS1 | `10.99.0.1` | **Produção** (Traefik + apps) | rootless | | VPS3 | `10.99.0.3` | Postgres (prod) + Gitea + registry | — | | VPS4 | `10.99.0.4` | **Dev + Builder** (código; runner CI; `pgdev`) | rootless | - Repo: `ssh://git@10.99.0.3/ruicesar/scoreodonto.com.git`. Project Compose: `scoreodontocom`. --- ## 4. De onde vem a versão (`/api/version`) ```json { "name": "ScoreOdonto API", "version": "v1.0.16", "commit": "471c2c2", "build": "2026-06-17 10:35 UTC", "environment": "PROD" } ``` - **commit / build** = carimbados no **build** (imutáveis — identidade do artefato). - **version / environment** = **injetados no deploy em runtime** (a partir da tag). A mesma imagem é promovida sob qualquer tag, sem rebuild. - **Frontend** lê tudo de `/api/version` (`useAppVersion()`); não embute versão. Fonte única = backend. --- ## 5. Runbook — subir um release ✅ > ⚠️ **Regra de ouro:** confirme **DEV ou PRODUÇÃO**. `dev.scoreodonto.com` = VPS4 (banco `pgdev`). `scoreodonto.com` = VPS1 (real). **Nunca** `git push --force` na `main`. Push/tag exigem autorização explícita. ```bash # 1) VPS4: validar em 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 # https://dev.scoreodonto.com # 2) commit + push → CI builda 1x e publica : + :latest (NÃO deploya) git add -A && git commit -m "feat: " git push origin main # 3) tag = "aprovado p/ produção" → job PROMOTE re-tagueia :→:vX.Y.Z (SEM rebuild) + deploya # (o número da versão é livre — não precisa casar com nenhum arquivo) git tag -a vX.Y.Z -m "release vX.Y.Z" && git push origin vX.Y.Z # 4) verificar (sem SSH) curl -s https://scoreodonto.com/api/version # → vX.Y.Z · · PROD ``` **Backup do banco de PRODUÇÃO antes de releases sensíveis** (via container, pois não há shell na VPS3): ```bash docker run --rm -e PGPASSWORD= -v /home/deploy/backups:/b postgres:18 \ pg_dump -h 10.99.0.3 -U scoreodonto_user -d scoreodonto -Fc -f /b/scoreodonto_$(date +%Y%m%d_%H%M).dump ``` --- ## 6. Rollback ✅ Por imagem, **sem rebuild** — na VPS1: ```bash /home/deploy/stack/scoreodonto.com/deploy-prod.sh # pull + up --no-build ``` ⚠️ A referência confiável é o **commit** (`:`). Tags `:vX.Y.Z` antigas (pré-Passo 5) podem ter sido republicadas com código diferente — a partir do Passo 5, `:vX.Y.Z` é sempre um alias imutável do commit. Migrações são aditivas (voltar o código é seguro). Dump em `/home/deploy/backups/` só em extremo. --- ## 7. Follow-ups (não bloqueiam) 🎯 - `GET /api/health` (monitoramento/diagnóstico). - **Política de retenção** do registry (uma imagem `:` por push acumula). - Reconciliar o repo na VPS1 (working tree sujo, não afeta o deploy por imagem). Racional do modelo em `deploy.md`; histórico em `project-cicd-tag-promove-nao-rebuilda` (memória).