Git y dependencias de proyectos
Nicola Paolucci
Experto en desarrollo
Analiza estas preguntas:
¿Cómo gestionas las dependencias de proyectos con Git
?
Nuestro proyecto se compone de varios repositorios interdependientes. Actualmente los gestionamos con svn:externals
. ¿Cuál es la mejor manera de gestionarlos con Git
?
¿Cómo se divide un repositorio muy grande en componentes más pequeños usando Git
?
Estas son algunas de las preguntas más frecuentes.
Este tema parece ser uno de los grandes problemas de muchos equipos de software que adoptan Git
, así que en este artículo intentaré aclararlo un poco.
Obviamente, las dependencias de proyectos y la infraestructura de compilación son dos áreas interrelacionadas, e incluso en Atlassian se ha generado un debate interno sobre el "futuro de las compilaciones".
Tener distintos repositorios en lugar de uno solo puede complicar algunas cosas. No obstante, es un paso relativamente natural (a veces obligatorio) en la evolución de un proyecto de software por, al menos, dos razones principales: el aumento de los tiempos de compilación y las dependencias compartidas entre proyectos.
Visión general: directrices y soluciones que dejan mucho que desear
Volvamos a la pregunta inicial: ¿Cómo supervisas y gestionas las dependencias de proyectos con Git
?
Si es posible, ¡no lo haces!
Bromas aparte, voy a responder primero de forma general y luego profundizaré más en la cuestión. Ten en cuenta que no hay una fórmula mágica, ni en Git
ni en otras soluciones, que resuelva fácilmente todos los problemas relacionados con las dependencias de proyectos.
Una vez que un proyecto supera cierto tamaño, es una buena idea dividirlo en componentes lógicos, pero no esperes a tener más de 100 millones de líneas de código en un único repositorio para hacerlo. Las directrices siguientes son solo pautas para que puedas diseñar tu propia estrategia.
Material relacionado
Instalar Git
VER LA SOLUCIÓN
Aprende a usar Git con Bitbucket Cloud
Primera opción: usar una herramienta de compilación o dependencias apropiada en lugar de Git
Una herramienta de gestión de dependencias es el método que recomiendo actualmente para gestionar los problemas de crecimiento y los tiempos de compilación de los proyectos de gran tamaño.
Mantén cada módulo en un repositorio distinto y gestiona su interdependencia con una herramienta diseñada para ello. Hay una para (casi) cada pila tecnológica existente. Algunos ejemplos:
- Maven (o Gradle) si usas Java
- Npm para aplicaciones de nodo
- Bower, Component.io, etc. si usas JavaScript (actualizado)
- Pip y requirements.txt si usas Python
- RubyGems y Bundler si usas Ruby
- NuGet para .NET
- Ivy (o alguna acción CMake personalizada) para C++ (actualizado)
- Aplicaciones iOS de CocoaPods para Cocoa
- Composer o Phong para PHP (añadido)
- En Go, la infraestructura de compilación o dependencia está bastante integrada en el lenguaje (aunque se ha estado trabajando en una solución más completa; consulta godep). Para nuestro servidor Git (Bitbucket), usamos tanto Maven como Bower. En el momento de la compilación, la herramienta elegida extraerá las versiones correctas de las dependencias para que se pueda compilar tu proyecto principal. Algunas de estas herramientas tienen limitaciones y hacen suposiciones que no son óptimas, pero están probadas y son viables.
Problemas de dividir tu proyecto
Al comienzo de un proyecto, todo se empaqueta de manera simplista en una compilación. No obstante, a medida que el proyecto crece, esto puede hacer que la compilación sea demasiado lenta, por lo que deberás almacenarla "en caché" y aquí es donde entra en acción la gestión de dependencias. Esto, por cierto, significa que los submódulos (consúltalos más abajo) se prestan muy bien a los lenguajes dinámicos, por ejemplo. Básicamente, creo que los tiempos de compilación son algo a lo que todos debemos prestar atención en algún momento. Por eso, deberías usar una herramienta de gestión de dependencias.
Dividir los componentes en distintos repositorios conlleva serios problemas. Aquí los expongo en un orden cualquiera:
- Para realizar un cambio en un componente se requiere una versión.
- Lleva tiempo y puede fallar por muchas razones estúpidas.
- Parece inútil para realizar pequeños cambios.
- Requiere la configuración manual de nuevas compilaciones para cada componente.
- Dificulta la detección de los repositorios.
- Se debe usar la refactorización cuando no todas las fuentes están disponibles en un único repositorio.
- En algunas configuraciones (como la nuestra), la actualización de las API requiere una versión de hito del producto, luego del complemento y de nuevo del producto. Probablemente nos hayamos dejado algunas cosas, pero seguro que entiendes la idea general. Estamos lejos de tener una solución perfecta para resolver el problema.
Segunda opción: usar un submódulo de Git
Si no puedes o no quieres usar una herramienta de dependencias, Git
te permite gestionar submódulos
. Los submódulos pueden ser prácticos, sobre todo para los lenguajes dinámicos. Sin embargo, no siempre lograrán evitar que la compilación sea lenta. Ya he escrito directrices y consejos al respecto y también he buscado alternativas. En Internet en general también hay argumentos en contra de ellos.
Coincidencia 1:1 entre svn:external y Git
Pero, si buscas una coincidencia uno a uno entre svn:externals
y Git
, debes usar submódulos
y asegurarte de que estos submódulos
solo hagan un seguimiento de las ramas de versiones y no de confirmaciones aleatorias.
Tercera opción: usar otras herramientas de compilación y dependencia de pila cruzada
No siempre tendrás un proyecto completamente uniforme y que se pueda compilar y ensamblar con una sola herramienta. Por ejemplo, algunos proyectos móviles necesitarán hacer malabarismos con las dependencias de Java y C++, o usar herramientas patentadas para generar activos. Para esas situaciones más complejas, puedes mejorar Git
con una capa adicional en la parte superior. Un claro ejemplo de ello es el repositorio de Android.
Otras herramientas de compilación que merece la pena considerar:
Conclusiones y lecturas adicionales
Recomendaciones de Charles O'Farrell para leer más sobre la infraestructura de construcción (y Maven):
- In quest of the ultimate build tool
- Y su continuación Maven is broken by design
- Recursive Maven considered harmful
Me gustaría terminar con esta excelente cita del último de los artículos que acabo de mencionar. Aunque hace referencia a Maven, también se podría aplicar a otras herramientas de compilación y dependencias:
"Lo único que hace una caché es acelerar las cosas. Aunque eliminaras una caché por completo, el sistema circundante funcionaría igual, solo que más despacio. Una caché tampoco tiene efectos secundarios. Independientemente de lo que hayas hecho con una caché en el pasado, una consulta concreta a la caché devolverá el mismo valor a la misma consulta en el futuro.
La experiencia de Maven es muy diferente de lo que estoy explicando. Los repositorios de Maven se usan como cachés, pero sin tener las propiedades de las cachés. Cuando pides algo a un repositorio de Maven, lo que has hecho en el pasado es muy importante. La consulta devuelve lo más reciente que has introducido. Incluso puede fallar, si pides algo antes de introducirlo".
Compartir este artículo
Tema siguiente
Lecturas recomendadas
Consulta estos recursos para conocer los tipos de equipos de DevOps o para estar al tanto de las novedades sobre DevOps en Atlassian.