Contenido

Integración continua: Jenkins

Ahora está de moda eso de la integración continua (en adelante, CI. de Continuous Integration). Pero… ¿Qué es eso de la CI?

Contaré a grandes rasgos algunos aspectos y después me centraré en la configuración de un trabajo con jenkins.

Jenkins

¿Qué es CI?

Asumamos que tenéis una aplicación terminada; sin embargo, descubrís un pequeño defecto que no os lleva más de 5 minutos arreglar. Desde el mismo momento en el que lo corregís, guardáis y queréis hacer el cambio definitivo, ¿cuánto tiempo pasa?

En ese momento es cuando comienza el infierno:

  • Vuestro cambio se mezcla con otros cambios
  • Dependencias con librerías que ya no existen
  • Hay que volver a desplegar, configurar,… el producto para poder probarlo
  • Con suerte, otro departamento necesitará que le echéis una mano para probarlo. Con mala suerte, no se realizan pruebas en absoluto.
  • Se vuelven a abrir incidencias con problemas que ya tenía la versión anterior
  • Hay que actualizar la documentación
  • Posiblemente os pregunten si hay algo nuevo para traducir
  • Hay que empaquetar el producto
  • Además, tenemos que probar el nuevo paquete del producto

Todos estos puntos y otros dependen de cada empresa, pero siempre es un dolor. Pensadlo en serio: ¿cuánto tiempo puede pasar? ¿Un mes?

Pues bien… La integración continua consiste, idealmente, en que en el momento en el que metes tu cambio en el repositorio, se desencadena un proceso automático que termina con la publicación/denegación de la nueva versión y un mail en tu cuenta de correo con el resultado. Así de simple. Idealmente. Y, de nuevo idealmente, se trata de un proceso de minutos.

Claro, entre un caso y el otro hay miles de grises, pero conviene saber cuál es el objetivo real de la CI.

Por definición, en un proceso automático no intervienen las personas. Así que tenéis dos opciones: o bien confiais en lo buenísimos programadores que sois, ya que nunca rompéis nada, o bien asumís que no somos perfectos y que necesitáis probar el producto. Lo que nos da la…

Primera condición: Necesitamos pruebas automáticas

Si no hay pruebas automáticas, apaga y vámonos. Y éstas deben desencadenarse con un único comando o con un conjunto de comandos suficientemente pequeño. Y deben cumplir las condiciones de las buenas pruebas (FIRST):

  • Fast: Más rápidas que Usain Bolt. La idea es que una rama que intenta entrar en la rama principal, si pasa las pruebas, se mergea automáticamente.
  • Independant: No importa el orden. De hecho, es mejor que se ordenen de forma aleatoria (pero dejen constancia del orden, para que puedan cumplir la siguiente condición).
  • Repeatable: Todo esto no sirve si no podéis reproducir el problema si se produce.
  • Small: Pequeñines sí, gracias. Así acotarán más la zona problemática.
  • Transparent: Que se vea claramente la intención de la prueba.

Segunda condición: Fácil de desplegar

También necesitamos que se pueda desplegar de forma automática. Si no es así, ya hemos terminado. No sirve de nada tener que estar hackeando mogollón de ficheros en el sistema. Con un poquito de suerte, podéis utilizar puppet o chef [yo mismo escribí un “tutorial sobre puppet] no hace mucho) para hacer todas las ñapas que sean necesarias.

Tercera condición: Servidor de integración contínua

Es importante tener un programa que detecte el cambio y lance todas estas pruebas.

Aunque es posible que necesitéis más cosas, como un servidor de artefactos/librerías, bases de datos, licencias, gestión de documentación, gestión de traducciones, publicación del artefacto resultante, servidores de mail, …

En este caso vamos a utilizar Jenkins.

Instalando Jenkins

Lo siento, no me voy a extender. Os lo descargáis y ejecutáis:

1
$ java -jar jenkins.war

Y comenzará a soltar guarrería javera por la pantalla. No os preocupéis por eso.

Por lo demás, ya está. Podéis abrir un navegador en la dirección https://localhost:8080 .

Configurando un proyecto: pyDoubles

He elegido pyDoubles para el ejemplo, ya que es pequeñín y tiene pruebas.

