Structure de référentiel Mercurial avec communications d'entreprise lourdes, gestion de la configuration et exigences de test

16

Je suis encore un autre utilisateur de Subversion qui a du mal à me rééduquer dans le Tao du contrôle de version distribué.

Lorsque j'utilisais Subversion, j'étais un grand fan de l'approche des projets mineurs et, avec la plupart de mes anciens employeurs, nous structurions nos succursales de référentiel; balises et tronc comme suit:

branches-+
         +-personal-+
         |          +-alice-+
         |          |       +-shinyNewFeature
         |          |       +-AUTOMATED-+
         |          |                   +-shinyNewFeature
         |          +-bob-+
         |                +-AUTOMATED-+
         |                            +-bespokeCustomerProject
         +-project-+
                   +-shinyNewFeature
                   +-fixStinkyBug
tags-+
     +-m20110401_releaseCandidate_0_1
     +-m20110505_release_0_1
     +-m20110602_milestone
trunk

Dans l'arborescence source proprement dite, nous utiliserions (quelque chose comme) la structure suivante:

  (src)-+
        +-developmentAutomation-+
        |                       +-testAutomation
        |                       +-deploymentAutomation
        |                       +-docGeneration
        |                       +-staticAnalysis
        |                       +-systemTest
        |                       +-performanceMeasurement
        |                       +-configurationManagement
        |                       +-utilities
        +-libraries-+
        |           +-log-+
        |           |     +-build
        |           |     +-doc
        |           |     +-test
        |           +-statistics-+
        |           |            +-build
        |           |            +-doc
        |           |            +-test
        |           +-charting-+
        |           |          +-build
        |           |          +-doc
        |           |          +-test
        |           +-distributedComputing-+
        |           |                      +-build
        |           |                      +-doc
        |           |                      +-test
        |           +-widgets-+
        |                     +-build
        |                     +-doc
        |                     +-test
        +-productLines-+
        |              +-flagshipProduct-+
        |              |                 +-coolFeature
        |              |                 +-anotherCoolFeature
        |              |                 +-build
        |              |                 +-doc
        |              |                 +-test
        |              +-coolNewProduct
        +-project-+
                  +-bigImportantCustomer-+
                  |                      +-bespokeProjectOne
                  |                      +-bespokeProjectTwo
                  +-anotherImportantCustomer-+
                                             +-anotherBespokeProject

L'idée était (et est toujours) d'utiliser la structure du référentiel pour aider à structurer la communication entre l'équipe d'ingénierie; la partie client de l'entreprise et divers autres intervenants et experts du domaine.

À savoir: les documents sources qui se trouvent dans l'un des répertoires "projet" ne sont utilisés (et gagnent de l'argent) qu'une seule fois. Les documents qui se trouvent dans l'un des répertoires "productLines" gagnent de l'argent autant de fois qu'un produit de cette ligne particulière est vendu. Les documents qui se trouvent dans l'un des répertoires des "bibliothèques" gagnent de l'argent autant de fois que n'importe lequel des produits qui les utilisent est vendu.

Il rend explicite la notion d'amortissement des coûts et aide à renforcer la prise en charge de la réutilisation des documents source dans toute l'entreprise.

