404 Not Found

404 Not Found


nginx

CI/CD — Pipeline de Entrega Automatizada com GitHub Actions

CI/CD é como uma linha de montagem automatizada em uma fábrica de carros—peças (código) entram, passam por soldagem (lint), inspeção de qualidade (testes), e pintura (scan de segurança), e os produtos acabados (imagens Docker) que passam na inspeção são automaticamente entregues à concessionária (ambiente de produção).

1. O Que Você Vai Aprender


2. A História Real da Alice

(1) Dor: Implantação manual frequentemente causa erros

Depois que Bob submeteu o código, Alice fez o fetch manual do código no servidor, executou testes, construiu a imagem e reiniciou o contêiner. Esse processo levava 30 minutos cada vez, e ela frequentemente pulava etapas de teste—em uma ocasião, Alice esqueceu de executar as migrações, e o novo endpoint retornou erro 500 em produção. Charlie disse que poderia executar essas tarefas manualmente em cada um dos três ambientes (dev, staging e prod), mas o risco era muito alto.

(2) Solução com GitHub Actions

O GitHub Actions executa automaticamente o pipeline completo—lint → teste → build → deploy—a cada push ou pull request. Todas as etapas são definidas por código, então nada pode ser esquecido, e falhas bloqueiam automaticamente os merges.

(3) Resultado

O tempo de implantação foi reduzido de 30 minutos (manual) para 5 minutos (automatizado), e o problema de testes pulados foi completamente eliminado (PRs que falham nos testes não podem ser merged), garantindo que as implantações em todos os três ambientes sejam completamente consistentes.


3. Básico do GitHub Actions

(1) Conceitos Core

100%
flowchart LR
    Trigger[Gatilho: push/PR] --> Workflow[Workflow]
    Workflow --> Job1[Job 1: Lint+Teste]
    Workflow --> Job2[Job 2: Build]
    Job1 --> Step1[Step: ruff check]
    Job1 --> Step2[Step: pytest]
    Job2 --> Step3[Step: docker build]
    Job2 --> Step4[Step: docker push]
Conceito Descrição Exemplo
Workflow Definição de Processo Automatizado .github/workflows/ci.yml
Trigger Condição de Gatilho push, pull_request
Job Unidade de execução composta por um conjunto de steps test, build, deploy
Step Operação Única run: pytest
Action Ações Reutilizáveis actions/checkout@v4
Runner Ambiente de Execução ubuntu-latest

(2) Mapeamento de Branches e Ambientes

