CI para Python com GitHub Actions

    CI para Python garante que cada pull request passa por testes automáticos, verificação de tipos e lint antes de entrar no branch principal. Com pytest, ruff e mypy no GitHub Actions, você tem um pipeline robusto em menos de 50 linhas de YAML que roda em qualquer projeto Python moderno.

    Workflow de CI completo para Python

    Pipeline com testes, lint, tipagem e cobertura:

    yaml
    # .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:

    bash
    # 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:

    yaml
    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=short

    Testar API FastAPI no CI

    Use TestClient do httpx para testar endpoints FastAPI sem servidor externo:

    python
    # 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 data

    Pre-commit hooks para CI local

    Use pre-commit para rodar as mesmas verificações do CI antes do commit:

    yaml
    # .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

    $ runstack deploy --plan starter

    Não quer configurar manualmente?

    Não quer configurar manualmente? Implante o VPS em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.

    Perguntas frequentes