maintenir une base de code croissante et diversifiée avec une intégration continue


10

J'ai besoin d'aide pour la philosophie et la conception d'une configuration d'intégration continue.

Notre configuration CI actuelle utilise buildbot. Quand j'ai commencé à le concevoir, j'ai hérité (enfin, pas strictement, car j'ai été impliqué dans sa conception un an plus tôt) un constructeur de CI sur mesure qui a été conçu pour exécuter la construction entière en une seule fois, du jour au lendemain. Après un certain temps, nous avons décidé que cela était insuffisant et avons commencé à explorer différents cadres CI, en choisissant finalement buildbot. L'un de mes objectifs lors de la transition vers buildbot (en plus de profiter de tous les extras whiz-bang) était de surmonter certaines des insuffisances de notre constructeur nocturne sur mesure.

Humourez-moi un instant et laissez-moi vous expliquer ce dont j'ai hérité. La base de code de mon entreprise est constituée de près de 150 applications Windows c ++ uniques, chacune ayant des dépendances sur une ou plusieurs d'une douzaine de bibliothèques internes (et beaucoup sur des bibliothèques tierces également). Certaines de ces bibliothèques sont interdépendantes et ont des applications dépendantes qui (alors qu'elles n'ont rien à voir les unes avec les autres) doivent être construites avec la même version de cette bibliothèque. La moitié de ces applications et bibliothèques sont considérées comme "héritées" et non transférables, et doivent être construites avec plusieurs configurations distinctes du compilateur IBM (pour lesquelles j'ai écrit des sous-classes uniques de Compile), et l'autre moitié est construite avec Visual Studio.ShellCommands, car il n'y a pas de support pour VSS).

Notre constructeur nocturne original a simplement pris la source de tout et construit des trucs dans un certain ordre. Il n'y avait aucun moyen de créer une seule application, de choisir une révision ou de regrouper des choses. Il lancerait des machines virtuelles pour créer un certain nombre d'applications. Ce n'était pas très robuste, ce n'était pas distribuable. Ce n'était pas terriblement extensible. Je voulais pouvoir surmonter toutes ces limitations dans buildbot.

La façon dont je l'ai fait à l'origine était de créer des entrées pour chacune des applications que nous voulions créer (toutes les 150), puis de créer des planificateurs déclenchés qui pouvaient créer diverses applications en tant que groupes, puis subsumer ces groupes sous un planificateur de construction nocturne global. Ceux-ci pouvaient fonctionner sur des esclaves dédiés (plus de chicane de machine virtuelle), et si je le voulais, je pourrais simplement ajouter de nouveaux esclaves. Maintenant, si nous voulons faire une construction complète en dehors du calendrier, c'est un clic, mais nous pouvons également créer une seule application si nous le désirons.

Cependant, cette approche présente quatre faiblesses. L'un est le réseau complexe de dépendances de notre arbre source. Afin de simplifier la maintenance de la configuration, tous les générateurs sont générés à partir d'un grand dictionnaire. Les dépendances sont récupérées et construites d'une manière pas terriblement robuste (à savoir, la désactivation de certaines choses dans mon dictionnaire cible de génération). La seconde est que chaque build a entre 15 et 21 étapes de build, ce qui est difficile à parcourir et à regarder dans l'interface Web, et comme il y a environ 150 colonnes, le chargement prend une éternité (pensez de 30 secondes à plusieurs minutes). Troisièmement, nous n'avons plus de découverte automatique des cibles de construction (bien que, autant qu'un de mes collègues me harpe à ce sujet, je ne vois pas ce que cela nous a apporté en premier lieu). Finalement,

Maintenant, en passant à un nouveau développement, nous commençons à utiliser g ++ et subversion (ne pas porter l'ancien dépôt, attention - juste pour les nouveaux trucs). De plus, nous commençons à faire plus de tests unitaires ("plus" pourrait donner la mauvaise image ... c'est plus comme tout ), et les tests d'intégration (en utilisant python). J'ai du mal à trouver comment les intégrer dans ma configuration existante.

Alors, où me suis-je trompé philosophiquement ici? Comment puis-je avancer (avec buildbot - c'est la seule pièce du puzzle sur laquelle j'ai une licence pour travailler) afin que ma configuration soit réellement maintenable? Comment puis-je corriger certaines des faiblesses de ma conception? Qu'est-ce qui fonctionne vraiment en termes de stratégies de CI pour des bases de code volumineuses (peut-être trop) complexes?

ÉDITER:

Je pensais avoir expliqué mon problème, mais évidemment je n'étais pas assez clair. Je ne cherche pas de suggestions pour changer les plateformes CI. Cela ne va pas se produire, et les réponses suggérant que cela ne sera pas accepté. Ce que je veux savoir, c'est comment d'autres personnes gèrent des bases de code complexes en utilisant CI. J'ai une douzaine de produits différents au carré , et j'ai des dépendances dispersées au vent, et elles sont toutes différentes. C'est ce que je veux savoir comment gérer.


Moi aussi, j'aimerais connaître la réponse à cela. Notre environnement n'est pas aussi complexe que le vôtre, mais nous avons des dépendances de dépendances de dépendances (dans le cas du projet d'installation, il a des dépendances profondes de quatre couches). Je ne sais pas si chaque projet doit être un projet CI, ou si je dois simplement utiliser le fichier Visual Studio .sln pour m'en occuper afin que je n'aie pas à recréer l'arborescence des dépendances pour chaque projet (et projet futur).
moswald

Le fait de ne pas pouvoir s'appuyer sur votre machine locale rend la mission de votre serveur CI critique pour votre entreprise. Vous voudrez peut-être repenser cela.
Thorbjørn Ravn Andersen

Réponses:


3

Bien que je n'aie pas fait face à une situation aussi grave que vous le décrivez, j'ai conservé une configuration CI avec des dizaines de composants, entre lesquels il existe des dépendances «simples». J'espère que mon approche pourra vous donner quelques conseils pour poursuivre. Le problème n'est certainement pas uniquement lié au choix du serveur CI, mais également au processus de construction global et à la structure du projet.

Je décompose le problème en 2 parties: Building et CI.

Bâtiment

Pour "Building", je veux dire le processus de, changer le code source en main à l'artefact final. Notre entreprise utilise principalement Java dans le développement, et l'outil de construction que nous avons utilisé est Maven. Vous ne pourrez probablement pas l'utiliser à cause de la nature de votre projet, mais il existe dans Maven un concept précieux à noter:

1) Dans le monde Maven, chaque artefact (une lib, le programme réel, etc.) doit être clairement isolé et découplé. Les dépendances entre les artefacts doivent être claires. Je dois souligner que les dépendances désordonnées, en particulier les dépendances circulaires entre vos artefacts de build, vont rendre le processus de build un gâchis.

Par exemple, j'ai vu quelques projets java avant cela, bien qu'après tout le processus de construction il y ait plusieurs JAR (vous pouvez le considérer comme lib / dll en Java) construits, ils sont en fait interdépendants. Comme, A.jar utilise des choses dans B.jar, et vice versa. Ce type de «modularisation» est totalement dénué de sens. A.jar et B.jar doivent toujours être déployés et utilisés ensemble. L'implication est que, plus tard, si vous souhaitez les séparer en différents projets (pour les réutiliser dans d'autres projets par exemple), vous ne pourrez pas le faire, car vous ne pouvez pas déterminer quel projet, A ou B, à construire en premier.

