La automatización de procesos es una de las tareas esenciales en integración continua, ya que permite estandarizar los procesos (que siempre sean iguales) y ahorrar muchísimo tiempo en el desarrollo de los proyectos.
Lo primero que vamos a automatizar es la creación de repositorios. Para ello vamos a utilizar un script en Bash para poder ejecutarlo en consola a través de ssh. Es posible crear algo parecido con otros lenguajes de programación y además, con una interfaz web que facilite la gestión; pero de momento vamos con lo más básico.
CONVENCIONES
Como recordarás del último post (Integración continua: Metodologías y convenciones), debemos especificar unas convenciones o reglas que nos permitan diseñar cómo se van a crear los repositorios. En este caso esas reglas son las siguientes:
- Se utilizará Git y Subversion.
- Para Subversion se automatizará la creación de las ramas «trunk«, «branches» y «tags«.
- Los repositorios se guardarán en «/var/repos«.
- Dentro de cada repositorio se crearán los siguientes directorios: src, metrics, changes, test, db, phing y sh. En el directorio src guardaremos los datos del proyecto. En metrics guardaremos las métricas en subdirectorios, uno para cada una de las métricas (phpcs, pdepend, phpcpd, phpmd). El directorio changes dispondrá de un subdirectorio llamado modules que nos permitirá guardar en un archivo los cambios realizados sobre módulos o plugins que se distribuyan con el cms. En los directorio test habrá dos subdirectorios: units y functionals, para separar los test unitarios de los funcionales. En db habrá tres subdirectorios: basic, modules-dev y modules, que nos permitirá guardar una copia del proyecto al inicio sin módulos ni plugins (basic); con los módulos en desarrollo (modules-dev) y en producción (modules). Por último los directorios phing y sh nos permitirán guardar los xml de algunas tareas automatizadas específicas para el proyecto (phing, lo veremos más adelante) y los scripts en bash, también específicos para el proyecto en el que estemos trabajando, en sh.
- Cada proyecto se nombrará con su dominio (interadictos.es). El nombre de los repositorios en Git terminarán en «.git».
- Se creará un archivo «.conf» en Apache para poder acceder a los repositorios de Subversion desde el navegador.
Con esto ya podemos empezar a desarrollar nuestro script.
PERO ANTES
Como ves, el último elemento de la lista es la creación de un archivo «.conf» para poder ver el repositorio a través del navegador. Esta es una opción que te puedes saltar si no tienes necesidad de esta característica. Pero con el fin de hacer esta guía lo más detallada posible lo comentaré.
Para realizar estos cambios debes ser «root» o disponer del comando «sudo».
Nos vamos al directorio de Apache:
cd /etc/apache2
Y creamos el directorio
mkdir repos
Comprueba que los permisos del directorio son correctos.
CREAR UN SUBDOMINIO AL REPOSITORIO
Ahora entra en el directorio «sites-available»:
cd sites-available
Crearemos un subdominio para el dominio «ic.net», que te recuerdo era el dominio de nuestro servidor de integración continua.
Utiliza tu editor favorito y crea un archivo llamado «001-repos.ic.net.conf». El número al principio es simplemente una forma de ordenar los archivos. Puedes obviarlo si lo deseas.
Ahora introduce el siguiente código en el archivo y guárdalo:
[codesyntax lang=»apache»]
<VirtualHost *:80> ServerName repos.ic.net DocumentRoot /var/www/repos.ic.net <Directory /> Options FollowSymLinks AllowOverride None </Directory> <Directory /var/repos> Options Indexes FollowSymLinks MultiViews AllowOverride None Order allow,deny allow from all </Directory> ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ <Directory "/usr/lib/cgi-bin"> AllowOverride None Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch Order allow,deny Allow from all </Directory> ErrorLog ${APACHE_LOG_DIR}/repos.ic.net.error.log # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. LogLevel warn CustomLog ${APACHE_LOG_DIR}/repos.ic.net.access.log combined Alias /doc/ "/usr/share/doc/" <Directory "/usr/share/doc/"> Options Indexes MultiViews FollowSymLinks AllowOverride None Order deny,allow Deny from all Allow from 127.0.0.0/255.0.0.0 ::1/128 </Directory> Include /etc/apache2/repos </VirtualHost>
[/codesyntax]
Lo más destacable del archivo es el segundo directorio al que le hemos dado permisos:
<Directory /var/repos> Options Indexes FollowSymLinks MultiViews AllowOverride None Order allow,deny allow from all </Directory>
Y la última línea antes de cerrar la etiqueta «VirtualHost»:
Include /etc/apache2/repos
Esta línea le dice a Apache que incluya también los archivos de configuración que haya en ese directorio. No incluyas esta línea si no quieres que tus repositorios se puedan acceder desde el navegador.
DESARROLLO DEL SCRIPT
Abrimos Gedit o el Bloc de notas si estás en Windows, o cualquier otro editor de textos o IDE de desarrollo que permita crear scripts en Bash y escribimos lo siguiente:
[codesyntax lang=»bash»]
#!/bin/bash #Variables necesarias DIR_BASE_REPOS='/var/repos' #Limpiamos la pantalla clear #Iniciamos el script echo 'Bienvenido al creador automatizado de repositorios.' echo 'A continuación se le realizarán algunas preguntas para realizar el proceso.' echo ' ' echo '¿Está de acuerdo? (y/n):' read ACEPTADO #Comprobamos que el usuario está de acuerdo if [ "$ACEPTADO" == 'n' ] || [ "$ACEPTADO" != 'y' ] then #Si no está de acuerdo se le echa echo 'Hasta luego xD' exit 0 else fi exit
[/codesyntax]
Bien, en primer lugar indicamos que el archivo que vamos a crear es un script en Bash con la línea:
#!/bin/bash
A continuación creamos la variable que guardará el directorio donde guardaremos los repositorios:
DIR_BASE_REPOS='/var/repos/'
Después limpiamos la pantalla con el comando «clear».
Iniciamos el script y le preguntamos al usuario si acepta que se le realicen unas preguntas para realizar el proceso.
Si contenta con la letra «n» o con algo distinto a «y» entonces se le envía un mensaje de despedida y se finaliza el script:
#Iniciamos el script echo 'Bienvenido al creador automatizado de repositorios.' echo 'A continuación se le realizarán algunas preguntas para realizar el proceso.' echo ' ' echo '¿Está de acuerdo? (y/n):' read ACEPTADO #Comprobamos que el usuario está de acuerdo if [ "$ACEPTADO" == 'n' ] || [ "$ACEPTADO" != 'y' ] then #Si no está de acuerdo se le echa echo 'Hasta luego xD' exit 0
En el caso de que haya aceptado, añadiremos todos bloques de código siguientes dentro del «else» del condicional.
Le preguntamos el nombre del proyecto:
[codesyntax lang=»bash»]
#Se pide el nombre del repositorio o proyecto echo 'Nombre del nuevo repositorio o proyecto:' read NOMBRE_REPOSITORIO #Se comprueba que la variable no esté vacía if [ -z $NOMBRE_REPOSITORIO ] then echo 'No se ha escrito el nombre del repositorio. Adios.' exit 0 fi
[/codesyntax]
Si no se ha escrito el nombre del repositorio, el script finaliza. En caso contrario, en la variable «NOMBRE_REPOSITORIO» tendremos el nombre que el usuario le ha dado al proyecto.
Ahora le preguntamos qué repositorio desea usar para el proyecto:
[codesyntax lang=»php»]
#Preguntamos el tipo de cvs a usar echo '¿Qué repositorio usará, git o subversion? (git/svn):' read TIPO_REPOSITORIO #Se comprueba que se ha elegido un cvs if [ "$TIPO_REPOSITORIO" != 'git' ] && [ "$TIPO_REPOSITORIO" != 'svn' ] then #Si no se ha elegido ninguno, por defecto será git echo "No se ha elegido repositorio." echo "Por defecto se usará git" $TIPO_REPOSITORIO='git' fi
[/codesyntax]
Como necesitamos un repositorio para nuestro proyecto, si el usuario no ha elegido ninguno le ponemos por defecto Git.
Creamos varias variables con la url del repositorio:
[codesyntax lang=»bash»]
#Dependiendo del tipo de repositorio elegido creamos las variables correspondientes #a la url y la ruta en el sistema de archivos if [ "$TIPO_REPOSITORIO" == 'git' ] then URI_REPO="${DIR_BASE_REPOS}${NOMBRE_REPOSITORIO}.git/" URL_REPO="${NOMBRE_REPOSITORIO}.git" elif [ "$TIPO_REPOSITORIO" == 'svn' ] then URI_REPO="${DIR_BASE_REPOS}$NOMBRE_REPOSITORIO" URL_REPO="$NOMBRE_REPOSITORIO" fi
[/codesyntax]
En «URL_REPO» se guarda el nombre del repositorio que, como puedes observar, si es Git se añade al final del nombre el texto «.git», tal como hemos acordado en nuestras convenciones.
A continuación creamos el directorio del repositorio:
[codesyntax lang=»bash»]
echo 'Creo el directorio del repositorio.' REPO_DIR=$(mkdir $URI_REPO) if [ $REPO_DIR ] then echo 'No se ha podido crear el directorio del repositorio' exit 0 else echo 'Creado' fi
[/codesyntax]
Si por alguna razón (quizá por los permisos) no se ha podido crear el repositorio, mostramos un mensaje y finalizamos el script.
Ahora creamos el repositorio propiamente dicho:
[codesyntax lang=»bash»]
#Se crea el repositorio utilizando la uri o ruta del sistema de archivos #donde se encuentra el repositorio. if [ $TIPO_REPOSITORIO == 'git' ] then REPO_GIT=$(git init --bare $URI_REPO) if [ "$REPO_GIT" == "Initialized empty Git repository in ${URI_REPO}" ] then echo 'Creado' else echo 'Algo falló.' echo $REPO_GIT exit 0 fi elif [ $TIPO_REPOSITORIO == 'svn' ] then REPO_SVN=$( svnadmin create "$URI_REPO" ) if [ -z $REPO_SVN ] then echo 'Creado' else echo 'Algo falló.' echo $REPO_SVN exit 0 fi fi
[/codesyntax]
Como tenemos dos tipos de repositorios (Git y Subversion) tenemos que comprobar cual de ellos a elegido el usuario. Seguidamente se ejecuta el comando correspondiente para crear el repositorio, y se comprueba si ha funciona o se ha producido algún error, con lo que habría que finalizar el script.
Tenemos que crear un directorio temporal para crear los directorios que hemos acordado en las convenciones (src y metrics):
[codesyntax lang=»bash»]
echo "Creo un directorio temporal para crear los directorios src y metrics" TEMP_DIR=$(mkdir /tmp/directory) if [ $TEMP_DIR ] then echo 'Algo falló' echo $TEMP_DIR exit 0 else if [ $TIPO_REPOSITORIO == 'git' ] then TEMP_DIR='/tmp/directory' TEMP_DIR_TRUNK='/tmp/directory' elif [ $TIPO_REPOSITORIO == 'svn' ] then echo "Creo los directorios trunk, branches y tags para el repositorio de subversion." $(mkdir -pv /tmp/directory/trunk /tmp/directory/branch /tmp/directory/tag) TEMP_DIR='/tmp/directory' TEMP_DIR_TRUNK='/tmp/directory/trunk' fi fi
[/codesyntax]
Observa que dependiendo del tipo de repositorio, la variable TEMP_DIR_TRUNK tendrá un subdirectorio «trunk», si es Subversion, o no si es Git.
Ahora los directorios de la covención:
[codesyntax lang=»bash»]
# Creamos los directorios para las métricas y la aplicación echo "Creo las carpetas para las métricas y la aplicación." mkdir ${TEMP_DIR_TRUNK}/src mkdir ${TEMP_DIR_TRUNK}/metrics mkdir ${TEMP_DIR_TRUNK}/metrics/phpcs mkdir ${TEMP_DIR_TRUNK}/metrics/pdepend mkdir ${TEMP_DIR_TRUNK}/metrics/phpcpd mkdir ${TEMP_DIR_TRUNK}/metrics/phpmd mkdir ${TEMP_DIR_TRUNK}/changes mkdir ${TEMP_DIR_TRUNK}/changes/modules mkdir ${TEMP_DIR_TRUNK}/test mkdir ${TEMP_DIR_TRUNK}/test/functionals mkdir ${TEMP_DIR_TRUNK}/test/units mkdir ${TEMP_DIR_TRUNK}/db mkdir ${TEMP_DIR_TRUNK}/db/basic mkdir ${TEMP_DIR_TRUNK}/db/modules-dev mkdir ${TEMP_DIR_TRUNK}/db/modules mkdir ${TEMP_DIR_TRUNK}/phing mkdir ${TEMP_DIR_TRUNK}/sh
[/codesyntax]
Si quieres que sea posible acceder a los repositorios de Subversion desde el navegador, deberás añadir las siguientes líneas:
[codesyntax lang=»bash»]
#Se modifica Apache para permitir ver el repositorio desde el navegador if [ $TIPO_REPOSITORIO == 'svn' ] then echo 'Creo la ruta en Apache para el repositorio' FILE_CONF="/etc/apache2/repos/${URL_REPO}.conf" echo "<Location /${URL_REPO}>" >> $FILE_CONF echo " DAV svn" >> $FILE_CONF echo " SVNPath /var/repos/${URL_REPO}" >> $FILE_CONF echo "</Location>" >> $FILE_CONF echo 'Recarga Apache' $( /etc/init.d/apache2 reload ) fi
[/codesyntax]
Este código crea un archivo «.conf» con los datos necesarios para poder navegar por el repositorio desde el navegador.
Para finalizar realizamos el primer commit del repositorio:
[codesyntax lang=»bash»]
echo 'Entro al directorio temporal y hago un commit' cd $TEMP_DIR #Se hace un primer commit con el código base if [ "$TIPO_REPOSITORIO" == 'git' ] then git init $(git update-server-info) git remote add origin "ssh://nombre_usuario@repos.ic.net/${DIR_BASE_REPOS}${URL_REPO}" $(git add *) git commit -m "Commit inicial" $(git push origin master) elif [ "$TIPO_REPOSITORIO" == 'svn' ] then $(svn checkout http://repos.ic.net/$NOMBRE_REPOSITORIO $URI_REPO) $(svn commit -m "Commit inicial" http://repos.ic.net/$NOMBRE_REPOSITORIO) fi
[/codesyntax]
En el caso de que el repositorio sea Git tendrás que sustituir «nombre_usuario» por el nombre de un usuario que tenga permisos para acceder al directorio /var/repos.
Y aquí el script final:
[codesyntax lang=»bash»]
#!/bin/bash #Variables necesarias DIR_BASE_REPOS='/var/repos' #Limpiamos la pantalla clear #Iniciamos el script echo 'Bienvenido al creador automatizado de repositorios.' echo 'A continuación se le realizarán algunas preguntas para realizar el proceso.' echo ' ' echo '¿Está de acuerdo? (y/n):' read ACEPTADO #Comprobamos que el usuario está de acuerdo if [ "$ACEPTADO" == 'n' ] || [ "$ACEPTADO" != 'y' ] then #Si no está de acuerdo se le echa echo 'Hasta luego xD' exit 0 else #Se pide el nombre del repositorio o proyecto echo 'Nombre del nuevo repositorio o proyecto:' read NOMBRE_REPOSITORIO #Se comprueba que la variable no esté vacía if [ -z $NOMBRE_REPOSITORIO ] then echo 'No se ha escrito el nombre del repositorio. Adios.' exit 0 fi #Preguntamos el tipo de cvs a usar echo '¿Qué repositorio usará, git o subversion? (git/svn):' read TIPO_REPOSITORIO #Se comprueba que se ha elegido un cvs if [ "$TIPO_REPOSITORIO" != 'git' ] && [ "$TIPO_REPOSITORIO" != 'svn' ] then #Si no se ha elegido ninguno, por defecto será git echo "No se ha elegido repositorio." echo "Por defecto se usará git" $TIPO_REPOSITORIO='git' fi #Dependiendo del tipo de repositorio elegido creamos las variables correspondientes #a la url y la ruta en el sistema de archivos if [ "$TIPO_REPOSITORIO" == 'git' ] then URI_REPO="${DIR_BASE_REPOS}${NOMBRE_REPOSITORIO}.git/" URL_REPO="${NOMBRE_REPOSITORIO}.git" elif [ "$TIPO_REPOSITORIO" == 'svn' ] then URI_REPO="${DIR_BASE_REPOS}$NOMBRE_REPOSITORIO" URL_REPO="$NOMBRE_REPOSITORIO" fi echo 'Creo el directorio del repositorio.' REPO_DIR=$(mkdir $URI_REPO) if [ $REPO_DIR ] then echo 'No se ha podido crear el directorio del repositorio' exit 0 else echo 'Creado' fi #Se crea el repositorio utilizando la uri o ruta del sistema de archivos #donde se encuentra el repositorio. if [ $TIPO_REPOSITORIO == 'git' ] then REPO_GIT=$(git init --bare $URI_REPO) if [ "$REPO_GIT" == "Initialized empty Git repository in ${URI_REPO}" ] then echo 'Creado' else echo 'Algo falló.' echo $REPO_GIT exit 0 fi elif [ $TIPO_REPOSITORIO == 'svn' ] then REPO_SVN=$( svnadmin create "$URI_REPO" ) if [ -z $REPO_SVN ] then echo 'Creado' else echo 'Algo falló.' echo $REPO_SVN exit 0 fi fi echo "Creo un directorio temporal para crear los directorios src y metrics" TEMP_DIR=$(mkdir /tmp/directory) if [ $TEMP_DIR ] then echo 'Algo falló' echo $TEMP_DIR exit 0 else if [ $TIPO_REPOSITORIO == 'git' ] then TEMP_DIR='/tmp/directory' TEMP_DIR_TRUNK='/tmp/directory' elif [ $TIPO_REPOSITORIO == 'svn' ] then echo "Creo los directorios trunk, branches y tags para el repositorio de subversion." $(mkdir -pv /tmp/directory/trunk /tmp/directory/branch /tmp/directory/tag) TEMP_DIR='/tmp/directory' TEMP_DIR_TRUNK='/tmp/directory/trunk' fi fi # Creamos los directorios para las métricas y la aplicación echo "Creo las carpetas para las métricas y la aplicación." mkdir ${TEMP_DIR_TRUNK}/src mkdir ${TEMP_DIR_TRUNK}/metrics mkdir ${TEMP_DIR_TRUNK}/metrics/phpcs mkdir ${TEMP_DIR_TRUNK}/metrics/pdepend mkdir ${TEMP_DIR_TRUNK}/metrics/phpcpd mkdir ${TEMP_DIR_TRUNK}/metrics/phpmd mkdir ${TEMP_DIR_TRUNK}/changes mkdir ${TEMP_DIR_TRUNK}/changes/modules mkdir ${TEMP_DIR_TRUNK}/test mkdir ${TEMP_DIR_TRUNK}/test/functionals mkdir ${TEMP_DIR_TRUNK}/test/units mkdir ${TEMP_DIR_TRUNK}/db mkdir ${TEMP_DIR_TRUNK}/db/basic mkdir ${TEMP_DIR_TRUNK}/db/modules-dev mkdir ${TEMP_DIR_TRUNK}/db/modules mkdir ${TEMP_DIR_TRUNK}/phing mkdir ${TEMP_DIR_TRUNK}/sh #Se modifica Apache para permitir ver el repositorio desde el navegador if [ $TIPO_REPOSITORIO == 'svn' ] then echo 'Creo la ruta en Apache para el repositorio' FILE_CONF="/etc/apache2/repos/${URL_REPO}.conf" echo "<Location /${URL_REPO}>" >> $FILE_CONF echo " DAV svn" >> $FILE_CONF echo " SVNPath /var/repos/${URL_REPO}" >> $FILE_CONF echo "</Location>" >> $FILE_CONF echo 'Recarga Apache' $( /etc/init.d/apache2 reload ) fi echo 'Entro al directorio temporal y hago un commit' cd $TEMP_DIR #Se hace un primer commit con el código base if [ "$TIPO_REPOSITORIO" == 'git' ] then git init $(git update-server-info) git remote add origin "ssh://nombre_usuario@repos.ic.net/${DIR_BASE_REPOS}${URL_REPO}" $(git add *) git commit -m "Commit inicial" $(git push origin master) elif [ "$TIPO_REPOSITORIO" == 'svn' ] then $(svn checkout http://repos.ic.net/$NOMBRE_REPOSITORIO $URI_REPO) $(svn commit -m "Commit inicial" http://repos.ic.net/$NOMBRE_REPOSITORIO) fi fi exit
[/codesyntax]
Este es el script inicial para crear repositorios en Git y Subversion en nuestro servidor de integración continua. Más adelante iremos añadiendo nuevas tareas como los hooks de los repositorios, los archivos ignore, el código base de los frameworks o cms, unirlos con Jenkins, etc, etc, etc.