Automatización y soporte en CI/CD
Hay una parte del trabajo con pipelines que casi nadie documenta y que termina siendo la mitad de la rutina: no crear el pipeline, sino sostenerlo cuando falla a las siete de la mañana un viernes. Un pipeline verde es el resultado visible. El trabajo real está en saber leer una ejecución roja, encontrar la etapa exacta que se rompió y decidir si el problema es código, configuración o infraestructura.
Por eso conviene separar dos cosas que suelen ir juntas en la misma conversación. Automatizar es escribir la definición de lo que tiene que pasar. Dar soporte es entender por qué no pasó. Las dos piden el mismo conocimiento del flujo completo, y solo la primera se enseña en la documentación oficial.
Qué es CI/CD y por qué existe
CI/CD son dos palabras para un mecanismo con dos partes. Integración continua es fusionar cambios con frecuencia y verificar que el conjunto sigue funcionando. Entrega continua es dejar ese resultado listo para llegar a producción de forma repetible y con menos pasos manuales.
El problema que viene resuelto es sencillo: cuando la verificación depende de que alguien se acuerde de hacerla, hay días en que no se hace. Y cuando alguien la hace a mano, la hace distinta cada vez, así que el resultado del equipo deja de ser predecible.
Las tareas que se automatizan suelen ser las mismas en todos los proyectos:
- Obtener el código desde el repositorio.
- Instalar las dependencias.
- Compilar o construir la aplicación.
- Ejecutar las pruebas.
- Ejecutar validaciones adicionales.
- Generar el artefacto que se va a desplegar.
- Desplegar.
La automatización no reemplaza ninguna de esas tareas. Lo que hace es ejecutarlas siempre en el mismo orden y, sobre todo, dejar constancia de qué comando falló y con qué mensaje.
Jenkins
Jenkins es la herramienta más veterana de las tres y también la más distinta. No viene con un formato único de configuración: su unidad es el pipeline, que se escribe en su propio lenguaje y permite encadenar etapas.
Un pipeline básico tiene esta forma:
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'npm ci'
sh 'npm run build'
}
}
stage('Pruebas') {
steps {
sh 'npm test'
}
}
}
}
Lo que hace característico de Jenkins es la extensibilidad por plugins. Cada plugin agrega una funcionalidad concreta: integración con un sistema de control de versiones, con Docker, con Slack, con un proveedor de nube o con un analizador de código. Esa capacidad es su ventaja y también su costo, porque cada plugin es una dependencia más que puede entrar en conflicto con otra.
En soporte, los puntos donde más se atasca Jenkins suelen ser estos:
- Ejecuciones que fallan sin que el código haya cambiado.
- Logs repartidos en varias etapas y hay que reconstruir la secuencia completa.
- Configuración de credenciales que caduca sin avisar.
- Agentes o nodos no disponibles, con los jobs en cola indefinidamente.
- Errores de resolución de dependencias.
- Variables de entorno definidas en un lugar y ausentes en otro.
- Jobs duplicados que nadie sabe cuál es el bueno.
Un pipeline puede, por ejemplo, comenzar obteniendo el código, instalar dependencias, correr las pruebas y generar el artefacto de despliegue. La estructura mental es siempre esa; lo que cambia es cómo se declara.
GitHub Actions
GitHub Actions vive dentro del propio repositorio. Los workflows son archivos YAML guardados en .github/workflows, versionados junto con el código. Eso significa que el historial de Git ya contiene la historia de cómo cambió la automatización.
name: build-y-pruebas
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Instalar dependencias
run: npm ci
- name: Ejecutar pruebas
run: npm test
El flujo completo se puede leer como una línea:
Repositorio → Build → Pruebas → Validaciones → Despliegue
La ventaja principal es de configuración, no de potencia: el archivo está en el repositorio, se revisa como parte del pull request y cualquiera que tenga acceso puede leer exactamente qué corre y en qué orden.
Para dar soporte hay que interpretar el log de cada ejecución, ubicar la etapa del problema y revisar elementos que se encapsulan bastante bien en la interfaz:
| Elemento | Qué revisar |
|---|---|
| Secrets | Que existan y que no hayan caducado |
| Permisos | GITHUB_TOKEN y permisos del repositorio |
| Variables | Que las del entorno estén definidas en el ámbito correcto |
| Dependencias | Versiones fijadas o rangos que se movieron |
| Workflow | Reglas de on, ramas y condiciones |
Un detalle que genera muchas confusiones: un workflow definido en un pull request no tiene acceso a los secrets. Si el pipeline pasa en la rama y falla al integrar, esa es la primera diferencia que hay que revisar.
Bitbucket Pipelines
Bitbucket Pipelines es la alternativa integrada con Bitbucket y funciona con el mismo esquema declarativo en YAML. La configuración vive en el repositorio, en el archivo bitbucket-pipelines.yml.
image: node:22
pipelines:
default:
- step:
name: Pruebas
caches: [node]
script:
- npm ci
- npm test
branches:
main:
- step:
name: Despliegue
script:
- npm run build
Lo interesante de este formato es la sección branches. Permite que el mismo archivo defina comportamientos distintos según la rama o según un evento concreto, de manera que la rama de desarrollo no ejecute el despliegue y producción sí.
Además hay un detalle de recursos que conviene conocer antes de diseñar un pipeline lento: los steps pueden ejecutarse en paralelo declarando varios en el mismo nivel. Con dependencias entre ellos, cada paso declara de qué otros depende, y la plataforma calcula el orden.
Las tres, en una tabla
| Jenkins | GitHub Actions | Bitbucket Pipelines | |
|---|---|---|---|
| Dónde vive la config | En el servidor, en el job | En el repositorio | En el repositorio |
| Formato | Groovy o interfaz web | YAML | YAML |
| Extensibilidad | Plugins | Mercado de acciones | Integraciones nativas |
| Ejecución | Agente o nodo propio | Runner | Runner de Bitbucket |
| Ideal cuando | Necesitas control del entorno | El equipo ya vive en GitHub | El equipo ya vive en Bitbucket |
La conclusión honesta es que la elección importa menos de lo que parece. Las tres resuelven la misma necesidad con nombres distintos para las mismas piezas. Lo que sí cambia de verdad es quién opera la infraestructura donde corre el runner.
El soporte también forma parte del proceso
Un pipeline no se mantiene solo. Una parte importante del trabajo es dar soporte cuando la automatización falla, y esa parte no aparece en ninguna guía de inicio rápido.
El análisis de una ejecución fallida se puede ordenar en cuatro pasos.
1. Identificar el punto de fallo. La primera pregunta nunca es por qué falló, sino dónde. Instalación, compilación, pruebas, artefacto o despliegue. Cada etapa tiene causas típicas distintas, y adivinar antes de ubicar el punto desperdicia tiempo.
2. Revisar los logs. El log dice el comando que falló y el mensaje que emitió la herramienta o la aplicación. Muchas veces el mensaje de la última línea es una consecuencia y el problema real está unas líneas más arriba.
3. Determinar el origen. El fallo puede estar en el código, en las dependencias, en la configuración, en las credenciales, en las variables de entorno, en la infraestructura o en un servicio externo.
4. Aplicar y validar. Después del cambio hay que volver a ejecutar y comprobar que el pipeline quedó verde. Sin esa segunda ejecución, el arreglo no está verificado, solo aplicado.
No todas las fallas están en el código
Esta es la idea que más valor tiene para quien empieza. La suposición común es que una ejecución roja significa que hay un error en la aplicación. La realidad es bastante más amplia:
| Síntoma | Causa habitual |
|---|---|
| Falla la instalación de dependencias | Registro de paquetes inalcanzable o versiones que se movieron |
| Falla al publicar el artefacto | Credencial vencida o permisos revocados |
| Falla un comando del runner | Herramienta no instalada en la imagen |
| Falla una etapa del despliegue | Variable de entorno ausente o mal nombrada |
| Falla solo en una rama | Configuración distinta por rama o por evento |
Un par de ejemplos reales para reconocerlos rápido:
npm ERR! code ETIMEDOUT
npm ERR! network request to https://registry.npmjs.org/... failed
error: Input required and not supplied: version
El primero no dice nada del código. El segundo es un parámetro obligatorio que el workflow esperaba y nadie definió. En los dos casos, revisar el repositorio un rato más no mejora nada.
Los errores que se repiten
Con el tiempo la misma lista aparece una y otra vez en equipos distintos. Vale la pena tenerla presente porque reduce el tiempo de diagnóstico:
- Credenciales vencidas. Una clave de despliegue deja de funcionar y el error se reporta como
deniedounauthorized, sin mencionar la fecha. - Herramientas ausentes en el runner. El pipeline funciona en la máquina de alguien y falla en el agente porque ahí no está instalado el compilador o el cliente de Docker.
- Caché corrupto o desactualizado. Una caché guardada con una versión distinta de Node o de una dependencia nativa produce fallos que parecen del código.
- Rangos de versión abiertos. Un
^1.2.3permite que la instalación de hoy resuelva a una versión que la de ayer no resolvió. - Secretos expuestos en los logs. Un comando de depuración imprime la variable completa y la deja en el historial de la ejecución.
- Zonas horarias y permisos de archivo en Linux. Un caso clásico: en Windows no hay problema y en Linux el proceso no tiene permiso de escritura.
CI/CD como parte del desarrollo
Las tres herramientas usan conceptos distintos y se configuran de forma distinta, pero comparten la misma idea: automatizar el camino que hay entre un cambio en el código y su validación o su entrega.
Conocerlas no es solo saber escribir un archivo YAML. Es entender cómo encajan el repositorio, el código fuente, las dependencias, las pruebas, los artefactos, las variables de entorno y la infraestructura donde corre todo eso. Ese es el mapa que permite leer un log y entenderlo, y es la diferencia entre copiar un ejemplo y crear un pipeline propio.
Por eso aprender a configurar y a dar soporte en CI/CD es una habilidad completa. La automatización es la parte visible; el diagnóstico del fallo es la que se usa todos los días.
© Arturo Juárez Monroy