Ramificación rompe la integración continua?

18

Creo que este artículo, A Successful Git Branching Model , es muy conocido entre los usuarios experimentados de DVCS.

Lo uso hgprincipalmente, pero diría que esta discusión está bien para cualquier DVCS.

Nuestro flujo de trabajo actual es que cada desarrollador clona el repositorio principal. Escribimos código en nuestro propio repositorio local, ejecutamos pruebas y, si todo va bien, lo empuja al maestro.

Por lo tanto, queremos configurar servidores CI como Jenkins y mejorar nuestro flujo de trabajo con el futuro sistema de aprovisionamiento (chef, puppet, ansible, etc.).

Parte real

Bueno, el modelo presentado anteriormente funciona bien, pero las ramas pueden romper CI. La rama de la característica debe sincronizarse con el origen (según el artículo, sería una developmentrama) para hacer que CI y la fusión sean suaves, ¿verdad?

Digamos que Alice y Bob están trabajando en dos características. Pero Alice terminó al día siguiente. La característica de Bob lleva una semana. Cuando Bob termina, sus cambios están desactualizados (tal vez Alice refactorizó / renombra algunas clases).

Una solución es cada mañana que los desarrolladores deben sacar master/originpara verificar si hay algún cambio. Si Alice se comprometió, Bob debería ingresar y fusionarse en su espacio de trabajo para que su rama de características esté actualizada.

  1. ¿Es esta una buena manera?
  2. ¿Deberían existir estas ramas en el repositorio principal (no en el clon local?) ¿Es decir, todo desarrollador debe tener privilegios de compromiso para el repositorio principal en GitHub / Bitbucket para poder crear una nueva sucursal? ¿O esto se hace localmente?
  3. Por último, el modelo presentado por el artículo debería romper el CI si las ramas no están sincronizadas con el origin/master. Dado que queremos hacer una compilación nocturna, ¿deberían los desarrolladores extraer y fusionar antes de dejar el trabajo, y hacer que CI se ejecute también en cada rama de características?
CppLearner
fuente

Respuestas:

12

En primer lugar, el uso de ramas de características (para aislar el trabajo realizado en una característica) y CI (para encontrar problemas de integración tan pronto como se comprometan) están ligeramente en desacuerdo.

En mi opinión, ejecutar CI en ramas de características es una pérdida de tiempo. A medida que las ramas de características van y vienen con frecuencia, las herramientas de CI tendrían que reconfigurarse una y otra vez. Y eso para una sucursal que probablemente solo reciba actualizaciones de una o dos fuentes que coordinen sus registros para evitar los problemas que un sistema de CI debe detectar.
Por lo tanto, tampoco tiene sentido tener las ramas de características en el servidor de repositorio maestro.

En cuanto a las preguntas 1 y 3: es responsabilidad del desarrollador asegurarse de que la construcción en la rama de desarrollo principal no se rompa cuando fusionen su rama de características en ella. Cómo lo hacen es su problema, pero dos formas posibles son:

  • Tire de los cambios realizados en la rama de desarrollo principal en la rama de características de forma regular (por ejemplo, diariamente)
  • Cuando termine la función, combine la rama de desarrollo principal en la rama de característica y empuje el resultado de la fusión a la rama de desarrollo principal.

En cualquier caso, los problemas obvios de integración (por ejemplo, clases / archivos renombrados) se encuentran y se solucionan primero en la rama de características. Es probable que los problemas más sutiles solo se encuentren cuando se ejecuta la compilación nocturna y se deben solucionar allí mismo.

Bart van Ingen Schenau
fuente
Estoy de acuerdo en que el uso de ramas de características está (ligeramente) en desacuerdo con el concepto de CI. Sin embargo, es posible crear un sistema de CI que no requiera reconfiguración para ejecutarse en ramas de características. (Lo he hecho en el pasado con algunos simples scripts de Python), y puede ser útil cuando sus ramas de "características" se están utilizando realmente como ramas de estabilización de liberación, donde CI es absolutamente necesario.
William Payne
1
En realidad, creo que necesitamos dos ramas "centrales": una como una rama de "integración desechable" que existe únicamente como una comprobación rápida de fusión y prueba para las características que se están desarrollando activamente, y otra rama "maestra" o "estable" que contiene características después de que hayan alcanzado un cierto nivel de estabilidad / madurez. Los desarrolladores extraen código estable para trabajar desde la segunda rama "estable" / "maestra", y combinan y empujan los cambios con frecuencia a la primera rama "inestable" / "throwaway_integration". Las pruebas de CI deberían ejecutarse en ambas ramas, por supuesto.
William Payne
@WilliamPayne: Creo que una rama tan "inestable" a menudo se llama "desarrollar"
Bart van Ingen Schenau
5

