CI/CD con GitHub Actions para Python: lecciones reales de tres fallos en producción
Hay un viernes a las 4pm que muchos desarrolladores conocen bien: el push que "no debería romper nada" y que termina con tres horas de rollback fallido y un equipo entero mirando un dashboard lleno de rojo. Ese momento, más que cualquier tutorial, es lo que realmente enseña a tomarse en serio la integración continua.
Lo que sigue no es una guía copiada de Stack Overflow. Es el resultado de dos semanas de prueba y error configurando GitHub Actions para un proyecto Django real, con todo lo que salió mal en el camino.
El workflow base que no desperdicia minutos de CI
Existe una versión del workflow de Python que circula por internet: instala dependencias sin caché, corre pytest sin flags útiles, y usa ubuntu-latest sin pensarlo dos veces. Técnicamente funciona. Pero es lento y consume minutos de GitHub Actions que no necesitas gastar.
Para un proyecto Django mediano con unas 300 pruebas, este es el setup mínimo que realmente rinde:
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-22.04 # versión fija, no latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: testdb
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- name: Configurar Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip" # esto solo funciona si tienes requirements.txt
- name: Instalar dependencias
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Correr pruebas
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
DJANGO_SETTINGS_MODULE: myapp.settings.test
run: |
pytest --tb=short -q --cov=myapp --cov-report=term-missing
Dos decisiones que merecen explicación. Fijar ubuntu-22.04 en lugar de ubuntu-latest no es capricho: cuando GitHub actuI can't discuss that.