Bitbucket Pipelines

La integración continua ha llegado a los repositorios de Bitbucket y, aunque tiene sus limitaciones como el resto de servicios, se incluye gratuitamente. Por ahora, nos permitirá la ejecución ininterrumpida de scripts durante un máximo de 2 horas donde contaremos con 4GB de RAM y 5GB de espacio en disco. La limitación mensual es de 500 minutos por usuario, y para los equipos de más de un usuario se multiplican estos minutos por cada uno, acumulando los minutos de ejecución (más información sobre las limitaciones del servicio aquí).

La ejecución se hará sobre un contenedor Docker, pudiendo seleccionar cualquier imagen pública o privada de Docker Hub o incluso nuestro propio contenedor con acceso público.

¿Cómo empezar?

Primero debemos habilitar Pipelines en el repositorio y a continuación se nos mostrarán las diferentes plantillas a seleccionar dependiendo del lenguaje utilizado.

Sección Pipelines en Bitbucket

El script a ejecutar debe estar en el directorio raíz y llamarse bitbucket-pipelines.yml; este archivo indicará en formato YAML la imagen de Docker, la rama a la que afecta, los pasos a ejecutar…

Estructura del archivo bitbucket-pipelines.yml

Ejecutando Pipelines

En el siguiente ejemplo vamos a configurar y ejecutar el script de Pipelines para un proyecto Java construido con Maven únicamente cuando hagamos commits en la rama de develop (siguiendo el modelo de ramificaciones de Vincent Driessen git-flow) o se cree en el repositorio un tag específico con la condición de que su nombre comience por release, en cuyo caso queremos realizar una acciones adicionales.

Contenedor

En primer lugar indicaremos la imagen de Docker a utilizar. Queremos disponer de Java 7 y Maven 3, por tanto indicaremos en el archivo de configuración la siguiente propiedad:

image: maven:3.3-jdk-7

Rama

En segundo lugar vamos a indicar la rama de nuestro repositorio en la que se ejecutará Pipelines (bajo la sección pipelines podemos indicar la sección default que nos permitirá ejecutar el script en todas las ramas), que como hemos dicho antes, será solo cuando se revisen commits en develop. Por tanto, en el archivo de configuración veremos los steps dentro de la siguiente sección:

pipelines:
  branches:
    develop:

Acciones

También debemos indicar los steps a reproducir. Estos pasos se ejecutarán en el contenedor de Docker el cual se iniciará de nuevo en cada ejecución. En el caso de ejemplo que se propone aquí, queremos ejecutar el comando de Maven llamado verify en los commits de la rama develop. Por tanto, la sección quedará así:

pipelines:
  branches:
    develop:
	  - step:
        script:
          - mvn verify

Una vez generado y agregado el archivo bitbucket-pipelines.yml en la rama develop y hagamos el commit (y posterior push) se ejecutará nuestro primer Script de Pipelines.

Es común que las dependencias utilizadas en nuestro proyecto se encuentren en repositorios privados y por tanto tengamos que indicar a Maven la url donde poder encontrar estos repositorios. Por tanto, desde bash, para construir nuestro proyecto Java, ejecutaríamos el comando de Maven como mvn -s settings.xml verify. En el caso de Pipelines haremos exactamente lo mismo, incluyendo el archivo settings.xml en el repositorio.

Es recomendable ejecutar Maven con el parámetero -B que nos dará información del proceso build y por tanto en caso de fallo podremos obtener un mayor contexto que nos ayudará a depurar los errores.

Problemas con el plugin surefire

En el caso de usar el plugin surefire que ejecuta los test unitarios hay que tener en cuenta el uso de memoria que realiza. Si excedemos el uso de memoria, el proceso de Pipelines finalizará indicándonos que hemos excedido la memoria virtual asignada a nuestro proceso.

Sección Pipelines en Bitbucket

Puede provocarse este error en el caso de ejecutar test unitarios que levanten contextos de Spring para cada uno de los tests ejecutados; sin embargo solo sabremos que el proceso ha terminado por exceder la memoria, pero no quién o qué ha sido el causante del error. Para evitar que el motivo sea la ejecución de este plugin, en el archivo pom.xml de nuestro proyecto definiremos la siguiente propiedad:

<properties>
  ...
  <argLine>-Xms512m -Xmx1024m -XX:MaxPermSize=512m</argLine>
</properties>

Este argumento es leído por el plugin surefire y con esto nos aseguramos que la ejecución de los tests solo consuma como máximo 1GB comenzando a utilizar 512MB. Puesto que Bitbucket nos ofrece 4GB de memoria podemos aumentar esta cifra si lo consideramos oportuno.

Tag

Vamos ahora a indicar la otra sección (dentro de la sección pipelines), que indicará los steps a realizar cuando se cree un tag que comience por la palabra release-* (nos ayudaremos de los wildcards):

  tags:
    release-*:

En este caso, cada vez que se haga una versión release del código, queremos ejecutar un scrip de bash el cual nos realiza de manera automática un deploy en Heroku por ejemplo. Solo tendríamos que incluir el script dentro de nuestro código e indicar la ejecución del step:

  tags:
    release-*:
      script:
        - chmod +x scripts/release.bash
        - ./scripts/release.bash

En el caso anterior también contábamos con el comando git y por lo tanto podemos hacer un push desde el contenedor Docker usando ssh con autenticación por claves. Pipelines nos permite definir variables de contexto que podemos utilizar en la ejecución de cada Pipeline (en el propio YAML o incluso en el script de bash del ejemplo anterior) y por tanto definir claves privadas o tokens con los que acceder a servicios externos.

Finalmente, nuestro archivo bitbucket-pipelines.yml ha quedado así:

image: maven:3.3-jdk-7

pipelines:
  branches:
    develop:
	  - step:
        script:
          - mvn -B -s settings.xml verify
          
  tags:
    release-*:
      script:
        - chmod +x scripts/release.bash
        - ./scripts/release.bash

Ejecutando Pipelines en local

Si queremos ejecutar Pipelines en la máquina local solo tenemos que instalar Docker y ejecutar desde el terminal el siguiente comando:

local$ docker run -it --volume=/Users/myUserName/code/localDebugRepo:/localDebugRepo --workdir="/localDebugRepo" --memory=4g --memory-swap=4g --entrypoint=/bin/bash maven:3.3-jdk-7

Esto creará un contenedor Docker con las carácterísticas definidas y que actuará sobre el directorio indicado, que es el directorio del repositorio local. Solo nos quedará ejecutar los steps dentro del contenedor; por tanto, ejecutaremos el comando siguiente:

docker$ mvn -B -s settings.xml verify

Resultados

Por defecto nuestro Pipeline se ejecutará siempre que se lean nuevos commits pero desde la web de Bitbucket siempre podemos ejecutar de nuevo cada Pipeline manualmente.

Al ejecutarse nuestro Pipeline es posible obtener dos estados: correcto o incorrecto. Si la ejecución de nuestro script provoca un mensaje por la salida de error, Pipelines acabará con el estado incorrecto indicando que la ejecución falló (y además lo notificará enviando un mail al propietario del commit que causó el error):

Estructura del archivo bitbucket-pipelines.yml

En el caso contrario nos marcará que el estado es correcto:

Estructura del archivo bitbucket-pipelines.yml
Escrito el 30/04/2017