Cela signifie également qu'il existe une structure commune sur laquelle nos outils d'automatisation de construction peuvent fonctionner. (Nos scripts de construction parcourent l'arborescence source à la recherche de dossiers "build" dans lesquels ils trouvent des fichiers de configuration spécifiant comment chaque composant doit être construit; un processus similaire se produit pour la génération et le test de la documentation).

De manière significative, les produits sur lesquels je travaille prennent généralement beaucoup de temps pour exécuter des tests de mesure et de caractérisation des performances; de 20 à 200 heures; générer quelque part entre plusieurs Go et plusieurs To de résultats de test / données intermédiaires traités (qui doivent être stockés et liés à une configuration de système particulière afin que l'amélioration des performances au fil du temps puisse être mesurée). Ce problème fait de la gestion de la configuration une considération importante et impose également une certaine exigence de centralisation, car généralement les ressources de calcul nécessaires pour exécuter les tests de mesure et de caractérisation des performances sont limitées; (un petit groupe de 64-128 cœurs).

Comme une dernière note; le système d'intégration continue sait qu'il doit déclencher une construction; analyse statique; test de fumée et test unitaire exécutés chaque fois que le tronc est modifié, chaque fois qu'une branche "tag" est modifiée, et chaque fois qu'une branche "AUTOMATISÉE" est modifiée. De cette façon, les développeurs individuels peuvent utiliser le système CI avec leurs branches personnelles, une capacité importante, à mon humble avis.

Maintenant, voici ma question: comment puis-je reproduire tout ce qui précède (et l'améliorer, si possible), avec Mercurial.

--Éditer:

Ma réflexion actuelle consiste à utiliser un référentiel Subversion central, à définir la structure globale, mais à autoriser l'utilisation de hg en tant que client afin que les développeurs puissent avoir des référentiels disponibles localement.

William Payne
la source
1
Sensationnel. Une bonne réponse à cela va être un très long essai, je pense.
Ed James
Je pense que la question clé est de savoir comment et où vont les fusions de code car cela définira probablement le chemin de moindre résistance. Alors, comment le code est-il fusionné?
Wyatt Barnett
En règle générale, une fusion peut provenir d'une branche personnelle dans une branche de projet ou de fonctionnalité, puis dans le tronc. Je n'ai jamais rencontré trop de difficultés avec les fusions (nous utilisions TortoiseSVN sur Win32), même si nous n'avons jamais couru trop longtemps (une seule itération au maximum) sans réintégrer le tronc. De toute façon, nous avions tendance à faire la plupart de notre travail dans le coffre, même si l'objectif était de simplifier la gestion de l'homme plutôt que le flux de travail de développement. (Un dev-lead, de nombreux développeurs travaillant indépendamment, donc avoir tout dans le tronc a permis au dev-lead de garder une trace de ce qui se passait.)
William Payne
Un point clé était une forte dépendance à l'égard des tests pilotés par le système CI, en particulier au niveau des tests système. Cela devait aider à renforcer la confiance que les différents développeurs n'interféraient pas entre eux et à promouvoir une mentalité de nombreuses petites itérations. (De plus, les lourdes charges de calcul nécessaires pour exécuter les tests du système signifiaient qu'il y avait moins de conflits pour les ressources de calcul si les gens travaillaient principalement sur le tronc).
William Payne

Réponses:

10

La réponse de Spoike est excellente, mais il y a certaines choses que je pense qu'il serait utile d'ajouter qui sont trop grandes pour les commentaires.

Organisation de la succursale

Avec Mercurial, vous pouvez joyeusement ignorer l'ensemble de votre premier organigramme. Comme l'a dit Spoke, chaque référentiel possède son propre ensemble de balises, de branches (nommées et anonymes) et peut être organisé en fonction des besoins de l'entreprise.

Si bespokeProjectTwocharting vous avez besoin d'une version spéciale de la bibliothèque, vous pouvez chartingcréer une branche , ajouter les nouvelles fonctionnalités et l'utiliser dans bespokeProjectTwo. Les nouvelles installations (et leurs bogues) ne seraient pas utilisées par d'autres projets qui feraient référence à la chartingbibliothèque standard . Si la chartingbibliothèque principale avait des bogues corrigés, vous pouvez fusionner ces modifications dans la branche. Si d'autres projets avaient également besoin de ces fonctionnalités, vous pouvez soit faire en sorte que ces projets utilisent la branche spéciale , soit fusionner la branche dans la ligne principale et fermer la branche.

De plus, rien ne vous empêche d'avoir une politique pour structurer les noms de succursales afin de fournir des installations spécifiques comme vos succursales AUTOMATION.

Organisation du répertoire

Il n'y a aucune raison pour laquelle vous ne pouvez pas conserver votre répertoire source exactement comme il l'est avec Mercurial. La seule différence est que, alors qu'avec Subversion, vous n'avez qu'un seul (src)référentiel monolithique , avec Mercurial, vous feriez mieux de vous diviser en référentiels qui sont logiquement regroupés. De votre arborescence source, j'extrais probablement chacun des éléments suivants en tant que référentiels individuels:

src-+
      +-(developmentAutomation)
      +-libraries-+
      |           +-(log)
      |           +-(statistics)
      |           +-(charting)
      |           +-(distributedComputing)
      |           +-(widgets)
      +-productLines-+
      |              +-(flagshipProduct)
      |              +-(coolNewProduct)
      +-project-+
                +-bigImportantCustomer-+
                |                      +-(bespokeProjectOne)
                |                      +-(bespokeProjectTwo)
                +-anotherImportantCustomer-+
                                           +-(anotherBespokeProject)

Cela permet à tout produit ou projet sur mesure d'utiliser n'importe quelle combinaison de bibliothèques, à n'importe quelle révision. Jetez un œil aux sous-référentiels mercurial pour un moyen facile de gérer les bibliothèques utilisées pour une version donnée d'un produit ou d'un projet.

Workflow

Une alternative au flux de travail suggéré par Spoike (le développeur tire du repo béni, travaille localement, émet une demande de pull et enfin l'intégrateur tire ces changements et les fusionne) serait d'utiliser le système d'intégration continue comme intermédiaire.

Comme précédemment, le développeur se retire du dépôt béni et travaille localement, mais une fois terminé, il se retire du dépôt béni à nouveau et se fusionne avant de passer à un dépôt non béni. Toutes les modifications apportées au référentiel non béni sont ensuite examinées (manuellement ou automatiquement) et déplacées vers le référentiel béni uniquement si elles sont approuvées.

Cela signifie que l'intégrateur a uniquement accepté ou rejeté une modification, pas la fusion. D'après mon expérience, il est presque toujours préférable pour le développeur qui a écrit le code d'effectuer la fusion que pour quelqu'un d'autre de le faire.

Comme suggéré dans le livre mercurial, les crochets peuvent être utilisés pour automatiser cette procédure:

Lorsque quelqu'un envoie un ensemble de modifications au serveur dont tout le monde extrait, le serveur testera l'ensemble de modifications avant de l'accepter comme permanent et le rejettera s'il ne réussit pas la suite de tests. Si les utilisateurs ne tirent que les modifications de ce serveur de filtrage, cela servira à garantir que toutes les modifications que les gens tirent ont été automatiquement vérifiées.

Autres issues

Le problème des grands ensembles de données de test peut également être résolu en plaçant ces données de test dans un sous-référentiel mercurial . Cela empêchera le référentiel de code d'être gonflé de données de test, tout en gardant les données de test sous contrôle de révision.

Mark Booth
la source
Encore une fois, une autre excellente réponse informative. Je vous remercie.
William Payne
OBJET: Organisation de la succursale. Je suis d'accord que le premier organigramme peut heureusement être ignoré. De toute façon, il ne communiquait pas particulièrement bien le flux de travail et ne fournissait donc aucune utilité réelle au-delà du renforcement de la convention. Je voudrais cependant le remplacer par quelque chose qui communique fortement un flux de travail (aussi simple que possible) et encourage les commits fréquents. Peut-être que le fait d'appeler la branche "tronc / développement" principale "quotidiennement" ferait cela?
William Payne
RE: Organisation du répertoire. J'utilisais l'organisation du répertoire source comme moyen de communication subliminal; imposer une structure implicite à l'organisation du code (et à travers cela à l'entreprise dans son ensemble). Je commence à comprendre que Mercurial a tendance à être utilisé de manière très très flexible; mais je veux vraiment limiter une partie de cette flexibilité pour imposer une structure sur la façon dont les gens pensent de l'entreprise en imposant une structure sur la façon dont leurs documents sont organisés sur leurs postes de travail et nos zones de stockage réseau. (Plus de communications d'entreprise que de technologie.)
William Payne
RE: Workflow. Je pense que le flux de travail le plus simple serait de sortir d'un référentiel "quotidien", de travailler dessus localement, puis de repousser (fréquemment) vers le référentiel "quotidien", de lancer l'analyse statique, les tests de fumée et les tests de régression via le système CI. Je suis heureux que le dépôt principal soit "cassé", tant que je le sais, et tant qu'il est réparé rapidement. En fait, j'envisage de faire de la validation du repo "quotidien" le seul moyen de compiler et de compiler, pour encourager les validations fréquentes et une bonne couverture des tests. (Beaucoup plus important que la capacité de travailler de manière isolée, à mon humble avis).
William Payne
@WilliamPayne - Merci. Bien que Mercurial soit flexible, avec des référentiels, des branches et des hooks appropriés, vous pouvez créer les restrictions que vous souhaitez, au niveau de l'organisation ou du référentiel. Personnellement, je commencerais simplement par des contrôles organisationnels et quelques crochets CI, et étendre ces contrôles à l'avenir au fur et à mesure de leurs besoins. En outre, une utilisation judicieuse des sous-référentiels pourrait, par exemple, encourager les gens à vérifier les choses localement dans la même structure que sur le serveur, par exemple en ayant productLinesou en bigImportantCustomertant que super-référents.
Mark Booth
9

D'accord, essayant de répondre simplement à cette question.

Que souhaitez-vous savoir

Première chose que vous devez savoir: Mercurial est un contrôle de version distribué et possède certaines propriétés que vous devez connaître répertoriées ci-dessous.

  • La source provient d'un référentiel, où ce référentiel peut être cloné. Tous les référentiels clonés peuvent partager du code entre eux par le biais de la synchronisation (avec des commandes pull et push, dont l'accès est limité).
  • Chaque utilisateur qui possède une copie du code possède un clone du référentiel. S'ils veulent se ramifier, ils peuvent le faire dans leur clone local. Cela signifie que vous n'avez pas besoin d'organiser la façon dont chaque utilisateur doit créer une branche. Ils peuvent le faire eux-mêmes.
  • Les balises sont créées dans mercurial par un commit (qui est le même que les balises dures dans git). Cela signifie que vous n'avez pas besoin d'un répertoire dans votre structure de référentiel pour les balises.
  • Le modèle habituel avec lequel les gens travaillent dans DVCS (qui est utilisé dans github et bitbucket) est de le faire semi-centralisé.

    Chaque utilisateur a un référentiel public (dans certains partages ou sur un serveur sécurisé) et un référentiel privé (dans leurs propres postes de travail). Ils sont tous deux des clones du référentiel "béni" d'un intégrateur. Chaque fois qu'ils se sentent prêts à publier leur code, ils peuvent transférer les modifications de leur référentiel public. Un intégrateur peut alors choisir les utilisateurs à extraire du code dans le référentiel "béni".

    Si l'intégrateur ne peut pas fusionner facilement le code d'un utilisateur, les modifications sont rejetées et il appartient à cet utilisateur particulier de mettre à jour son référentiel et de corriger lui-même la fusion. Ce n'est généralement pas si difficile si vous fusionnez souvent (car c'est moins de code qui doit être fusionné) et généralement cet utilisateur doit savoir ce qui n'a pas fonctionné avec la fusion.

Configuration des référentiels par projet

Ainsi, la configuration habituelle est que pour chaque projet, il y a ce qui suit:

  • Un référentiel public en lecture seule dont l'intégrateur est responsable. C'est "béni".

    C'est-à-dire que tous les utilisateurs peuvent extraire / récupérer du contenu mais n'ont pas accès pour le pousser.

  • Chaque utilisateur peut avoir son propre clone public du référentiel.

    Configuration la plus simple, car placée dans un lecteur de partage (bien que vous puissiez envisager l'hébergement tel que bitbucket). L'intégrateur reçoit les demandes d'extraction des utilisateurs et essaie d'extraire le nouveau code de ces référentiels. Lorsque les fusions sont effectuées sans accroc, elles sont placées dans le référentiel en lecture seule. Sinon, les utilisateurs sont invités à corriger les conflits de fusion qui surviennent en le mettant à jour et en le fusionnant localement.

  • Chaque utilisateur peut avoir ses propres clones privés du référentiel.

    La bonne pratique consiste à se retirer de leur clone public, mais peu importe s'ils tirent de leur public ou de celui de l'intégrateur. Tous les commits sont identifiables de manière unique, donc la fusion des commits que vous avez oublié de récupérer dans le public est relativement facile à corriger (en poussant les modifications du privé vers le public, il obtient automatiquement les changements de l'intégrateur également).

Organisation du code source

Comme pour organiser la source du projet lui-même, vous devez réfléchir. Si un artefact doit être contrôlé par la source, mettez-le en contrôle de source. Personnellement, je n'aime pas l'idée d'archiver des artefacts créés par la génération ou l'exécution (en raison du risque élevé de conflits de fusion sur ces types d'artefacts) tels que les fichiers binaires ou les fichiers journaux.

Vous pouvez également archiver la configuration, à condition qu'ils facilitent le démarrage des développeurs et ne gâchent pas la configuration des versions ou de l'environnement de production / en direct (comme les paramètres du serveur d'application / Web). Cela conduit à l'idée que si la configuration que vous avez empêche sérieusement les développeurs de commencer dans les cinq minutes après avoir extrait le code, il doit être refactorisé. Une autre exigence est qu'il devrait être extrêmement difficile pour les développeurs de gâcher la version ou l'environnement de production / live.

Vous mentionnez que vous disposez de données de test qui doivent être liées à une version du code. Maintenant, c'est un peu plus délicat car les systèmes DVCS tels que Mercurial et Git ont tendance à ralentir lorsque vous enregistrez des données ENORMES. D'après mon expérience, cela devient vraiment insupportable après 5 Go de fichiers binaires (votre kilométrage peut varier, vous devriez donc tester comment cela fonctionne pour vous). Je recommanderais cependant que vous placiez les données générées dans son propre référentiel et que votre système de test les étiquette correctement lors de leur archivage (et / ou créez des fichiers texte pour les mêmes fins de métadonnées).

J'espère que tout cela a du sens. Veuillez commenter ci-dessous si j'ai raté certains détails ou si quelque chose doit être expliqué plus en détail et je vais essayer de le modifier.

Spoike
la source
+1 pour une très belle réponse avec plusieurs points très utiles. En réponse à la première section de votre réponse, je n'avais pas saisi l'importance de chaque utilisateur ayant son propre référentiel public. J'ai peut-être besoin de réfléchir davantage à la façon dont les flux de travail poste à poste pourraient être organisés.
William Payne
En réponse à la deuxième section de votre réponse, l'intérêt (dans mon esprit) d'avoir un référentiel unique pour toute l'organisation est de créer une image mentale partagée de la façon dont le travail est structuré et de faciliter la recherche de composants qui peuvent être réutilisé. (Beaucoup la cathédrale plutôt que le bazar, mais c'est l'environnement dans lequel je travaille). J'aimerais vraiment savoir comment atteindre le même sens de l'organisation structurée (système de classement) avec un DCVS.
William Payne du
En réponse à la troisième section de votre réponse: je suis tout à fait d'accord pour dire que le système de contrôle des sources est destiné aux documents sources et que les artefacts dérivés n'y appartiennent pas. Je conviens également qu'il n'est pas pratique de stocker de gros fichiers binaires de toute description dans VCS. Je crois cependant que vous pouvez stocker de gros fichiers binaires dans un emplacement réseau convenu, avec un nom défini, et les référencer à partir du VCS. Par exemple, le ou les environnements de génération peuvent être stockés en tant qu'images de disque VM nommées et référencés à partir des différents scripts de génération. (par exemple: construisez-moi sur build_env_A). La même chose vaut pour les données de test.
William Payne
Dans le passé, j'ai utilisé une hiérarchie de répertoires sur un lecteur réseau, où les noms de répertoire sont dérivés du numéro de révision de subversion + hachage de l'emplacement de la branche pour lier les fichiers intermédiaires et les résultats des tests à des révisions particulières. Cela signifie que nous avons une traçabilité sans avoir besoin de stocker des fichiers dérivés dans le contrôle de version.
William Payne