Oui, il doit être pris en compte dans la conception de votre logiciel, mais je crois toujours qu'au lieu de payer du temps pour faire fonctionner un outil / modèle de construction compliqué dans un projet en désordre, je préfère passer du temps à réorganiser la conception pour utiliser un modèle de construction simple.

2) Les dépendances doivent être déclaratives. Il y a beaucoup de processus de construction que j'ai vu auparavant, qui incluent toutes les bibliothèques dont il a besoin localement dans le projet. Cela rendra le processus de construction extrêmement gênant si certaines des bibliothèques sont en fait d'autres artefacts que vous devez construire.

3) Stockage "centralisé" pour les artefacts pour obtenir des dépendances, ou pour déployer des artefacts une fois qu'il est compilé. Il n'a pas besoin d'être "centralisé" pour l'ensemble du bureau (ce sera bien si c'est le cas), juste un répertoire local sera déjà très bien.

Plus de détails sur 2 et 3. Pour donner un exemple, je suis tombé sur un projet qui impliquait 3 projets indépendants. Chaque projet est construit sur la base de la source, plus les bibliothèques sous le répertoire lib / dans le répertoire source. Le projet A créera plusieurs bibliothèques, qui sont à leur tour utilisées par les projets B et C. Il y a quelques inconvénients: la procédure de construction est compliquée et plus difficile à automatiser; le contrôle de source se gonfle de fichiers JAR dupliqués inutiles réutilisés dans différents projets

Dans le monde de Maven, ce qui est fait, les projets B et C ne contiennent pas vraiment A.jar (et d'autres dépendances, comme d'autres bibliothèques tierces) dans la source du projet. C'est déclaratif. Par exemple, dans la configuration de construction du projet B, il déclare simplement qu'il a besoin de: A.lib v1.0, xyz.lib v2.1 etc., et le script de construction recherchera la bibliothèque depuis /A/1.0/A. pot et /xyz/2.1/xyz.lib

Pour le monde non-Maven, ce répertoire d'artefact doit simplement être un ou deux répertoires avec une structure de répertoires convenue. Vous pouvez placer toutes les bibliothèques tierces dans un emplacement de partage et laisser les développeurs se synchroniser ou copier sur leurs machines locales. Dans mes projets C ++ que j'ai faits de nombreuses années auparavant, ce que je fais, c'est de définir la lib et l'en-tête sur $ {artifact_dir} / lib_name / ver, et avec artifact_id déclaré comme variable d'environnement.

Lorsque le projet A est construit, il aura une copie de son résultat dans ce répertoire d'artefact, de sorte que lorsque nous construisons le projet B, B puisse obtenir le résultat de A automatiquement, sans copie manuelle.

4) Libération non mutable. Une fois que vous avez publié A.lib 1.0, c'est tout, vous ne vous attendez pas à un changement de contenu d'A .lib 1.0 après 1 mois juste bcos il y a quelques corrections de bugs. Dans ce cas, ce devrait être A.lib 1.1. L'artefact d'une base de code changeante devrait considérer une "version" spéciale, pour laquelle dans Maven nous l'appelons instantané.

