Automatización y soporte en CI/CD

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:

  1. Obtener el código desde el repositorio.
  2. Instalar las dependencias.
  3. Compilar o construir la aplicación.
  4. Ejecutar las pruebas.
  5. Ejecutar validaciones adicionales.
  6. Generar el artefacto que se va a desplegar.
  7. 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:

ElementoQué revisar
SecretsQue existan y que no hayan caducado
PermisosGITHUB_TOKEN y permisos del repositorio
VariablesQue las del entorno estén definidas en el ámbito correcto
DependenciasVersiones fijadas o rangos que se movieron
WorkflowReglas 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

JenkinsGitHub ActionsBitbucket Pipelines
Dónde vive la configEn el servidor, en el jobEn el repositorioEn el repositorio
FormatoGroovy o interfaz webYAMLYAML
ExtensibilidadPluginsMercado de accionesIntegraciones nativas
EjecuciónAgente o nodo propioRunnerRunner de Bitbucket
Ideal cuandoNecesitas control del entornoEl equipo ya vive en GitHubEl 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íntomaCausa habitual
Falla la instalación de dependenciasRegistro de paquetes inalcanzable o versiones que se movieron
Falla al publicar el artefactoCredencial vencida o permisos revocados
Falla un comando del runnerHerramienta no instalada en la imagen
Falla una etapa del despliegueVariable de entorno ausente o mal nombrada
Falla solo en una ramaConfiguració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 denied o unauthorized, 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.3 permite 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