La Integración Continua (CI) y el Despliegue Continuo (CD) se han convertido en algo casi omnipresente en el desarrollo de software. En WATA Factory, hemos configurado casi todos los proyectos para usar al menos algún tipo de mecanismo automatizado de integración y despliegue. A medida que estas prácticas evolucionan, entender cómo estructurar y automatizar tus flujos de trabajo de manera efectiva se vuelve cada vez más importante.
En este artículo, veremos todo lo que necesitas saber antes de configurar CI/CD con Bitbucket Pipelines: aclararemos la terminología básica, explicaremos cómo está estructurado Pipelines y mostraremos ejemplos de configuración tanto simples como más avanzados que podrás aplicar a tus propios proyectos. En resumen, este artículo te dará una visión práctica y fundamentada de cómo funciona Bitbucket Pipelines y lo que puedes conseguir con él.
Pero antes, aclaremos parte de la terminología.
¿Qué es CI?
CI (Continuous Integration) se refiere a la práctica de compilar automáticamente, ejecutar pruebas y análisis estático de código, así como (posiblemente) fusionar el código.
La palabra clave aquí es “automáticamente”. Ejecutar todos los pasos para compilar y probar suele llevar cierto tiempo, y tener que ejecutar estos pasos repetitivos (o al menos una parte de ellos) manualmente no solo es ineficiente, sino que también introduce otra posible fuente de errores humanos (porque los humanos no somos muy buenos haciendo tareas tediosas y repetitivas).
En su lugar, configuras un proceso (normalmente llamado “pipeline”) que se ejecuta automáticamente cada vez que haces push de tu código al repositorio. El desarrollador recibe una notificación si alguno de los pasos falla y puede entonces solucionar el problema lo antes posible.
También puedes configurar diferentes pipelines para diferentes ramas. Por ejemplo, para tu rama feature puede que no quieras ejecutar siempre todas las pruebas de integración o de front-end end-to-end que consumen mucho tiempo con cada push, porque en un proyecto grande estas pruebas pueden tardar horas en ejecutarse. Para evitar eso, podrías configurar el sistema de forma que los pasos de pruebas más costosos solo se ejecuten cuando algo se fusiona en la rama master/release (dependiendo de cómo esté definido el modelo de ramas del proyecto).
En general, la salida de la fase de CI suele ser algún tipo de artefacto que se puede desplegar en un entorno (como un archivo .jar o .war o una imagen Docker).
¿Qué es CD?
CD significa Continuous Delivery o Continuous Deployment, y puede considerarse una extensión de CI. En términos generales, es lo que ocurre con el código después de que la fase de CI (compilación y pruebas) se haya completado correctamente, aunque no siempre es posible trazar una línea clara entre ambas (especialmente porque ambos procesos suelen definirse en el mismo sitio).
Tu pipeline de CD normalmente toma el artefacto que se ha creado en el paso de CI y lo despliega en un entorno de pruebas o de staging para que los testers puedan acceder a él. Esto incluye copiar archivos, establecer configuraciones o reiniciar el servidor. También puede incluir scripts para aumentar automáticamente números de versión o desplegar artefactos en repositorios compartidos. Incluso puedes ir más allá y desplegar automáticamente tu aplicación en producción, si lo deseas.
Cómo configurar tus pipelines de CI/CD depende siempre de las necesidades del proyecto, y no hay dos proyectos iguales.
En resumen, aunque al principio suponga algo más de trabajo configurar una pipeline de CI/CD, compensará con el tiempo, porque automatiza tareas repetitivas y además detecta errores más pronto, ya que es posible ejecutar pruebas importantes en cada push.
Algunas plataformas alternativas de CI/CD
En WATA Factory usamos Bitbucket Pipelines principalmente porque se integra sin problemas con otros productos de Atlassian como Jira y Confluence, que usamos a diario.
Sin embargo, existen bastantes frameworks de CI/CD además de Bitbucket Pipelines. Veamos algunas de las alternativas más populares:
- GitHub Actions
- Nativo para repositorios GitHub
- Gran comunidad y fácil de usar
- Bueno para proyectos open source
- GitLab CI/CD
- Totalmente integrado con GitLab
- Registro de contenedores y análisis de seguridad integrados
- Muy utilizado en empresas y equipos centrados en DevOps
- Jenkins
- Completamente open source
- Probablemente uno de los participantes más antiguos de esta lista
- Self-hosted
- Altamente personalizable, pero más complejo
- A menudo utilizado para entornos empresariales complejos y autoalojados
- CircleCI
- Utiliza contenedores Docker o máquinas virtuales
- Paralelización y caching muy rápidos
- Buen soporte para Docker y Kubernetes
- Azure DevOps Pipelines
- Integración profunda con el ecosistema Microsoft y Azure (ideal para stacks Microsoft)
- Soporta pipelines en YAML, pero también ofrece una interfaz gráfica (GUI)
Configurar una Bitbucket Pipeline sencilla
Para usar Bitbucket Pipelines, obviamente necesitas una cuenta de Bitbucket y tener tu código subido allí.
Enable Pipelines
Para poder usar pipelines en un repositorio, primero tienes que activarlas desde la interfaz web de Bitbucket. Ve a tu repositorio de Bitbucket, haz clic en Repository settings, busca la sección Pipelines en el menú de la izquierda, haz clic en Settings y activa Enable pipelines.
Hasta que no hagas esto, ninguno de los pasos siguientes tendrá efecto.
El bitbucket-pipelines.yml
Toda la configuración de una Bitbucket Pipeline reside en un único archivo YAML, bitbucket-pipelines.yml, que debe ubicarse en la raíz de tu repositorio. Eso significa, por supuesto, que si tienes tu proyecto dividido en varios repositorios (Frontend y Backend, por ejemplo), tendrás una pipeline y un bitbucket-pipelines.yml por separado para cada uno de tus repositorios.
Todos los archivos bitbucket-pipelines.yml tienen una estructura concreta que define los pasos, entornos y condiciones del proceso de CI/CD. Los elementos clave son los siguientes:
- image – la imagen Docker dentro de la cual se ejecutan los pasos (puede ser una imagen estándar para el entorno de ejecución o una imagen personalizada que emule el entorno de desarrollo o de producción).
- pipelines – el bloque principal y elemento de nivel superior que define cuándo y cómo se ejecutan los pasos. Algunas de las propiedades útiles son:
- branches – se ejecuta en cada commit para ramas que tienen un nombre concreto o siguen un patrón de nombres (por ejemplo, “master” o “/feature”).
- pull–requests – se ejecuta cuando se crea un pull request para una rama determinada. También permite restringirlo a ciertos patrones de nombres, similar a branches.
- tags – se ejecuta para ciertas etiquetas (tags) en el repositorio Git.
- default – como su nombre sugiere, es una opción de reserva. Se ejecuta para cualquier commit en cualquier rama que no esté cubierta por ninguna de las propiedades anteriores.
- custom – se usa para pipelines manuales o programadas.
- step – una unidad de ejecución (por ejemplo “build and test”), definida mediante comandos de shell. Aquí es donde está “la chicha”. Un paso puede contener lo siguiente (entre otros):
- caches – directorios (por ejemplo “node_modules”) que se almacenan en caché entre ejecuciones, lo que puede acelerar significativamente los runs, porque no es necesario descargar todas las dependencias cada vez.
- artifacts – archivos generados en un paso (por ejemplo, un archivo .jar) que luego pueden utilizarse en otro paso.
- deployment – marca un paso como despliegue (aparecerá en la interfaz “Deployments” de Bitbucket).
- pipe – una imagen Docker personalizada para un contenedor que contiene un script para realizar una tarea. Pueden verse como piezas “prefabricadas” para tu pipeline que son especialmente útiles para integrarse con herramientas de terceros.
Un Ejemplo Sencillo
La interfaz web de Bitbucket en realidad proporciona algunas plantillas para configuraciones típicas, pero para este artículo crearemos el archivo bitbucket-pipelines.yml a mano. Como yo soy de Java, el siguiente ejemplo será para un proyecto Java con Maven. Este es uno de los ejemplos más simples posibles:
image: maven:3.9.6-eclipse-temurin-17
pipelines:
default:
- step:
name: Build and Test
caches:
- maven
script:
- mvn -B clean install
Veamos qué hace esto:
- La primera línea indica a la pipeline que use JDK 17 con Maven 3.9.
- La palabra clave default nos dice que esta pipeline se va a ejecutar en cada commit, para cada rama.
- Cacheamos la carpeta maven, de modo que las compilaciones futuras serán mucho más rápidas.
- Por último, ejecutamos el comando de Maven que limpia, descarga dependencias (si es necesario), compila y ejecuta las pruebas unitarias.
Así que, incluso con este poco código, ya tenemos una pipeline que compila el código y ejecuta los tests automáticamente en cada commit. También te enviará un correo electrónico cuando alguno de los pasos falle.
Un Ejemplo Más Complejo
El siguiente ejemplo muestra una pipeline más compleja, con pasos separados e independientes para compilar, ejecutar pruebas unitarias, ejecutar pruebas de integración, empaquetar y desplegar el código. Mientras que la compilación y las pruebas unitarias se ejecutarán en cada commit, las pruebas de integración, el empaquetado y el despliegue solo se realizarán para las ramas release.
image: maven:3.9.6-eclipse-temurin-17
pipelines:
default:
- step:
name: Build (Compile Only)
caches:
- maven
script:
- mvn -B -DskipTests compile
artifacts:
- target/**
- step:
name: Unit Tests
caches:
- maven
script:
- mvn -B test
artifacts:
- target/**
branches:
'release/*':
- step:
name: Build (Compile Only)
caches:
- maven
script:
- mvn -B -DskipTests compile
artifacts:
- target/**
- step:
name: Unit Tests
caches:
- maven
script:
- mvn -B test
artifacts:
- target/**
- step:
name: Integration Tests
caches:
- maven
script:
- mvn -B verify -DskipUnitTests
artifacts:
- target/**
- step:
name: Package as JAR
caches:
- maven
script:
- mvn -B package -DskipTests
artifacts:
- target/*.jar
- step:
name: Deploy
deployment: Development
script:
- pipe: atlassian/scp-deploy:1.6.0
variables:
USER: $DEPLOY_USER
SERVER: $DEPLOY_HOST
REMOTE_PATH: "/var/deploy"
LOCAL_PATH: "target/*.jar"
Lo primero que notarás es que debajo de pipelines tenemos dos bloques: un bloque default (que se ejecuta en cada commit) y un bloque branches, que solo se ejecuta para las ramas release (que hemos definido como todas las que están en el release/ directorio).
También debes fijarte en que los pasos Build (Compile Only) y Unit Tests se repiten (porque queremos ejecutarlos también para las ramas release).
Además de los pasos de compilación y pruebas unitarias, la sección branches también ejecuta las pruebas de integración y empaqueta la aplicación en un archivo JAR. Estos pasos solo se ejecutarán para las ramas release.
Lo más interesante es el último paso, que utiliza la pipe scp-deploy. Esta pipe (como su nombre sugiere) toma los artefactos que recibe del paso anterior y los copia a un servidor remoto usando scp. Para que esto funcione, necesita la información proporcionada en la sección variables. Los valores que van precedidos de un ‘$’ son variables definidas como repository variables en la interfaz web de Bitbucket (porque no quieres subir datos sensibles a tu repositorio).
Conclusión
CI/CD es una herramienta indispensable en el desarrollo actual. En WATA Factory utilizamos Bitbucket Pipelines para este propósito. Como hemos visto en este artículo, es fácil configurar una pipeline de CI/CD (aunque en algunos proyectos puedan llegar a ser muy complejas).
Además de todas las ventajas mencionadas de usar CI/CD, Bitbucket Pipelines también se integra muy bien con otros productos de Atlassian, y por eso es nuestra herramienta de referencia.



