Le branchement rompt l'intégration continue?

18

Je pense que cet article, A Successful Git Branching Model , est très bien connu des utilisateurs expérimentés de DVCS.

J'utilise hgprincipalement, mais je dirais que cette discussion est très bien pour n'importe quel DVCS.

Notre flux de travail actuel est que chaque développeur clone le référentiel maître. Nous écrivons du code sur notre propre dépôt local, exécutons des tests et, si tout se passe bien, poussons le maître.

Nous voulons donc configurer des serveurs CI comme Jenkins et améliorer notre flux de travail avec le futur système d'approvisionnement (chef, marionnette, ansible, etc.).

Partie réelle

Eh bien, le modèle présenté ci-dessus fonctionne bien mais les branches peuvent casser l'IC. La branche de fonctionnalité devrait se synchroniser avec l'origine (selon l'article, ce serait une developmentbranche) pour rendre le CI et la fusion fluides, non?

Imaginons qu'Alice et Bob travaillent sur deux fonctionnalités. Mais Alice a fini le lendemain. Le long métrage de Bob prend une semaine. Au moment où Bob a terminé, ses modifications sont obsolètes (peut-être qu'Alice a refactorisé / renommé certaines classes).

Une solution consiste chaque matin, les développeurs doivent tirer master/originpour vérifier s'il y a des changements. Si Alice s'est engagée, Bob devrait tirer et fusionner dans son espace de travail afin que sa branche de fonctionnalité soit à jour.

  1. Est-ce un bon moyen?
  2. Ces branches doivent-elles exister dans le référentiel maître (pas dans le clone local?). Cela signifie-t-il que chaque développeur doit avoir des privilèges de validation sur le référentiel maître sur GitHub / Bitbucket pour pouvoir créer une nouvelle branche? Ou cela se fait localement?
  3. Enfin, le modèle présenté par l'article devrait casser CI si les branches ne sont pas synchronisées avec le origin/master. Étant donné que nous souhaitons effectuer la génération tous les soirs, les développeurs doivent-ils tirer et fusionner avant de quitter le travail et avoir également des exécutions CI sur chaque branche de fonctionnalité?
CppLearner
la source

Réponses:

12

Tout d'abord, l'utilisation des branches de fonctionnalités (pour isoler le travail effectué sur une fonctionnalité) et CI (pour trouver les problèmes d'intégration dès qu'ils sont validés) sont légèrement en désaccord.

À mon avis, exécuter CI sur des branches de fonctionnalité est une perte de temps. Comme les branches de fonctionnalités vont et viennent fréquemment, l'outillage CI devrait être reconfiguré encore et encore. Et cela pour une branche qui n'obtient probablement que des mises à jour provenant d'une ou deux sources qui coordonnent leurs enregistrements pour éviter les problèmes qu'un système CI est censé détecter.
Par conséquent, il est également inutile d'avoir les branches de fonctionnalité sur le serveur de référentiel maître.

Quant aux questions 1 et 3: il est de la responsabilité du développeur de s'assurer que la construction sur la branche de développement principale ne se casse pas lorsqu'il y fusionne sa branche de fonctionnalités. Comment ils font cela est leur problème, mais deux façons possibles sont:

  • Tirez régulièrement les modifications apportées à la branche de développement principale dans la branche des fonctionnalités (par exemple, quotidiennement)
  • Lorsque la fonctionnalité est terminée, fusionnez la branche de développement principale dans la branche de fonctionnalité et poussez le résultat de la fusion sur la branche de développement principale.

Dans les deux cas, les problèmes d'intégration évidents (par exemple les classes / fichiers renommés) sont trouvés et résolus en premier sur la branche de fonctionnalité. Les problèmes les plus subtils ne sont très probablement trouvés que lorsque la construction nocturne s'exécute et doivent être corrigés de temps en temps.

Bart van Ingen Schenau
la source
Je suis d'accord que l'utilisation des branches de fonctionnalités est (légèrement) en contradiction avec le concept CI. Cependant, il est possible de créer un système CI qui ne nécessite pas de reconfiguration pour s'exécuter sur des branches de fonctionnalités. (Je l'ai fait dans le passé avec quelques scripts python simples), et cela peut être utile lorsque vos branches "fonctionnalité" sont réellement utilisées comme branches de stabilisation de version, où CI est absolument requis.
William Payne du
1
En fait, je pense que nous avons besoin de deux branches "centrales" - une en tant que branche "throwaway_integration" qui existe uniquement comme une vérification rapide de fusion et de test pour les fonctionnalités activement en cours de développement, et d'une autre branche "master" ou "stable" qui contient des caractéristiques après avoir atteint un certain niveau de stabilité / maturité. Les développeurs extraient du code stable pour travailler à partir de la deuxième branche "stable" / "maître", et fusionnent et poussent fréquemment les modifications vers la première branche "instable" / "throwaway_integration". Les tests CI doivent bien sûr s'exécuter sur les deux branches.
William Payne du
@WilliamPayne: Je crois qu'une telle branche "instable" est souvent appelée "développer"
Bart van Ingen Schenau
5

