docs(wiki): Deploy V.1.0.1 - consolida deploy + versionamento servidor e cliente numa pagina

VPS 4 Deploy Agent
2026-05-30 15:09:26 +02:00
parent ad6eca43e4
commit 246164ff03
2 changed files with 377 additions and 8 deletions
+368
@@ -0,0 +1,368 @@
# 🚀 Deploy V.1.0.1 — Guia Completo (Deploy + Versionamento)
> Versão **1.0.1** — consolida tudo num único documento: pipeline de deploy automático,
> versionamento do servidor e do cliente Windows, convenções e regras de ouro.
> Um agente ou desenvolvedor deve conseguir executar qualquer tarefa lendo apenas esta página.
---
## 📋 Índice
1. [Arquitetura Multi-VPS](#1-arquitetura-multi-vps)
2. [Pipeline de Deploy Automático (GitOps)](#2-pipeline-de-deploy-automático-gitops)
3. [O Listener do Webhook](#3-o-listener-do-webhook)
4. [Dois Modelos de Deploy](#4-dois-modelos-de-deploy)
5. [Configurar um Novo Projeto](#5-configurar-um-novo-projeto)
6. [Versionamento — Servidor rx.scoreodonto.com](#6-versionamento--servidor-rxscoredontcom)
7. [Versionamento — Cliente Windows](#7-versionamento--cliente-windows)
8. [Convenção de Numeração (Semver)](#8-convenção-de-numeração-semver)
9. [Relação entre Versão do Servidor e do Cliente](#9-relação-entre-versão-do-servidor-e-do-cliente)
10. [Segurança](#10-segurança)
11. [Troubleshooting](#11-troubleshooting)
12. [Changelog desta Wiki](#12-changelog-desta-wiki)
---
## 1. Arquitetura Multi-VPS
| Servidor | IP (VPN WireGuard) | Função |
|---|---|---|
| **VPS 1 — App** | `10.99.0.1` | Produção — roda os containers Docker (rootless) |
| **VPS 3 — Data/Git** | `10.99.0.3` | Gitea + PostgreSQL |
| **VPS 4 — Builder** | `10.99.0.4` | Desenvolvimento, build e disparo do deploy |
```
Dev (VPS4) ──git push──► Gitea (VPS3) ──webhook──► Listener (VPS4:porta)
deploy-*.sh (VPS4)
┌─────────────────────┴──────────────────────┐
▼ ▼
Modelo A: build na VPS4 Modelo B: rsync do fonte
rsync artefatos → VPS1 build na VPS1
docker compose up -d docker compose up --build
```
**Princípios:**
- A **fonte de verdade é sempre o Gitea** — o deploy faz `git reset --hard origin/main` antes de qualquer coisa.
- **Produção (VPS1) nunca recebe edição manual** — o `rsync --delete` sobrescreve tudo no próximo deploy.
- **Docker rootless na VPS1** — sempre usar `DOCKER_HOST=unix:///run/user/1000/docker.sock`.
- Toda comunicação VPS↔VPS é pela **VPN** (`10.99.0.x`) via chave SSH do usuário `deploy`.
---
## 2. Pipeline de Deploy Automático (GitOps)
```
git push origin main
└─► Gitea dispara POST http://10.99.0.4:<porta>/webhook?token=<TOKEN>
└─► listener valida token + trava de concorrência
└─► responde 202 imediatamente (não trava o Gitea)
└─► executa deploy-*.sh em background
└─► git reset --hard
└─► build / version bump
└─► rsync → VPS1
└─► ssh VPS1: docker restart/rebuild
└─► git push do bump ("chore(deploy): bump version")
└─► listener IGNORA este commit (anti-loop)
└─► reporta status ao Gitea: pending → success / failure (✔ ✘ no commit)
```
**Um listener por projeto, cada um em uma porta:**
| Projeto | Porta | Script | Service systemd |
|---|---|---|---|
| clube67 / newwhats | 9000 | `clube67/deploy-prod-builder.sh` | `webhook-listener.service` ✅ ativo |
| scoreodonto.com | 9002 | `deploy-prod-scoreodonto.sh` | `webhook-scoreodonto.service` ✅ ativo |
| rx.scoreodonto.com | — | `rx.scoreodonto.com/deploy-prod.sh` | **nenhum — deploy manual** |
| rx.scoreodonto.com-client | — | `rx.scoreodonto.com-client/deploy-client.sh` | **nenhum — deploy manual** |
---
## 3. O Listener do Webhook
Código de referência: `webhook-listener/listener.js` neste repositório.
**Lógica:**
1. Valida token (`x-gitea-token` header ou `?token=` query string).
2. **Trava `isBuilding`** — retorna `429` se já há build rodando (sem deploys concorrentes).
3. **Anti-loop de CI** — ignora commits cuja mensagem case com `^chore\(deploy\): bump version`.
4. Responde **202** imediatamente; roda o script em background.
5. Reporta status via API do Gitea: `pending``success` / `failure`.
**Variáveis de ambiente** (configurar em `<projeto>.env`, nunca commitar):
```
PORT=<porta única por projeto>
WEBHOOK_TOKEN=<token forte>
DEPLOY_SCRIPT=/home/deploy/stack/<projeto>/deploy-prod-builder.sh
LOG_FILE=/home/deploy/stack/webhook-listener/deploy.log
GITEA_API_URL=http://10.99.0.3:3000/api/v1
GITEA_TOKEN=<token da API do Gitea com escopo repo:status>
```
**Instalar como serviço:**
```bash
sudo cp webhook-listener/webhook-listener.service /etc/systemd/system/webhook-<projeto>.service
sudo systemctl daemon-reload && sudo systemctl enable --now webhook-<projeto>
```
---
## 4. Dois Modelos de Deploy
### 🅰️ Builder-to-Host — build na VPS4, VPS1 só reinicia
Usado pelo clube67 / newwhats. Indicado para frontends pesados (Next.js / Vite).
Script: `scripts/deploy-prod-builder.sh`
```
git reset --hard ► npm install + build ► rsync dist/ + node_modules/ → VPS1 ► docker compose up -d
```
- VPS1 nunca roda `npm install` ou `npm build`.
- Ao final: `git commit "chore(deploy): bump version" && git push` (anti-loop).
### 🅱️ Build no Host — rsync do fonte, VPS1 builda a imagem Docker
Usado pelo scoreodonto.com e rx.scoreodonto.com. Indicado para apps leves (Node servindo estáticos).
Script: `scripts/deploy-prod-onhost.sh`
```
git reset --hard ► rsync do código-fonte → VPS1 ► ssh VPS1: docker compose build app + up -d --no-deps app
```
- Swap do container com quase zero downtime (`--no-deps`).
- VPS1 builda a imagem com `Dockerfile` local (no caso do rx: `node:22-alpine`).
### Promoção manual Staging → Produção
Script: `scripts/deploy-to-prod.sh` — usado quando há staging na VPS4 e se quer promover a build validada para produção sem rebuild.
---
## 5. Configurar um Novo Projeto
```bash
# 1. Criar o .env do listener (nunca commitar)
cp webhook-listener/config.example.env /home/deploy/stack/webhook-listener/<projeto>.env
# editar: PORT, WEBHOOK_TOKEN, DEPLOY_SCRIPT, GITEA_TOKEN
# 2. Instalar o service systemd
sudo cp webhook-listener/webhook-listener.service /etc/systemd/system/webhook-<projeto>.service
# editar: Description, SyslogIdentifier, EnvironmentFile
sudo systemctl daemon-reload && sudo systemctl enable --now webhook-<projeto>
# 3. Criar webhook no Gitea
# Repo → Settings → Webhooks → Add Webhook → Gitea
# URL: http://10.99.0.4:<PORT>/webhook?token=<WEBHOOK_TOKEN>
# Método: POST | Evento: Push | Branch: main
# 4. Criar GITEA_TOKEN de status
# Gitea → Settings → Applications → Generate Token (escopo: repo:status / write:repository)
# 5. Testar
git commit --allow-empty -m "test: dispara webhook" && git push origin main
tail -f /home/deploy/stack/webhook-listener/deploy.log
```
**Pré-requisitos:**
- Chave SSH do `deploy` (VPS4) com acesso sem senha à VPS1: `ssh deploy@10.99.0.1 echo ok`
- Chave SSH do `deploy` com permissão de push no Gitea.
- Porta do listener liberada apenas na VPN (`10.99.0.0/24`).
---
## 6. Versionamento — Servidor `rx.scoreodonto.com`
### Onde a versão existe (3 arquivos, hoje dessincronizados)
| Arquivo | Valor atual | Quem usa |
|---|---|---|
| `dental-server/version.txt` | `2.1.0` | Lido pelo servidor em runtime |
| `dental-server/package.json``"version"` | `2.0.5` | Metadado Node (não exibido ao usuário) |
| `dental-server/public/index.html` linha 33 | `v2.1.2` | **Exibido na sidebar do painel web** |
A versão que o usuário vê é o `index.html` — hardcoded diretamente no HTML:
```html
<h2>RF Dental <span>v2.1.2</span></h2>
```
Não existe script que sincronize os três automaticamente. Qualquer um pode ficar para trás.
### Consultar versão em produção
```bash
# O que o usuário vê no painel:
ssh deploy@10.99.0.1 "grep -o 'v[0-9]\+\.[0-9]\+\.[0-9]\+' /home/deploy/stack/rx.scoreodonto.com/dental-server/public/index.html | head -1"
# O que o servidor lê em runtime:
ssh deploy@10.99.0.1 "cat /home/deploy/stack/rx.scoreodonto.com/dental-server/version.txt"
```
### Como bumpar (processo atual — manual)
```bash
# 1. Editar os 3 arquivos para o mesmo número
nano /home/deploy/stack/rx.scoreodonto.com/dental-server/version.txt
nano /home/deploy/stack/rx.scoreodonto.com/dental-server/package.json
nano /home/deploy/stack/rx.scoreodonto.com/dental-server/public/index.html # linha 33
# 2. Commitar e deploy
cd /home/deploy/stack/rx.scoreodonto.com
git add -p && git commit -m "chore(version): bump para vX.Y.Z"
git push origin main
./deploy-prod.sh
# 3. Verificar no painel web
```
---
## 7. Versionamento — Cliente Windows `rx.scoreodonto.com-client`
### Fonte única de verdade: `package.json`
```json
{
"version": "1.2.7"
}
```
O `electron-builder` lê esse valor e:
- Nomeia o instalador: `ScoreOdonto Dental Client Setup 1.2.7.exe`
- Gera o `latest.yml` que vai para `dental-server/updates/`:
```yaml
version: 1.2.6 ← versão atual nas clínicas
files:
- url: ScoreOdonto Dental Client Setup 1.2.6.exe
sha512: zEYuIRBU...
size: 102459655
```
### Como o auto-update funciona nas clínicas
```
Clínica (Windows) ao iniciar o app:
└─► GET https://rx.scoreodonto.com/updates/latest.yml
├─ version no yml > version instalada? SIM → baixa .exe em background → instala ao reiniciar
└─ NÃO → nada acontece, app continua normalmente
```
> ⚠️ **Regra de ouro:** a versão que as clínicas têm é determinada exclusivamente pelo `latest.yml`
> em produção. O `.exe` na VPS4 não vale nada enquanto o `latest.yml` não for atualizado.
### Estado atual (detectado em 28/05/2026)
| Local | Versão |
|---|---|
| `package.json` (VPS4) | 1.2.7 |
| `.exe` em `dist/` (VPS4) | 1.2.7 — buildado |
| `latest.yml` em `updates/` (VPS1) | **1.2.6** — as clínicas não recebem o 1.2.7 |
### Como bumpar (processo correto)
```bash
# 1. Subir a versão no package.json
nano /home/deploy/stack/rx.scoreodonto.com-client/package.json
# "version": "1.2.7" → "1.2.8"
# 2. Commitar
cd /home/deploy/stack/rx.scoreodonto.com-client
git add package.json && git commit -m "chore(version): bump client para 1.2.8"
git push origin main
# 3. Build + deploy (gera .exe e envia latest.yml para VPS1)
./deploy-client.sh
# 4. Confirmar que o latest.yml foi atualizado
curl -s https://rx.scoreodonto.com/updates/latest.yml
# → version: 1.2.8
```
### Histórico de versões (dist/ VPS4)
| Versão | Build | Status |
|---|---|---|
| 1.2.0 | 28/05/2026 02:59 | publicado |
| 1.2.1 | 28/05/2026 03:09 | publicado |
| 1.2.2 | 28/05/2026 03:18 | publicado |
| 1.2.3 | 28/05/2026 03:32 | publicado |
| 1.2.4 | 28/05/2026 03:56 | publicado |
| 1.2.5 | 28/05/2026 05:34 | publicado |
| 1.2.6 | 28/05/2026 05:45 | ✅ **em produção (VPS1 / clínicas)** |
| 1.2.7 | 28/05/2026 20:38 | ⚠️ buildado — não publicado |
---
## 8. Convenção de Numeração (Semver)
Ambos os projetos seguem `MAJOR.MINOR.PATCH`. As regras são independentes — servidor e cliente têm ciclos de versão separados.
### Servidor (`rx.scoreodonto.com`) — versão atual: `v2.1.2`
| Parte | Quando subir | Exemplos |
|---|---|---|
| **PATCH** `v2.1.X` | Qualquer bugfix, ajuste visual, melhoria pequena, correção de CSS/JS | `v2.1.2 → v2.1.3` |
| **MINOR** `v2.X.0` | Nova funcionalidade no painel, nova rota de API, novo modal/fluxo | `v2.1.x → v2.2.0` |
| **MAJOR** `vX.0.0` | Rewrite de arquitetura, mudança de banco, quebra de compatibilidade | `v2.x.x → v3.0.0` |
### Cliente Windows (`rx.scoreodonto.com-client`) — versão atual: `1.2.7`
| Parte | Quando subir | Exemplos |
|---|---|---|
| **PATCH** `1.2.X` | Bugfix no monitoramento, ajuste de config, correção de conexão | `1.2.7 → 1.2.8` |
| **MINOR** `1.X.0` | Nova funcionalidade no cliente (nova tela, novo comportamento, novo evento socket) | `1.2.x → 1.3.0` |
| **MAJOR** `X.0.0` | Mudança de protocolo (Socket.IO incompatível), rewrite completo | `1.x.x → 2.0.0` |
**Regra prática:** na dúvida, sobe o PATCH. MINOR e MAJOR são decisões conscientes.
---
## 9. Relação entre Versão do Servidor e do Cliente
Servidor e cliente são **projetos independentes** com versões independentes. Não há numeração sincronizada entre `v2.1.2` (servidor) e `1.2.7` (cliente).
**O que importa é a compatibilidade de protocolo:**
| Ponto de integração | Onde está definido | Risco de quebra |
|---|---|---|
| URL do servidor Socket.IO | `index.js` do cliente → `https://rx.scoreodonto.com` | Baixo — só muda se o domínio mudar |
| Evento `send-image` (socket) | `client-monitor.js` + `server.js` | Alto — se mudar o payload, servidor e cliente devem ser atualizados juntos |
| Autenticação via `machine_token` JWT | `routes/client-auth.js` + `index.js` | Alto — mudança de algoritmo exige deploy sincronizado |
| URL de auto-update | `package.json``publish.url` = `https://rx.scoreodonto.com/updates/` | Médio — se o path mudar no servidor, clientes antigos param de atualizar |
> **Quando fazer deploy sincronizado (servidor + cliente no mesmo ciclo):**
> qualquer mudança no contrato do Socket.IO (eventos, payload) ou na autenticação JWT exige que
> servidor e cliente sejam publicados juntos, ou na ordem: **servidor primeiro, cliente depois**.
---
## 10. Segurança
- **Sem segredos no Git** — tokens, senhas, JWT em `.env` + `EnvironmentFile=` no systemd. `.gitignore` bloqueia `*.env`.
- **Docker rootless na VPS1** — `DOCKER_HOST=unix:///run/user/1000/docker.sock` em todos os comandos remotos. Daemon root desabilitado de propósito; se reativado junto com `deploy` no grupo `docker`, cria containers root paralelos invisíveis ao Traefik.
- **SSH** — chave do `deploy` sem passphrase para automação, `BatchMode=yes` nos scripts, acesso só pela VPN.
- **Commite antes de buildar** — o deploy faz `git reset --hard` e descarta mudanças locais não commitadas.
- **Nunca editar na VPS1** — o `rsync --delete` sobrescreve tudo no próximo deploy.
- **Tokens distintos** por projeto (WEBHOOK_TOKEN e GITEA_TOKEN). Rotacionar periodicamente.
---
## 11. Troubleshooting
| Problema | Diagnóstico | Solução |
|---|---|---|
| Webhook não dispara | `systemctl status webhook-<projeto>` | Reiniciar o listener |
| `429` no webhook | Build concorrente em andamento | Aguardar o build terminar |
| `rsync` / `ssh` falha | `ssh deploy@10.99.0.1 echo ok` | Checar chave SSH e VPN WireGuard |
| Build falha na VPS1 | `docker logs <container>` | Ver erro de npm/Dockerfile |
| Deploy em loop | Commit de bump re-dispara | Confirmar regex anti-loop no listener |
| Versão não atualiza no painel | `index.html` não foi editado | Editar os 3 arquivos do servidor sincronizados |
| Clínicas não recebem update | `curl https://rx.scoreodonto.com/updates/latest.yml` | Verificar se `latest.yml` tem a versão nova; se não, re-rodar `deploy-client.sh` |
| Port 8020 ocupada na VPS1 | `sudo ss -tlnp \| grep 8020` | `docker-proxy` root zumbi — ver seção de Docker rootless |
| `docker.service` root ativo na VPS1 | `sudo systemctl status docker` | `sudo systemctl disable --now docker` |
---
## 12. Changelog desta Wiki
| Versão | Data | O que mudou |
|---|---|---|
| **1.0.1** | 2026-05-30 | Consolidação total: deploy + versionamento servidor + versionamento cliente + convenções semver + relação entre versões. Tudo numa página só. |
| 1.0.0 | 2026-05-30 | Versão inicial: listener de webhook, modelos A/B de deploy, configuração no Gitea, segurança. |
+9 -8
@@ -1,17 +1,18 @@
# 🏠 Deploy-Automático — Wiki
Documentação da automação de deploy GitOps (Gitea + Multi-VPS) usada no ecossistema.
Documentação da automação de deploy GitOps (Gitea + Multi-VPS) e versionamento dos projetos.
## Versões
- [Deploy V.1.0.0](Deploy-V.1.0.0) — versão inicial: listener de webhook, modelos A/B de deploy,
configuração no Gitea e segurança.
## Versão atual
## Documentação complementar
- [Versionamento](Versionamento) — como o servidor e o cliente Windows controlam versões,
onde cada número é definido, problemas atuais de dessincronização e como garantir que
as clínicas recebem o update correto.
- **[Deploy V.1.0.1](Deploy-V.1.0.1)** ← versão mais recente — contém tudo: deploy automático,
versionamento do servidor, versionamento do cliente Windows, convenções semver, segurança e troubleshooting.
## Arquivo de versões anteriores
- [Deploy V.1.0.0](Deploy-V.1.0.0) — versão inicial (apenas deploy, sem versionamento).
## Repositório de exemplos
Scripts e instruções de referência (sanitizados) estão no repositório
[`ruicesar/Deploy-Automatico`](http://10.99.0.3:3000/ruicesar/Deploy-Automatico):
`webhook-listener/`, `scripts/` e `docs/`.