12 · CI con GitHub Actions
'Funciona en mi máquina' es donde se esconden los bugs. La Integración Continua corre tus pruebas y el linter en cada push y pull request, en una máquina limpia — así un cambio roto se detecta en el PR, no en producción. GitHub Actions define esto como un workflow YAML.
La Integración Continua corre tu suite de pruebas y el linter AUTOMÁTICAMENTE en cada push y pull request, en una máquina nueva. Un workflow de GitHub Actions (`.github/workflows/ci.yml`) declara CUÁNDO correr (`on:` push/PR), DÓNDE (`runs-on:` un runner limpio) y QUÉ (`jobs:` → `steps:`: checkout, configurar Python, instalar dependencias, correr pytest + ruff) — convirtiendo 'funciona en mi máquina' en una verificación verde-o-roja en cada cambio.
Sin esto:
Sin CI, las pruebas solo corren cuando alguien se acuerda de correrlas localmente — así el código roto se mergea en silencio, y el bug aparece en producción o en el clon de un compañero en lugar de en el pull request.
Tienes pruebas para tu código de entrenamiento y serving. El problema es cuándo corren: si correrlas es un ritual manual que alguien debe recordar, se saltarán bajo presión de fechas, y un cambio roto navegará hasta main. La Integración Continua (CI) quita al humano del bucle: las pruebas y verificaciones corren automáticamente, en una máquina limpia, en cada push y pull request. Un cambio se verifica antes de mergearse, no después de que rompa algo.
GitHub Actions es el sistema de CI integrado de GitHub. Añades un archivo YAML bajo .github/workflows/ y GitHub lo corre por ti en sus servidores. Un workflow tiene una anatomía pequeña y consistente:
on:— los disparadores (triggers).on: [push, pull_request]significa "corre esto en cada push y cada PR". Este es el corazón de CI — el workflow se dispara solo cuando el código cambia.jobs:— una o más unidades de trabajo que corren (por defecto) en paralelo, cada una en su propia máquina virtual nueva.runs-on:— la máquina que usa cada job, p. ej.ubuntu-latest. Crucialmente este es un entorno limpio — nada de "tu máquina" está ahí, que es justo lo que atrapa los bugs de "pero funciona localmente" (una dependencia faltante, un archivo sin commitear).steps:— los comandos ordenados dentro de un job: hacer checkout del código, configurar Python, instalar dependencias, correrpytest, correrruff. Cada paso es o una acción reutilizable (uses: actions/checkout@v4) o un comando de shell (run: pytest).
Dos refinamientos hacen a CI rápido y minucioso:
Caché de dependencias. Instalar dependencias desde cero en cada corrida es lento. Un paso de caché conserva la caché de descargas de pip entre corridas, indexada por un hash de requirements.txt, así las instalaciones son casi instantáneas hasta que las dependencias realmente cambian. (La acción setup-python puede hacerlo con cache: pip.)
Una matriz de pruebas. strategy.matrix corre el mismo job a través de varias configuraciones — p. ej. Python 3.10, 3.11, 3.12 — en paralelo, así verificas tu código en cada versión soportada a la vez en lugar de esperar que funcione más allá de la que usaste para desarrollar.
El resultado es un ✓ verde o un ✗ rojo en cada pull request. Una verificación roja bloquea el merge: el cambio roto se atrapa en la puerta. GitHub Actions corre en los servidores de GitHub (no en el navegador), así que el workflow de abajo es solo para leer — pero on / jobs / runs-on / steps es todo el esqueleto.
Un `.github/workflows/ci.yml`: dispara `on` push/PR, un job `test` que `runs-on: ubuntu-latest` a través de una matriz de versiones de Python, con pasos para checkout, configurar Python (con caché de pip), instalar dependencias, correr ruff y correr pytest.
Pipeline de CI/CD: cada etapa debe pasar antes de la siguiente — un fallo detiene el despliegue.
¿Cuándo corre un workflow de CI de GitHub Actions con `on: [push, pull_request]`?
- CI corre tus pruebas y el linter automáticamente en cada push y pull request, en una máquina limpia — atrapando cambios rotos en el PR en lugar de en producción.
- Un workflow de GitHub Actions tiene una anatomía simple: `on:` (disparadores), `jobs:` (unidades paralelas), `runs-on:` (un runner limpio) y `steps:` (checkout, configuración, instalación, ejecución).
- La caché de dependencias (`cache: pip`) mantiene las instalaciones rápidas entre corridas, y una `strategy.matrix` corre el job a través de múltiples versiones de Python en paralelo.
Todo repo de ML colaborativo controla los merges con un workflow de CI: pruebas, linting y a menudo verificaciones de datos/modelo corren en cada PR, así que una regresión se bloquea antes de llegar a main — la misma disciplina que permite a los equipos de software lanzar de forma segura.
Si lo quitas: Las pruebas correrían solo cuando alguien se acuerda localmente, así que el código roto se mergea en silencio y los bugs de 'funciona en mi máquina' aparecen en el clon de un compañero o en producción.