P4 · Tercer sprint: entrega continua
Publicado: semana 11 · Entrega: semana 14 (demo del sprint 3) · En equipo · Tag: p4
En el sprint 3 el producto sale a producción, y sale solo: cada pull request
pasa por un pipeline que impide integrar código roto, y cada merge a main se
despliega sin pasos a mano. El equipo además observa su aplicación en
producción (/health y logs estructurados), cuida sus dependencias y aprende
de un incidente con un postmortem.
Cubre las semanas 12 (integración continua, build, dependencias y cadena de suministro), 13 (contenedores, entornos, observabilidad, incidentes y postmortems) y 14 (desarrollo con agentes). La revisión de seguridad con OWASP Top 10 viene en P5, después de la semana 15. La revisión de P4 vuelve a correr las de P1 a P3.
1. Qué entregan
| Archivo | Qué es |
|---|---|
.github/workflows/*.yml |
El pipeline de GitHub Actions |
Dockerfile (y .dockerignore) |
La imagen de producción |
GET /health |
Un endpoint de salud |
| Logs estructurados | Una línea JSON por evento en stdout |
README.md, ## Despliegue |
La URL de producción y cómo se despliega |
docs/postmortems/AAAA-MM-DD-tema.md |
Al menos un postmortem |
docs/dependencias.md y package-lock.json |
La política de dependencias y el lockfile |
docs/sprints/sprint-3.md |
Planificación, revisión (demo) y retrospectiva del sprint 3 |
El pipeline (.github/workflows/)
Uno o más workflows de GitHub Actions que:
- Se disparan con
pull_requesty corren un job connpm run check(el mismo de P3: tipos, linter, pruebas con cobertura) y contimeout-minutes: 10: un proceso colgado no falla, y sin este límite GitHub espera 360 minutos (semana 12). Si su revisión de verdad tarda más, súbanlo hasta 30 como máximo. Activen en GitHub la regla de protección demainque exige ese check en verde para el merge (Require status checks to pass). En un repositorio privado, la protección de ramas necesita GitHub Pro, gratis con GitHub Education, o una organización con GitHub Team; sin ella, el equipo no integra un pull request con el pipeline en rojo, y el docente lo mira en la historia de GitHub Actions. - En cada
pushamaindespliegan, en un job cuyo nombre o id dicedeploy(odespliegue) y que tieneneeds:con el job de revisión: no se despliega lo que no pasó. Si el mismo workflow también corre enpull_request, el job de deploy llevaif: github.event_name == 'push' && github.ref == 'refs/heads/main', o desplegaría cada pull request.
En GitHub Actions no está el contenedor del curso: el job instala las
herramientas con npm ci (por eso las versiones de desarrollo van en
package.json y en el lockfile, ver Dependencias) y usa actions/setup-node
con node-version: 26.
Dockerfile
- Imagen base
node:26(por ejemplonode:26-trixie-slim), como el contenedor del curso. - Copia solo lo que hace falta para correr (
src/,public/,package.json); nunca.env. Un.dockerignoreexcluye al menosnode_modulesy.git. - Corre como un usuario que no es root (
USER node). - Termina con
CMD(oENTRYPOINT) que levanta la aplicación, que leePORTyDB_PATHcomo en P2. Si los datos tienen que sobrevivir a un despliegue, van en un volumen.
La revisión no construye la imagen (no hay Docker dentro del contenedor del
curso): la construyen en el pipeline y en su computadora con
docker build -t app . && docker run -p 3000:3000 app.
GET /health
Responde 200 con un JSON que tiene un campo status
({"status": "ok", ...}) cuando la aplicación puede atender: idealmente revisa
también la base de datos y responde 503 si no responde. Lo usan el hosting, el
pipeline (prueba de humo después del deploy) y un monitor externo. Agréguenlo a
docs/api/openapi.yaml.
Logs estructurados
La aplicación escribe en stdout una línea JSON por evento, con al menos
la hora (time o timestamp), el nivel (level) y el mensaje (msg o
message), más los campos que sirvan (método, ruta, código de estado,
duración):
{"time":"2026-06-16T19:25:04.022Z","level":"info","msg":"request","method":"POST","path":"/api/orders","status":409,"durationMs":6}
Sin datos personales ni secretos en los logs (ni tokens, ni contraseñas, ni el celular de un cliente).
README.md: ## Despliegue
La URL https de producción (la revisión intenta URL/health), dónde está
desplegada, cómo llega un cambio a producción, cómo se vuelve a la versión
anterior (rollback), dónde están los logs y, si hay base de datos, cómo se
respalda.
Dónde desplegar lo deciden ustedes en un ADR. Los planes gratuitos cambian seguido: revisen en el momento qué ofrece cada servicio (contenedores, disco persistente, si la aplicación se apaga sin tráfico, si pide tarjeta) y si la universidad tiene un servidor para proyectos. Si el plan gratuito no guarda archivos entre despliegues, SQLite pierde los datos: es una decisión de diseño, no un detalle.
docs/postmortems/
Al menos un postmortem sin culpables, como en la
semana 13, en AAAA-MM-DD-tema.md. Puede ser de un
incidente real del sprint (lo más probable: algo se rompe en producción) o
de un game day: un incidente que el equipo provoca a propósito en un
entorno controlado (apagar la base de datos, llenar el disco, desplegar una
versión rota) para ver cómo responde. Al inicio dice Tipo: incidente real o
Tipo: game day, y tiene estas secciones:
## Resumen: qué pasó, en tres frases.## Impacto: a quién afectó, cuánto tiempo, cuántos pedidos o usuarios, con datos (de los logs).## Línea de tiempo: con horas, desde que empezó hasta que se cerró, incluida la detección.## Causa raíz: por qué pasó y por qué el proceso (pruebas, revisión, monitoreo) no lo detectó antes.## Acciones: qué se cambia, quién y para cuándo, con su estado.
Dependencias: docs/dependencias.md y package-lock.json
package-lock.jsonestá en el repositorio y corresponde apackage.json(si agregan, quitan o cambian una versión, regeneran el lockfile en el contenedor connpm install --package-lock-only, que necesita red). Las herramientas de desarrollo que usa el pipeline (TypeScript, ESLint, typescript-eslint, fast-check,@types/node) van endevDependencies, con las mismas versiones del contenedor del curso.docs/dependencias.mdcon estas secciones:## Cómo elegimos una dependencia: las preguntas que se responden antes de agregar un paquete (¿lo resuelve Node?, ¿quién lo mantiene?, ¿existe de verdad con ese nombre?, ¿licencia?).## Versiones y lockfile: versiones exactas o rangos, y cómo se instala en el pipeline (npm ci).## Auditoría: cuándo y cómo se correnpm audity qué se hace con lo que encuentra.
Sin vulnerabilidades altas ni críticas en las dependencias de producción
(npm audit --omit=dev). Nunca suban .env ni llaves: la revisión los busca en
los archivos del repositorio.
docs/sprints/sprint-3.md
Como los anteriores (## Planificación, ## Revisión, ## Retrospectiva). La
demo de la semana 14 es con la aplicación en producción.
2. Revisión automática
Dentro del contenedor del curso, desde la carpeta del repositorio del equipo, clonado dentro de la carpeta del curso (P1, sección 1):
cd /work/equipo-nombre
make -f /work/projects/p4-entrega-continua/Makefile check
Corre las reglas de P1 a P3 y estas:
| Regla | Qué revisa |
|---|---|
P4-WORKFLOW-PR |
Un workflow con pull_request cuyo job corre npm run check (o make check), con timeout-minutes entre 1 y 30 |
P4-WORKFLOW-DEPLOY |
Un job deploy con needs: en un workflow que corre en push a main; si ese workflow también corre en pull_request, el job tiene un if: que lo limita a main |
P4-DOCKERFILE |
Dockerfile con FROM, CMD o ENTRYPOINT y sin copiar .env; avisa si la imagen no es node:26, si corre como root o si falta .dockerignore |
P4-HEALTH |
Con la aplicación levantada (como en P2-ESQUELETO), GET /health responde 200 con JSON y un campo status |
P4-LOGS |
La aplicación escribe en stdout al menos una línea JSON con hora, nivel y mensaje |
P4-URL |
## Despliegue del README tiene una URL https; si hay red, que URL/health responda (si no responde: aviso) |
P4-POSTMORTEM |
Al menos un postmortem con su tipo y sus cinco secciones |
P4-DEPENDENCIAS |
docs/dependencias.md con sus tres secciones |
P4-LOCKFILE |
package-lock.json (versión 2 o más) con las mismas dependencias y versiones que package.json |
P4-AUDIT |
Con red: npm audit --omit=dev sin vulnerabilidades altas ni críticas (sin red: aviso) |
P4-SECRETOS |
Ningún .env (salvo .env.example) ni llave privada en los archivos que sigue git |
P4-SPRINT-3 |
docs/sprints/sprint-3.md con sus tres secciones y al menos una historia que existe |
3. Cómo entregar
Con el tag p4, como en P1. La demo del sprint 3 es en la semana 14, con la
URL de producción.
4. Consejos y errores comunes
- Dejar el pipeline para la última semana. Es lo primero del sprint: desde que existe, cada pull request se revisa solo. Un pipeline que nunca frenó nada tampoco se probó: abran un pull request con una prueba rota y vean que no se puede integrar.
- Desplegar a mano “por esta vez”. Si hay un paso manual, documenten por qué
y automatícenlo en el sprint siguiente. Lo que llega a producción tiene que
ser lo que está en
main. - Secretos en el repositorio. Un token en el YAML o un
.enven un commit quedan en la historia aunque se borren después: hay que cambiarlos. Usen los secrets de GitHub y las variables del hosting. /healthque siempre dice ok. Si la base de datos no responde y/healthdice 200, el monitor no sirve. Que revise lo que la aplicación necesita para atender.- Un postmortem que busca culpables o se queda en “fue un error humano”. La pregunta útil es por qué el sistema permitió el error y por qué no se detectó antes.
- Con IA. Un agente escribe un workflow o un Dockerfile en segundos, y a veces con acciones de terceros que no conocen, versiones que no existen o permisos de más. Lean cada línea, fijen las versiones, y no instalen un paquete sugerido sin buscarlo en npm (los nombres inventados son un ataque conocido de cadena de suministro).