19 KiB
Guia completo – CI/CD Web + Desktop
Este material detalha, passo a passo, como configurar o pipeline que entrega o front/backend (Next.js + Convex) e os instaladores do aplicativo Tauri, usando apenas uma VPS Linux (Ubuntu) e um computador/VM Windows. Siga na ordem sugerida e marque cada etapa conforme concluir.
1. Visão geral rápida
- Objetivo: ao fazer push na branch
main, a VPS atualiza o site/backend. Ao criar uma tagvX.Y.Z, o app desktop é reconstruído, assinado e disponibilizado em/updatespara auto-update. - Ferramentas principais:
- GitHub Actions com dois runners self-hosted (Linux e Windows).
- Docker Compose (ou scripts equivalentes) para subir Next.js/Convex na VPS.
- Tauri para build dos instaladores desktop.
- Nginx servindo arquivos estáticos de update.
- Fluxo:
- Desenvolvedor envia código para o GitHub.
- Job deploy roda na própria VPS (runner Linux) e atualiza containers/processos.
- Ao criar uma tag
v*.*.*, job desktop_release roda no runner Windows, gera instaladores, assina e envialatest.json+ binários para a VPS.
2. Pré-requisitos obrigatórios
- Repositório GitHub com o código do projeto (
sistema-de-chamados). - VPS Ubuntu com:
- Acesso SSH com usuário sudo (ex.:
renan). - Docker + Docker Compose (ou ambiente que você desejar usar em produção).
- Nginx (ou outro servidor web capaz de servir
/updatesvia HTTPS).
- Acesso SSH com usuário sudo (ex.:
- Computador/VM Windows 10 ou 11 (64 bits) que ficará ligado durante os builds:
- Acesso administrador.
- Espaço livre para builds (mínimo 15 GB).
- Conta GitHub com permissão Admin no repositório (para registrar runners e secrets).
- SSH key dedicada para o pipeline acessar a VPS (não reaproveite a sua pessoal).
3. Preparação do repositório
- Na raiz do projeto, confirme os caminhos usados pelo workflow:
APP_DIR: diretório na VPS onde o código (ou docker-compose) ficará. Exemplo:/srv/apps/sistema.VPS_UPDATES_DIR: diretório público servido pelo Nginx. Exemplo:/var/www/updates.
- Garanta que o arquivo
apps/desktop/src-tauri/tauri.conf.jsonserá atualizado com:- Chave pública do updater (
updater.pubkey). - URL do
latest.json(por exemplohttps://seu-dominio.com/updates/latest.json). - Exemplo de bloco a adicionar mais tarde:
"updater": { "active": true, "endpoints": ["https://seu-dominio.com/updates/latest.json"], "pubkey": "FINGERPRINT_PUBLIC_KEY" }
- Chave pública do updater (
- Crie (ou mantenha) um arquivo
.github/workflows/ci-cd-web-desktop.ymlpara o workflow. O conteúdo será incluído na etapa 9 após todos os preparativos. Como você utiliza Docker Swarm com Portainer, já separe ostack.yml(ou compose compatível) que o workflow irá acionar.
4. Ajustes iniciais na VPS (Ubuntu)
-
Atualize pacotes:
sudo apt update && sudo apt upgrade -y -
Instale Docker (o Swarm usa o próprio engine; mantenha o plugin compose se quiser testar localmente):
sudo apt install -y docker.io docker-compose-plugin sudo systemctl enable docker sudo usermod -aG docker $USERSaia e entre novamente na sessão SSH para aplicar o grupo Docker.
-
Se o Swarm ainda não estiver ativo, inicialize-o no nó manager:
docker info | grep Swarm # se retornar "inactive", rode: sudo docker swarm init -
Verifique o Portainer:
- Acesse o painel na porta configurada (padrão
https://seu-dominio:9443). - Confirme que o cluster Swarm está saudável e que o nó aparece como manager.
- Acesse o painel na porta configurada (padrão
-
Crie diretórios usados pelo deploy:
sudo mkdir -p /srv/apps/sistema sudo mkdir -p /var/www/updates sudo chown -R $USER:$USER /srv/apps/sistema sudo chown -R $USER:$USER /var/www/updates -
(Opcional) Clone o repositório atual dentro de
/srv/apps/sistemase você mantém arquivos comostack.ymlali:git clone git@github.com:SEU_USUARIO/sistema-de-chamados.git /srv/apps/sistema -
Teste manualmente seu processo de deploy (Docker Swarm/Portainer ou scripts equivalentes) antes de automatizar. Exemplo via CLI:
docker stack deploy --with-registry-auth -c stack.yml sistema docker stack services sistemaSe preferir Portainer, faça o deploy manual pelo painel para validar. Confirme que o site sobe corretamente e que
/var/www/updatesé servido pelo Nginx (ver etapa 7). -
Sobre o Convex:
- Convex Cloud (recomendado): apenas garanta que suas variáveis
NEXT_PUBLIC_CONVEX_URLeCONVEX_DEPLOYMENTapontam para o deploy gerenciado. Não é necessário subir container. - Convex self-hosted: inclua um serviço adicional no
stack.yml(ex.:convex) com a imagem oficial (ghcr.io/get-convex/convex:latest). Configure volume para o diretório de dados e exponha a porta 3210 internamente. Atualize o Next.js para apontar parahttp://convex:3210dentro da rede do Swarm.
- Convex Cloud (recomendado): apenas garanta que suas variáveis
-
Exemplo de
stack.ymlintegrado (baseado no modelo que você já usa no Portainer):version: "3.8" services: web: image: ghcr.io/SEU_USUARIO/sistema-web:latest # ajuste para a imagem real deploy: mode: replicated replicas: 2 placement: constraints: - node.role == manager resources: limits: cpus: "1.0" memory: 1.5G labels: - traefik.enable=true - traefik.http.routers.sistema.rule=Host(`app.seu-dominio.com.br`) - traefik.http.routers.sistema.entrypoints=websecure - traefik.http.routers.sistema.tls=true - traefik.http.routers.sistema.tls.certresolver=le - traefik.http.services.sistema.loadbalancer.server.port=3000 env_file: - ./envs/web.env # variáveis do Next.js networks: - traefik_public - sistema_network convex: image: ghcr.io/get-convex/convex:latest deploy: mode: replicated replicas: 1 placement: constraints: - node.role == manager command: ["start", "--port", "3210"] volumes: - convex_data:/convex/data networks: - sistema_network networks: traefik_public: external: true sistema_network: external: false volumes: convex_data: external: false- Adapte nomes das imagens (
web,convex) e os labels do Traefik conforme seu ambiente. - Caso use Portainer, faça upload desse arquivo na interface e execute o deploy da stack.
- Adapte nomes das imagens (
5. Gerar chaves do updater Tauri
- Em qualquer dispositivo com Node/pnpm (pode ser seu computador local):
pnpm install pnpm --filter appsdesktop tauri signer generate - O comando gera:
- Chave privada (
tauri.private.key). - Chave pública (
tauri.public.key).
- Chave privada (
- Guarde os arquivos em local seguro. Você usará o conteúdo da chave privada nos secrets
TAURI_PRIVATE_KEYeTAURI_KEY_PASSWORD. A chave pública vai notauri.conf.json. - Copie a chave pública para o arquivo
apps/desktop/src-tauri/tauri.conf.jsonno bloco"updater"(conforme indicado na etapa 3).
6. Configurar Nginx para servir as atualizações
- Certifique-se de ter um domínio apontando para a VPS e um certificado TLS válido (Let's Encrypt é suficiente).
- Crie (ou edite) o arquivo
/etc/nginx/sites-available/sistema-updates.confcom algo semelhante:server { listen 80; listen 443 ssl; server_name seu-dominio.com; # Configuração SSL (ajuste conforme seu certificado) ssl_certificate /etc/letsencrypt/live/seu-dominio.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/seu-dominio.com/privkey.pem; location /updates/ { alias /var/www/updates/; autoindex off; add_header Cache-Control "no-cache"; } } - Crie o link simbólico e teste:
sudo ln -s /etc/nginx/sites-available/sistema-updates.conf /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx - Verifique pelo navegador:
https://seu-dominio.com/updates/deve listar vazio (ou mostrar erro 403 se o autoindex estiver desativado, o que é aceitável). Apenas confirme que não retorna 404.
7. Registrar runner self-hosted na VPS (Linux)
- No GitHub, acesse o repositório → Settings → Actions → Runners → New self-hosted runner.
- Escolha Linux x64 e anote a URL e o token fornecidos.
- Na VPS, prepare um usuário dedicado (opcional, mas recomendado):
sudo adduser --disabled-password --gecos "" actions sudo usermod -aG docker actions sudo su - actions - Baixe e instale o runner (substitua
<URL>e<TOKEN>):mkdir actions-runner && cd actions-runner curl -o actions-runner.tar.gz -L <URL> tar xzf actions-runner.tar.gz ./config.sh --url https://github.com/SEU_USUARIO/sistema-de-chamados \ --token <TOKEN> \ --labels "self-hosted,linux,vps" - Instale como serviço:
sudo ./svc.sh install sudo ./svc.sh start - Volte ao GitHub e confirme que o runner aparece como
online. - Teste executando um workflow simples (pode ser o pipeline de deploy após concluir todas as etapas). Lembre-se: o runner precisa ter permissão de escrita para
/srv/apps/sistemae/var/www/updates.
8. Registrar runner self-hosted no Windows
- Baixe e instale os pré-requisitos:
- Git para Windows.
- Node.js 20 (instalação inclui npm).
- Habilite o Corepack: abra o PowerShell como administrador e rode:
corepack enable corepack prepare pnpm@latest --activate - Rust toolchain: https://rustup.rs (instale padrão).
- Visual Studio Build Tools (C++ build tools) ou
Desktop development with C++. - WebView2 Runtime (https://developer.microsoft.com/microsoft-edge/webview2/).
- Opcional: instale as dependências do Tauri rodando uma vez:
pnpm install pnpm --filter appsdesktop tauri info - No GitHub → Settings → Actions → Runners → New self-hosted runner → escolha Windows x64 e copie URL/token.
- Em
C:\actions-runner(recomendado):mkdir C:\actions-runner cd C:\actions-runner Invoke-WebRequest -Uri <URL> -OutFile actions-runner.zip Expand-Archive -Path actions-runner.zip -DestinationPath . .\config.cmd --url https://github.com/SEU_USUARIO/sistema-de-chamados ` --token <TOKEN> ` --labels "self-hosted,windows,desktop" - Instale como serviço (PowerShell administrador):
.\svc install .\svc start - Confirme no GitHub que o runner aparece como
online. - Mantenha a dispositivo ligada e conectada durante o período em que o workflow precisa rodar:
- Para releases desktop, o runner só precisa estar ligado enquanto o job
desktop_releaseestiver em execução (crie a tag e aguarde o workflow terminar). - Após a conclusão, você pode desligar o computador até a próxima release.
- Para releases desktop, o runner só precisa estar ligado enquanto o job
- Observação importante: o runner Windows pode ser sua dispositivo pessoal. Garanta apenas que:
- Você confia no código que será executado (o runner processa os jobs do repositório).
- O serviço do runner esteja ativo enquanto o workflow rodar (caso desligue o PC, as releases ficam na fila).
- Há espaço em disco suficiente e nenhuma política corporativa bloqueando a instalação dos pré-requisitos.
9. Configurar secrets e variables no GitHub
- Acesse o repositório → Settings → Secrets and variables → Actions.
- Adicione os secrets:
VPS_HOST→ domínio ou IP da VPS.VPS_USER→ usuário com acesso SSH (ex.:renan).VPS_SSH_KEY→ conteúdo completo da chave privada gerada apenas para o pipeline (ver abaixo).TAURI_PRIVATE_KEY→ conteúdo do arquivotauri.private.key.TAURI_KEY_PASSWORD→ senha informada ao gerar a chave (se deixou em branco, repita em branco aqui).
- Gerar chave SSH exclusiva para o pipeline (se ainda não fez):
ssh-keygen -t ed25519 -C "github-actions@seu-dominio" -f ~/.ssh/github-actions- Suba o conteúdo de
~/.ssh/github-actions(privada) para o secretVPS_SSH_KEY. - Adicione a chave pública
~/.ssh/github-actions.pubem~/.ssh/authorized_keysdo usuário na VPS.
- Suba o conteúdo de
- Adicione Environment variables (opcional) para evitar editar o YAML:
APP_DIR→/srv/apps/sistemaVPS_UPDATES_DIR→/var/www/updates(Se preferir, mantenha-as definidas direto no workflow.)
10. Criar o workflow GitHub Actions
- No repositório, crie o arquivo
.github/workflows/ci-cd-web-desktop.ymlcom o conteúdo:name: CI/CD - Web + Desktop on: push: branches: [ main ] tags: - "v*.*.*" permissions: contents: write env: VPS_UPDATES_DIR: /var/www/updates APP_DIR: /srv/apps/sistema jobs: deploy: name: Deploy Web/Backend (VPS) runs-on: [self-hosted, linux, vps] if: startsWith(github.ref, 'refs/heads/main') steps: - uses: actions/checkout@v4 - name: Setup pnpm & Node uses: pnpm/action-setup@v4 with: version: 9 - uses: actions/setup-node@v4 with: node-version: 20 cache: pnpm - name: Deploy stack (Docker Swarm) working-directory: ${{ env.APP_DIR }} run: | # git pull origin main || true # Atualize o arquivo stack.yml ou compose compatível antes do deploy. docker stack deploy --with-registry-auth -c stack.yml sistema docker stack services sistema desktop_release: name: Release Desktop (Tauri) runs-on: [self-hosted, windows, desktop] if: startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 - name: Setup pnpm & Node uses: pnpm/action-setup@v4 with: version: 9 - uses: actions/setup-node@v4 with: node-version: 20 cache: pnpm - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@stable - name: Install deps run: pnpm install --frozen-lockfile - name: Build + Sign + Release (tauri-action) uses: tauri-apps/tauri-action@v0 env: TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} with: tagName: ${{ github.ref_name }} releaseName: "Desktop ${{ github.ref_name }}" releaseDraft: false prerelease: false updaterJsonKeepName: true - name: Upload latest.json + bundles para VPS uses: appleboy/scp-action@v0.1.7 with: host: ${{ secrets.VPS_HOST }} username: ${{ secrets.VPS_USER }} key: ${{ secrets.VPS_SSH_KEY }} source: | **/bundle/**/latest.json **/bundle/**/* target: ${{ env.VPS_UPDATES_DIR }} overwrite: true - Ajuste o bloco de deploy conforme seu processo (por exemplo, use
pnpm build && pm2 restartse não usar Docker ou substitua por chamada à API do Portainer caso faça o deploy por lá). - Faça commit desse arquivo e suba para o GitHub (
git add .github/workflows/ci-cd-web-desktop.yml,git commit,git push).
11. Testar o pipeline
- Teste do runner Linux:
- Faça uma alteração simples na branch
main. git push origin main.- No GitHub, verifique o workflow → job
deploy. - Confirme via SSH na VPS (ex.:
docker stack services sistema) ou pelo Portainer que os serviços foram atualizados.
- Faça uma alteração simples na branch
- Teste do runner Windows:
- Atualize
apps/desktop/src-tauri/tauri.conf.jsoncom a chave pública e URL do updater. - Faça commit e
git push. - Crie a tag:
git tag v1.0.0→git push origin v1.0.0. - Verifique no GitHub → job
desktop_release. - Após concluir, confira em
/var/www/updatesse existemlatest.jsone os instaladores gerados.
- Atualize
- Instale o app desktop (Windows, macOS ou Linux conforme artefatos) e abra-o:
- O aplicativo deve carregar a interface web apontando para sua URL.
- Ao publicar nova tag (ex.:
v1.0.1), o app deve oferecer update automático.
12. Rotina diária de uso
- Desenvolvimento comum:
- Trabalhe em branch própria.
- Abra PR para
main. - Ao fazer merge na
main, o jobdeployroda e publica a nova versão da stack no Swarm (visível no Portainer).
- Nova versão desktop:
- Ajuste o app, aumente o campo
versionnotauri.conf.json. git commitegit push.- Crie tag
vX.Y.Ze envie (git tag v1.2.0,git push origin v1.2.0). - Aguarde a finalização do job
desktop_release. - Usuários recebem o update automático na próxima abertura.
- Ajuste o app, aumente o campo
- Renovação de certificado:
- Garanta que o certificado TLS usado pelo Nginx é renovado (p. ex.
certbot renew).
- Garanta que o certificado TLS usado pelo Nginx é renovado (p. ex.
- Manter runners:
- VPS: monitore serviço
actions.runner.*. Reinicie se necessário (sudo ./svc.sh restart). - Windows: mantenha dispositivo ligada e atualizada. Se o serviço parar, abra
services.msc→GitHub Actions Runner→ Start.
- VPS: monitore serviço
13. Boas práticas e segurança
- Proteja a chave privada do updater; trate como segredo de produção.
- Use usuário dedicado na VPS para o runner e restrinja permissões apenas aos diretórios necessários.
- Faça backup periódico de
/var/www/updates(para poder servir instaladores antigos se necessário). - Nunca faça commit do arquivo
.envnem das chaves privadas. - Atualize Docker, Node e Rust periodicamente.
14. Solução de problemas comuns
| Sintoma | Possível causa | Como corrigir |
|---|---|---|
Job deploy falha com “permission denied” |
Runner não tem acesso ao diretório do app | Ajuste permissões (sudo chown -R actions:actions /srv/apps/sistema). |
Job desktop_release falha na etapa tauri-action |
Toolchain incompleto no Windows | Reinstale Rust, WebView2 e componentes C++ do Visual Studio. |
| Artefatos não chegam à VPS | Caminho incorreto ou chave SSH inválida | Verifique VPS_HOST, VPS_USER, VPS_SSH_KEY e se a pasta /var/www/updates existe. |
| App não encontra update | URL ou chave pública divergente no tauri.conf.json |
Confirme que endpoints bate com o domínio HTTPS e que pubkey é exatamente a chave pública gerada. |
| Runner aparece offline no GitHub | Serviço parado ou dispositivo desligada | VPS: sudo ./svc.sh status; Windows: abra Services e reinicie o GitHub Actions Runner. |
15. Checklist final de implantação
- VPS atualizada, Docker/Nginx funcionando.
- Diretórios
/srv/apps/sistemae/var/www/updatescriados com permissões corretas. - Nginx servindo
https://seu-dominio.com/updates/. - Runner Linux registrado com labels
self-hosted,linux,vps. - Runner Windows registrado com labels
self-hosted,windows,desktop. - Chaves do updater Tauri geradas e chave pública no
tauri.conf.json. - Secrets e variables configurados no GitHub (
VPS_*,TAURI_*). - Workflow
.github/workflows/ci-cd-web-desktop.ymlcriado e commitado. - Deploy automático testado com push em
main. - Release desktop testada com tag
v1.0.0.
Com todos os itens marcados, o pipeline estará pronto para ser usado sempre que você fizer novas entregas.