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
- Básico de GitHub Actions: Conceitos de Workflow, Job, Step e Action
- Pipeline CI: lint → pytest → scan de segurança → build Docker
- Pipeline CD: Push de Imagem → Docker Hub/GHCR → Implantação no Servidor
- Gerenciamento de Ambientes: Estratégia de Branches para dev / staging / production
- Cenário Alice: Bob submete um PR → testes automatizados → Charlie faz merge → implantação automatizada
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
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
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
# .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
# .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:
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
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
# .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:
Pipeline CI/CD carregado
Status do pipeline: passed
Testes: 12 passed, 0 failed
❓ Perguntas Frequentes
Base.metadata.create_all() para criar tabelas diretamente (sem Alembic). Execute alembic upgrade head no script de implantação do CD.SERVER_SSH_KEY, DB_PASSWORD, e outros são armazenados lá, e você pode referenciá-los no YAML usando ${{ secrets.XXX }}.on: push: tags: ['v*'] para acionar a implantação apenas quando uma tag é criada. Na branch de desenvolvimento, execute apenas testes—não implante.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.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
- GitHub Actions consiste em uma estrutura de quatro níveis: Workflow → Job → Step → Action
- Pipeline CI: lint (ruff) → teste (pytest + contêineres de serviço PostgreSQL/Redis) → segurança → build
- Pipeline CD: gatilho de tag → buildar imagem Docker → push para GHCR → implantar no servidor via SSH
- Estratégia de branches: PR feature para testes, develop para implantação Dev, main para implantação Staging, tags v* para implantação Produção
- Cache de camadas Docker + cache GitHub Actions para acelerar builds; Secrets para gerenciamento seguro de chaves
📝 Exercícios
- Exercício Básico (Dificuldade ⭐): Crie
.github/workflows/ci.yml. Configure para queruff checkepytestsejam 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] - 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 - 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 }}
---|



