Necesito ayuda con la filosofía y el diseño de una configuración de integración continua.
Nuestra configuración actual de CI utiliza buildbot. Cuando comencé a diseñarlo, heredé (bueno, no estrictamente, ya que estaba involucrado en su diseño un año antes) un generador de CI a medida que se diseñó para ejecutar toda la compilación de una vez, durante la noche. Después de un tiempo, decidimos que esto era insuficiente y comenzamos a explorar diferentes marcos de CI, y finalmente elegimos buildbot. Uno de mis objetivos en la transición al buildbot (además de poder disfrutar de todos los extras geniales) era superar algunas de las deficiencias de nuestro creador nocturno a medida.
Compláceme por un momento y permítame explicarle lo que heredé. La base de código para mi empresa son casi 150 aplicaciones únicas de Windows C ++, cada una de las cuales depende de una o más de una docena de bibliotecas internas (y muchas de bibliotecas de terceros también). Algunas de estas bibliotecas son interdependientes y tienen aplicaciones dependientes que (aunque no tienen nada que ver entre sí) tienen que construirse con la misma compilación de esa biblioteca. La mitad de estas aplicaciones y bibliotecas se consideran "heredadas" e inportables, y deben construirse con varias configuraciones distintas del compilador de IBM (para el que he escrito subclases únicas Compile
), y la otra mitad está construida con Visual Studio.ShellCommand
s, ya que no hay soporte para VSS).
Nuestro creador nocturno original simplemente eliminó la fuente de todo y construyó cosas en un cierto orden. No había forma de construir solo una aplicación, o elegir una revisión, o agrupar cosas. Lanzaría máquinas virtuales para construir una serie de aplicaciones. No era muy robusto, no era distribuible. No era terriblemente extensible. Quería poder superar todas estas limitaciones en buildbot.
La forma en que hice esto originalmente fue crear entradas para cada una de las aplicaciones que queríamos construir (todas las 150), luego crear planificadores activados que pudieran construir varias aplicaciones como grupos, y luego subsumir esos grupos en un planificador de construcción nocturno general. Estos podrían ejecutarse en esclavos dedicados (no más máquinas virtuales), y si quisiera podría simplemente agregar nuevos esclavos. Ahora, si queremos hacer una compilación completa fuera de horario, es un clic, pero también podemos compilar solo una aplicación si así lo deseamos.
Sin embargo, hay cuatro puntos débiles de este enfoque. Una es la compleja red de dependencias de nuestro árbol fuente. Para simplificar el mantenimiento de la configuración, todos los constructores se generan a partir de un diccionario grande. Las dependencias se recuperan y construyen de una manera no terriblemente robusta (es decir, borrando ciertas cosas en mi diccionario de compilación de destino). El segundo es que cada compilación tiene entre 15 y 21 pasos de compilación, lo cual es difícil de examinar y mirar en la interfaz web, y dado que hay alrededor de 150 columnas, lleva una carga infinita (piense de 30 segundos a varios minutos). En tercer lugar, ya no tenemos autodescubrimiento de objetivos de compilación (aunque, por mucho que uno de mis compañeros de trabajo me critique sobre esto, no veo lo que nos consiguió en primer lugar). Finalmente,
Ahora, al pasar a un nuevo desarrollo, estamos comenzando a usar g ++ y subversion (no portamos el repositorio antiguo, eso sí, solo para las cosas nuevas). Además, estamos comenzando a hacer más pruebas unitarias ("más" podría dar una imagen incorrecta ... es más como cualquiera ), y pruebas de integración (usando python). Me está costando descubrir cómo encajarlos en mi configuración existente.
Entonces, ¿dónde me he equivocado filosóficamente aquí? ¿Cómo puedo seguir adelante (con buildbot, es la única pieza del rompecabezas en la que tengo licencia para trabajar) para que mi configuración sea realmente mantenible? ¿Cómo abordo algunas de las debilidades de mi diseño? ¿Qué funciona realmente en términos de estrategias de CI para bases de código grandes (posiblemente demasiado) complejas?
EDITAR:
Pensé que había explicado mi problema, pero obviamente no estaba lo suficientemente claro. Estoy no buscando sugerencias para cambiar las plataformas de CI. Se no va a pasar, y no lo acepten respuestas que sugieren que. Lo que quiero saber es cómo otras personas manejan bases de código complicadas usando CI. Tengo una docena de productos diferentes al cuadrado , y tengo dependencias dispersas al viento, y todas son diferentes. ESTO es con lo que quiero saber cómo lidiar.
Respuestas:
Aunque no me he enfrentado a una situación tan mala como la describe, he mantenido una configuración de CI con decenas de componentes, entre los cuales hay algunas dependencias 'simples'. Espero que mi enfoque pueda darle algunas pistas con las que proceder. El problema definitivamente no está relacionado solo con la elección del servidor CI, sino también con el proceso general de construcción y la estructura del proyecto.
Desglosaré el problema en 2 partes: Edificio y CI.
edificio
Para "Construcción" me refiero al proceso de cambiar el código fuente disponible al artefacto final. Nuestra empresa utiliza principalmente Java en el desarrollo, y la herramienta de compilación que utilizamos es Maven. Probablemente no pueda usar eso debido a la naturaleza de su proyecto, pero hay algunos conceptos valiosos en Maven que vale la pena señalar:
1) En el mundo de Maven, cada artefacto (una lib, el programa real, etc.) debe estar claramente aislado y desacoplado. Las dependencias entre artefactos deben ser claras. Debo enfatizar que la dependencia desordenada, especialmente las dependencias circulares entre los artefactos de compilación harán que el proceso de compilación sea un desastre.
Por ejemplo, vi algunos proyectos de Java antes de eso, aunque después de todo el proceso de compilación hay varios JAR (puede considerarlos como lib / dll en Java), de hecho son interdependientes. Como, A.jar está usando cosas en B.jar, y viceversa. Este tipo de 'modularización' carece totalmente de sentido. A.jar y B.jar siempre deben implementarse y usarse juntos. La implicación es que, más adelante, si desea separarlos en diferentes proyectos (para reutilizar en otros proyectos, por ejemplo), no podrá hacerlo, porque no puede determinar qué proyecto, A o B, construir primero.
Sí, debe ser atendido en el diseño de su software, pero siempre creo que, en lugar de perder tiempo para hacer que las herramientas / modelos de construcción complicados funcionen en un proyecto desordenado, prefiero dedicar tiempo a reorganizar el diseño para utilizar un Modelo de construcción simple.
2) Las dependencias deben ser declarativas. Hay muchos procesos de compilación que vi antes, que incluyen todas las bibliotecas que necesita localmente en el proyecto. Hará que el proceso de compilación sea extremadamente problemático si algunas de las bibliotecas son, de hecho, otros artefactos que necesita compilar.
3) Almacenamiento "centralizado" para los artefactos para obtener dependencias, o para desplegar artefactos una vez que se compila. No necesita estar "centralizado" para toda la oficina (será genial si lo es), solo un directorio local ya estará bien.
Más detalles sobre 2 y 3. Solo para dar un ejemplo, me he encontrado con un proyecto que involucró 3 proyectos independientes. Cada proyecto se basa en la fuente, más las bibliotecas en el directorio lib / en el directorio fuente. El Proyecto A construirá varias librerías, que a su vez es utilizado por los proyectos B y C. Hay algunos inconvenientes: el procedimiento de compilación es complicado y más difícil de automatizar; el control de origen se está hinchando con JAR duplicados innecesarios reutilizados en diferentes proyectos
En el mundo de Maven, lo que se hace es que el proyecto B y C realmente no contiene A.jar (y otras dependencias, como otras bibliotecas de terceros) en la fuente del proyecto. Es declarativo. Por ejemplo, en la configuración de compilación del proyecto B, simplemente declara que necesita: A.lib v1.0, xyz.lib v2.1, etc., y el script de compilación buscará la biblioteca desde /A/1.0/A. jar y /xyz/2.1/xyz.lib
Para el mundo no Maven, este directorio de artefactos solo necesita ser uno o dos directorios con la estructura de directorios acordada. Puede colocar todas las bibliotecas de terceros en una ubicación compartida y permitir que los desarrolladores se sincronicen o copien en sus máquinas locales. En mis proyectos de C ++ que hice muchos años antes, lo que estoy haciendo es establecer la biblioteca y el encabezado en $ {artifact_dir} / lib_name / ver, y con artifact_id declarado como variable de entorno.
Cuando se construye el proyecto A, tendrá una copia de su resultado en ese artifact_dir, de modo que cuando construyamos el proyecto B, B pueda obtener el resultado de A automáticamente, sin copia manual.
4) Liberación no mutable. Una vez que lance A.lib 1.0, listo, no esperará que el contenido de A.lib 1.0 cambie después de 1 mes solo porque hay algunas correcciones de errores. En tal caso, debería ser A.lib 1.1. El artefacto de una base de código cambiante debería considerar una "versión" especial, para lo cual en Maven lo llamamos instantánea.
La liberación no mutable es más un problema ético. Pero lo que resolvió es claro: cuando tienes muchos proyectos, usando la misma biblioteca, sabes qué versión de esa biblioteca estás usando, y estarás seguro de que, la misma versión de la biblioteca usada en diferentes proyectos, es realmente la misma . Creo que muchos de nosotros hemos pasado por el problema de: ¿Por qué los proyectos X e Y están usando lib A pero el resultado de la compilación es diferente? (y resulta que la lib A que usa X e Y es diferente después de profundizar en el contenido o el tamaño del archivo de la lib).
Todo esto garantiza que sus proyectos se puedan construir de forma independiente, sin muchos trucos manuales, como construir el proyecto A primero, copiar A.lib al proyecto B y luego construir el proyecto B ...
Después de realizar un ejercicio como este, por ejemplo, cuando crea el proyecto A, intentará obtener las dependencias del almacenamiento centralizado de artefactos. En caso de que no se encuentren algunas dependencias (que son otros proyectos de su empresa, por ejemplo, el proyecto B), lo que debe hacer es obtener la fuente del proyecto B, compilarlo (que se implementará en el almacenamiento centralizado en caso de éxito) y luego construya el proyecto A nuevamente.
CI
Con un sistema de compilación fácil, con dependencias claras, CI será mucho más fácil. Esperaré que su servidor CI cumpla con los siguientes requisitos:
1) Supervise el control de origen, solo finalice la compra + compilación cuando haya cambios en el origen
2) Capaz de configurar dependencias entre proyectos
Con una dependencia clara para los proyectos, simplemente necesita configurar proyectos en CI de acuerdo con sus proyectos reales, configurar la dependencia del proyecto de CI de acuerdo con la dependencia de sus proyectos. Su servidor CI debe estar configurado para construir proyectos dependientes antes de construir cualquier proyecto (y, por supuesto, la construcción solo ocurre si la fuente del proyecto realmente ha cambiado).
Si todo va bien, debe tener un CI enorme, complejo pero manejable (y aún más importante, una estructura de proyecto manejable)
fuente
Lo que ha funcionado en situaciones similares para mí:
Te sugiero que trates la compilación en sí misma como un proyecto de desarrollo de software. Esto significa que necesita modularizar la base de código de compilación y escribir algunas pruebas automatizadas para ello (por ejemplo, compile un número de revisión conocido y verifique si produce resultados correctos).
fuente
Consideraría a Jenkins como un servidor de CI, puede ver las características aquí:
https://wiki.jenkins-ci.org/display/JENKINS/Meet+Jenkins
¿Por qué? Es fácil de instalar, tiene una interfaz maravillosa, es fácil de configurar y ampliar y hay MUCHOS complementos listos para casi todo:
https://wiki.jenkins-ci.org/display/JENKINS/Plugins
Darle una oportunidad :)
fuente
Ahora usamos Go by ThoughtWorks antes de eso, estábamos usando CruiseControl.Net . Ha sido útil teniendo en cuenta que tenemos dos equipos a medio mundo de distancia y hay una gran diferencia de zona horaria entre nosotros.
Estamos gestionando múltiples proyectos y la mayoría de las tareas implica superposiciones entre dos desarrolladores en ambos lados del mundo, por lo que debemos tener en cuenta que todos los archivos no deben romper la compilación de otra persona.
Además, la administración es más fácil con Go y al ser un experto en procesos ágiles, nos ha dado menos dolores de cabeza después de instalarlo. Las pruebas también se han integrado con Go.
fuente