13 Feb 2017

El repositorio de código

En esta entrada veremos la forma de almacenar el codigo y como controlar los cambios

Para continuar con nuestro entorno de CI, vamos al siguiente paso. En el punto anterior teníamos un script que compilaba el código diariamente para asegurarnos de que nuestro proyecto estaba en estado "correcto". A no ser que seamos muy cuidadosos y copiemos los últimos cambios en otra ubicación, este script va a tener que emplear la carpeta de trabajo que usamos como programador, es decir, vamos a tener un proceso externo trabajando sobre la misma carpeta en la que programamos.  Como podréis intuir, esta práctica se va a convertir en poco tiempo en una fuente de problemas constantes. Desde errores por inconsistencias a perdidas del control, si el script se lanza al mismo tiempo que nosotros estamos programando, compilando o haciendo pruebas, podemos obtener resultados confusos.

La solución consiste en tener carpetas separadas para cada tarea, por un lado la carpeta de trabajo manual y por otro la carpeta que se usará en el sistema automático (el que hemos llamado sistema de Integración Continua, IC o CI en inglés). Evidentemente podemos hacer esto a mano si somos disciplinados y al final del dia copiamos nuestro trabajo en dicha carpeta de trabajo (o usando scripts de sincronización). Pero en este caso la mejor opción es utilizar una de las herramientas que ya existen para este propósito. Estas herramientas para compartir código entre diferentes sitios, entre otra funcionalidades, es lo que nos proporciona un sistema de control de cambios/versiones (en inglés un sistema SCM o Version Control).

Como estamos hablando en abstracto, un sistema SCM consiste en una base de datos (un registro de entradas) donde se pueden ir guardando todos los cambios que sufre un conjunto de ficheros y carpetas. Por seguir con nuestro primer ejemplo; supongamos nuestra carpeta de proyecto “Super Tiendas”, la primera versión de nuestro proyecto se guardaría en nuestro SCM como "revisión 1”, al poco guardamos la “revisión 2”. En cualquier momento podemos comparar los cambios hechos entre revisiones, para encontrar donde se han realizado los cambios y, por supuesto si es necesario, deshacerlos  (total o parcialmente) y guardarlos en una nueva “revisión 3”.

En muchos sitios esto queda reflejado en un grafico parecido a esto :

image

En el gráfico, cada “entrada” es una operacion de guardado en el repositorio de SCM, asi que en el repositorio, desde otro sitio, puedo obtener la ultima versión guardada del código.

Versionado del código usando un SCM

En desarrollo de software se suele hablar de versiones del código. Así un programa, en su fase de desarrolllo, puede tener una versión 1.0, 1.1, 2.0-Alpha, etc… más adelante explicaré el significado de los números en el versionado de codigo con más detalle, ahora  mismo me interesa solamente que se entienda que un codigo va evolucionando en el tiempo y que se suelen ir añadiendo etiquetas en las diferentes fases por las que pasa.

En un SCM, esta forma de etiquetado de versiones se suele hacer usando su nombre en ingles "TAG”. Un tag en un SCM no es mas que una marca que indica un cambio en el nombre de la versión o un “evento” en nuestro proyecto. Piensa en un TAG como esa pequeña etiqueta que pones en la comida congelada donde pones una fecha de congelación y lo que es. En un SCM haremos lo mismo, marcamos un TAG con una versión (la fecha suele estar implícita en la creacion del TAG), podemos etiquetar nuestro codigo como “Versión 1.0” o “Versión entregada a la tienda de Luis”….

Una vez se entiende lo que es un TAG, voy a introducir otro nuevo concepto. La versión que estamos trabajando ahora mismo, la que está activa, es lo que se suele llamar “head” (cabeza en inglés).  Obtener la revision HEAD de un SCM indica que queremos la última que haya. Dependiendo del SCM que usemos podemos encontrarnos que el codigo HEAD del proyecto este en una rama “trunk”, “master” o “develop”… pero, un momento, nos hemos adelantado ¿rama?, ¿de que estamos hablando?.

