CI/CD com GitHub Actions: automatize deploy e testes
Entenda o que é CI/CD na prática e monte dois workflows de GitHub Actions do zero, um rodando lint e teste em cada Pull Request, outro fazendo deploy automático quando o código chega na main.
Já mostrei aqui como fazer deploy em servidor do zero, entrando por SSH e subindo o código na mão. Isso funciona, mas tem um problema: depender de alguém lembrar de rodar teste antes de subir, ou de repetir os mesmos passos de deploy toda vez sem esquecer nenhum. CI/CD existe justamente pra tirar essa responsabilidade das mãos de uma pessoa e colocar num processo automático.
O que é CI/CD, sem o jargão
CI (Continuous Integration) é o hábito de, toda vez que alguém sobe código, rodar automaticamente uma checagem: os testes passam, o lint não acusa nada, o build funciona. Isso pega problema antes de chegar na branch principal, não depois.
CD (Continuous Deployment ou Continuous Delivery) é o passo seguinte: depois que o código já foi validado, ele vai pro ar sozinho, sem alguém precisando entrar em servidor nenhum. Os dois juntos formam o pipeline: código sobe, é testado automaticamente, e se passar, vai pra produção sem intervenção manual.
GitHub Actions: onde isso roda
O GitHub Actions é a ferramenta de CI/CD já embutida em qualquer repositório do GitHub, sem precisar contratar serviço externo. Ela funciona a partir de arquivos YAML dentro da pasta .github/workflows/, cada arquivo descrevendo um workflow: quando ele roda, e o que ele faz.
Workflow 1: testar todo Pull Request
O primeiro workflow que vale criar é o mais simples: rodar lint e teste automaticamente em todo PR, antes de qualquer merge.
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm run lint
- run: npm testRepara na estrutura: on.pull_request diz quando esse workflow dispara, nesse caso toda vez que um PR é aberto ou atualizado tendo main como destino. jobs.test.steps é a sequência de passos, de cima pra baixo: baixar o código (checkout), instalar o Node na versão certa, instalar dependências com npm ci (mais previsível que npm install em ambiente automatizado, porque respeita exatamente o package-lock.json), e então rodar lint e teste.
Se qualquer um desses passos falhar, o GitHub marca o PR com um X vermelho, direto na tela de revisão, antes mesmo de alguém revisar o código manualmente. Isso evita a situação clássica de aprovar um PR só pra descobrir depois que ele quebra o build.
Workflow 2: deploy automático na main
O segundo workflow cuida do deploy, disparado só quando o código já foi mergeado na main, ou seja, já passou pela revisão e pelo primeiro workflow:
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm run build
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd /var/www/meu-projeto
git pull origin main
npm ci
npm run build
pm2 restart meu-projetoO último passo automatiza exatamente aquele processo manual de entrar por SSH: puxar o código novo, reinstalar dependência, buildar, reiniciar o processo. A diferença é que agora ninguém precisa lembrar de fazer isso, acontece sozinho a cada merge.
Secrets: nunca deixe credencial no YAML
Repara que host, usuário e chave SSH não aparecem escritos direto no arquivo, eles vêm de secrets.SERVER_HOST e afins. Isso segue o mesmo princípio que já falei aqui sobre gerenciamento de segredos: essas informações ficam configuradas na aba Settings > Secrets and variables > Actions do repositório no GitHub, criptografadas, e o workflow só referencia o nome delas. O YAML pode ficar público sem expor credencial nenhuma.
Vale ir aos poucos
Não precisa sair automatizando o pipeline inteiro de uma vez. O primeiro workflow (lint e teste em cada PR) já entrega valor sozinho, mesmo mantendo o deploy manual por enquanto, porque pega erro antes de chegar na branch principal. O deploy automático é o passo seguinte, natural, quando o processo manual já estiver bem entendido e repetitivo o suficiente pra valer a pena tirar da mão de alguém.
Fechando
CI/CD não é sobre ferramenta chique, é sobre parar de confiar na memória de alguém pra rodar teste ou repetir passo de deploy sem errar. Uma vez configurado, o pipeline vira parte do fluxo normal do time: PR aberto já mostra se passou nos testes, merge na main já sobe pra produção sozinho. É trabalho de configurar uma vez, que se paga em cada deploy depois disso.
Leia também