La libération non mutable est davantage un problème éthique. Mais ce qu'il a résolu est clair: lorsque vous avez beaucoup de projets, en utilisant la même bibliothèque, vous savez quelle version de cette bibliothèque que vous utilisez, et vous serez sûr que, la même version de la bibliothèque utilisée dans différents projets, est en effet la même . Je pense que beaucoup d'entre nous sont passés par le problème de: Pourquoi les projets X et Y utilisent-ils tous les deux la lib A mais le résultat de la construction est différent? (et il s'avère que la lib A utilisant X et Y est en fait différente après avoir creusé dans le contenu ou la taille du fichier de la lib).


Tout cela garantit que vos projets peuvent être construits indépendamment, sans trop d'astuces manuelles, comme construire le projet A d'abord, copier A.lib dans le projet B, puis construire le projet B ...

Après avoir effectué un exercice comme celui-ci, par exemple, lorsque vous générez le projet A, il essaiera d'obtenir les dépendances du stockage centralisé des artefacts. Dans le cas où certaines dépendances (qui sont d'autres projets de votre entreprise, par exemple le projet B) ne sont pas trouvées, ce que vous devez faire est d'obtenir la source du projet B, de le construire (qui sera déployé sur le stockage centralisé en cas de succès), et puis générez à nouveau le projet A.


CI

Avec un système de construction facile, avec des dépendances claires, CI sera beaucoup plus facile. Je m'attends à ce que votre serveur CI réponde aux exigences suivantes:

