Módulo 7: Gitops Beyond Terraform
2. GitLab CI, CircleCI y Jenkins por contraste
Descripción
En el Módulo 1, lección 5, conociste GitLab CI, CircleCI y Jenkins en una tabla conceptual —quién los mantiene, dónde vive la config, el modelo de hosting— y la evidencia de mercado que motivó, con honestidad, la elección de GitHub Actions para esta guía. Esta lección va un paso más allá: te muestra el mismo pipeline de Andes Cargo —fmt/validate/plan en cada cambio, apply en cada fusión a main— escrito en la sintaxis real de las otras tres herramientas, verificada contra su documentación oficial. El objetivo no es que aprendas a escribir .gitlab-ci.yml o un Jenkinsfile de memoria —eso es contenido de otra guía, si alguna vez lo necesitas—, es que confirmes con tus propios ojos algo que hasta ahora solo leíste en prosa: el patrón es idéntico en las cuatro herramientas. Cambia el nombre de las llaves del YAML, cambia si el archivo es YAML o Groovy, cambia cómo se filtra por rama — pero la forma del pipeline (verificar, planificar, revisar, aplicar) es la misma.
Conexión con el módulo
Esta es la lección que abre el módulo, inmediatamente después del mapa de la lección 1. Es, a propósito, la comparación "más cercana a casa" de las cuatro que trae este módulo: GitLab CI, CircleCI y Jenkins resuelven el mismo problema que ci.yml y apply.yml —CI/CD sobre un repositorio de código, con revisión antes de aplicar—, solo que con otra herramienta. Las lecciones 3 a 6 se alejan progresivamente de ese terreno conocido, hacia problemas que Terraform ni GitHub Actions resuelven en esta guía. Ningún YAML de esta lección corre — no hay act para GitLab CI, CircleCI ni Jenkins—, así que cada bloque está marcado explícitamente como sintaxis nombrada, no ejecutada.
La evidencia de mercado, retomada completa
Antes de entrar a la sintaxis, vale la pena traer de vuelta, sin editar, la cita exacta que ya viste en el Módulo 1, lección 5 — porque esta lección es, literalmente, la que esa lección prometió:
"CookUnity 'GitHub and GitHub Actions'; EarnIn 'GitHub Actions, Argo CD'; frente al stack español real: IRIUM 'Git, Jenkins, Artifactory, SonarQube', Apptiva 'Jenkins administration expertise', MediaStream 'Jenkins, TeamCity' — Jenkins aparece en 5 de 13, casi el doble que GitHub Actions (~3)."
Jenkins aparece en 5 de 13 ofertas del stack español relevado por la auditoría de este ecosistema (src/paths/aws-cloud-ecosystem/VALIDACION.md, julio 2026) — casi el doble que GitHub Actions. Esta lección no repite esa evidencia para insistir en el mismo punto dos veces: la trae de vuelta porque es, exactamente, la razón por la que "aprender GitHub Actions" no puede ser el final del camino si tu objetivo es estar preparado para el mercado real de trabajo en español. Lo que vas a ver abajo —el mismo pipeline, en la sintaxis de Jenkins— es la respuesta concreta a esa evidencia: no un curso completo de Jenkins, pero sí la confirmación de que el conocimiento que ya tienes se traduce, con esfuerzo bajo, a la herramienta que más se pide.
Analogía: el mismo trámite, cuatro oficinas distintas
Imagina que necesitas renovar un documento de identidad, y existen cuatro oficinas distintas donde puedes hacerlo — cada una con su propio formulario, su propio número de ventanilla, su propio horario. El formulario de la oficina A tiene una casilla llamada "Fecha de nacimiento"; el de la oficina B la llama "Nacido el"; el de la oficina C te pide el dato en un campo separado por día/mes/año. Los formularios se ven distintos, el papel es de otro color, hasta el trámite en sí varía un poco en el orden de los pasos — pero las cuatro oficinas te piden, en el fondo, la misma información, para el mismo propósito: confirmar quién eres y actualizar un registro. Alguien que ya hizo el trámite una vez, en cualquiera de las cuatro oficinas, sabe qué información va a necesitar reunir antes de entrar — el resto es aprender dónde está cada casilla en el formulario específico.
Eso es, con precisión, lo que estás por ver: cuatro "formularios" (YAML de GitHub Actions, YAML de GitLab CI, YAML de CircleCI, Groovy de Jenkins) para el mismo trámite (verificar un cambio de infraestructura, mostrarlo para revisión, aplicarlo automáticamente cuando se aprueba).
El pipeline de referencia: ci.yml de Andes Cargo, en cuatro sintaxis
Vas a ver el mismo pipeline conceptual —los cinco pasos de ci.yml (Módulo 3) simplificados a tres para que la comparación sea legible: fmt (verificar estilo), plan (calcular el cambio), y el disparo de apply en la fusión a main— traducido a cada herramienta. Ninguno de los tres bloques que siguen corrió — son sintaxis real, verificada contra la documentación oficial de cada herramienta, mostrada para que reconozcas la forma, no para que la copies y esperes que funcione sin ajustes.
GitHub Actions (la que ya conoces, como punto de partida)
name: ci
on:
pull_request:
branches: [main]
jobs:
terraform-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: terraform fmt -check -recursive
- run: terraform plan -input=false
GitLab CI — .gitlab-ci.yml (nombrado, no ejecutado)
stages:
- verify
- plan
terraform-fmt:
stage: verify
script:
- terraform fmt -check -recursive
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
terraform-plan:
stage: plan
script:
- terraform plan -input=false
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
La estructura vive en el mismo repositorio, igual que en GitHub Actions — la diferencia visible más grande es stages/stage (GitLab CI agrupa jobs en fases explícitamente nombradas y ordenadas) contra jobs (GitHub Actions ordena con needs, sin una lista central de fases), y rules/if en vez de on/pull_request para decidir cuándo corre cada job. $CI_PIPELINE_SOURCE es una variable predefinida de GitLab CI —el equivalente al github.event_name que ya usaste en el Módulo 2— que te dice qué disparó esta corrida.
CircleCI — .circleci/config.yml (nombrado, no ejecutado)
version: 2.1
jobs:
terraform-checks:
docker:
- image: hashicorp/terraform:1.15
steps:
- checkout
- run: terraform fmt -check -recursive
- run: terraform plan -input=false
workflows:
ci:
jobs:
- terraform-checks:
filters:
branches:
ignore: main
CircleCI separa jobs (qué trabajo existe) de workflows (cuándo y en qué orden corre ese trabajo) de forma más explícita que GitHub Actions, donde ambas cosas conviven en el mismo archivo bajo jobs:. docker: con una imagen específica reemplaza al runs-on: ubuntu-latest + uses: hashicorp/setup-terraform@v3 que usaste en el Módulo 3 — en vez de una Action que instala Terraform sobre una imagen genérica, CircleCI corre el job directamente dentro de una imagen que ya trae Terraform instalado. checkout es un step reservado, igual de directo que actions/checkout@v4, pero sin necesidad de referenciar una Action externa por versión.
Jenkins — Jenkinsfile (nombrado, no ejecutado)
pipeline {
agent any
stages {
stage('Terraform fmt') {
when { changeRequest() }
steps {
sh 'terraform fmt -check -recursive'
}
}
stage('Terraform plan') {
when { changeRequest() }
steps {
sh 'terraform plan -input=false'
}
}
}
}
Esta es la diferencia visible más grande de las cuatro: no es YAML, es Groovy (un lenguaje de programación real, con su propia sintaxis de bloques { }), lo que le da a Jenkins una expresividad mayor —puedes escribir lógica condicional compleja, funciones, variables— a costa de una curva de aprendizaje más empinada que YAML declarativo. pipeline { agent any stages { ... } } es la estructura mínima obligatoria de cualquier pipeline declarativo de Jenkins (existe también un modo "scripted", más antiguo y más libre, fuera del alcance de esta lección). when { changeRequest() } es el equivalente de Jenkins a "correr solo en un Pull Request" — el nombre viene de que Jenkins, al integrarse con GitHub o GitLab, llama change request al equivalente genérico de un PR/MR.
Lo que NO cambia entre las cuatro (la tabla que importa)
| Concepto | GitHub Actions | GitLab CI | CircleCI | Jenkins |
|---|---|---|---|---|
| Archivo de configuración | .github/workflows/*.yml | .gitlab-ci.yml | .circleci/config.yml | Jenkinsfile |
| Lenguaje | YAML | YAML | YAML | Groovy (DSL propio) |
| Agrupar pasos | jobs → steps | stages → jobs por stage | jobs → steps, orquestado en workflows | stages → stage → steps |
| Condicionar por evento/rama | on: + branches: | rules: + if: | filters: dentro de workflows | when { } dentro de un stage |
| Traer el código al runner | uses: actions/checkout@v4 | Automático (implícito en cada job) | checkout (step reservado) | Automático si el Jenkinsfile vive en el repo (Declarative: Checkout SCM) |
| Instalar una herramienta | uses: hashicorp/setup-terraform@v3 | image: con la herramienta preinstalada, o before_script | docker: con una imagen que la incluya | sh 'apt-get install ...' o un tool preconfigurado en el servidor |
Cuatro filas de esa tabla dicen, en el fondo, lo mismo con otro nombre: "¿qué trabajo hay que hacer, en qué orden, y bajo qué condición corre". Ninguna herramienta de las cuatro inventó un concepto que las otras tres no tengan — lo que varía es la palabra clave exacta y si esa palabra vive en YAML declarativo o en un lenguaje de programación completo (el caso de Jenkins).
apply en la fusión: el mismo disparador, cuatro formas de nombrarlo
El patrón de apply.yml (Módulo 5) —correr terraform apply automáticamente cuando un cambio llega a main— también se traduce directo:
| Herramienta | Cómo se dispara apply en la fusión (nombrado) |
|---|---|
| GitHub Actions | on: push: branches: [main] en un workflow separado (apply.yml), exactamente lo que construiste en el Módulo 5 |
| GitLab CI | Un job en el mismo .gitlab-ci.yml, con rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' |
| CircleCI | Un job en el mismo workflows, con filters: branches: only: main |
| Jenkins | Un stage con when { branch 'main' } en el mismo Jenkinsfile, o un job de Jenkins separado disparado por un webhook de fusión |
Fíjate en un detalle de diseño real: GitHub Actions y GitLab CI/CircleCI/Jenkins difieren en si separan CI y CD en archivos distintos (el enfoque de esta guía, con ci.yml y apply.yml como dos workflows independientes) o los combinan en un único archivo con condiciones distintas por job (el patrón más común en GitLab CI, CircleCI y Jenkins, donde stages/workflows ya agrupan todo el ciclo de vida). Ninguno de los dos enfoques es "más correcto" — esta guía eligió separar los archivos porque hace más simple razonar sobre "qué dispara qué" mientras aprendes (Módulo 5, lección 2), pero un pipeline real de GitLab CI que combina todo en un solo archivo, con rules bien pensadas, logra exactamente el mismo resultado de seguridad y orden.
Errores comunes
Copiar uno de los YAML de esta lección esperando que funcione tal cual (el más directo). Qué pasa: alguien copia el .gitlab-ci.yml o .circleci/config.yml de esta lección a un proyecto real y se sorprende de que falle. Por qué pasa: el resto de esta guía te acostumbró a que cada bloque de código "corrió de verdad" — esta lección rompe ese patrón a propósito, y lo dice, pero es fácil pasarlo por alto. Cómo detectarlo: si intentaste correr alguno de los tres YAML de GitLab CI/CircleCI/Jenkins de esta lección fuera de esta lección. Cómo corregirlo: relee el encabezado de cada bloque — dice explícitamente "nombrado, no ejecutado". Son ejemplos simplificados para mostrar la forma del pipeline, no artefactos probados como el ci.yml real del Módulo 3.
Creer que Jenkins es "peor" por usar Groovy en vez de YAML (de juicio de valor). Qué pasa: alguien, acostumbrado a YAML declarativo simple, ve el Jenkinsfile con sintaxis de { } y concluye que Jenkins es una herramienta más complicada o anticuada sin necesidad. Por qué pasa: YAML se siente, a primera vista, más simple de leer que un lenguaje de programación completo. Cómo detectarlo: si tu conclusión de esta lección es "Jenkins es peor" en vez de "Jenkins es más expresivo, a costa de una curva de aprendizaje distinta". Cómo corregirlo: Groovy le da a Jenkins una capacidad que YAML declarativo puro no tiene fácilmente — lógica condicional compleja, funciones reutilizables, bucles — precisamente la flexibilidad que explica por qué equipos con pipelines muy grandes y muy antiguos (recordá la evidencia de mercado: Jenkins domina el stack español) siguen invirtiendo en mantenerlo, en vez de migrar.
Pensar que "el patrón transfiere" significa "no hace falta aprender la sintaxis nueva" (de sobreconfianza). Qué pasa: alguien, después de ver esta comparación, asume que podría escribir un Jenkinsfile de producción sin estudiar la documentación de Jenkins primero, solo porque "ya entiende el patrón". Por qué pasa: reconocer la forma general de un pipeline (verificar, planificar, revisar, aplicar) se siente parecido a saber la sintaxis exacta de una herramienta específica. Cómo detectarlo: si crees que esta lección te preparó para escribir Jenkins/GitLab CI/CircleCI de producción, en vez de para reconocer su forma y saber qué buscar primero en su documentación. Cómo corregirlo: lo que transfiere es el vocabulario mental —"esto es el paso de verificación", "esto es la condición de rama", "esto es donde se instala la herramienta"— no la sintaxis exacta de cada llave. Aprender la sintaxis real de cualquiera de estas tres herramientas seguiría siendo trabajo real, solo que con una curva mucho más corta que empezar de cero.
Ejercicios
Ejercicio 1 — Traduce una llave de GitHub Actions a las otras tres. Sin mirar las tablas de esta lección, escribe el equivalente de on: pull_request: branches: [main] en GitLab CI, CircleCI y Jenkins — no necesita ser sintácticamente perfecto, pero sí debe usar el nombre de concepto correcto de cada herramienta.
Ver solución
GitLab CI: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' (dentro de un job). CircleCI: filters: dentro del bloque workflows, típicamente sobre la rama de destino en vez del evento (CircleCI no distingue "PR" como evento de la misma forma que GitHub — corre sobre cada push a una rama que abrió un PR). Jenkins: when { changeRequest() } dentro de un stage. Si escribiste el nombre correcto de la llave en las tres herramientas (rules, filters, when), tienes clara la traducción del concepto, aunque la sintaxis exacta de cada una tenga sus propios detalles que solo su documentación oficial cubre a fondo.
Ejercicio 2 — Identifica la diferencia estructural más grande de las cuatro. De las cuatro herramientas de esta lección, ¿cuál tiene la diferencia más fundamental respecto a las otras tres, y por qué?
Ver solución
Jenkins, por dos razones combinadas: usa Groovy, un lenguaje de programación completo, en vez de YAML declarativo (que comparten GitHub Actions, GitLab CI y CircleCI); y, en la inmensa mayoría de los casos reales, corre sobre un servidor auto-hospedado que la propia empresa mantiene, mientras que las otras tres son SaaS integrado (GitHub Actions, GitLab CI) o SaaS independiente (CircleCI) con runners hospedados por defecto. Estas dos diferencias explican, en conjunto, por qué Jenkins tiene la curva de aprendizaje más alta de las cuatro, y también por qué sigue siendo, según la evidencia de esta lección, la herramienta con mayor presencia en el stack español relevado — la inversión ya hecha en infraestructura y conocimiento de Jenkins tiene un costo real de reemplazar.
Ejercicio 3 — Explica el patrón a un reclutador técnico. Un reclutador que revisa tu portfolio ve que solo tienes experiencia con GitHub Actions y te pregunta: "¿podrías trabajar con Jenkins si el puesto lo requiere?". Responde en dos o tres frases, usando la evidencia y el contenido concreto de esta lección — no una afirmación vaga de "aprendo rápido".
Ver solución
Una respuesta completa suena, más o menos, así: "El pipeline que construí con GitHub Actions sigue el mismo patrón que casi cualquier herramienta de CI/CD usa: verificar el cambio, calcular qué modificaría, dejarlo para revisión, y aplicarlo automáticamente cuando se aprueba — ese patrón es el mismo en Jenkins, solo cambia la sintaxis: Groovy en vez de YAML, stage/when en vez de jobs/on. Sé, en concreto, qué buscar en la documentación de Jenkins para escribir ese mismo pipeline —el pipeline block, stages, when { branch }— aunque todavía no tenga horas de práctica real con la herramienta." La clave de una buena respuesta: nombrar el patrón compartido con precisión, sin exagerar experiencia que no tienes con la sintaxis específica de Jenkins.
Resumen y siguiente paso
En esta lección viste el mismo pipeline de Andes Cargo —verificar, planificar, aplicar en la fusión— traducido a la sintaxis real de GitLab CI, CircleCI y Jenkins, verificada contra la documentación oficial de cada una, aunque ninguna corrió de verdad. Confirmaste, con ejemplos concretos en vez de solo en prosa, que el patrón transfiere: cambia el nombre de la llave (on/rules/filters/when), cambia el lenguaje (YAML contra Groovy en Jenkins), pero la forma del pipeline es la misma en las cuatro. También retomaste la evidencia de mercado completa del Módulo 1 —Jenkins en 5 de 13 ofertas del stack español— ahora con el contraste de sintaxis concreto que esa lección prometió.
Antes de avanzar deberías poder: escribir de memoria el equivalente conceptual de on/jobs/steps en las otras tres herramientas; explicar por qué Jenkins es la más distinta de las cuatro; y responder, con evidencia concreta, si tu conocimiento de GitHub Actions transfiere a un puesto que pide Jenkins.
La lección 3 se aleja del terreno conocido: vas a conocer ArgoCD y Flux, dos herramientas que no resuelven "el mismo problema con otra sintaxis" — resuelven GitOps con un mecanismo genuinamente distinto, diseñado específicamente para Kubernetes.
Recursos
- GitLab Docs — CI/CD YAML syntax reference — la referencia completa de
stages,rules,scripty el resto de las llaves usadas en esta lección. - CircleCI Docs — Configuration reference — la referencia completa de
jobs,workflows,filtersydockerusadas en esta lección. - Jenkins — Pipeline syntax — la referencia completa del pipeline declarativo (
pipeline,stages,when) usada en esta lección. - Jenkins — Documentation — documentación general de Jenkins, la herramienta con mayor presencia en el stack español según la evidencia de esta lección.
src/paths/aws-cloud-ecosystem/VALIDACION.md(NIEVA, auditoría de mercado, jul-2026) — fuente exacta de la cita de Jenkins vs. GitHub Actions retomada al inicio de esta lección.