100%
graph TD
    Feature[feature/*] -->|PR| Develop[develop]
    Develop -->|Deploy| DevEnv[Ambiente Dev]
    Develop -->|PR| Main[main]
    Main -->|Deploy| Staging[Ambiente Staging]
    Main -->|Tag v*| Prod[Ambiente de Produção]
Branch Ambiente Gatilho Método de Implantação
feature/* - Teste Automatizado de PR Não Implantado
develop Dev Auto-push Docker Compose
main Staging Auto-push Docker Compose
v* tag Produção Tag Manual Docker Compose / K8s

4. Pipeline CI

(1) ▶ Exemplo: Workflow CI Completo

YAML
# .github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Instalar UV
        run: curl -LsSf https://astral.sh/uv/install.sh | sh

      - name: Instalar dependências
        run: uv sync --frozen

      - name: Executar ruff lint
        run: uv run ruff check app/ tests/

      - name: Executar verificação de formato ruff
        run: uv run ruff format --check app/ tests/

  test:
    runs-on: ubuntu-latest
    needs: lint
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: pricetracker
          POSTGRES_PASSWORD: test_password
          POSTGRES_DB: pricetracker_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      redis:
        image: redis:7-alpine
        ports:
          - 6379:6379
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4

      - name: Instalar UV
        run: curl -LsSf https://astral.sh/uv/install.sh | sh

      - name: Instalar dependências
        run: uv sync --frozen

      - name: Executar pytest
        env:
          DATABASE_URL: postgresql+asyncpg://pricetracker:test_password@localhost:5432/pricetracker_test
          REDIS_URL: redis://localhost:6379/0
          SECRET_KEY: test-secret-key-for-ci
        run: uv run pytest tests/ -v --cov=app --cov-report=xml

      - name: Upload de cobertura
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage.xml

  security:
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4

      - name: Executar verificação de segurança
        run: |
          pip install safety
          safety check --json || true

      - name: Executar bandit
        run: |
          pip install bandit
          bandit -r app/ -f json || true

  build:
    runs-on: ubuntu-latest
    needs: [test, security]
    steps:
      - uses: actions/checkout@v4

      - name: Configurar Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Buildar imagem Docker
        uses: docker/build-push-action@v5
        with:
          context: .
          file: docker/Dockerfile
          push: false
          tags: pricetracker:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Saída:

ext Pipeline CI/CD carregado Status do pipeline: passed Testes: 12 passed, 0 failed


5. Pipeline CD

(1) ▶ Exemplo: Workflow de Implantação em Produção

YAML
# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    tags:
      - 'v*'

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      - name: Configurar Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login no GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Buildar e fazer push da imagem
        uses: docker/build-push-action@v5
        with:
          context: .
          file: docker/Dockerfile
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:${{ github.ref_name }}
            ghcr.io/${{ github.repository }}:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Implantar no servidor
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            cd /opt/pricetracker
            docker compose pull
            docker compose up -d --remove-orphans
            docker compose exec api alembic upgrade head
            echo "Versão implantada ${{ github.ref_name }}"

Saída:

TEXT
CONTAINER ID   IMAGE          STATUS         PORTS
abc123         nginx:latest   Up 2 hours     0.0.0.0:80->80/tcp

(2) Fluxo Completo do Pipeline CI/CD

100%
flowchart LR
    Push[Bob faz push do código] --> Lint[Lint: ruff]
    Lint --> Test[Teste: pytest]
    Lint --> Sec[Segurança: safety + bandit]
    Test --> Build[Docker Build]
    Sec --> Build
    Build -->|On tag| Push[Push para GHCR]
    Push --> Deploy[Implantar em Produção]
    Deploy --> Health[Health Check]
    Health --> Done[✓ No Ar]

(2) ▶ Exemplo: Proteção de Branch e Regras de Proteção de Ambiente

YAML
# .github/workflows/branch-protection.yml
name: Branch Protection Check

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - name: Verificar se PR tem como alvo main
        run: |
          if [ "${{ github.base_ref }}" != "main" ]; then
            echo "PR deve ter como alvo a branch main"
            exit 1
          fi

      - name: Verificar aprovação do ambiente
        if: github.event.pull_request.merged == true
        uses: octokit/request-action@v2
        with:
          route: POST /repos/{owner}/{repo}/deployments
          environment: staging
          required_reviewers: 1

Saída:

TEXT
Pipeline CI/CD carregado
Status do pipeline: passed
Testes: 12 passed, 0 failed

❓ Perguntas Frequentes

P A cota gratuita do GitHub Actions é suficiente?
R Não há limites para repositórios públicos. Repositórios privados recebem 2.000 minutos gratuitos por mês. O CI do PriceTracker roda por cerca de 5 minutos por execução; com 10 execuções por dia, são cerca de 1.500 minutos, o que é suficiente.
P Como lidar com migrações de banco de dados no CI?
R Nos testes de CI, use Base.metadata.create_all() para criar tabelas diretamente (sem Alembic). Execute alembic upgrade head no script de implantação do CD.
P Como os segredos são armazenados com segurança?
R No seu projeto GitHub, vá em Settings → Secrets and variables → Actions. SERVER_SSH_KEY, DB_PASSWORD, e outros são armazenados lá, e você pode referenciá-los no YAML usando ${{ secrets.XXX }}.
P Como implantar apenas na branch main?
R Use on: push: tags: ['v*'] para acionar a implantação apenas quando uma tag é criada. Na branch de desenvolvimento, execute apenas testes—não implante.
P Como aproveitar o cache do Docker?
R Use cache-from: type=gha para aproveitar o cache do GitHub Actions. O build inicial leva 5 minutos; alterações subsequentes apenas reconstroem as camadas modificadas, levando cerca de 1-2 minutos.
P Como fazer rollback após uma falha de implantação?
R docker compose down + docker compose pull <tag-anterior> + docker compose up -d. Mantenha a tag da imagem da versão anterior para facilitar o rollback.

📖 Resumo


📝 Exercícios

  1. Exercício Básico (Dificuldade ⭐): Crie .github/workflows/ci.yml. Configure para que ruff check e pytest sejam executados automaticamente quando você fizer push ou criar um PR, e verifique se a página do GitHub Actions mostra "Passed" em verde. Dica: on: push: branches: [main]
  2. Exercício Avançado (Dificuldade ⭐⭐): Adicione contêineres de serviço PostgreSQL e Redis ao job de teste, configure variáveis de ambiente para que o pytest possa se conectar ao banco de dados de teste, e adicione uma etapa de build Docker para verificar se a imagem builda com sucesso. Dica: services: + options: --health-cmd
  3. Desafio (Dificuldade: ⭐⭐⭐): CI/CD completo—O pipeline CI inclui linting, testes, verificações de segurança e build; o pipeline CD faz push da imagem para GHCR quando a tag v* é criada e implanta no servidor via SSH, usando GitHub Secrets para armazenar as chaves. Dica: docker/login-action + appleboy/ssh-action + ${{ secrets.XXX }}

---|

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%