En réponse à 1)

Toute façon qui fonctionne est une bonne façon. Cependant : toute la prémisse de l'intégration continue est de s'intégrer en continu . L'idée est de détecter les bogues d'intégration non seulement le plus tôt possible, mais dans le cycle de rétroaction du développement - c'est-à-dire que tous les détails du code testé sont dans la mémoire à court terme du développeur effectuant les modifications. Cela signifie que, dans des circonstances normales et quotidiennes, le travail doit être intégré entre les branches de fonctionnalités à chaque validation - peut-être une fois toutes les 15 minutes environ. Pour réitérer: Le but principal de l'intégration continue est d'exposer les bogues d'intégration tandis que tous les détails sont dans la mémoire à court terme du ou des développeurs effectuant les changements.

2)

Généralement, les branches sont créées dans Mercurial en clonant les référentiels, vous n'avez donc pas besoin d'accorder à chaque développeur des privilèges de validation sur le référentiel maître. Cependant, vous souhaitez probablement donner aux développeurs la possibilité de créer des référentiels clonés sur le serveur d'intégration continue, car il n'est pas toujours possible d'exécuter des tests localement. (J'ai déjà eu un système CI où les tests unitaires ont pris 8 heures pour s'exécuter sur un cluster de 128 cœurs) - Inutile de dire que les développeurs ne l'ont pas fait, ne pouvaient pas exécuter les tests localement.

3)

Si vous disposez des ressources de calcul nécessaires, oui, les développeurs doivent être à jour en permanence avec la ligne de développement principale, en particulier avant de quitter le travail, et vous devez exécuter des tests du jour au lendemain sur toutes les lignes de développement (bien que la plupart des systèmes CI ne rend pas cela facile).

William Payne
la source
1
"La plupart du temps, les branches sont créées dans Mercurial en clonant les référentiels" - cela peut avoir été vrai en 2013, mais de nos jours, les signets Mercurial sont fonctionnellement équivalents aux branches Git en tout sauf le nom.
Kevin
@Kevin: Vous avez très probablement raison. J'utilise git (presque) exclusivement depuis février 13 - environ un mois après avoir écrit la réponse ci-dessus ... donc je ne suis pas particulièrement à jour sur les changements qui sont arrivés à Mercurial depuis lors.
William Payne du
1

Voici comment vous pouvez le faire: ramification de fonctionnalités.

  1. Pour toute nouvelle tâche (correction de bug, fonctionnalité, etc.), démarrez une nouvelle branche (par exemple bugfix-ticket123-the_thingie_breaks)
  2. Pendant que vous travaillez, mettez à jour et fusionnez continuellement défaut (ou master in git) dans votre branche de fonctionnalité . Cela vous aide à mettre à jour votre branche sans avoir à travailler dans la branche par défaut
  3. Lorsque votre fonctionnalité est prête et que vos tests unitaires réussissent , puis tirez et fusionnez à nouveau la valeur par défaut dans votre branche, fermez votre branche et poussez-la sans fusionner , votre intégrateur remarquera la nouvelle tête et qu'il s'agit d'une branche fermée, donc il / elle veillera à l'intégrer. Si vous n'avez pas d'intégrateur, passez à la valeur par défaut et fusionnez votre branche de fonctionnalité à celle par défaut .

L'important ici est que vous aurez 0 conflits dans la branche par défaut lorsque vous y fusionnerez votre branche de fonctionnalité, et tous vos tests réussiront .

Avec ce flux de travail simple, vous aurez toujours une branche par défaut vierge et stable, faites de même pour les branches de version, mais en intégrant par défaut . Si vous devez intégrer des correctifs directement dans les branches de publication, vous pouvez toujours le faire en ignorant la branche par défaut, mais encore une fois, en ne sélectionnant que les branches qui viennent d'être mises à jour dans la branche cible et sans conflit et leurs tests unitaires réussissent.

dukeofgaming
la source
Vous mélangez et substituez des choses plutôt orthogonales. 0 conflit de fusion! = 0 test unitaire défectueux, fusion réussie! = Code réussi
Lazy Badger
Ajout de quelques précisions, j'ai oublié de mentionner que les tests unitaires devraient également réussir :)
dukeofgaming