| Capa | Qué hace | Qué pasa sin ella |
|---|---|---|
| El workflow | Corre lint, tests y build | No hay señal |
| Status check | Publica el resultado en el PR | La señal existe, pero hay que ir a buscarla |
| Required status check | Impide el merge si está en rojo | La señal es una sugerencia |
| Require branches to be up to date | Obliga a reejecutar sobre main actual | Verde sobre una base que ya cambió |
| Do not allow bypassing | Aplica la regla también a los administradores | La regla es para los demás |
Las dos últimas filas son las que se olvidan, y no son adorno. La cuarta resuelve un fallo real:
dos PRs pueden pasar los tests por separado y romper main al juntarse, porque
ninguno de los dos se probó nunca contra el estado final. La quinta no resuelve un fallo técnico
sino social — una regla que el admin puede saltarse es una regla que se salta el día que hay
prisa, que es exactamente el día que importa.
jobs:
lint:
name: Lint # ← esto es lo que sale en la lista de checks requeridos
runs-on: ubuntu-latest
Si renombras el job, el required check que configuraste apunta a un nombre que ya no existe. Según cómo esté la regla, el PR se queda bloqueado para siempre esperando un check que nunca va a llegar, o —peor— deja de exigirse sin que nadie se entere. Es la avería más tonta de este montaje y también la más frecuente, porque el renombrado ocurre en el YAML y la regla vive en otro sitio.
// la protección de rama es configuración fuera del repositorio: no viaja en el fork ni en el clonestrategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [18, 20, 22]
exclude:
- os: windows-latest
node-version: 18
Tres sistemas por tres versiones son nueve combinaciones; el exclude deja
ocho jobs en paralelo. Y fail-fast: false es lo que hace útil el resultado: con
el valor por defecto, el primer fallo cancela el resto de la matriz y te quedas sin saber si el
problema es de Node 18 o de Windows — justo la pregunta para la que montaste la matriz.
Cache. La forma corta es cache: 'npm' dentro de setup-node. La
manual da más control:
- uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
La clave se construye con el hash del fichero de bloqueo: si el lockfile no cambió, la clave es la misma y se restaura; si cambió, la clave es otra y se reinstala. Cachear por rama o por fecha —que es lo intuitivo— es la forma clásica de servir dependencias obsoletas a un pipeline que las declara actualizadas.
Services. Levantan contenedores junto al job para los tests de integración:
services:
postgres:
image: postgres:15
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-retries 5
El bloque options no es decorativo. Sin health check, el job arranca los
tests antes de que Postgres acepte conexiones y falla de forma intermitente — el peor modo
de fallo posible, porque se reejecuta, pasa, y el equipo aprende a reejecutar en vez de a leer.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
Alguien empuja tres commits seguidos a un PR. Sin esto corren tres pipelines completos y solo importa el resultado del último. Con esto, los dos primeros se cancelan en cuanto llega el siguiente.
jest.config.js cuya clave es coverageThresholds, en plural. ¿Es ese el
nombre que lee Jest, o el que lee es coverageThreshold en singular? Conviene
comprobarlo antes de dar el ejercicio por resuelto, porque la diferencia no es cosmética: una
opción desconocida en la configuración de Jest no produce ningún error, se ignora en
silencio. Si el nombre fuese el equivocado, el pipeline pasaría en verde con un 12 % de cobertura
y el criterio que vale 15 puntos del reto no estaría comprobando absolutamente nada.
Sobre un repositorio tuyo, en este orden: lint automático en cada PR; tests en cada PR; los dos
declarados como required status checks en main; require branches to be up to
date; y do not allow bypassing, contigo incluido.
Cuando termines, la comprobación que vale: abre un PR con un test roto a propósito e intenta mergearlo. Si puedes, alguna de las cinco no está donde creías — y ahora sabes cuál mirar.