La propriété svn:externals et les révisions
Vous devez être nombreux à utiliser Subversion comme outil de contrôle de code source. Même si on entend parler de plus en plus des VCS distribués comme Git, Bazaar ou Mercurial, Subversion reste une excellente solution. C’est en tout cas celle que j’utilise quotidiennement depuis un moment. Si vous avez l’habitude d’intervenir au niveau de la gestion de configuration de vos projets, vous vous êtes probablement déjà posés tout un tas de questions relatives aux « bonnes pratiques svn ».
J’ai envie aujourd’hui de vous parler de l’une d’elles, concernant la propriété svn:externals
, qui permet de créer des dépendances entre projets versionnés.
Au niveau de l’utilisation, c’est très simple. Je ne vais d’ailleurs pas m’étaler sur le sujet – ce n’est pas le but – mais je vous invite tout de même à jeter un coup d’oeil aux nouvelles fonctionnalités supportées par la version 1.5 de Subversion quant aux externals. Non, la question qui m’intéresse est précise :
Faut-il ou non spécifier systématiquement la révision vers laquelle pointe la référence externe ?
Mais ouais !
Si vous pensez le contraire, vous avez sans doute d’excellentes raisons (n’hésitez alors pas à m’en faire part), mais peut-être aussi avez-vous été abusés (comme moi) par les deux mythes suivants :
- Ne pas spécifier la révision d’une dépendance externe et toujours pointer sur la dernière, c’est plus pratique.
- Editer les externals pour mettre à jour les révisions tout le temps, c’est chiant.
Je pense que ce n’est pas exact.
Lorsque vous travaillez sur un projet et que vous utilisez la propriété svn:externals
, vous matérialisez une dépendance de ce dernier vers un ou plusieurs autres projets tiers. Il se peut que vous soyez en charge de leur développement – c’est d’ailleurs fréquemment le cas – mais ils restent indépendants de votre projet. Cela signifie qu’ils sont susceptibles d’évoluer de leur côté ou bien d’être référencés et utilisés par d’autres projets dépendants. Si ce n’est pas le cas, remettez en cause leur existence.
Vous pouvez référencer les sources d’un projet indépendant via la propriété svn:external
pour plusieurs raisons. Par exemple, pour débugger plus facilement, pour mieux comprendre comment la dépendance fonctionne, ou tout simplement parce que vous contribuez également sur le projet dont vous dépendez. Quoi qu’il en soit, à partir du moment où vous référencez un projet externe, gardez bien en tête que n’importe qui est susceptible de le faire évoluer sans même savoir qu’il est référencé par votre projet. Vous me répondrez qu’il est très improbable que cela arrive : vous savez précisément qui fait quoi dans votre équipe. OK, admettons ; après tout il s’agit d’une objection valide. Je doute cependant que le projet externe soit uniquement référencé par un seul projet. Si c’est le cas, il ne peut s’agir que d’une mauvaise idée ou d’une solution temporaire : vous anticipez des dépendances futures. Et s’il y a plusieurs projets qui affichent une dépendance vers un même projet externe, alors chaque évolution de ce dernier concerne tous les projets dépendants.
Surtout si vos projets dépendants référencent en permanence la dernière révision du projet dont ils dépendent.
C’est le comportement par défaut de la propriété svn:externals
lorsque vous n’utilisez pas l’option -r
pour spécifier vers quelle révision donnée pointe votre référence externe.
Pour revenir au premier mythe, celui de la facilité, il est vrai qu’avoir toutes les évolutions du projet externe poussées vers tous les projets dépendants (à chaque update de ces derniers) peut sembler pratique. « Au moins, on est sûr de profiter des dernières évolutions ». C’est déjà plus discutable lorsque les évolutions du projet externe impactent sa surface publique et nécessitent que certains projets dépendants soient adaptés, modifiés, corrigés.
Imaginons 4 projets : Project1, Project2, Project3, et Hourra. Imaginons que ces 4 projets dépendent d’un 5ème projet externe : SharedProject. Imaginons enfin que pour répondre à un besoin du projet Hourra, on fasse évoluer SharedProject en introduisant au passage un breaking change. La conséquence est aussi évidente que l’exemple est trivial :
Henri, qui doit faire un commit sur le Project2 (Henri ne travaille que sur ce projet, et ça lui convient bien) se fait avertir par Tortoise qu’il n’est pas à jour, et qu’il doit faire un update avant de publier ses modifications. Henri grogne un peu, son méchant manager (Brigitte) lui met la pression pour que le QA puisse récupérer via TeamCity une build de Project2 incluant son travail récent (la fenêtre About). Il met donc à jour sa copie locale, puis fait son commit. 2 minutes plus tard TeamCity notifie tout le monde d’une erreur de compilation sur la build de Project2. Le pauvre Henri vient de se manger le breaking change de SharedProject, et quoi qu’il fasse, il perdra du temps.
Je pourrais vous raconter l’histoire de Gustave sur Project1 et de Catherine sur Project3, mais je ne pense pas que cela soit nécessaire.
Pour s’éviter la peine de réfléchir, et de maintenir les références externes, on perturbe tout le processus de développement. L’exemple est grossier, mais je pense qu’il a le mérite de bien faire comprendre que la seule chose qui devient plus facile lorsqu’on référence la head revision d’un external, c’est de fragiliser le cycle de construction du projet. Cela peut ne pas arriver souvent, mais à chaque fois que cela arrive, le temps perdu et l’approximation des méthodes de résolution font regretter de ne pas avoir pris la peine de gérer finement la configuration.
Que nécessite le fait de spécifier la révision du projet externe dont on dépend ?
- Un peu de concentration : on doit savoir qu’on dépend d’une révision spécifique d’un projet.
- Un peu de discernement : on décide de faire dépendre son projet de la nouvelle version du projet externe.
Un peu de talentUne minute pour éditer la propriétésvn:external
et mettre à jour la révision.
Il ne faut pas toujours penser que l’on peut tout simplifier. La gestion de configuration est une discipline délicate, caractérisée notamment par l’évolution exponentielle de sa complexité lorsque le nombre de projets augmente. Je parle dans mon exemple de 5 projets, mais on peut en imaginer plus, avec différents niveaux de dépendances, plusieurs branches, des versions déployées, taguées, etc.
Toute complexité n’est pas forcément compressible, et constater qu’on dispose d’une solution simple à un problème compliqué peut aussi bien faire penser que ladite solution est géniale ou carrément douteuse.
Quant à la pénibilité de la tâche consistant à mettre à jour une propriété svn, et bien, je ne sais pas trop quoi dire. Il me semble qu’il s’agit d’une chose évidente, et terriblement simple. Je veux dire : si vous êtes capable d’avoir un avis au sujet de la bonne utilisation de votre VCS, vous êtes évidemment en mesure de mettre à jour un argument d’une propriété svn sans souffrir.
A la rigueur, si vous devez gérer un certain nombre de projets ayant de multiples références externes, ou si vous désirez convaincre les différentes membres de votre équipe, vous pouvez passer quelques heures à réaliser un petit outil rendant le monitoring et la mise à jour des externals ridiculement simples.
Après tout, vous êtes des geeks, non ?
Evidemment, il est possible de ne jamais spécifier de révision dans la propriété svn:externals
sans pour autant être un crétin ; ce n’est certainement pas ce que je dis. J’ai pendant très longtemps négligé l’option -r
, et mes projets n’ont pas été compromis outre mesure à cause de ça. Je cherche juste à attirer l’attention sur le fait que cette pratique me semble être conceptuellement plus juste, plus sûre, et qu’elle n’est pas si contraignante qu’on le pense souvent au premier abord. Un soupçon de rigueur à ce niveau, même sur de petits projets, peut donner à votre gestion de configuration une stabilité et une solidité rassurantes.
Lorsque toutes les références externes d’un projet pointent sur les head revisions de chaque dépendance et qu’il est temps de tagger une version, ne trouvez-vous pas la tâche à la fois pénible et risquée ? Vous aurez d’un coup à fixer chaque dépendance externe vers une révision spécifique, ce qui est laborieux dès lors qu’il y a plusieurs niveaux de dépendances. Prendre l’habitude de fixer les révisions en permanence est selon moi une bonne chose, une sorte d’investissement rapidement rentable et encore une fois, pas vraiment coûteux.
J’ajouterai juste une remarque suite à un commentaire pertinent de Julien sur le groupe de discussion ALT.NET :
Par exemple, en mode agile avec de petites équipes et de l’intégration continu en place, ca passe très bien. Apres tout, le but de l’intégration continue est justement d’intégrer les dernières modifications et de detecter les breaking changes.
Effectivement, l’intégration continue permet de détecter les problèmes de ce genre, mais selon moi ce n’est pas suffisant. L’intégration continue ne permet pas d’éviter ce genre de problème. Dans mon histoire, Henri aurait préféré décider du moment où il allait devoir intégrer les breaking changes. C’est un peu comme lorsqu’on décide de migrer de Rhino.Mocks 2.x à Rhino.Mocks 3.x : il s’agit d’une tâche que l’on veut planifier, sur laquelle on veut avoir le contrôle. On choisit d’utiliser une version plus récente d’une dépendance externe, on évite de subir la mise à jour en la découvrant parfois au mauvais moment.
Lorsque je travaille sur un projet, et que je décide d’utiliser la dernière version d’une dépendance externe :
- Je mets à jour la propriété
svn:externals
de ma copie de travail locale (avec mon super outil) pour qu’elle pointe vers la dernière révision de la dépendance. - Je fais un update pour récupérer les sources correspondant à cette dernière révision.
- Je fais la migration (si besoin), toujours dans ma copie locale. Je m’assure que tout fonctionne avec la dernière révision de la dépendance externe.
- Je fais un commit pour publier à la fois la propriété
svn:externals
mise à jour, et les éventuelles modifications nécessaires à l’intégration de la nouvelle version de la dépendance. - Si je suis responsable d’autres projets dépendants, je recommence le même processus.
Ainsi, tous les projets restent sains, et les équipes gardent une bonne maitrise de la gestion de configuration. Je n’arrive pas à voir quel pourrait être le réel avantage à fonctionner différemment.
Bravo super article!
Il a l’air super cet outil! J’achète ;-)
Tiens tiens, ça me rappelle à une citation :
« You know tools are great, but they only take you so far. It’s the principles and knowledge that really matter. The best tools are those that embed the knowledge and encourage the principles (e.g. Resharper.) »
…qu’on retourne dans la description de la communauté ALT.Net. :-)
Je suis assez d’accord avec Julien sur le cotés agilité, mais avec un outil comme ça on reste dans un contexte agile tout en encourageant les bonnes pratiques.
Comme quoi, un bon outil peut faire changer beaucoup de choses.
Par curiosité, c’est un outil que tu as développé ou c’est un projet? Car en effet il y a plusieurs projet open source qui exploite l’api de svn. Notamment NSVN. NSVN, j’avais regardé ça un jour et de mémoire l’api marchait pas très bien quand on utilise le _svn au lieu du .svn. Enfin ça a peut être changé depuis. Tu as utilisé quelle lib si ce n’est pas indiscret?
Max
Merci !
C’est un project quick & dirty développé en quelques heures, qui utilise directement svn.exe et tortoiseProc.exe :) Rien de très brillant, mais assez utile.
Je ne connaissais pas NSvn ; j’ai juste testé SharpSvn mais trop superficiellement pour pouvoir me prononcer quant à sa stabilité. Je sais que c’est la librairie sur laquelle repose AnkhSvn (qui est lui, par contre, bien buggy.)
J’aime les articles avec un peu d’humour :)
En parlant d’external, on souhaite parfois packager plusieurs logiciels dans une suite. J’ai rencontré ce cas, et je préconise un projet « package » qui ne contient que des externals vers des versions spécifiques.
On peut aussi utiliser les external pour les lib des projets, et pas seulement les sources. Je n’ai pas besoin des sources de RhinoMock ou de Spring.net pour compiler mon projet! Dans ce cas, l’intégration continue peut compiler les sources et packager/commiter dans Tag qui peut ensuite être référencé.
Il ne manque plus qu’une vue « graphe » des dépendances, peut-être dans ton outil open-source ? ;)
Petite info: je travail sur un post sur TortoiseSVN, et je parle de ses points d’entrés COM. De plus, ce dernier est fait en .Net http://tortoisesvn.tigris.org/svn/tortoisesvn/trunk/src/ :)
Intéressant de savoir qu’il y a une interface COM ; je vais regarder ce qu’on peut faire de ce côté.
Pour info, j’ai passé un peu de temps à modifier mon outil de façon à ce qu’il repose sur SharpSvn. Ça fonctionne très bien, pour peu qu’on utilise l’une de leur dernière builds : les repos svn1.5 ne sont pas supportés sinon.
Eh, M’sieur Verdier, elle vient quand la nouvelle version de cet outil?
Celle qui ne tronque plus la config, qui se raffraichit hyper vite et ne consomme plus 50Mo?
Tu connais peut-être pas la commande commit …;)
Andreone> Eh, j’espère que tu n’as pas accès aux sources, sinon je serai obligé de te demander un patch :’)
Très intéressant cet outils (l’article aussi d’ailleurs). Où peut on se le procurer svp ?
Merci
Fabien
M. Verdier bonjour.
J’utilise couramment svn sur des projets avec des dépendances externes assez compliquées et je serais, moi aussi, intéressé par cet outil; quitte à investir du temps dans sa fiabilisation si nécessaire.
Pourvez-vous le diffuser ?
D’avance merci,
Philippe.
Fabien, Philippe> J’espère pouvoir annoncer le lancement du projet open-source correspondant bientôt, vous serez bien évidemment invités à contribuer. Stay tuned, comme qui dirait.
– Un dépot dans un dépot, je suis sur que c’est possible.
– Ah, svn:externals c’est ça qu’il me faut, comment ça marche.
– Tiens un article sur les bonnes pratique de svn:externals …
Et me voilà ! Je suis content de t’avoir lu, je sait maintenant comment organiser mes repos.
Je vais omettre -r sur le projet en cours, et le préciser sur les projets en stand by et (enfin en gros).
En tous cas ton outils m’intéresse vraiment, pense pas pouvoir t’aider, mais je peut donner des idées, et tester.
Je reviendrais :)
salut
svn:externals c’est bien oui, mais comment éviter lorsqu’on utilise un projet externe, qu’un commit dans notre working dev propage la modif dans le projet externe ?
je viens de faire l’essai. Je tire le projet XX dans mon projet YY. Je fais l’update, avec l’option -r de externals je recupere bien la version de XX que je connais. Maintenant je fais des modif dans mon XX local pour faire des debug ou autre et je m’en souviens plus. Je fais un commit de tout mon working dev YY et hop la modif que j’ai fait en local dans XX se propage dans le repository du projet externe.
Comment eviter cela ?
Polo
Et bien, déjà, lorsque tu fais ton commit dans YY, tu peux choisir de ne commiter que les changements de YY, et non pas les changements dans XX. Si tu utilises Tortoise SVN sous Windows, par exemple, la fenêtre de commit distingue bien les changements qui impactent les externals.
Mais peu importe, si ton projet XX nécessite des modifications dans XX, alors il te suffit de les commiter, et de mettre ensuite la révision pointée par la référence externe à jour. Si les autres projets qui dépendent de XX utilisent bien l’option -r, tu es sûr de ne pas les avoir impactés : ils pointeront toujours sur la révision de XX qui les intéressent.
Merci beaucoup pour cette réponse
Je pensais à quelque chose disons de plus « automatique » pour éviter que par mégarde une modification faite en local sur un dépôt tiré en svn:externals ne soit propagée sur le dépôt original. Le genre on tire un dépôt externe car on a besoin d’une librairie par exemple dans notre projet et pour des raisons divers on fait des changements en local dans cette librairie (pour débug au autre). Ensuite on commit tout notre working dev et paf les modifs locales qu’on a fait dans la librairie partent dans le dépôt de celle ci…
C’est vrai qu’un coup d’œil dans tortoise permet rapidement de se rendre compte qu’une modification a été fait dans un external et doit mettre la puce à l’oreille mais j’aurais aimé une solution qui verrouille à coup sur, sans avoir a vérifier.
Polo
Un post très intéressant. Je penserais à mettre -r mainteant :)