Je travaille sur un très grand projet open source mené par la recherche, avec un tas d'autres contributeurs réguliers. Parce que le projet est maintenant assez grand, un consortium (composé de deux employés à temps plein et de quelques membres) est en charge de la maintenance du projet, de l'intégration continue (CI), etc. Ils n'ont tout simplement pas le temps pour l'intégration des externes contributions cependant.
Le projet est composé d'un cadre "de base", d'environ un demi-million de lignes de code environ, d'un tas de "plugins" qui sont maintenus par le consortium, et de plusieurs plugins externes, dont la plupart nous ne sommes pas ' t même au courant.
Actuellement, notre CI construit le noyau et les plugins maintenus.
L'un des gros problèmes auxquels nous sommes confrontés est que la plupart des contributeurs (et en particulier ceux occasionnels) ne construisent pas 90% des plugins maintenus, donc quand ils proposent de refactoriser les changements dans le noyau (ce qui se produit ces jours-ci de manière assez régulière), ils ont vérifié que le code se compile sur leur machine avant de faire une pull request sur GitHub.
Le code fonctionne, ils sont satisfaits, puis le CI termine la construction et les problèmes commencent: la compilation a échoué dans un plugin géré par le consortium, que le contributeur n'a pas construit sur sa machine.
Ce plugin peut avoir des dépendances sur des bibliothèques tierces, comme CUDA par exemple, et l'utilisateur ne veut pas, ne sait pas comment, ou peut simplement Pour des raisons matérielles, compilez ce plugin cassé.
Alors alors - soit le PR reste ad aeternam dans les limbes du jamais-à-fusionner PRs - Ou le contributeur saisit la variable renommée dans la source du plugin cassé, change le code, pousse sa branche, attend que le CI termine la compilation, obtient généralement plus d'erreurs et réitère le processus jusqu'à ce que CI soit satisfait - Ou l'un des deux permanents déjà surbookés du consortium donne un coup de main et essaie de fixer le PR sur leur machine.
Aucune de ces options n'est viable, mais nous ne savons tout simplement pas comment procéder différemment. Avez-vous déjà été confronté à une situation similaire de vos projets? Et si oui, comment avez-vous géré ce problème? Y a-t-il une solution que je ne vois pas ici?
Le développement piloté par CI est parfait! C'est bien mieux que de ne pas exécuter de tests et d'inclure du code cassé! Cependant, il y a quelques choses pour rendre cela plus facile pour toutes les personnes impliquées:
Définir les attentes: Avoir une documentation de contribution qui explique que CI trouve souvent des problèmes supplémentaires, et que ceux-ci devront être corrigés avant un fusionner. Peut-être expliquer que les petits changements locaux sont plus susceptibles de bien fonctionner - il peut donc être judicieux de diviser un grand changement en plusieurs RP.
Encouragez les tests locaux: Facilitez la configuration d'un environnement de test pour votre système. Un script qui vérifie que toutes les dépendances ont été installées? Un conteneur Docker prêt à l'emploi? Une image de machine virtuelle? Votre lanceur de test dispose-t-il de mécanismes permettant de hiérarchiser les tests les plus importants?
Expliquez comment utiliser CI pour eux-mêmes: Une partie de la frustration est que cette rétroaction ne vient qu'après avoir soumis un PR. Si les contributeurs configurent CI pour leurs propres référentiels, ils recevront des commentaires plus tôt - et produiront moins de notifications CI pour d'autres personnes.
Résolvez tous les PR, de toute façon: Si quelque chose ne peut pas être fusionné parce qu'il est cassé, et s'il n'y a aucun progrès vers la résolution des problèmes, fermez-le. Ces RP ouverts abandonnés encombrent tout, et toute rétroaction est meilleure que de simplement ignorer le problème. Il est possible de formuler cela très bien et de préciser que, bien sûr, vous seriez heureux de fusionner lorsque les problèmes seront résolus. (voir aussi: L'art de la fermeture par Jessie Frazelle , Meilleures pratiques pour les mainteneurs: apprendre à dire non )
Pensez également à rendre ces RP abandonnés découvrables afin que quelqu'un d'autre puisse les récupérer. Cela peut même être une bonne tâche pour les nouveaux contributeurs, si les problèmes restants sont plus mécaniques et ne nécessitent pas une connaissance approfondie du système.
Pour la perspective à long terme, le fait que les changements semblent rompre des fonctionnalités non liées si souvent pourrait signifier que votre conception actuelle est un peu problématique. Par exemple, les interfaces de plug-in encapsulent-elles correctement les composants internes de votre cœur? C++ facilite la fuite accidentelle des détails d'implémentation, mais permet également de créer des abstractions fortes qui sont très difficiles à détourner. Vous ne pouvez pas changer cela pendant la nuit, mais vous pouvez accompagner l'évolution à long terme du logiciel vers une architecture moins fragile.
Construire un modèle de plugin durable nécessite que votre infrastructure principale expose une interface stable sur laquelle les plugins peuvent s'appuyer. La règle d'or est que vous pouvez introduire de nouvelles interfaces au fil du temps mais vous ne pouvez jamais modifier une interface déjà publiée. Si vous suivez cette règle, vous pouvez refactoriser le implémentation du framework de base tout ce que vous voulez sans craindre de casser accidentellement des plugins, qu'il s'agisse d'un consortium ou d'un externe.
D'après ce que vous avez décrit, il semble que vous n'ayez pas d'interface bien définie, ce qui rend difficile de dire si un changement cassera les plugins. Travaillez à définir cette interface et à la rendre explicite dans votre base de code, afin que les contributeurs sachent ce qu'ils ne doivent pas modifier.
Pour être honnête, je ne pense pas que vous puissiez mieux gérer cela - si les changements entraînent rupture des parties maintenues de votre projet le CI devrait échouer.
Votre projet a-t-il un contributing.md
ou quelque chose de similaire pour aider les contributeurs nouveaux et occasionnels à préparer leurs contributions? Avez-vous une liste claire, quels plugins font partie du noyau et doivent rester compatibles?
S'il est difficile de tout construire sur une machine en raison de dépendances, etc., vous pouvez penser à créer des images de docker prêtes à l'emploi en tant qu'environnements de construction pour vos contributeurs.
donc quand ils proposent de refactoriser les changements dans le noyau (ce qui arrive de nos jours de façon assez régulière), ils ont vérifié que le code se compile sur leur machine avant de faire une pull request sur github.
Je pense donc que c'est là que le style lâche des projets open source peut tomber; la plupart des projets organisés de manière centralisée se méfient du refactoring de base, en particulier lorsqu'il traverse une frontière API. S'ils refactorisent une limite d'API, il s'agit généralement d'un "big bang" où toutes les modifications sont planifiées en même temps avec un incrément à la version principale de l'API, et l'ancienne API est conservée.
Je proposerais une règle "toutes les modifications de l'API doivent être planifiées à l'avance": si un RP arrive qui apporte une modification incompatible en amont à l'API, de quelqu'un qui n'a pas été en contact avec les responsables pour convenir à l'avance de son approche, il se ferme tout simplement et l'auteur de la communication pointe la règle.
Vous aurez également besoin d'une version explicite de l'API du plugin. Cela vous permet de développer la v2 pendant que tous les plugins v1 continuent de se construire et de fonctionner.
Je remettrais également en question un peu plus pourquoi tant de refactoring de base et de changements d'API sont en cours. Sont-ils vraiment nécessaires ou simplement des personnes imposant leur goût personnel au projet?
On dirait que le processus de CI doit être plus strict, plus complet et plus visible pour les contributeurs avant qu'ils n'élèvent un PR. Par exemple, BitBucket a une fonctionnalité de pipelines qui permet cela, où vous lui donnez un fichier qui définit dans le code le processus de construction de CI, et s'il échoue, la branche ne peut pas être fusionnée.
Indépendamment de la technologie, fournir des builds automatiques lorsqu'un contributeur pousse vers une branche leur donnera une rétroaction beaucoup plus rapide de ce qu'il faut rechercher lors des modifications et conduira à des RP qui n'ont pas besoin d'être réparés après coup.
Il serait bon de résoudre les problèmes de conception, mais ils sont orthogonaux à ce problème.
Le code fonctionne, ils sont satisfaits, puis le CI termine la construction et les problèmes commencent: la compilation a échoué dans un plugin géré par le consortium, que le contributeur n'a pas construit sur sa machine.
Ce plugin peut avoir des dépendances sur des bibliothèques tierces, comme CUDA par exemple, et l'utilisateur ne veut pas, ne sait pas ou ne peut tout simplement pas, pour des raisons matérielles, compiler ce plugin cassé.
Votre solution est simple: abaisser la barrière à la contribution.
Le moyen le plus simple (1) d'accélérer le cycle d'édition-compilation-test et (2) de lisser les différences d'environnement est de fournir construire des serveurs:
Et puis ouvrez ces serveurs de build aux contributeurs. Ils devraient pouvoir se connecter à distance dans une nouvelle image Docker et éditer-compiler-tester à distance sur cette machine.
Alors:
En général, les serveurs de build peuvent être partagés entre plusieurs contributeurs, mais lorsque des périphériques matériels spéciaux sont impliqués, il peut être nécessaire qu'un contributeur utilise lui-même ledit périphérique.
Source: travailler sur un logiciel utilisant des FPGA, étant donné le prix des bêtes et la variété de modèles dont nous avons besoin, vous ne trouvez pas chaque modèle de FPGA installé sur la machine de chaque développeur.
Si contribuer au cœur sans modifier aucun contrat peut casser un logiciel dépendant, il suggère que:
L'un ou l'autre problème devrait être facile à résoudre, mais vous mentionnez que l'équipe principale pourrait ne pas avoir la capacité de le faire. Une option consisterait à demander de l'aide à la communauté pour résoudre le problème.
Personne d'autre ne semble avoir évoqué cela comme une solution potentielle.
Lors du développement du noyau, encouragez les développeurs à exécuter ces tests de compatibilité. S'ils échouent, ne vous enregistrez pas.
Cela ne garantira pas à 100% la compatibilité, mais cela entraînera beaucoup plus de problèmes tôt.
Un avantage secondaire est que ces enregistrements peuvent mettre en évidence les interfaces activement utilisées et les fonctionnalités utilisées activement.
J'ai du mal à comprendre la situation telle qu'elle semble être: le CI ne construit qu'une seule branche?
Y a-t-il une raison pour laquelle vous ne pouvez pas créer plus d'une branche avec le CI?
La solution la plus simple à ce problème serait de permettre à tout contributeur d'exécuter le build CI sur sa branche de fonctionnalité.
Ensuite, vous avez simplement besoin d'une construction CI réussie sur la branche de fonctionnalité pour que la demande d'extraction de cette branche soit acceptée.