En respuesta a 1)

Cualquier forma que funcione es una buena manera. Sin embargo : toda la premisa de la integración continua es integrarse continuamente . La idea es detectar errores de integración no solo lo antes posible, sino también dentro del ciclo de retroalimentación del desarrollo, es decir, mientras que todos los detalles del código bajo prueba están dentro de la memoria a corto plazo del desarrollador que realiza los cambios. Esto significa que, en circunstancias normales y cotidianas, el trabajo debe integrarse en las ramas de características en cada confirmación, tal vez una vez cada 15 minutos más o menos. Para reiterar: El objetivo principal de la integración continua es exponer los errores de integración, mientras que todos los detalles se encuentran en la memoria a corto plazo del desarrollador (s) que realiza los cambios.

2)

En su mayoría, las sucursales se crean en Mercurial mediante la clonación de repositorios, por lo que no es necesario que otorgue privilegios de confirmación de desarrollador al repositorio principal. Sin embargo, es probable que desee ofrecer a los desarrolladores la capacidad de crear repositorios clonados en el servidor de integración continua, ya que no siempre es posible ejecutar pruebas localmente. (Una vez tuve un sistema de CI en el que las pruebas unitarias tardaron 8 horas en ejecutarse en un clúster de 128 núcleos). No hace falta decir que los desarrolladores no pudieron ejecutar pruebas localmente.

3)

Si tiene los recursos computacionales para ello, sí, los desarrolladores deben estar completamente actualizados con la línea principal de desarrollo en todo momento, particularmente antes de dejar el trabajo, y debe ejecutar pruebas nocturnas en todas las líneas de desarrollo (aunque la mayoría de los sistemas de CI no hagas esto fácil).

William Payne
fuente
1
"En su mayoría, las sucursales se crean en Mercurial mediante la clonación de repositorios"; esto puede haber sido cierto en 2013, pero en estos días, los marcadores de Mercurial son funcionalmente equivalentes a las sucursales de Git en todo menos en el nombre.
Kevin
@ Kevin: Probablemente tengas razón. He estado usando git (casi) exclusivamente desde febrero '13, aproximadamente un mes después de que escribí la respuesta anterior ... por lo que no estoy particularmente actualizado sobre los cambios que le han sucedido a Mercurial desde entonces.
William Payne
1

Así es como puede hacerlo: ramificación de características.

  1. Para cualquier tarea nueva ( corrección de errores , función, etc.), inicie una nueva rama (por ejemplo, bugfix-ticket123-the_thingie_breaks)
  2. Mientras trabaja, actualice y combine continuamente el valor predeterminado (o maestro en git) en su rama de características . Esto le ayuda a actualizar su sucursal sin tener que trabajar en la sucursal predeterminada
  3. Cuando su función esté lista y las pruebas de su unidad pasen , luego tire y combine el valor predeterminado en su rama una vez más, cierre su rama y empújela sin fusionar , su integrador notará la nueva cabeza y que es una rama cerrada, por lo que él / ella se encargará de integrarlo. Si no tiene integrador, cambie a predeterminado y combine su rama de características en predeterminada .

Lo importante aquí es que tendrá 0 conflictos en la rama predeterminada cuando combine su rama de características en ella, y todas sus pruebas pasan .

Con este flujo de trabajo simple, siempre tendrá una ramificación predeterminada impecable y estable, ahora hará lo mismo para las ramificaciones de lanzamiento, pero se integrará de manera predeterminada . Si necesita integrar las revisiones directamente en las ramas de lanzamiento, puede hacerlo omitiendo la rama predeterminada, pero de nuevo, solo seleccionando las ramas que se acaban de actualizar desde la rama de destino y no tienen conflictos y sus pruebas unitarias pasan.

dukeofgaming
fuente
Mezclas y sustituyes cosas bastante ortogonales. 0 conflicto de fusión! = 0 prueba de unidad defectuosa, fusión exitosa = código exitoso
Lazy Badger
Se agregó alguna aclaración, se olvidó de mencionar que las pruebas unitarias deberían pasar también :)
dukeofgaming