O deploy que todo time teme
Se alguém no seu time já disse "não faz push agora, está perto do almoço" ou "sexta à tarde não fazemos deploy", você conhece a sensação. Deploy manual é ansiedade em forma de processo.
CI/CD — Continuous Integration e Continuous Delivery — é o conjunto de práticas e ferramentas que transforma o deploy de um ritual estressante em um processo automático, confiável e reversível. Com GitHub Actions, você pode implementar isso de forma gratuita, integrada ao repositório que seu time já usa.
Neste guia, vamos montar um pipeline completo do zero para projetos .NET — desde o primeiro commit até o deploy em produção.
Conceitos essenciais antes de começar
O que é CI (Continuous Integration)?
É a prática de integrar código ao repositório principal com frequência — idealmente várias vezes ao dia — e verificar automaticamente se o código compila, se os testes passam e se não há regressões. O objetivo é encontrar problemas cedo, quando são baratos de corrigir.
O que é CD (Continuous Delivery/Deployment)?
Continuous Delivery garante que o código está sempre em um estado deployável — pronto para ser enviado a produção a qualquer momento, com um clique. Continuous Deployment vai um passo além: qualquer commit que passa nos testes vai automaticamente para produção, sem intervenção humana.
O que é GitHub Actions?
É a plataforma de automação integrada ao GitHub. Você define workflows em arquivos YAML dentro do repositório, e o GitHub executa esses workflows em resposta a eventos — push, pull request, tag, agendamento, etc.
Para repositórios públicos, é gratuito sem limite. Para privados, o plano gratuito inclui 2.000 minutos/mês — suficiente para times pequenos.
Estrutura de um workflow do GitHub Actions
Todo workflow é um arquivo .yml dentro da pasta .github/workflows/ do repositório. Vamos entender a anatomia básica:
name: CI Pipelineon: push: branches: [ main, develop ] pull_request: branches: [ main ]
jobs: build-and-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ‘9.0.x’ - name: Restore dependencies run: dotnet restore - name: Build run: dotnet build —no-restore - name: Test run: dotnet test —no-build —verbosity normal
Isso já é um pipeline funcional de CI. A cada push para main ou develop, e a cada pull request, o GitHub vai:
- Fazer checkout do código
- Instalar o .NET 9
- Restaurar os pacotes NuGet
- Compilar o projeto
- Executar todos os testes
Pipeline completo para projetos .NET
Vamos evoluir para um pipeline de produção, com múltiplas etapas e boas práticas:
name: CI/CD Pipelineon: push: branches: [ main ] pull_request: branches: [ main ]
env: DOTNET_VERSION: ‘9.0.x’ REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }}
jobs:
Job 1: Build e testes
build-and-test: name: Build & Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup .NET ${{ env.DOTNET_VERSION }} uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Cache NuGet packages uses: actions/cache@v4 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} restore-keys: | ${{ runner.os }}-nuget- - name: Restore dependencies run: dotnet restore - name: Build run: dotnet build --no-restore --configuration Release - name: Run unit tests run: | dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage" --results-directory ./coverage - name: Upload coverage report uses: actions/upload-artifact@v4 with: name: coverage-report path: ./coverageJob 2: Análise de qualidade (roda em paralelo com build-and-test)
code-quality: name: Code Quality runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Install dotnet-format run: dotnet tool install -g dotnet-format - name: Check code formatting run: dotnet format --verify-no-changes --severity warnJob 3: Build e push da imagem Docker (só na main)
build-image: name: Build Docker Image runs-on: ubuntu-latest needs: [build-and-test, code-quality] if: github.ref == ‘refs/heads/main’ && github.event_name == ‘push’ permissions: contents: read packages: write steps: - uses: actions/checkout@v4
- name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata for Docker id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=sha,prefix=sha- - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }}Job 4: Deploy para staging
deploy-staging: name: Deploy to Staging runs-on: ubuntu-latest needs: build-image environment: staging steps: - name: Deploy to staging uses: appleboy/ssh-action@v1 with: host: ${{ secrets.STAGING_HOST }} username: ${{ secrets.STAGING_USER }} key: ${{ secrets.STAGING_SSH_KEY }} script: | docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main docker stop app-staging || true docker rm app-staging || true docker run -d —name app-staging —restart unless-stopped -p 8080:8080 -e ASPNETCORE_ENVIRONMENT=Staging -e ConnectionStrings__Default=”${{ secrets.STAGING_DB_CONNECTION }}” ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
Explicando as partes-chave
Cache de dependências
A etapa de cache dos pacotes NuGet é fundamental para pipelines rápidos. Sem cache, cada execução baixa todos os pacotes do zero — podendo levar minutos. Com cache, execuções subsequentes pulam o download se os arquivos .csproj não mudaram.
Jobs em paralelo
Os jobs build-and-test e code-quality rodam em paralelo — isso reduz o tempo total do pipeline. O job de build da imagem Docker só começa depois que ambos terminam com sucesso (needs: [build-and-test, code-quality]).
Condicionais por branch
O build da imagem Docker e o deploy só acontecem em pushes para main. Pull requests apenas executam os testes e checagem de qualidade — sem efeitos colaterais em infraestrutura.
Environments e aprovações
O environment: staging permite configurar no GitHub quem precisa aprovar o deploy antes que ele aconteça. Para produção, você pode exigir aprovação manual de um líder técnico antes do deploy seguir.
Secrets
Credenciais como chaves SSH, strings de conexão e tokens nunca ficam no código. São armazenadas em GitHub Secrets (Settings → Secrets and variables → Actions) e referenciadas como ${{ secrets.NOME_DO_SECRET }}.
Adicionando verificação de segurança
Para projetos que lidam com dados sensíveis, vale adicionar um job de análise de vulnerabilidades:
security-scan: name: Security Scan runs-on: ubuntu-latest steps: - uses: actions/checkout@v4- name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Check for vulnerable NuGet packages run: dotnet list package --vulnerable --include-transitive - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: scan-type: 'fs' scan-ref: '.' severity: 'CRITICAL,HIGH'
Protegendo a branch main
O pipeline de CI só tem valor se você proteger a branch principal. No GitHub, vá em Settings → Branches → Branch protection rules e configure:
- Require a pull request before merging — ninguém faz push direto para main
- Require status checks to pass before merging — selecione os jobs de CI como obrigatórios
- Require at least 1 approval — code review obrigatório
- Do not allow bypassing the above settings — vale para administradores também
Com isso, é fisicamente impossível chegar código quebrado na branch principal.
Monitorando os pipelines
Com o pipeline em produção, crie o hábito de monitorar:
- Tempo médio de execução: pipelines que demoram mais de 10 minutos precisam de otimização
- Taxa de sucesso: muitas falhas podem indicar testes instáveis ou problemas de infraestrutura
- Notificações: configure alertas no Slack ou e-mail para falhas em produção
Próximos passos
Com o pipeline básico funcionando, você pode evoluir para:
- Deploy com blue/green ou canary releases para zero downtime
- Testes de integração e end-to-end automatizados no pipeline
- Infraestrutura como código com Terraform integrada ao workflow
- Pipeline de preview environments para cada pull request
Conclusão
Um pipeline de CI/CD não é complexidade desnecessária — é a diferença entre um time que tem medo de fazer deploy e um time que faz deploy com confiança, várias vezes ao dia.
O GitHub Actions torna isso acessível a times de qualquer tamanho, sem custo adicional para repositórios privados dentro do plano gratuito. O investimento de algumas horas para configurar paga dividendos por anos.
Se o seu time precisa de ajuda para montar ou otimizar pipelines de CI/CD, a Neryx tem experiência com GitHub Actions, GitLab CI e Azure DevOps em projetos .NET de diferentes escalas. Fale conosco para uma consultoria gratuita.