1) Surveillez le contrôle des sources, uniquement checkout + build en cas de changement de source

2) Capable de configurer les dépendances entre les projets

Avec une dépendance claire pour les projets, il vous suffit de configurer les projets dans CI en fonction de vos projets réels, de configurer la dépendance des projets CI en fonction de la dépendance de vos projets. Votre serveur CI doit être configuré pour créer des projets dépendants avant de construire un projet (et bien sûr, la génération ne se produit que si la source du projet a vraiment changé).

Si tout se passe bien, vous devriez avoir un CI énorme, complexe mais toujours gérable (et encore plus important, une structure de projet gérable)


J'aime où va cette réponse, mais pourriez-vous entrer plus en détail dans la section «bâtiment»? C'est-à-dire, donner quelques exemples, expliquer certains concepts de manière plus approfondie, élaborer.
Nate

hm ... comme quelle partie? J'essaie de modifier le message pour donner plus de détails sur chaque partie. Ce sera mieux si vous pouvez me dire sur quelle partie vous sentez le besoin d'être développé. Soit dit en passant, si vous utilisez également Java, jetez un œil à maven.apache.org. Maven n'est peut-être pas la chose la plus facile à adopter, mais cela vous oblige à rendre vos affaires propres et organisées.
Adrian Shum

1

Ce qui a fonctionné dans des situations similaires pour moi:

  • Abstraction au niveau de l'application en shuntant certaines parties de la build dans des applications de build dédiées (dans notre cas, ce sont des scripts PHP appelés depuis la build principale). Cela peut réduire quelques dizaines de lignes d'étapes de construction en une seule étape de construction.
  • Abstraction au niveau de la build en créant des sous-scripts de build qui sont lancés à partir du script de build principal. Aucune idée de sa pertinence pour buildbot (aucune expérience avec ce produit).

Je suggère que vous traitez la construction elle-même comme un projet de développement logiciel. Cela signifie que vous devez modulariser la base de code de génération et écrire des tests automatisés pour celle-ci (par exemple, construire un numéro de révision connu et vérifier s'il produit des résultats corrects).


0

Je considérerais Jenkins comme un serveur CI, vous pouvez regarder les fonctionnalités ici:

https://wiki.jenkins-ci.org/display/JENKINS/Meet+Jenkins

Pourquoi? C'est facile à installer, il a une interface merveilleuse, il est facile à configurer et à étendre et il y a BEAUCOUP de plugins prêts pour presque tout:

https://wiki.jenkins-ci.org/display/JENKINS/Plugins

Essaie :)


2
J'ai l'impression que vous avez lu le titre de ma question, mais que vous n'avez pas lu le corps ... Je ne cherche pas de suggestion de produit. J'ai choisi un produit. Je cherche comment organiser une configuration CI complexe.
Nate

Nate, j'ai lu tous vos articles et je pense que vous avez choisi le mauvais produit, c'est pourquoi j'ai suggéré Jenkins. J'utilise Jenkins au travail pour différents types de tests de produits C ++ (même des tests écrits en plusieurs langues) avec un clustering sur plusieurs machines et cela fonctionne très bien. Il est très facile à administrer et à configurer. Vraiment, essayez-le :)
Patrizio Rullo

Regardez aussi ce billet de blog: vperic.blogspot.com/2011/05/…
Patrizio Rullo

0

Nous utilisons maintenant Go by ThoughtWorks avant cela, nous utilisions CruiseControl.Net . Cela a été utile étant donné que nous avons deux équipes à l'autre bout du monde et qu'il y a une grande différence de fuseau horaire entre nous.

Nous gérons plusieurs projets et la plupart des tâches impliquent des chevauchements entre deux développeurs des deux côtés du globe, nous devons donc garder à l'esprit que tous les fichiers ne doivent pas casser la construction de quelqu'un d'autre.

De plus, la gestion est plus facile avec Go et étant un irréductible Agile Process, cela nous a donné moins de maux de tête après l'avoir installé. Les tests ont également été intégrés à Go.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.