Como veis en un segundo hemos añadido un concepto nuevo que no había explicado, la RAMA (o más comunmente, en inglé branch). Para no confundir mucho, voy a volver a nuestro ejemplo casero sin usar ninguna herramienta, si no que voy a emular el comportamiendo de un SCM a mano mediante carpetas y ficheros ZIP.

Imaginad que nuestro proyecto está asi :

image

Hemos llegado a esta estructura porque el día X entregamos a nuestros clientes una versión que funciona, la llamamos versión 1.0. Cuando se lo entregamos al cliente, lo empaquetamos y lo guardamos en una carpeta TAG. Nos olvidamos de este fichero, lo tenemos simplemente para futuras referencias.

Pasa el tiempo y hemos llegado a una versión mejorada, la hemos llamada SuperTienda 2.0, como es normal, la empaquetamos, la enviamos y guardamos para futuras referencias.

Hasta ahora todo va viento en popa, tenemos dos versiones de nuestro producto en la calle, tenemos usuarios y tenemos nuestro código bajo control y a punto de entregar nuestra flamante nueva versión 3.0. ¿Simple verdad?. Mientras nuestro proyecto principal sigue desarrollandose en la carpeta principal (HEAD), tenemos almacenado una versión de lo que hay en producción (lo que tienen instalados nuestros clientes).

Trabajando con ramas

Decíamos que nuestro proyecto sigue evolucionando, pasa el tiempo y aquel pequeño programa sin demasiadas pretensiones ya ha alcanzado la versión 3.0 o más allá.  Sin embargo tenemos un compromiso con nuestros usuarios, aquellos que tenían la versión 1.0, y llega el día en que uno de aquellos felices usuarios te reportan un error que, además, se reproduce tanto en las posteriores 2.0 y 3.0.

Así que toca revisar aquellas versiones antiguas y resolver el problema, para eso cogemos el fichero desde TAGS , lo descomprimimos en una carpeta de trabajo y arreglamos el bug.

En nuestro sistema de carpetas puede que tengamos algo como esto :

image

Me he tomado la libertad de llamar esta carpeta “RAMAS”, en esa carpeta está el codigo extraido del TAG 1.0, ahora tenemos exactamente el mismo código que liberamos como 1.0. Revisamos el fallo, lo solucionamos y generamos un nuevo TAG, esta vez lo llamaremos versión 1.0.1 . Si queremos, podemos borrar la carpeta dentro de RAMAS o podemos dejarla.

image

Este mismo proceso podemos hacerlo con cada una de las versiones que hemos sacado, descomprimimos el TAG de 2.0, resolvemos el bug, generamos la versión 2.0.1, la guardamos en TAGS.

En nuestro caso nuestro flujo de trabajo ha sido el siguiente :

image

Una Rama, conceptualmente, no es más que una versión del código que está siendo modificada o ha sido modificada, lo que nos permite cambiar rápidamente de una versión del codigo a otra.

En el desarrollo real, normalmente, las ramas salen a partir de la rama principal y es a partir de las ramas desde donde salen los TAGS. Este ejemplo muestra lo que hemos hecho nosotros, no la forma más habitual de trabajar ni la única que hay. Pero como estamos intentando comprender conceptos, nos quedamos con eso, el concepto.

Recapitulamos

La rama principal de trabajo la llamamos HEAD (o trunk), los tags nos marcan eventos de nuestro código, generalmente entregas. Las ramas son bifurcacione del código que se usan para modificar versiones antiguas. Los TAGS son versiones fijas del código, nunca se modifica el contenido de un TAG, puesto que es lo que el cliente tiene, si queremos modificar el código, lo copiamos a una rama (branch),  hacemos el cambio y volvemos a crear un nuevo TAG.

En siguientes entradas veremos otros conceptos y las formas de trabajar con los sistemas más comunes de SCM, Subversion, GITMercurial y como resuelven ellos los problemas que Tags y Ramas.