Programación y sistemas

Integración continua: Automatizando la creación de repositorios con Git y Subversion

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.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Descubre más desde Interadictos

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo