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.ShellCommand
s, 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.
Réponses:
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)
la source
Ce qui a fonctionné dans des situations similaires pour moi:
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).
la source
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 :)
la source
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.
la source