DevOps CI/CD GitHub Actions .NET

CI/CD do zero com GitHub Actions: guia prático para times .NET

Aprenda a montar um pipeline de integração e entrega contínua completo com GitHub Actions para projetos .NET — com build, testes.

N
Neryx Digital Architects
29 de setembro de 2025
10 min de leitura
210 profissionais leram
Categoria: DevOps & Cloud Público: Times técnicos estruturando pipeline e deploy Etapa: Decisão

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 Pipeline

on: 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:

  1. Fazer checkout do código
  2. Instalar o .NET 9
  3. Restaurar os pacotes NuGet
  4. Compilar o projeto
  5. 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 Pipeline

on: 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: ./coverage

Job 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 warn

Job 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.

Quer medir o nível de maturidade atual?

Comece pelo diagnóstico e veja rapidamente onde estão os gargalos de deploy, testes, observabilidade e infraestrutura.

Fazer diagnóstico

Newsletter

Receba artigos como este no seu e-mail

Conteúdo técnico sobre arquitetura de software, .NET, IA e gestão de produto. Sem spam.