Según wikipedia integración continua es : “La integración continua (continuous integration en inglés) es una metodología informática propuesta inicialmente por Martin Fowler que consiste en hacer integraciones automáticas de un proyecto lo más a menudo posible para así poder detectar fallos cuanto antes. Entendemos por integración la compilación y ejecución de test de todo un proyecto”. Bonito, verdad?.
Actualmente en el proyecto en el que estoy involucrado (Storetto) tengo que reconocer que hemos dejado un poco dejado de la mano de Dios este asunto. Hacemos las subidas usando la técnica “manubrio”, muy funcional, pero arcaica y muy dada a fallos (compilamos con nuestros propios IDE’s y realizamos la subida). Esto nos produce multitud de quebraderos de cabeza debido, principalmente, a que no es lo mismo una subida a producción, integración o desarrollo en cuanto a archivos properties y xml’s.

Después de algún susto y viendo lo poco productivo que supone hacer eso así hemos decidido arrancar este camino hacia la integración continua. Con menos verguenza que el destista de Pedro Guerra nos embarcarnos en esta aventura.
En un entorno de integración continua deben intervenir muchos actores : el proyecto a integrar, una sistema de control de versiones, test, una manera de construir ese proyecto y de integrarlo. Veamos qué tenemos. Ahora mismo cuento con un sistema de control de versiones subversion, algo que sí que no nos podía faltar, con el proyecto propiamente dicho y…. poco más. Así que habrá que empezar por alguna parte, y como no se puede comenzar la casa por el tejado (el servidor de integración continua) creo que comenzaremos por algo que nos ayude a construir el proyecto: usaré Ant, sencillo y efectivo. En este humilde artículo os intentaré mostrar cómo he resuelto yo este problema, pero no voy a poner todo el fichero build.xml que he creado (sería muy repetitivo) sino sólo las partes más interesantes, y tampoco será una guía de Ant, por lo que daré muchas cosas por supuestas.
Pues nada, manos a la obra. Crear un script de ant que compile que se actualize tu proyecto, compile y genere los jar, war y finalmente tu ear realmente no es excesivamente difícil. Como ya he dicho algo que quería era actualizarme el proyecto de mi svn cada vez que contruyera la aplicación. Para conseguir que Ant tenga soporte para svn he tenido que “parchear” o meterle un pluggin . Podría enrollarme contando cómo hacerlo, pero creo que no tiene mucho sentido y prefiero dejaros la web de donde yo lo hice . Es bastante fácil, a grandes rasgos consiste en bajarte unas fuentes, compilarlas y referenciar en tu script de ant a el “jar” generado.
<path id= “svnant.classpath” >
<fileset dir= “./svnant-1.2.1/svnant-1.2.1/lib” >
<include name= “*.jar” />
</fileset>
</path>
<typedef resource=”org/tigris/subversion/svnant/svnantlib.xml” classpathref=”svnant.classpath” />
Antes de continuar con nuestro script, tendremos que hacer un checkout de nuestro proyecto. El lugar donde lo hagamos es indistinto, luego configuraremos una propiedad en nuestro script que apuntará al proyecto. Se me pasó por la cabeza hacer un checkout cada vez que se lanzara la tarea y luego borrar todos los archivos; esto estaría bien si se tratara de un proyecto pequeño, pero en mi casa no es sostenible, el script tardaría una barbaridad en bajarse todo y luego en borrar todo de nuevo. Así que me creo una especie de repositorio de fuentes
Primero tenemos que definir una serie de propiedades que serán usadas a lo largo de todo el script. Sobra decir la utilidad que tiene esto a la hora de pasarle a otra persona el script o cambiarlo de ubicación, etc…. . Estas son alguna de las que yo he utilizado:
<property name=”src” location=”./src”/>
<property name=”build” location=”./classes”/>
<property name=”dist” location=”./dist”/>
<property name=”temp” location=”./temp”/>
<property name=”repository.url” location=”svn://svn.varma.es”/>
<property name=”libraries” location=”D:/Storetto_JAR”/>
Mi problema principal a la hora de realizar las subidas era el tener que renombrar ficheros de propiedades dependiendo de a donde fuera a subir el archivo generado. Así que la primera tarea Ant que se debe ejecutar en mi script será la que decida a que entorno subir, para eso creamos una estructura similar a esta:
<target name=”LOCAL”>
<antcall target=”RUN-LOCAL”/>
</target>
<target name=”PROD”>
<antcall target=”RUN-PROD”/>
</target>
Que nos permite llamar a una u otra tarea dependiendo de la construcción que queramos hacer y simplemente poniendo en la línea de comandos >ANT PROD/LOCAL. Veamos una de estas tareas:
<target name=”RUN-PROD” >
<echo message=”————> GENERANDO STORETTO PARA PRODUCCION:”/>
<mkdir dir=”${temp}/descriptors”/>
<copy todir=”${temp}/descriptors/”>
<fileset dir=”${src}/viewcontroller/src/” includes=”storetto.properties.PROD” />
<fileset dir=”${src}/viewcontroller/WebRoot/WEB-INF/” includes=”web.xml.PROD” />
<fileset dir=”${src}/viewcontroller/WebRoot/WEB-INF/” includes=”trinidad-config.xml.PROD” />
</copy>
<move file=”${temp}/descriptors/storetto.properties.PROD” tofile=”${temp}/descriptors/storetto.properties”/>
<move file=”${temp}/descriptors/web.xml.PROD” tofile=”${temp}/descriptors/web.xml”/>
<move file=”${temp}/descriptors/trinidad-config.xml.PROD” tofile=”${temp}/descriptors/trinidad-config.xml”/>
<antcall target=”generate-ear”/>
</target>
Esta tarea cómo se puede ver, crea la carpeta donde copiaremos los archivos que cambian dependiendo de la construcción, los renombra y llama a otro target de Ant, ese target será al que todos los “modos de despliegue” llamen. Ahora usaremos la típica estructura de dependencias de Ant. Llamaremos al target “generate-ear” que dependerá de otro target, ese a su vez de otro, etc.. para crear una estructura secuancial de acuerdo con nuestras necesidades de despligue. Vamos a ver algunos ejemplos:
<target name=”clean” description=”clean up” >
<mkdir dir=”${build}”/>
<mkdir dir=”${dist}”/>
<delete includeemptydirs=”true”>
<fileset dir=”${build}” includes=”**/*”/>
</delete>
<delete includeemptydirs=”true”>
<fileset dir=”${dist}” includes=”**/*”/>
</delete>
</target>
Primero creamos los directorios, puede ser que sea la primera vez que lanzamos el script. Y luego, como de pequeño me enseñaron que había que dejar las cosas según las encontraras, vaciamos su contenido. Creamos dos directorios, una donde iremos poniendo los compilados y ficheros que necesitemos para constriur nuestros ficheros de despliegue y otro donde depositaremos nuestros ficheros ear,war,xml que usaremos más tarde para crear el fichero ear.
Una vez que hemos barrido nuestra casa, procederemos a actualizar nuestro proyecto, veamos cómo:
<target name=”svn-update” >
<echo message=”————> ACTUALIZAMOS EL PROYECTO:”/>
<svn>
<update dir=”${src}/Communications” />
<update dir=”${src}/viewcontroller” />
<update dir=”${src}/model” />
<update dir=”${src}/util” />
<update dir=”${src}/jasperreports” />
<update dir=”${src}/pdf” />
<update dir=”${src}/STORETTO” />
</svn>
</target>
Lo siguiente que vamos a hacer es contruir nuestro jar de modelos de datos. Storetto utiliza como sistema de acceso a datos EJB 3.0. Éstos van empaquetados , juntos con muchas otras cosas en un jar. Veamos cómo lo construimos:
<target name=”generate-model” depends=”generate-util” description=”compile the source ” >
<echo message=” ———> GENERAMOS EL MODULO MODEL:”/>
<javac srcdir=”${src}/model/src/” destdir=”${build}”
classpath=”${toString:storetto.classpath};${dist}/lib/util.jar” fork=”true”
memoryInitialSize=”192m” memoryMaximumSize=”256m” >
</javac>
<mkdir dir=”${build}/com/six/storetto/model/util/pbnoteagrupationline/resources/”/>
<copy todir=”${build}/com/six/storetto/model/util/pbnoteagrupationline/resources/”>
<fileset dir=”${src}/model/src/com/six/storetto/model/util/pbnoteagrupationline/resources” includes=”ovlfpicking.properties” />
</copy>
<copy todir=”${build}/com/six/storetto/model/validator”>
<fileset dir=”${src}/model/src/com/six/storetto/model/validator” includes=”plugings.properties” />
</copy>
<mkdir dir=”${src}/META-INF/”/>
<copy todir=”${build}/META-INF/”>
<fileset dir=”${src}/model/src/META-INF/” includes=”persistence.xml” />
</copy>
<jar jarfile=”${dist}/lib/model.jar” basedir=”${build}”/>
<!–Borramos los compilados generados –>
<delete includeemptydirs=”true”>
<fileset dir=”${build}” includes=”**/*”/>
</delete>
</target>
Como se puede ver la técnica a seguir es compilar y copiar todos los archivos que queremos que formen parte de nuestra jar a un directorio “temporal”, para finalmente lanzar la tarea “jar”. Esto lo repetiremos tantas veces como ficheros .war o .jar necesitemos crear para nuestro fichero EAR. Una vez creados todos estos ficheros se lanzará la generación del fichero de despliegue:
<target name=”generate-ear” depends=”generate-viewcontroller” description=”Crea el EAR de Storettor” >
<mkdir dir=”${dist}/lib/META-INF/”/>
<copy todir=”${dist}/lib/META-INF/”>
<fileset dir=”${src}/STORETTO/META-INF/” includes=”sun-application.xml” />
</copy>
<ear destfile=”${dist}/Storetto.ear” appxml=”${src}/STORETTO/META-INF/application.xml”>
<fileset dir=”${dist}/lib” includes=”*.jar,*.war,**/*.xml”/>
</ear>
</target>
Evidentemente esto son sólo algunos casos, sirvan cómo ejemplo de cómo construir un fichero de despliegue. Con esto ya hemos dado un paso adelante. Hemos conseguido unificar la construcción de nuestro proyecto. Este script ant nos valdrá, con algunos retoques, para los siguientes pasos en nuestro camino hacia la Integración Continua total. En el día a día de un equipo de desarrollo pequeño e invuclado en una puesta en producción no es factible abordar toda esta tarea de una sola tacada, así que volveremos a la carga con los siguientes pasos en nuestro camino : Test Unitarios y/o instalación de un servidor de integración continua Hudson.