Montar un entorno de integración continua con Django

La integración continua (CI) es un modelo de trabajo creado inicialmente por Martin Fowler que consiste en automatizar lo más posible el proceso de integración para que éste se pueda realizar lo más a menudo posible. ¿Y qué ganamos con esto? Poder detectar fallos y arreglarlos muy rápidamente.

La integración consiste en la compilación y ejecución de todas las pruebas (unitarias, de integración de performance..) así como su despliegue en producción. Antiguamente éste proceso podía consumir el mismo tiempo que el trabajo de desarrollo en sí, sin embargo hoy contamos cada vez con más herramientas que nos automatizan las tareas necesarias para realizar integraciones en tiempos cada vez más pequeños. Y uno de los ejemplos más conocidos es Jenkins.

Básicamente lo que Jenkins nos facilita con Django es el proceso de descargarse las fuentes desde el control de versiones, ejecutar todas las pruebas y generar informes. Veamos como montar un entorno así:

Instalación de un nuevo proyecto

Imaginemos que tenemos un proyecto Django en Bitbucket. Lo primero es descargárnoslo a nuestro entorno

git clone https://bitbucket.org/suporte_concrete/mi-proyecto-django.git

Por supuesto, tal como vimos en artículos anteriores, creamos e inicializamos nuestro entorno virtual:

virtualenv $HOME/proyetos-django/virtualenvs/mientorno
source $HOME/proyetos-django/virtualenvs/blog/bin/activate

...y por si acaso instalamos los requerimientos de nuestro proyecto, realizamos migraciones, arrancamos el servidor y ejecutamos test:

pip install -r requirements.txt
python manage.py migrate
python manage.py runserver
python manage.py test

Instalando Jenkins

Jenkins es un software desarrollado en Java, que podemos instalar fácilmente en local para ser ejecutado desde el navegador. En Debian/Ubuntu es tan sencillo como ésto:

wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins

Tras esto Jenkins está instalado, aunque te recomiendo varios puntos:

  • Configura Jenkins para que funcione como un servicio que arranca desde el inicio, a través de /etc/init.d/jenkins
  • Los log de la aplicación se almacenan en /var/log/jenkins/jenkins.log.
  • Puedes editar el fichero /etc/default/jenkins para cambiar aspectos de la configuración de Jenkins. Por ejemplo, si quieres cambiar el puerto por el que escucha, sólo tienes que reemplazar la línea ----HTTPPORT=8080---- por ----HTTPPORT=8081----
  • Para ver que el servicio de Jenkins funciona correctamente, sólo tienes que escribir:
sudo service jenkins status

Si todo está OK, sólo tienes que acceder a localhost:8080 para ver la pantalla principal de Jenkins (antes es probable que te pida configurar un usuario admin):

Jenkins y Django

En el menú "Administrar Jenkins -> Gestionar Plugins" podremos instalar los plugins que consideremos oportunos. Por ejemplo, si nuestro control de versiones está basado en Git, tendremos que instalar el plugin necesario. La lista de plugins es enorme y algunos nos permitirán incluso automatizar despliegues en diferentes servicios de la nube.

Configurar una tarea

En la pantalla inicial de Jenkins, tenemos que pulsar la opción "Nueva tarea". Podemos empezar por elegi la opción "Crear proyecto de estilo libre".

Desde ahí es posible configurar el repositorio (en nuestro caso Bitbucket se configura sin problema).

Otra cosa que podemos configurar son os triggers: por ejemplo, podemos establecer que en intervalos de 20 minutos Jenkins revise el repositorio y si hay un cambio, Jenkins lo descargará y hará las pruebas pertinentes.

Después de descargar la versión, podemos configurar Jenkins para que haga lo que queramos, por ejemplo abrir un shell script como éste y ejecutarlo:

#!/bin/bash
export WORKSPACE=`pwd`
# Create/Activate virtualenv
virtualenv venv
source venv/bin/activate
# Install Requirements
pip install -r requirements.txt
# Run tests
python manage.py test

En próximas entregas iré profundizando en este proceso que poco a poco nos irá permitiendo llegar a la integración contínua. :)