# CI/CD — validação e deploy automático Todo push é validado no GitHub Actions; todo push no `main` que passa na validação é publicado sozinho no servidor de produção (decisão D39). Workflow: [.github/workflows/deploy.yml](../.github/workflows/deploy.yml). ``` push em qualquer branch / pull request │ ▼ ┌──────────── validate (GitHub, ~5 min) ────────────┐ │ shell · compose · plugins C# · cliente web (tsc) │──── falhou → nada é publicado (job vermelho) └───────────────────────────────────────────────────┘ │ passou e é push no main ▼ ┌──────────── deploy (GitHub → VPS) ────────────────┐ │ git push do commit para /opt/mu (branch deploy) │ │ scripts/deploy.sh : │ │ backup → build → up → health → smoke test │──── falhou → rollback automático └───────────────────────────────────────────────────┘ para o commit anterior │ ▼ no ar em https://play.82-38-28-175.sslip.io ``` ## Produção | | | |---|---| | VPS | `82.38.28.175` (Ubuntu 24.04, 4 vCPU, 7,7 GB RAM, 77 GB), usuário `root` | | Diretório | `/opt/mu` (perfil `MU_ENV=prod`, `.env` só na VPS, `chmod 600`) | | Domínio | `BASE_DOMAIN=82-38-28-175.sslip.io` (sem DNS próprio; HTTPS Let's Encrypt automático) | | Jogo | https://play.82-38-28-175.sslip.io (WebSocket do jogo: `wss://ws.82-38-28-175.sslip.io`) | | Admin Panel | https://admin.82-38-28-175.sslip.io (usuário `admin`, senha em `OPENMU_ADMIN_PASSWORD` no `/opt/mu/.env`) | | Docs | https://docs.82-38-28-175.sslip.io | | Versão no ar | `/opt/mu/.deployed` (hash + título do commit) | Para trocar o domínio, ver [vps-deployment.md](vps-deployment.md#dns). ## O dia a dia - **Publicar**: commit no `main` + `git push`. Em ~6 min (validação + deploy) a mudança está no ar; o primeiro build de uma VPS nova leva 15–25 min, os seguintes usam o cache do Docker. - **Testar sem publicar**: trabalhe em outro branch (ou abra um pull request). Ele passa só pelo `validate`. O merge no `main` publica. - **Acompanhar**: GitHub → aba **Actions** → run *CI/CD*. Cada etapa tem log próprio; o job `deploy` mostra a saída completa do `deploy.sh` (backup, build, health e smoke test). - **Rodar de novo**: *Re-run jobs* no run do GitHub. *Run workflow* (manual) só valida; o deploy exige push no `main`. - **Um deploy por vez**: um push novo durante um deploy espera ele terminar (não cancela no meio). > Push no `main` = produção. Antes de mesclar, rode localmente o fluxo do [AGENTS.md](../AGENTS.md) > (build → `scripts/smoke-test.sh`). ## O que o `validate` verifica | Etapa | Pega | |---|---| | `bash -n` + `shellcheck -S error` em `scripts/*.sh`, `apply.sh` dos patches e `ranking.sh` | erro de sintaxe e erros graves de shell | | `docker compose config` (base, dev e prod) | YAML inválido, variável obrigatória faltando | | `services/openmu/patches/apply.sh` + `dotnet build` de cada `custom/server/src/MuCustom.*` (`-p:ci=true`, .NET 10) | patch do OpenMU que não aplica, erro de compilação nos plugins | | `services/web-client/patches/apply.sh` + `bun install --frozen-lockfile` + `tsc --noEmit` | patch do cliente que não aplica, erro de sintaxe ou de tipo no TypeScript | Os avisos de estilo (StyleCop) do código do OpenMU aparecem como *annotations*, mas não falham o job. ## O que o `deploy` faz 1. Confere a configuração (`VPS_HOST`, `VPS_PASS`). 2. Faz checkout com histórico completo e envia o commit por `git push` via SSH para o branch `deploy` de `/opt/mu`. O repositório é privado: assim a VPS não precisa de nenhuma credencial do GitHub. Os submodules (OpenMU e cliente Babylon) são upstreams públicos e a VPS os baixa nos SHAs registrados. 3. Executa na VPS [scripts/deploy.sh](../scripts/deploy.sh) ``: - trava (`flock`) para não haver dois deploys juntos; - recusa se houver alteração local não commitada em `/opt/mu`; - troca o código para o commit (HEAD destacado) e chama `scripts/update.sh --no-pull`: **backup** (`backups/`, rótulo `pre-update`) → `docker compose build --pull` → BasicAuth do admin (gerado só na primeira vez) → timer de backup e swap (só se faltarem) → papéis do banco (`db-roles.sh`) → `up -d` → espera os 7 serviços ficarem healthy → regras do jogo do `.env` (`apply-game-config.sh`; reinicia o openmu só se algo mudou) → `healthcheck.sh` → `smoke-test.sh --no-login` (inclui a versão do schema do mucustom); - o build roda com os containers antigos no ar: **um build quebrado não derruba o servidor**; só os serviços cuja imagem mudou são recriados (o jogo reinicia quando o `openmu` muda). 4. Sucesso: grava `/opt/mu/.deployed`. Falha: ver abaixo. ## Testes no CI | Job | Roda | Pega | |---|---|---| | `validate` | shell, compose, patches do OpenMU + plugins C#, patches do cliente + `tsc` + Vitest dos arquivos de teste que os patches tocam | erro de compilação, patch que não aplica, regressão nos testes do cliente que são nossos (as falhas de ambiente do upstream ficam de fora) | | `custom-tests` | `dotnet test` de `custom/server/tests` com um PostgreSQL de serviço | economia sob concorrência (50 chamadas simultâneas), guarda do 0xFC, validação de fotos, migrations | O `deploy` só roda depois dos dois. ## Operações sob demanda na VPS ("VPS ops") GitHub → **Actions** → **VPS ops** → *Run workflow*, escolhendo a operação ([.github/workflows/vps-ops.yml](../.github/workflows/vps-ops.yml)). Usa os mesmos segredos do deploy e a mesma fila: nunca roda junto com um deploy. | Operação | O que faz | Impacto | |---|---|---| | `audit` | `scripts/vps-audit.sh`: SO, CPU, RAM, swap, discos, portas, UFW, relógio, containers, timers, papéis, BasicAuth e off-site | nenhum | | `health` | healthcheck + smoke test | nenhum | | `logs` | erros dos últimos 60 min do openmu, ws-proxy e postgres | nenhum | | `perf` | `scripts/vps-perf.sh`: sysbench CPU/memória, fio curto (arquivo temporário), parâmetros e estatísticas do PostgreSQL | CPU/disco ocupados por ~1 min | | `loadtest` | `scripts/load-test.sh connections`: 10, 25, 50, 75 e 100 clientes até o GameServer, com CPU/RAM | lentidão possível por alguns minutos | | `backup-verify` | backup novo + restore num banco temporário + comparação | nenhum (banco temporário apagado no fim) | | `restart` | backup + `docker compose restart` + healthy + smoke | jogadores desconectados por ~1 min | | `reboot` | backup + reboot da VPS + espera voltar + auditoria + health + smoke | servidor fora por ~2–4 min | ## Falhas e rollback | Onde falhou | Resultado | |---|---| | `validate` | nada chega à VPS; o servidor segue na versão anterior | | conexão/`git push` | nada muda na VPS | | build, health ou smoke test na VPS | o `deploy.sh` volta o código ao commit anterior, refaz o build e sobe de novo; o job fica vermelho e o servidor segue na versão anterior | | o próprio rollback falhou | o job sai com código 2: entre na VPS e veja `./scripts/status.sh` e `./scripts/logs.sh openmu --errors` | O rollback é **só de código**. Se uma versão nova alterar o banco (schema ou dados) e for preciso voltar os dados também, restaure o backup pré-deploy: `./scripts/restore.sh backups/_pre-update`. Para desfazer uma mudança que já está no ar, prefira `git revert ` + push no `main` (passa pelo mesmo fluxo). ## Configuração no GitHub **Settings → Secrets and variables → Actions** (a aba *Agents* é outra coisa: serve só ao agente Copilot): | Nome | Tipo | Valor | |---|---|---| | `VPS_HOST` | variável | `82.38.28.175` | | `VPS_USER` | variável (ou segredo) | `root` (padrão se ausente) | | `VPS_PASS` | **segredo** | senha SSH do `root` | Nunca coloque a senha como *variável*: variáveis aparecem em texto puro para qualquer colaborador. Trocou a senha do root? Atualize o segredo `VPS_PASS`, senão o próximo deploy falha na conexão. ## Operação manual na VPS ```bash ssh root@82.38.28.175 cd /opt/mu cat .deployed # versão no ar ./scripts/status.sh # containers ./scripts/healthcheck.sh # saúde (rápido) ./scripts/smoke-test.sh --no-login # testes funcionais ./scripts/logs.sh openmu --errors # erros do servidor ./scripts/deploy.sh # deploy manual de um commit que já está na VPS ./scripts/backup.sh # backup sob demanda ``` Para levar um commit à VPS sem o GitHub (emergência), do seu computador: `git push ssh://root@82.38.28.175/opt/mu HEAD:refs/heads/deploy` e depois `./scripts/deploy.sh ` na VPS. ## Contas de teste na produção A instalação bloqueia todas as contas de teste do OpenMU (senha = nome da conta). Para os testes do dono, `test1` (jogador) e `testgm` (GM) foram reativadas **com senhas novas aleatórias** — as senhas não estão no Git (ficam com o dono). Para bloquear de novo: `./scripts/disable-test-accounts.sh` (`--list` mostra o estado). ## Limitações conhecidas - Autenticação por senha do `root` (escolha do dono). Mais seguro: chave SSH dedicada ao deploy e login de `root` por senha desativado. - O rollback automático ainda não foi exercitado em produção (nenhum deploy falhou até agora). - Backup diário pelo timer `mu-backup.timer` (instalado pelo deploy); a cópia fora da VPS depende de `RCLONE_REMOTE` + `.rclone.conf` (ainda não configurados: `OFFSITE_BACKUP_CONFIGURED=false`). - O `validate` não faz o build das imagens Docker nem testes de jogo; isso acontece no deploy, na VPS (com rollback). Não há ambiente de homologação separado. - No smoke test com login (`--account`), o LoginProbe TCP recusa senhas trocadas (`InvalidPassword`) enquanto o caminho web aceita (B23); o deploy usa `--no-login`. ## Problemas comuns | Sintoma | Causa provável | Solução | |---|---|---| | `deploy` falha em *Conferir configuração* | `VPS_HOST` ou o segredo `VPS_PASS` ausente | criar em Settings → Secrets and variables → Actions | | `Permission denied` no *Enviar o commit* | senha do root mudou | atualizar o segredo `VPS_PASS` | | `outro deploy está em andamento` | deploy manual ou anterior travado | esperar; se não houver deploy rodando, apagar `/opt/mu/.deploy.lock` | | `há alterações locais não commitadas na VPS` | alguém editou arquivo em `/opt/mu` | `git -C /opt/mu status`; descartar (`git checkout -- .`) ou levar a mudança para o repositório | | `patch não aplicou` no `validate` | submodule mudou de SHA ou patch desatualizado | refazer o patch (ver [development.md](development.md)) | | site sem HTTPS válido após trocar `BASE_DOMAIN` | DNS ainda não propagou | aguardar; o Traefik pede o certificado sozinho |