El pipeline de Reservalia construye una imagen de contenedor perfectamente correcta en cada pull request… y la tira a la basura cuando el runner muere. Falta la pieza que convierte una verificación en algo con lo que se puede desplegar: guardar el resultado, ponerle un nombre que signifique algo y hacerlo viajar entre entornos sin volver a construirlo. Esta lección gira alrededor de un principio único —construir una vez, desplegar muchas veces— y de todo lo que se deriva de él: qué es un artefacto inmutable, por qué reconstruir en cada entorno destruye la trazabilidad, qué estrategias de versionado existen y cuándo usar cada una, por qué la etiqueta latest es peligrosa dentro de un pipeline, dónde se guardan los artefactos y cuánto cuestan, cómo se promociona el mismo reservalia/api:a3f9c21 de dev a staging y a prod, y cómo averiguar desde una máquina en producción qué commit exacto está ejecutando. Lo que no veremos es cómo se despliega ese artefacto: eso es el módulo 3 entero.
Contenido
- Construir una vez, desplegar muchas veces
- El artefacto inmutable y el antipatrón de reconstruir por entorno
- Estrategias de versionado
- Por qué
latestes peligrosa - Versionado automático a partir de commits convencionales
- Dónde se guardan los artefactos: registros y retención
- Promoción entre entornos
- Trazabilidad: saber qué está corriendo en producción
- Firma y procedencia del artefacto
- El job
publicarde Reservalia - Errores Comunes y Consejos
- Ejercicios
- Conclusión
- Construir una vez, desplegar muchas veces
El principio. El pipeline construye un solo artefacto por commit. Ese artefacto, sin modificar ni reconstruir, es el que se prueba en
dev, el que se valida enstagingy el que acaba sirviendo peticiones enprod. Lo único que cambia entre entornos es la configuración inyectada desde fuera.
Ya apareció en la lección 01-04, cuando definimos los tres entornos de Reservalia. Ahora podemos ver por qué es tan importante: si reconstruyes en cada entorno, lo que pruebas y lo que despliegas son objetos distintos, y todas las pruebas del mundo dejan de garantizar nada sobre producción.
Las diferencias no tienen que ser dramáticas para hacer daño. Basta con que entre la build de staging (martes) y la de prod (jueves) una dependencia transitiva haya publicado un parche, o con que el runner haya cambiado de imagen base. Dos builds del mismo commit separadas por 48 horas pueden no ser el mismo software.
- El artefacto inmutable y el antipatrón de reconstruir por entorno
Un artefacto es la unidad desplegable que produce el pipeline: en Reservalia, la imagen reservalia/api:a3f9c21 y el paquete de ficheros estáticos de apps/web. Que sea inmutable significa que, una vez publicado con una etiqueta, ese contenido no cambia jamás. Si hay que corregir algo, se publica un artefacto nuevo con otra etiqueta.
flowchart LR
subgraph BIEN["Construir una vez"]
C1["commit a3f9c21"] --> B1["build"] --> A1["reservalia/api:a3f9c21"]
A1 --> D1[dev] --> S1[staging] --> P1[prod]
end
subgraph MAL["Reconstruir por entorno"]
C2["commit a3f9c21"] --> B2["build dev"] --> D2[dev]
C2 --> B3["build staging"] --> S2[staging]
C2 --> B4["build prod"] --> P2["prod ← ¿es lo que probaste?"]
end
Lo que se pierde al reconstruir por entorno:
| Se pierde | Consecuencia práctica |
|---|---|
| Trazabilidad | "Funciona en staging y falla en prod" pasa a ser irresoluble |
| Validez de las pruebas | Probaste el artefacto A y desplegaste el B |
| Tiempo | Tres builds en lugar de una |
| Rollback fiable | Volver atrás exige reconstruir, y quizá ya no salga igual |
Y el corolario incómodo: si el artefacto es el mismo en los tres entornos, no puede contener nada específico de un entorno. Ni la URL de la base de datos, ni la clave de la pasarela de pago, ni el nivel de log. Todo eso entra por variables de entorno o por un gestor de secretos en el momento de arrancar. Un artefacto que se construye con NODE_ENV=staging cocido dentro no es promocionable.
- Estrategias de versionado
Ponerle nombre al artefacto no es cosmético: es lo que permite hablar de él sin ambigüedad.
| Estrategia | Ejemplo | Ventaja | Inconveniente | Cuándo usarla |
|---|---|---|---|---|
| SemVer | 2.4.1 |
Comunica el impacto del cambio | Requiere decidir el número | Librerías y APIs públicas |
| SHA del commit | a3f9c21 |
Único y trazable al instante | No dice nada al humano | Servicios desplegados de forma continua |
git describe |
v2.4.0-13-ga3f9c21 |
Legible y trazable | Requiere etiquetas y fetch-depth: 0 |
Cuando quieres ambas cosas |
| Por fecha | 2026.03.02.1 |
Ordenable, intuitivo | No identifica el código | Releases periódicas |
| Incremental | build-1284 |
Simple | Se pierde al cambiar de herramienta | Sistemas antiguos |
Reservalia usa el SHA corto como etiqueta primaria: reservalia/api:a3f9c21. La razón es que es una plataforma SaaS con un único despliegue —no distribuye versiones a clientes— y lo que necesita es responder en un segundo a la pregunta "¿qué código está corriendo?". SemVer resolvería un problema que Reservalia no tiene.
Nada impide combinar estrategias: la misma imagen puede llevar varias etiquetas apuntando al mismo contenido.
reservalia/api:a3f9c21 # identidad: el commit exacto (nunca cambia)
reservalia/api:v2.4.0 # release legible para las personas
reservalia/api:main # puntero móvil al último main verdeSolo la primera es inmutable; las otras dos son punteros que pueden reasignarse.
- Por qué
latest es peligrosa
latest es peligrosalatest no es una versión: es una etiqueta móvil que apunta a lo último que alguien subió. Dentro de un pipeline provoca cuatro problemas concretos:
- No es reproducible.
docker pull reservalia/api:latesthoy y mañana pueden traer imágenes distintas. Si un contenedor se reinicia solo, puede levantarse con otra versión. - Rompe el rollback. "Vuelve a la versión anterior" no tiene respuesta:
latestno guarda historial. - Hace imposible el diagnóstico. Dos máquinas del mismo servicio pueden estar ejecutando código distinto y ambas reportar
latest. - Es una carrera. Dos pipelines simultáneos escriben la misma etiqueta y gana el último en terminar, que no tiene por qué ser el commit más reciente.
La regla: en un pipeline, cada despliegue referencia una etiqueta inmutable. Si quieres una etiqueta amable para tu portátil, adelante; para desplegar, el SHA.
- Versionado automático a partir de commits convencionales
Las convenciones de commit de la lección anterior habilitan algo muy práctico: deducir la versión siguiente leyendo el historial, sin que nadie decida nada a mano.
| Commits desde la última versión | Salto | De 2.4.1 a |
|---|---|---|
Solo fix:, chore:, docs: |
Parche | 2.4.2 |
Al menos un feat: |
Menor | 2.5.0 |
Alguno con BREAKING CHANGE: |
Mayor | 3.0.0 |
Herramientas como semantic-release o Changesets hacen tres cosas en un solo paso: calculan la versión, generan el changelog agrupando los commits por tipo, y crean la etiqueta de git y la release. El changelog resultante se escribe solo:
## 2.5.0 (2026-03-02)
### Funcionalidades
* **citas:** permitir reservas recurrentes semanales (a3f9c21)
### Correcciones
* **agenda:** no ofrecer huecos solapados con el descanso (b7e2d10)Reservalia lo adopta en modo mixto y por un motivo muy concreto: el SHA sigue siendo la identidad del artefacto —es lo que se despliega y lo que aparece en /version—, mientras que la versión SemVer y el changelog sirven para comunicarse con las personas: notas de release, avisos a los negocios y el mensaje de la incidencia cuando algo se rompe.
- Dónde se guardan los artefactos: registros y retención
No todos los artefactos son iguales ni deben vivir en el mismo sitio.
| Destino | Qué guarda | Vida útil | Uso en Reservalia |
|---|---|---|---|
actions/upload-artifact |
Ficheros del propio workflow | Días | Pasar dist/ entre jobs, informes de cobertura |
| Amazon ECR | Imágenes de contenedor | Meses o años | reservalia/api:a3f9c21, lo que se despliega |
| GitHub Packages | Imágenes y paquetes npm | Configurable | Alternativa cuando todo vive en GitHub |
| S3 | Ficheros estáticos | Años | El dist/ de apps/web servido por CloudFront |
Los artefactos efímeros del workflow resuelven el problema que vimos en la 02-01: los jobs no comparten disco. Si build genera algo que publicar necesita, hay que pasarlo explícitamente:
- uses: actions/upload-artifact@v4 # en el job build
with:
name: web-dist
path: apps/web/dist
retention-days: 7 # ← por defecto son 90
- uses: actions/download-artifact@v4 # en el job que lo consume
with: { name: web-dist, path: apps/web/dist }retention-days merece atención porque el almacenamiento se factura. Un informe de cobertura de 40 MB por cada ejecución, con 20 ejecuciones diarias y 90 días de retención, son unos 72 GB de basura acumulada. Política razonable en Reservalia: 7 días para informes y artefactos intermedios; para las imágenes de ECR, una regla de ciclo de vida que conserve las 30 últimas de main y borre las de ramas de PR a los 14 días. Lo que nunca se borra automáticamente es una imagen que esté desplegada en algún entorno.
- Promoción entre entornos
Promocionar es declarar que un artefacto ya validado avanza al siguiente entorno. No se reconstruye, no se recompila: se señala.
flowchart LR
M["merge en main<br/>commit a3f9c21"] --> B["build + publicar"]
B --> ECR["ECR<br/>reservalia/api:a3f9c21"]
ECR --> D["dev<br/>automático"]
D -- "pruebas humo OK" --> S["staging<br/>automático"]
S -- "validación + aprobación" --> P["prod"]
P --> T["etiqueta prod-2026-03-02<br/>sobre la MISMA imagen"]
Dos formas de registrar el avance, con implicaciones distintas:
- Etiquetas adicionales sobre el mismo digest. Al promocionar, se añade
stagingoprod-2026-03-02a la imagen existente. Es visible desde el propio registro y no copia bytes. - Metadatos externos. Una tabla o un fichero versionado que dice qué SHA está en cada entorno. Encaja mejor con GitOps y deja un historial auditable.
Lo que ambas comparten, y es lo esencial: el contenido no cambia. Un digest —sha256:4f3c…— identifica los bytes exactos, y ese digest es el mismo en dev, en staging y en prod. Las etiquetas son nombres; el digest es la identidad.
Cómo se ejecuta cada promoción (aprobaciones manuales, despliegue progresivo, rollback) es el contenido del módulo 3.
- Trazabilidad: saber qué está corriendo en producción
Son las 23:40, hay un incidente y la primera pregunta de Nuria es siempre la misma: "¿qué versión hay desplegada?". Debe poder responderse en menos de un minuto, desde fuera y sin acceso privilegiado. Dos mecanismos, complementarios.
Etiquetas OCI en la imagen. Metadatos estándar incrustados en la propia imagen:
ARG COMMIT_SHA
ARG FECHA_BUILD
LABEL org.opencontainers.image.revision="${COMMIT_SHA}" \
org.opencontainers.image.created="${FECHA_BUILD}" \
org.opencontainers.image.source="https://github.com/reservalia/reservalia" \
org.opencontainers.image.version="2.5.0"Se consultan sin arrancar el contenedor con docker inspect, y sobreviven aunque alguien renombre la etiqueta.
Un endpoint /version. La forma más rápida y la que no requiere acceso a la infraestructura:
// apps/api/src/rutas/version.ts
export const version = {
commit: process.env.COMMIT_SHA ?? 'desconocido', // a3f9c21
version: process.env.APP_VERSION ?? '0.0.0', // 2.5.0
construido:process.env.FECHA_BUILD ?? 'desconocido', // 2026-03-02T09:14:00Z
entorno: process.env.ENTORNO ?? 'desconocido', // prod
};
// GET /version → { "commit": "a3f9c21", "version": "2.5.0", ... }Los valores se inyectan en el docker build como ARG y se convierten en ENV de la imagen. Detalle de seguridad: /version no debe revelar rutas internas, dependencias ni configuración; el SHA y la fecha son suficientes. Y detalle práctico: este endpoint es lo que permite comprobar que un despliegue ha surtido efecto. Si tras desplegar b7e2d10 el /version sigue diciendo a3f9c21, el despliegue no ha llegado, por mucho que el pipeline esté verde.
- Firma y procedencia del artefacto
Que un artefacto sea inmutable no demuestra quién lo construyó ni a partir de qué. Un atacante con acceso al registro podría publicar una imagen con la etiqueta esperada.
Dos mecanismos responden a eso: la firma criptográfica del artefacto (con herramientas como Sigstore/cosign, que permiten verificar antes de desplegar que la imagen la firmó tu pipeline) y la procedencia o provenance, un documento verificable que declara qué commit, qué workflow y qué runner produjeron ese digest, en la línea del marco SLSA. Ambos se apoyan además en un SBOM, el inventario de todo lo que contiene la imagen.
Los tres pertenecen a la cadena de suministro de software y se tratan en la lección 04-03, Seguridad en CI/CD. Aquí basta con saber que existen y que el paso previo —artefactos inmutables e identificados por su digest— ya está dado.
- El job
publicar de Reservalia
publicar de Reservalia publicar:
name: Publicar artefacto
runs-on: ubuntu-22.04
needs: [calidad, test, build] # 1
if: github.ref == 'refs/heads/main' # 2
permissions:
id-token: write # 3
contents: read
steps:
- uses: actions/checkout@v4
- name: Calcular etiquetas
id: meta
run: | # 4
echo "sha_corto=$(git rev-parse --short=7 HEAD)" >> $GITHUB_OUTPUT
echo "fecha=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT
- name: Autenticarse en AWS
uses: aws-actions/configure-aws-credentials@v4 # 5
with:
role-to-assume: ${{ secrets.AWS_ROLE_CI }}
aws-region: eu-west-1
- uses: aws-actions/amazon-ecr-login@v2
id: ecr
- name: Construir y publicar la imagen
uses: docker/build-push-action@v5
with:
context: .
file: apps/api/Dockerfile
push: true # 6
tags: |
${{ steps.ecr.outputs.registry }}/reservalia/api:${{ steps.meta.outputs.sha_corto }}
${{ steps.ecr.outputs.registry }}/reservalia/api:main
build-args: |
COMMIT_SHA=${{ steps.meta.outputs.sha_corto }}
FECHA_BUILD=${{ steps.meta.outputs.fecha }}
cache-from: type=gha
cache-to: type=gha,mode=maxneeds: [calidad, test, build]hace que este job espere a que los tres estén verdes. Es la garantía de que no se publica nada sin verificar.if: github.ref == 'refs/heads/main': en los pull requests se construye para comprobar, pero no se publica. Publicar cada PR llenaría el registro de imágenes que nadie desplegará.permissions: id-token: writehabilita OIDC: el runner obtiene credenciales temporales de AWS asumiendo un rol, en lugar de guardar claves de acceso permanentes como secretos. Es la forma correcta de autenticarse contra la nube, y se detalla en la 04-03.$GITHUB_OUTPUTes el mecanismo para pasar valores entre steps del mismo job: lo que escribas ahí se lee luego comosteps.meta.outputs.sha_corto.role-to-assumereferencia el secretoAWS_ROLE_CI, que contiene el ARN del rol —no una credencial—.push: truecon dos etiquetas apuntando a la misma imagen: el SHA corto, inmutable y trazable, ymain, un puntero móvil cómodo para saber cuál es el último verde. Lo que se despliega es siempre la primera.
Con este job, el estado de Reservalia cambia de forma cualitativa: cada commit que entra en main deja un artefacto identificado, verificado y guardado, listo para desplegar.
Errores Comunes y Consejos
Error 1: reconstruir en cada entorno. El antipatrón central de la lección. Rompe la trazabilidad, invalida las pruebas y hace irreproducible el rollback.
Error 2: desplegar latest. No es reproducible, no permite volver atrás y hace imposible saber qué está corriendo. Usa siempre una etiqueta inmutable.
Error 3: meter configuración de entorno dentro del artefacto. Un artefacto con la URL de la base de datos de staging cocida dentro ya no se puede promocionar; habrá que reconstruirlo, y volvemos al error 1.
Error 4: publicar desde los pull requests. Llena el registro de imágenes que nadie usará y multiplica el coste de almacenamiento. Publica solo desde main o desde etiquetas.
Error 5: no fijar políticas de retención. El coste crece de forma silenciosa hasta que alguien mira la factura. Define retención desde el primer día, con la excepción de lo que esté desplegado.
Consejo 1: haz visible la versión. Un endpoint /version con el SHA es la mejor inversión de cinco minutos de todo el módulo; lo agradecerás a las 23:40 de un martes.
Consejo 2: piensa en digests, no en etiquetas. Las etiquetas son nombres reasignables; el digest sha256:… es la identidad real. Los sistemas de despliegue serios referencian digests.
Consejo 3: automatiza el changelog desde los commits. Si ya escribes commits convencionales, generarlo es gratis y el equipo deja de mantener a mano un fichero que nadie actualiza.
Ejercicios
Ejercicio 1
Un equipo tiene tres workflows: deploy-dev.yml, deploy-staging.yml y deploy-prod.yml. Los tres hacen checkout de main, npm ci, npm run build, construyen la imagen como app:latest y la despliegan. Enumera cuatro problemas y describe el pipeline correcto.
Ejercicio 2
Reservalia despliega reservalia/api:a3f9c21 en prod. A las dos horas aparece un error grave. Nuria quiere volver a la versión anterior, b7e2d10. Explica por qué esto es trivial con artefactos inmutables y qué habría que hacer si el equipo usara latest.
Ejercicio 3
Diseña el esquema de etiquetado de una imagen para un producto que sí se distribuye a clientes, con releases mensuales SemVer y correcciones urgentes entre ellas. Indica qué etiquetas pones, cuáles son inmutables y cuál usarías para desplegar.
Soluciones
Solución 1. Cuatro problemas: (1) se construye tres veces, así que lo desplegado en prod no es lo probado en staging —dos builds separadas por días pueden diferir—; (2) latest impide saber qué está corriendo y hace imposible el rollback; (3) hacer checkout de main en cada despliegue significa desplegar lo que haya ahora en la rama, no el commit validado, de modo que un merge intermedio se cuela sin querer en producción; (4) se triplica el tiempo y el coste de build sin obtener ninguna información adicional.
Pipeline correcto: un solo workflow de CI que, al entrar un commit en main, construye y publica reservalia/api:<sha> tras pasar calidad, test y build. Los tres despliegues son workflows separados que reciben como parámetro el SHA y se limitan a apuntar el entorno a esa imagen ya existente, promocionando el mismo digest de dev a staging y a prod.
Solución 2. Con artefactos inmutables, reservalia/api:b7e2d10 sigue existiendo en ECR sin modificar: el rollback consiste en decirle al servicio que use esa etiqueta y esperar a que arranquen las nuevas instancias. Son minutos, no requiere compilar nada y el resultado es exactamente el software que estuvo funcionando antes. Es justo el caso que reclamaba la frase de Nuria en el módulo 1 sobre deshacer un despliegue en cinco minutos.
Con latest, en cambio, no hay nada a lo que volver: la etiqueta apunta a la versión rota y la anterior no está identificada. Habría que averiguar qué commit era el bueno (sin registro fiable, probablemente mirando el historial de git a ojo), reconstruirlo —con el riesgo de que la nueva build no sea idéntica a la que funcionaba— y volver a publicar. En vez de minutos, media hora larga en plena incidencia, y con incertidumbre sobre el resultado.
Solución 3. Un esquema razonable:
| Etiqueta | ¿Inmutable? | Para qué sirve |
|---|---|---|
app:2.5.0 |
Sí | La release que se comunica al cliente |
app:a3f9c21 |
Sí | Identidad exacta del commit; para diagnóstico |
app:2.5 |
No | Puntero al último parche de esa menor |
app:2 |
No | Puntero a la última menor de esa mayor |
app:latest |
No | Comodidad para pruebas locales; jamás para desplegar |
Para desplegar se usa siempre una etiqueta inmutable, preferiblemente el SHA o, mejor aún, el digest sha256:…. Las etiquetas móviles 2.5 y 2 son un servicio a los clientes que quieren recibir parches automáticamente, y son una decisión de ellos, no tuya. Una corrección urgente sobre 2.5.0 se publica como 2.5.1 y reasigna los punteros 2.5, 2 y latest, sin tocar nunca las etiquetas inmutables anteriores.
Conclusión
Reservalia ya produce algo que se puede desplegar, y sabe exactamente qué es:
- Construir una vez, desplegar muchas veces. Un artefacto por commit, el mismo en
dev,stagingyprod; lo único que cambia entre entornos es la configuración inyectada desde fuera. Reconstruir por entorno rompe la trazabilidad y anula el valor de las pruebas. - Un artefacto inmutable no cambia nunca una vez publicado. Si hay que corregir, se publica otro con otra etiqueta.
- De las estrategias de versionado, Reservalia usa el SHA corto como identidad —lo que necesita un SaaS de despliegue continuo— y reserva SemVer para comunicarse con las personas.
latestno se despliega jamás: no es reproducible, impide el rollback y es una carrera entre pipelines. - Los commits convencionales permiten deducir la versión y generar el changelog automáticamente, sin que nadie mantenga un fichero a mano.
- Los artefactos viven en sitios distintos según su naturaleza —efímeros del workflow para pasar ficheros entre jobs, ECR para las imágenes desplegables, S3 para la web— y todos necesitan política de retención, porque el almacenamiento se factura.
- Promocionar es señalar, no reconstruir: el mismo digest avanza de entorno en entorno, registrado con etiquetas adicionales o con metadatos externos.
- La trazabilidad se resuelve con etiquetas OCI en la imagen y un endpoint
/versionque devuelve el commit; es también la forma de comprobar que un despliegue ha surtido efecto. - La firma y la procedencia del artefacto existen y son importantes; su desarrollo es la lección 04-03.
- El job
publicardepende decalidad,testybuild, solo se ejecuta enmain, se autentica en AWS por OIDC y sube a ECR la imagen etiquetada con el SHA y conmain.
Ya tenemos los cuatro jobs y un artefacto publicado. Falta la pieza que conecta todo esto con la forma real de trabajar del equipo. En la siguiente lección, Integración con Control de Versiones, veremos cómo el modelo de ramas condiciona la CI —comparando trunk-based development, GitHub Flow y Git Flow—, por qué las ramas de vida larga son incompatibles con la Integración Continua, cómo se configuran las reglas de protección de main, CODEOWNERS y la merge queue, cómo evitar ejecuciones inútiles con paths y concurrency, y qué efecto tiene cada estrategia de merge sobre la trazabilidad del artefacto y sobre el lead time de la 01-05. Y cerraremos el módulo con el ci.yml completo de Reservalia.
Curso de CI/CD: Integración y Despliegue Continuo
Módulo 1: Introducción a CI/CD
- Conceptos Básicos de CI/CD
- Beneficios de CI/CD
- Herramientas Populares de CI/CD
- El Proyecto del Curso: la Aplicación que Vamos a Automatizar
- Métricas DORA: Cómo se Mide la Entrega de Software
Módulo 2: Integración Continua (CI)
- Introducción a la Integración Continua
- Configuración de un Entorno de CI
- Automatización de la Construcción
- Pruebas Automatizadas
- Calidad de Código y Análisis Estático
- Artefactos, Versionado y Promoción
- Integración con Control de Versiones
Módulo 3: Despliegue Continuo (CD)
- Introducción al Despliegue Continuo
- Automatización del Despliegue
- Infraestructura como Código y Entornos Reproducibles
- Estrategias de Despliegue
- Feature Flags, Rollback y Recuperación ante Fallos
- Monitoreo y Retroalimentación
Módulo 4: Prácticas Avanzadas de CI/CD
- Pipelines de CI/CD
- Gestión de Dependencias
- Seguridad en CI/CD
- Escalabilidad y Rendimiento
- Pipeline as Code: Plantillas, Reutilización y Pruebas del Pipeline
- Bases de Datos en el Pipeline: Migraciones Seguras
Módulo 5: Implementación de CI/CD en Proyectos Reales
- Caso de Estudio: Proyecto Web
- Caso de Estudio: Aplicación Móvil
- Caso de Estudio: Microservicios
- Caso de Estudio: Modernizar un Proyecto Legacy
Módulo 6: Herramientas y Tecnologías
- Jenkins
- GitLab CI/CD
- CircleCI
- Travis CI
- Docker y Kubernetes
- GitHub Actions a Fondo
- Comparativa y Criterios para Elegir Herramienta
Módulo 7: Ejercicios Prácticos
- Ejercicio 1: Configuración de un Pipeline Básico
- Ejercicio 2: Integración de Pruebas Automatizadas
- Ejercicio 3: Despliegue en un Entorno de Producción
- Ejercicio 4: Monitoreo y Retroalimentación
- Ejercicio 5: Endurecer el Pipeline con Seguridad y Secretos
- Proyecto Final: Pipeline Completo de Extremo a Extremo
