Introducción
El objetivo principal es definir la estrategia de ramificación a implementar en el desarrollo de software bajo las prácticas DevSecOps en la Agencia Digital de Andalucía (ADA). Con esta estrategia se proporcionan un conjunto claro de prácticas para los equipos de desarrollo y servirá como guía para conocer los entornos en los que se va a trabajar, los perfiles involucrados en el proceso de desarrollo de software y despliegue.
Las estrategias de ramificación en el desarrollo de software constituyen un componente crítico para la gestión eficaz del código y la colaboración en equipo. La elección de la técnica más adecuada es una tarea importante, ya que impacta directamente en la eficiencia del flujo de trabajo colaborativo y en la capacidad de un equipo para desplegar cambios. A continuación, se detalla la estrategia a implementar en el desarrollo de software en la ADA.
Una estrategia de ramificación en Git es un enfoque organizativo para estructurar el repositorio de código. Las estrategias de ramificación ayudan a normalizar el uso del repositorio por todos los actores participantes al establecer convenciones y prácticas comunes para la creación, nombramiento y gestión de ramas. Además, proporcionan una estructura organizativa clara que facilita la colaboración entre el propio equipo de desarrollo, así como con otros actores que puedan participar en el ciclo de vida del desarrollo de software, al tiempo que fomentan la estabilidad, la trazabilidad y la calidad del código.
Entorno de trabajo
En este apartado se presentan los entornos en los que se va a implementar la estrategia de ramificación, los equipos que participarán en todo el ciclo de vida del desarrollo de software y algunas técnicas que se consideran de gran importancia realizar para tener un proceso de desarrollo de software de alta calidad. Para mayor entendimiento de la estrategia de ramificación que se describe a continuación, primero se deben tener en cuenta los siguientes conceptos:
- Rama (branch): línea independiente de desarrollo que representa una secuencia de confirmaciones (commits). Las ramas permiten a los desarrolladores trabajar en características nuevas, solucionar problemas o realizar experimentos sin afectar directamente al código en la rama principal.
- Rama protegida: Una rama protegida es una rama que está configurada para tener ciertas restricciones de acceso y seguridad. Estas restricciones están diseñadas para prevenir cambios no autorizados y garantizar la integridad del código en la rama. Normalmente, se modificarán vía solicitud de fusión.
- Commit: representa un punto en la historia del repositorio donde se registran cambios específicos en los archivos. Cada commit contiene una instantánea del estado de los archivos en ese momento, junto con un mensaje descriptivo que explica los cambio realizados.
- Push: operación que envía los commits locales que se han realizado en el repositorio de desarrollo local a un repositorio remoto, en este caso GitLab. En el momento en que se realiza esta operación, se actualiza el historial de cambios en el repositorio remoto con los “commits” desarrollados en el entorno local.
- Fusión entre ramas (merge): proceso de combinar los cambios de una rama en otra.
- Solicitud de fusión (merge request): también conocido como Pull Request (PR) en algunos sistemas de control de versiones, como GitHub, es una solicitud que un colaborador (desarrollador) del proyecto envía al repositorio principal para fusionar los cambios realizados en una rama secundaria con otra rama. Cuando un desarrollador completa una funcionalidad o una serie de cambios en una rama secundaria de un repositorio, envía una Solicitud de Fusión(Merge Request) al repositorio principal para que los revisores examinen los cambios propuestos y decidan si se debe fusionar con la rama destino. El MR incluye detalles sobre los cambios realizados, como qué archivos se modificaron, qué líneas de código se agregaron o eliminaron y una descripción detallada de los cambios.
- Tag: referencia estática a un punto específico en la historia de un repositorio. Es una forma de marcar ciertos commits como importantes, como versiones específicas de un proyecto.
- Integración continua (CI): práctica de desarrollo de software en la que los cambios realizados por los desarrolladores se integran en un repositorio compartido de manera frecuente y regular, lanzándose verificaciones automáticas sobre dichos cambios.
- Entrega continua (CD): este proceso se enfoca en automatizar la entrega de software hasta su puesta en producción. Este método se llevará a cabo, aunque siga existiendo un punto de decisión humano para llevar dicho cambio.
- Despliegue continuo (CD): En el despliegue continuo, el proceso automatizado de entrega va un paso más allá al implementar automáticamente cada cambio de código que pase por el proceso de entrega directamente en producción, sin intervención humana. Esto significa que cualquier cambio de código que supere con éxito las pruebas y controles de calidad se despliega automáticamente en producción de manera inmediata y sin intervención manual.
Entornos de despliegue
Cuando se habla de "entornos de trabajo", se hace referencia a los distintos entornos de tránsito por lo que pasará el software durante su ciclo de vida en la ADA, desde las pruebas de desarrollo, la entrega temprana para su integración continua, hasta el despliegue final en explotación. Dependiendo de las necesidades del sistema de información, es posible que se pueda contar con entornos adicionales donde realizar despliegues. Cada uno, con un objetivo y uso especifico, siempre alineado con lo indicado en los entornos de ejecución del software bajo DevSecOps.
Normalmente, se tendrán en común, los siguientes entornos:
- Test
- Preproducción
- Producción
Equipos de trabajo
En el flujo propuesto para la ADA, se ha definido un proceso colaborativo en el que participarán activamente los siguientes equipos clave (la responsabilidad de cada equipo se describe en el Modelo Operativo DevSecOps en el apartado Roles y participantes):
- Desarrollo.
- Operación: Este equipo, además participarán en el flujo realizando las aprobaciones necesarias para las integraciones sobre las ramas de pre y pro.
- Oficina de calidad.
Roles y permisos de usuarios
En la estrategia de ramificación los permisos de usuarios que afectan principalmente son los que se refieren a la gestión de ramas, fusiones y solicitudes de fusión.
El detalle sobre estos permisos y los roles a los que afectan se describen en el apartado Roles y permisos de usuarios de la página Como se gestionan los permisos en Gitlab.
Estrategia de ramificación
Se han evaluado y probado distintas estrategias existentes (GitFlow, GitHub Flow, GitLab Flow, Release Flow, Trunk-Based Development, Ship/Show/Ask, Feature Branch WorkFlow, Forking Flow, One Flow), para estudiar cual podría adaptarse mejor a los flujos actuales de trabajo y nivel de madurez de la organización, dando como resultado el flujo ADA Flow.
ADA Flow
Como resultado de la exploración y experimentación con distintos flujos de trabajo en Git, y del análisis del funcionamiento de la organización, se propone el uso de un flujo de trabajo simplificado y pragmático que toma como base la estrategia de ramificación GitLab Flow.
GitLab Flow como metodología utiliza una única rama principal (generalmente main o master) para el desarrollo continuo. Fomenta el uso de entornos de revisión de código integrados y la automatización del ciclo de vida del desarrollo a través de características como pipelines de CI/CD (Continuous Integration/Continuous Deployment). GitLab Flow está diseñado para aprovechar las capacidades específicas de la herramienta GitLab y así, ofrecer una experiencia de desarrollo fluida y eficiente al equipo de desarrollo.
En la siguiente figura, se puede ver una representación del flujo de trabajo de GitLab Flow.
Algunas de las características principales de GitLab Flow son:
-
Ramificación simple: promueve un modelo de ramificación sencilla, donde cada funcionalidad o corrección de errores se desarrolla en una rama independiente, facilitando el seguimiento de los cambios y el trabajo colaborativo.
-
Merge Request: técnica que permite la revisión de los cambios propuestos antes de fusionar con cualquier rama. Esto conlleva la revisión por pares, mejora la calidad del código y ayuda a evitar errores.
-
Entornos de despliegue: está metodología promueve la creación de entornos que reflejen cada etapa del ciclo de vida del software para probar y validar los cambios en un entorno controlado antes de llegar a producción. Algunos de los entornos propuesto son: pruebas, preproducción y producción.
-
Automatización de pruebas: GitLab proporciona herramientas para configurar pruebas automáticamente y despliegue de manera segura.
-
Retroalimentación: fomenta la retroalimentación continua a lo largo de todo el proceso de desarrollo de software. Esto incluye la revisión de código, pruebas automáticas, seguimiento de errores para identificar problemas rápidamente y realizar las mejoras adecuadas.
En conclusión, está metodología ofrece un marco de trabajo estructurado que facilita aplicar estrategias agiles para el desarrollo de software, el trabajo colaborativo, y la entrega y despliegue continuo.
Ramas de larga duración
Las ramas de larga duración en el repositorio de GitLab son aquellas que mantendrán una base estable para el desarrollo de software. Como mínimo existirán las siguientes ramas:
- main: Rama principal de desarrollo. Contiene la versión más actualizada con estado estable. Es la rama a partir de la cual se derivan y fusionan otras ramas para desarrollar nuevas características o marcar hitos. Será una rama protegida.
- Ramas protegidas por entorno:
- pru: Rama que representa el estado del código en entorno de TEST.
- pre: Rama que representa el estado del código en entorno de Pre-producción.
- pro: Rama que representan el estado del código en entorno de Producción.
Pueden existir ramas por entorno adicionales, dependiendo del número de entornos del que disponga del sistema de información, pero todas tendrán la misma consideración.
Ramas temporales
Las ramas temporales se usarán para el desarrollo de características o correcciones. Estas ramas proporcionaran un entorno aislado para trabajar en cambios sin afectar el flujo de trabajo principal del proyecto.
- feature/<nombre de la funcionalidad>: rama que se crea específicamente para desarrollar una nueva característica o funcionalidad.
- release/x.y.z: rama de versión, creada automáticamente a partir de un tag especifico cuando se activa el proceso de integración continua. Esta rama se utilizará para fusionarse con cada rama que represente un entorno. Esta rama podrá ser desplegada en los entornos de pru, pre y pro. Para versionar de forma correctael código, se tienen que seguir las directrices marcadas en Política de versionado softwate.
- test/x.y.z: rama de test, creada a partir de un tag especifico de la rama main, que se utilizará para fusionar con la rama pru disparando así el despliegue en dicho entorno.
- hotfix/<nombre de la funcionalidad>: rama donde se realizan los cambios que sean necesarios para solucionar errores en la rama de pro.
- hotfix/x.y.z: rama de versión, creada a partir de un tag especifico de la rama hotfix, que se utilizará para fusionar con la rama pro, disparando así el despliegue en dicho entorno. Además, para realizar pruebas específicas, esta rama podrá ser desplegada en los entornos de pru, pre y pro.
Estas ramas temporales serán eliminadas en cuanto se apruebe una fusión con la rama correspondiente.
Técnicas a implementar
A continuación, se presentan algunas técnicas que se consideran importantes implantar en la estrategia de ramificación propuesta.
- Cherry pick: Es una técnica utilizada para cuando se desea incorporar cambios específicos de una rama a otra sin fusionar toda la rama completa. Es decir, permite copiar un commit especifico y aplicarlo sobre la rama actual en la que se encuentra el usuario. Esta técnica se debería realizar para los casos de:
- Corrección de errores críticos en la rama de producción: si se descubre un error critico en el entorno de producción que necesita una solución urgente, se puede aplicar cherry-pick para seleccionar y aplicar el commit que contiene la corrección directamente a la rama de producción sin tener que fusionar todas las características (feature) que se estén desarrollando en la rama main.
- Aplicación selectiva de características: en determinadas situaciones se puede necesitar aplicar los cambios de una característica especifica que ya está desarrollada en la rama main a otra rama. En este caso, en lugar de fusionar toda la rama, se utiliza el cherry pick para seleccionar y aplicar únicamente los commits que contiene la característica deseada a la rama objetivo.
- Merge request (Solicitud de fusión): es una técnica fundamental en el flujo de trabajo colaborativo que permite a los desarrolladores solicitar la revisión y la incorporación de sus cambios a una rama principal del repositorio.
Descripción de la estrategia de ramificación
En esta sección se va a describir la estrategia de ramificación a implementar por los Sistemas de Información en la ADA.
En la siguiente figura se puede observar una visión general de esta estrategia.
En la Figura anterior se pueden reconocer los siguientes actores:
Rama Main
Es la rama principal del proyecto. Las características más importantes de esta rama son:
- De main van a partir las ramas de funcionalidades (feature).
- La estabilidad y fiabilidad de esta rama se garantizará ejecutando procesos de CI sobre cualquier cambio que se quiera realizar a la misma.
- Main es una rama protegida, así que cualquier cambio debe solicitarse vía Solicitud de fusción (MR, Merge Request). Esta solicitud solo puede ser aceptada por miembros del equipo con rol Maintainer o superior. En cuanto se realiza la MR se activa el proceso de CI para garantizar la calidad del código que se desea integrar en la rama main. Si el proceso de CI termina su ejecución satisfactoriamente se habilitará la opción de fusionar (merge) la rama especifica con main.
- De main se crearán las versiones a desplegar en los distintos entornos mediante tags.
Rama feature o de características
Esta rama es creada específicamente a partir de main para desarrollar un nueva característica o funcionalidad del proyecto, y debe llevar la siguiente nomenclatura feature/< nombre de la funcionalidad>. Para realizar una fusión de esta rama con main se debe solicitar vía Merge Request (MR). El usuario con rol Maintainer debe revisar y aprobar esta solicitud de fusión.
Tag
Es una referencia a un commit particular del repositorio. Se suele utilizar para indicar la versión del software, permitiendo acceder a ellos fácilmente. Un tag es inmutable, es decir, cuando se crea un tag ya no se puede modificar el código que le contiene, esto garantizará la estabilidad del punto especifico del repositorio con el que ha sido creada.
Los tags que se quieran generar deben partir de la rama main. Al crear un tag se activará el proceso CI para evaluar la calidad del código que automáticamente creará una rama con una versión según el entorno en el que se quiera desplegar. Los casos que pueden presentarse son:
- Para desplegar en el entorno de test, se debe crear un tag con la siguiente nomenclatura x.y.z-test que al lanzar el proceso CI creará la rama test/x.y.z de forma automática y solo podrá ser desplegada en el entorno de pruebas (Test).
- Para desplegar en los entornos de Pre-producción y Producción, se debe crear un tag con la siguiente nomenclatura x.y.z-release, que al lanzar el proceso CI creará una rama de tipo release/x.y.z de forma automática. Esta rama podrá ser desplegada en cualquiera de los entornos, no solo en los de Pre-producción y Producción.
Rama Test/x.y.z
Esta rama es generada automáticamente por el proceso CI al crearse un tag de tipo x.y.z test y se utiliza para fusionarse con la rama pru y desplegarse en el entorno de pruebas (Test). En esta rama no se podrán realizar cambios.
Rama Release/x.y.z
Esta rama representa el estado del código en un punto concreto, normalmente, una versión. Se creará a partir de un tag de tipo x.y.z-release. El despliegue de la versión del código se realizará mediante fusión de esta rama con la rama del entorno donde se quiera desplegar. La rama release/x.y.z se podrá desplegar en cualquier entorno. En esta rama no se podrán realizar cambios.
Entorno de pruebas (TEST)
Entorno donde se podrá desplegar la rama test/x.y.z o la rama release/x.y.z tras la fusión con la rama pru. En este entorno se podrán realizar las pruebas necesarias para validar el código antes de su despliegue en la rama de Pre-producción (pre) o Producción (pro).
Entorno de Pre-Producción (PRE)
Entorno donde se desplegará la rama pre tras una fusión desde la rama release/x.y.z para realizar las pruebas correspondientes para validar la versión del código y desplegarlo en el entorno de Producción (pro). Para realizar la fusión entre estas dos ramas se debe pedir una solicitud de fusión (MR) que el equipo de operaciones debe aprobar. Esta aprobación desplegará la rama pre en el entorno de Pre-producción de forma automática.
Entorno de Producción (PROD)
Entorno donde se podrá desplegar la rama pro tras una fusión desde la rama release/x.y.z. Para realizar esta fusión se debe pedir previamente una solicitud de fusión (MR) que el equipo de operaciones debe aprobar. Esta aprobación desplegará la rama pro en el entorno de Producción de forma automática.
En el caso que se detecten errores en el entorno de Producción, se aconsejan seguir los siguientes pasos:
- Opción preferente:
- Se debe crear una rama feature/<nombre del problema> desde main, donde se realizarán las correcciones correspondientes al error.
- Seguir el flujo normal de trabajo para desplegar estos cambios en producción.
- Opción (hotfix):
- Crear una rama de tipo hotfix/<nombre de la funcionalidad a corregir> desde la rama pro, donde se realizarán las correcciones y pruebas correspondientes al error.
- Se crea un tag x.y.z-hotfix desde la rama hotfix. Este paso activa el proceso CI que validará el código a desplegar. Si todo funciona correctamente, se generará de forma automática la rama hotfix/x.y.z. Esta rama podrá ser desplegada en cualquier entorno.
- Se solicita una solicitud de fusión (MR) para fusionar la rama hotfix/x.y.z con la rama pro.
- El equipo de operaciones aprueba el MR y se despliega el cambio en el entorno de Producción (PROD).
- Crear una rama de con nomenclatura feature-hotfix/<nombre de la funcionalidad a corregir> y mediante la técnica de Cherry pick actualizar esta rama con los cambios realizados en la rama hotfix.
- Solicitar un MR para actualizar la rama main con los cambios realilzados en la rama feature-hotfix/<nombre de la funcionalidad a corregir>. Como ya se tiene actualizada la rama main seguir el flujo de trabajo normal.
- Avisar a todos los miembros del equipo de desarrollo de los cambios realizados para que actualicen sus respectivas ramas de trabajo (features).
Recomendaciones
Commits
En esta sección se describen algunos pasos que se aconsejan llevar a cabo para mejorar la estrategia de ramificación con respecto a los commits. Algunos de los objetivos que se quieren cumplir con estas recomendaciones son:
-
Demostrar claramente el valor de dividir los cambios en commits individuales.
-
Enfatizar la importancia de escribir mensajes de commit informativos.
-
Fomentar el apego a las pautas para mejorar la calidad del historial de Git.
Para seguir una técnica de commits que sea limpia y entendible por todo el equipo de desarrollo se deben tener en cuenta los siguientes aspectos:
-
División Estructural de Cambios:
-
Regla Cardinal: Asegurarse de un cambio lógico por commit.
-
Cosas que Evitar:
-
Mezclar cambios de espacios en blanco con cambios de código funcional.
-
Mezclar dos cambios funcionales no relacionados.
-
Enviar grandes nuevas características en un solo commit gigante.
-
-
Regla Básica: Si un cambio puede dividirse en commits separados, debería hacerlo.
-
-
Información en los Mensajes de Commit:
-
Qué Hacer:
-
Describir el problema original y cómo se está solucionando.
-
Documentar por qué se está haciendo un cambio.
-
Incluir información suficiente para los revisores.
-
Usar un lenguaje claro y conciso.
-
-
Qué No Hacer:
-
Suponer que el revisor comprende el problema original.
-
Suponer que el código es evidente por sí solo.
-
Incluir comentarios específicos del conjunto de parches.
-
-
-
Inclusión de Referencias Externas:
-
Utilizar metadatos para uso de máquinas, como 'Change-id', 'Bug', 'Blueprint', etc.
-
Asegurarse de que todos los metadatos dirigidos a máquinas estén agrupados al final del mensaje de commit.
-
Seguir convenciones estándar para referenciar errores, blueprints y otros recursos externos.
-
-
Resumen de la Estructura del Mensaje de Commit de Git:
-
Proporcionar una breve descripción del cambio en la primera línea (limitado a 50 caracteres).
-
Insertar una línea en blanco después de la primera línea.
-
Proporcionar descripciones detalladas del cambio en líneas subsecuentes, con un límite de 72 caracteres.
-
Idioma de los commits preferiblemente en español.
Ejemplo:
-
[Tarea #121] Añadir autenticación de usuario
-
[Tarea #122] Actualización del servicio de pagos
-
-
Nomenclatura de ramificación
Para estructurar la nomenclatura de ramificación se aconseja seguir el siguiente formato:
-
Ramas de características o feature: feature/<nombre de la funcionalidad>
-
Ramas para hotfix: hotfix/<nombre del problema>
-
Rama para las versiones: release/x.y.z
Nomenclatura de versionado de Tgas
Para estructurar la nomenclatura de ramificación se aconseja seguir el siguiente formato:
-
Para el despliegue en entorno test: x.y.z-test
-
Para el despliegue en entorno de pre o pro: x.y.z-release