Workflow de CI completo para Python
Pipeline com testes, lint, tipagem e cobertura:
# .github/workflows/ci.yml
name: CI Python
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
ci:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.11', '3.12']
steps:
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Instalar dependências
run: |
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Lint com ruff
run: ruff check .
- name: Formatação com ruff format
run: ruff format --check .
- name: Type check com mypy
run: mypy app/ --ignore-missing-imports
- name: Testes com pytest e cobertura
run: pytest --cov=app --cov-report=xml --cov-report=term-missing
- name: Upload cobertura
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}requirements-dev.txt para o CI
Separe dependências de desenvolvimento das de produção:
# requirements.txt — produção
fastapi==0.115.0
uvicorn[standard]==0.32.0
sqlalchemy==2.0.36
asyncpg==0.30.0
pydantic-settings==2.6.0
# requirements-dev.txt — apenas desenvolvimento e CI
pytest==8.3.3
pytest-asyncio==0.24.0
pytest-cov==6.0.0
httpx==0.28.0 # cliente HTTP para testar FastAPI
ruff==0.8.0 # lint e formatter
mypy==1.13.0 # type checker
factory-boy==3.3.1 # factories para testes
# pyproject.toml — configuração centralizada
[tool.ruff]
line-length = 100
select = ["E", "F", "I", "N", "W", "UP"]
ignore = ["E501"]
[tool.mypy]
python_version = "3.12"
strict = true
ignore_missing_imports = true
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
addopts = "--strict-markers"Testes de integração com PostgreSQL
Use services para subir PostgreSQL no CI e testar com banco real:
jobs:
integration-tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
POSTGRES_DB: testdb
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-retries 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- run: pip install -r requirements.txt -r requirements-dev.txt
- name: Aplicar migrações
env:
DATABASE_URL: postgresql+asyncpg://testuser:testpass@localhost:5432/testdb
run: alembic upgrade head
- name: Testes de integração
env:
DATABASE_URL: postgresql+asyncpg://testuser:testpass@localhost:5432/testdb
ENVIRONMENT: test
run: pytest tests/integration/ -v --tb=shortTestar API FastAPI no CI
Use TestClient do httpx para testar endpoints FastAPI sem servidor externo:
# tests/test_api.py
import pytest
from httpx import AsyncClient, ASGITransport
from app.main import app
@pytest.fixture
async def client():
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test"
) as ac:
yield ac
@pytest.mark.asyncio
async def test_health_check(client):
response = await client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
@pytest.mark.asyncio
async def test_criar_usuario(client):
response = await client.post("/users", json={
"name": "Test User",
"email": "test@example.com"
})
assert response.status_code == 201
data = response.json()
assert data["email"] == "test@example.com"
assert "id" in dataPre-commit hooks para CI local
Use pre-commit para rodar as mesmas verificações do CI antes do commit:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
hooks:
- id: mypy
additional_dependencies: [types-all]
# Instalar e ativar:
pip install pre-commit
pre-commit install
# Rodar manualmente em todos os arquivos:
pre-commit run --all-files
# No CI — verificar que pre-commit passa:
- name: Pre-commit checks
uses: pre-commit/action@v3.0.1