El primer problema es que está en un repositorio mercurial , así que tendremos que añadir el plugin de mercurial a Jenkins:

Añadir el plugin de mercurial a Jenkins

Desde la ventana principal, pulsamos en “Administrar Jenkins”, y después en “Administrar Plugins”. Antes deberíamos pasar por “configurar el sistema”, pero esa parte no es imprescindible y os la dejo a vosotros.

Ahora, en la pestaña de “Todos los plugins” buscáis el de mercurial, lo marcáis y le dais al botón de instalar. No tiene pérdida.

Tras unos segundines os dirá que está todo listo para usarse.

Crear el proyecto

Volvemos a la ventana inicial y pulsamos en Nueva Tarea. Indicamos que es un Proyecto libre y le ponemos un nombre, algo imaginativo… No sé… “pyDoubles”.

Ahora vamos a lo básico:

  • origen del código fuente: mercurial

    • repository URL: ssh://hg@bitbucket.org/carlosble/pydoubles
    • branch: default
  • Ejecutar

    • “Añadir un nuevo paso”->“Ejecutar línea de comandos (shell)”:
1
2
3
mkdir $WORKSPACE/log || true
nosetests --with-xunit --xunit-file=$WORKSPACE/log/unit.xml pyDoublesTests/unit.py
nosetests --with-xunit --xunit-file=$WORKSPACE/log/hamcrest.xml pyDoublesTests/hamcrest_integration.py
  • “Acciones para ejecutar después”

    • “Publicar los resultados de los tests JUnit”:
1
**/log/*.xml

Y ya está.

Algunos comentarios: si en lugar de un único script hacéis varios, la diferencia será que parará en el primer error. Eso mismo puede hacerse ejecutando “set -e”, que es más cómodo.

No he configurado el polling ni ningún tipo de planificación. Sólo podremos ejecutarlo a mano. Creo que para el ejemplo es más que suficiente, y que la documentación os dará el resto.

Ejecutar

Es el momento en el que pulsáis sobre “Ejecutar ahora”. Al poco tiempo os llamará la atención una barrita de progreso en la sección “Estado de los nodos”. Voy a contar un poco más de esto.

Jenkins consta de dos partes: el maestro y los esclavos. El maestro es lo que hemos visto hasta ahora; los esclavos son programas más pequeños que Jenkins instala en otras máquinas y que son los que ejecutarán todo el proyecto en sí. Dado que Jenkins consume bastantes recursos, es normal dejarlo apartado en un único servidor y configurar los workers en otras máquinas. Por defecto vemos que ha configurado dos esclavos en la máquina actual. En un entorno real, eso es un suicidio, pero para nuestro ejemplo es más que suficiente. Cuantos más esclavos tengamos, más trabajo se podrá realizar en paralelo.

Una vez que haya terminado (no le llevará mucho tiempo), podréis navegar por la información de la build. Os recomiendo las secciones:

  • “Salida de Consola”, donde podréis ver cómo fue la ejecución
  • “Resultados de los tests”, donde se renderizan los tests en formato molón.

Conclusiones

Como podéis observar, resulta tremendamente sencillo crear un proyecto en Jenkins, siempre y cuando se cumplan las dos primeras condiciones: que haya pruebas automáticas y que resulte sencillo de desplegar.

Aunque hacer cosas sencillas con Jenkins es tremendamente sencillo, hacer cosas un poco más difíciles es un infierno. No os emocionéis todavía creando proyectos a mansalva, ya que en el siguiente artículo comentaré otro servidor de CI que puede gustaros aún más. Cada cual tiene sus ventajas y sus inconvenientes, claro está.

Si, finalmente, termináis adoptando Jenkins como vuestro CI habitual, os recomiendo que hagáis backup de los proyectos, que son archivos XML que se encontrarán, probablemente, en $HOME/.jenkins. Tened cuidado, porque ese directorio tiende a crecer desmesuradamente (Jenkins tiene mecanismos para controlarlo, investigad las opciones del proyecto).

En caso de que queráis esperar a ver la otra opción que quiero presentar, podéis ir adaptando vuestros proyectos para que se puedan probar y desplegar fácilmente. ¡Eso nunca está de más!