tag:blogger.com,1999:blog-17542579981366976312024-03-19T04:06:15.012+01:00John Banana QwertyJean-Baptiste Quenothttp://www.blogger.com/profile/05009942829916672633noreply@blogger.comBlogger10125tag:blogger.com,1999:blog-1754257998136697631.post-6087410222242450202010-08-30T16:56:00.003+02:002010-08-30T17:09:27.239+02:00A simple distributed lock with memcached... in PythonFollowing <a href="http://bluxte.net/musings/2009/10/28/simple-distributed-lock-memcached">Sylvain's advice</a> I just wrote a memcache lock in Python:<br /><br /><pre>class Lock():<br /> def __init__(self, key):<br /> self.key = "lock.%s" % key<br /><br /> def __enter__(self):<br /> while True:<br /> value = getClient().add(self.key, "1", 60000)<br /> if value == True:<br /> return<br /> time.sleep(.1)<br /><br /> def __exit__(self, exc_type, exc_val, exc_tb):<br /> getClient().delete(self.key)<br /> return False</pre><br /><br />Using the lock is as simple as doing:<br /><br /><pre>with Lock("critical1"):<br /> doSomeExpensiveStuff()</pre><br /><br />The advantage over Sylvain's Java version is that the locking code is reusable, thanks to the use of a <a href="http://docs.python.org/library/stdtypes.html#typecontextmanager">Python context manager</a>.Jean-Baptiste Quenothttp://www.blogger.com/profile/05009942829916672633noreply@blogger.com2tag:blogger.com,1999:blog-1754257998136697631.post-84173344978771672392008-09-15T13:38:00.007+02:002008-09-15T14:06:08.190+02:00Rencontre insoliteSur le chemin des ruines de Wilcahuaín à <a href="http://huaraz.com/">Huaraz</a>, la capitale de l'andinisme, j'ai fait une rencontre insolite. Ce qui pour certains privilégiés est un anachronisme agricole, est pour une majorité un moyen de subsistance. La vidéo parle d'elle-même, regardez plutôt.<br/><br /><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dxjZd7dgi43bhc7urqMW9Mh1cbLgQ7L_AF0L_AasC-F7Bt4O2lSdAVgjyKukeA0o9OR7B4eyIzv_711po8uOA' class='b-hbp-video b-uploaded' frameborder='0'></iframe></p>Jean-Baptiste Quenothttp://www.blogger.com/profile/05009942829916672633noreply@blogger.com0tag:blogger.com,1999:blog-1754257998136697631.post-63063362458342180692008-06-26T09:50:00.004+02:002008-09-15T14:07:04.865+02:00Service Public 2.0<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtWzZNDICcwYWr-W3zDNBntpcJ6PLZc2qYBk5DqswGm_Wkhdo10xEI1fkRTCQFJ_qKn_0nlpBwJ6TWI9zTEdUQ8sIygaw1FJx7UsBrhPTijivAYoHuobZ3m43pjU68vO1BwgYqqTPn5Zc/s1600-h/snap-unknown-20080625-174123-1.png"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtWzZNDICcwYWr-W3zDNBntpcJ6PLZc2qYBk5DqswGm_Wkhdo10xEI1fkRTCQFJ_qKn_0nlpBwJ6TWI9zTEdUQ8sIygaw1FJx7UsBrhPTijivAYoHuobZ3m43pjU68vO1BwgYqqTPn5Zc/s400/snap-unknown-20080625-174123-1.png" alt="" id="BLOGGER_PHOTO_ID_5216095328087222034" border="0" /></a>J'ai eu la bonne surprise de recevoir un SMS de la Mairie de Toulouse pour me prévenir que mon passeport était disponible. Ça c'est du service public 2.0! Incroyable!Jean-Baptiste Quenothttp://www.blogger.com/profile/05009942829916672633noreply@blogger.com2tag:blogger.com,1999:blog-1754257998136697631.post-31176882246733871002008-05-15T23:54:00.001+02:002008-05-17T14:05:50.336+02:00Tempête de grêle sur Toulouse<a href="http://www.ladepeche.fr/article/2008/05/15/454187-Toulouse-sous-la-grele.html">Une tempête de grêle sur Toulouse</a> , on voit ça très rarement. Dommage pour les plantes, va falloir replanter des nouveaux pieds de tomates!<br /><br /><div style="text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dwv4QqHyR3m6ADtoFYiUFL9C3qlqcOpuWwST2o4dGjr26gsDxzZGjbvVHp17TDaAQ9M4Leqn5X1yZ_lrCReJw' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div>Jean-Baptiste Quenothttp://www.blogger.com/profile/05009942829916672633noreply@blogger.com1tag:blogger.com,1999:blog-1754257998136697631.post-65092344269881634462008-04-10T14:23:00.003+02:002009-09-17T14:59:44.104+02:00Beware, Puppet power users!<strong><a href="http://reductivelabs.com/trac/puppet/wiki">Puppet</a></strong> is a tool to automate system administration and to save you from boring and repetitive tasks. The various services, packages and configuration files of the various hosts are organized into nodes. It's pretty interesting because nodes can be arranged in a tree, so that the configuration that is common between several machines is written only once, and inherited by the child nodes. Also, definitions let you write modular pieces of system configuration, with parameters, and default values.<br /><br />Puppet is written in Ruby, so it may sound fantastic news for power users that wish to extend Puppet capabilities. But there are some limitations that even a novice Puppet user can already face:<br /><ol><br /> <li>Configuration is not written in Ruby, so you have to stick with the plain Puppet vocabulary. That's actually good to have a well-defined format that models the basic concepts like <em>package</em>, <em>service</em>, <em>file</em>, etc, and that's a guarantee to survive a Ruby upgrade or even a complete Puppet rewrite. But Puppet has reinvented variables for example, and without any object-oriented design. So you're stuck with some kind of artificial global variables following poorly-documented rules. <strong>So I would actually warn against using Puppet variables.</strong></li><br /> <li><em>Classes</em>, as it's called in the Puppet semantic, are not classes like you would expect in the object-oriented sense. <em>Classes</em> in Puppet are just encapsulating other configuration bits. Using those <em>classes</em> simply consists in including their definition. <strong>Do not expect any sophisticated inheritance or any other OOP feature here.</strong> I would recommend to only use <em>definitions</em>, that are reusable wrappers with support for parameters and default values.</li><br /> <li>Puppet templates (actually ERB templates) can reference Puppet variables defined in the configuration, but <a href="http://reductivelabs.com/trac/puppet/ticket/779" title="Issue with variables in Puppet templates"><strong>this is a hack</strong></a>, so don't expect sophisticated constructs here. <strong>Template writers don't have access to any high-level Puppet-oriented API</strong>. You can use plain Ruby constructs however, like string manipulation, math functions, etc. And the error reporting is so weak, see for yourself:<br /><pre>err: Could not retrieve catalog: Uncaught exception compile error<br />(erb):73: syntax error, unexpected kEND, expecting $end<br /> end ; _erbout.concat "\n"<br /> ^ in method puppetmaster.getconfig</pre><br />You have to restart puppetmasterd in debug mode and use <tt>erb -x -T '-' mytemplate.erb</tt> to find out what goes wrong!<br /></li><br /> <li>Documentation is written in a wiki. It's based on user contribution, so don't expect to receive more than you can give yourself. I suggest reading Pulling Strings with Puppet, although I haven't read it myself, but I <em>hope</em> it can offer some more in-depth understanding of Puppet. (On a side note, the Wiki is using a Trac plugin providing <a href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> syntax, so make sure to preview your changes, it's not the Trac syntax).</li><br /></ol><br />Now let's look at the promise of extensibility.<br /><br /><h3> Writing a custom function</h3><br />That looks pretty seducing at a first glance, but it just doesn't work:<br /><ol><br /> <li>All so-called <em>functions</em> are run on the server. It's a bit misleading, and I think it is a wrong design. Not being able to write a custom function that will be run on the client side does not make sense. When I'm writing bits of configuration, I'm communicating to Puppet the details that make sense on the local machine, I'm never referencing files that are local to the server! At least the Puppet syntax for a function call should reflect the fact that functions are actually <em>parser functions</em>, not <em>general-purpose functions</em>.</li><br /> <li>Again, there is no OO design. For example, creating a function is achieved through calling Puppet::Parser::Functions::newfunction(:myfunction)</li><br /> <li>As you can see in newfunction example above, Puppet internals make extensive use of Ruby symbols, which in my opinion reflects the absence of a real API, and the lack of a real internal design.</li><br /></ol><br />Also, note that the Ruby code has to be deployed both on the server and on the client. The fileserver along with the pluginsync mechanism allows to distribute the code automatically on all clients, although I haven't tested this myself.<br /><br /><h3>Writing a custom type</h3><br />So, you want to write a custom <em>thing</em> that will be executed on the client? There is no choice, you need to write a custom type. But the problem here is that I won't be able to talk a lot about it, because after spending 6 hours trying to hack a custom type with a lot of copy paste, I'm not satisfied with the result, and <strong>although I'm both new to Ruby and Puppet-internals, I don't think anyone can actually write any useful custom thing with Puppet. Puppet really needs a true API, be it in Ruby or not. </strong>The fact that the manifests are written in an independent syntax offers the possibility to remain compatible in the eventuality of a complete rewrite of the Puppet API.<br /><br /><h3>The word of the end</h3><br />If you need to extend the core capabilities of Puppet, be warned! There is no reliable and documented way to achieve it. <strong>Puppet is a great tool to automate system deployment and maintenance, but still lacks a true API with clearly documented extension points.</strong> Stick with simple documented Puppet constructs, use the <em>exec</em> type with hardcoded paths if there is no other way, or better: prepare your data on the server-side beforehand outside the control of Puppet if you need something more elaborate. For example, to concatenate Apache htpasswd files, I ended up writing a two-line shell script in the <em>post-update</em> hook of my Git repository. Much simpler than trying to concatenate files with Puppet! <strong>Extending Puppet is far from being a trivial task, even for an expert programmer!</strong>Jean-Baptiste Quenothttp://www.blogger.com/profile/05009942829916672633noreply@blogger.com2tag:blogger.com,1999:blog-1754257998136697631.post-797007845991679462008-01-23T00:08:00.000+01:002008-11-05T11:38:47.644+01:00Refactoring without branchingDevelopers using the conventional Subversion source control management system (SCM) often come to a point where they cannot continue committing their work on <strong>trunk</strong> because their changes are impacting the work of others. When you're knee-deep in the code, you don't necessarily plan to create a branch beforehand. A refactoring is sometimes unforeseen, and once you realize it's going to be the big blast of the month, you refrain from committing your work to trunk and realize you'll have to decide: either to keep a huge set of modified files for several weeks at your own risk, or to create a branch and redo your changes there, with all the hassle of managing branches with Subversion <em>(did you ever find yourself entering into endless discussions about branching?)</em>. Either way, you feel bad.<br /><br />Fortunately, there's a new player in the world of source control: <a href="http://git.or.cz/"><strong>GIT</strong></a>.<br /><blockquote><em>Git is a popular version control system designed to handle very large projects with speed and efficiency. Git falls in the category of distributed source code management tools. Every Git working directory is a full-fledged repository with full revision tracking capabilities, not dependent on network access or a central server.</em></blockquote><br />Sounds great, heh? But there's no chance your CTO will stash SVN and adopt Git. This is where <strong>git-svn</strong> comes to the rescue! In a word, git-svn is a bridge between Subversion and Git:<br /><ol><br /> <li>You create a Git workspace by importing the SVN repository once</li><br /> <li>Make changes and commit them locally in your Git workspace</li><br /> <li>Repeat step 2. <em>ad lib.</em></li><br /> <li>Push back your changes to SVN when you're done</li><br /></ol><br />I'm using this approach since a few weeks at <a href="http://www.nomao.com">work</a> with success. When I push back my changes once a week to the SVN repository, a bunch of commits are made within in a few seconds, that's impressive! My colleagues know when I'll be pushing the next changes, so they can work safely until the next iteration. No more branches, no more hassle, only happy refactoring!<br /><br />If you want to know more, I suggest to look at:<br /><ul><br /> <li><a href="http://git.or.cz/course/svn.html">Introduction to Git for Subversion users</a></li><br /> <li><a href="http://utsl.gen.nz/talks/git-svn/intro.html">An introduction to git-svn for Subversion users and deserters</a></li><br /></ul>Jean-Baptiste Quenothttp://www.blogger.com/profile/05009942829916672633noreply@blogger.com1tag:blogger.com,1999:blog-1754257998136697631.post-58456820372908378822007-10-28T10:44:00.001+01:002008-11-05T11:52:38.851+01:00Java est-il vraiment adapté au développement d'applications Web?Drôle de question de la part d'un committer Apache Cocoon et Apache Wicket: tous deux sont écrits en Java et offrent une API Java pour le développement d'applications web. Mais prêtons-nous au jeu après plus de six ans de J2EE, quels-sont vraiment les points forts de Java pour le développement d'applications Web?<br /><ul><br /> <li>Les Threads et autre TimerTask sont bien utiles (si on compare avec PHP)</li><br /> <li>L'outillage (Eclipse JDT, JUnit) est très "pro", on oublie vite l'éditeur de texte et la ligne de commande</li><br /> <li>Certains très bons programmes n'existent qu'en Java: Lucene, FOP</li><br /> <li><em>sûrement d'autres avantages que j'ai oubliés?</em></li><br /></ul><br />En revanche l'approche Java a aussi ses inconvénients:<br /><ul><br /> <li>l'approche tout-objet multiplie le nombre de classes et augmente le volume global et la complexité structurelle de l'application: l'outil de build (Ant ou Maven) est devenu quasiment obligatoire, même si les développements réalisés ne justifient pas cette complexité.</li><br /> <li>Java n'est pas Open-Source, cela a deux principaux inconvénients: pendant longtemps il a été difficile d'intégrer Java avec les différents systèmes d'exploitation. D'autre part, la mise en place d'un réseau de distribution de composants a beaucoup tardé, et n'a pas été initiée par les concepteurs de Java. Face à la «complexité obscure» de Maven, on ne peut qu'envier à Perl la simplicité de son CPAN, à PHP son PEAR et à Ruby ses Gems.</li><br /> <li>Java ne s'intègre pas avec le serveur web Apache directement, on doit maintenir un serveur séparé qui tourne sur une JVM (ceci dit <a href="http://duncandavidson.com/archives/166">Ruby on Rails semble vouloir faire de même</a>)</li><br /> <li>cycle de développement plus lourd, le bon vieux <em>save+refresh</em> ne fonctionne pas avec Java et ne peut pas être implémenté simplement et de manière fiable (JavaRebel semble cependant gagner en popularité en ce moment). Un certain nombre de <em>frameworks</em> écrivent des <em>classloaders</em> pour compiler ou recharger les classes à la volée, mais c'est loin d'être une science exacte.</li><br /></ul><br />D'autre part les frameworks MVC comme Apache Wicket sont très sophistiqués et très séduisants pour le développeur (l'API est proche de Swing) mais posent problème pour les applications Web 2.0:<br /><ul><br /> <li>l'approche tout-composant éloigne de plus en plus le développeur de la problématique initiale: le serveur doit fournir au client le morceau de HTML ou de Javascript nécessaire au bon moment. Le framework devient alors un obstacle à contourner, ce qui peut se révéler difficile et fragile.</li><br /> <li>Les fonctionnalités Ajax sont implémentées de manière très abstraite, et la réponse est enveloppée dans du XML. Pourquoi ne pas plutôt renvoyer uniquement du code JavaScript directement interprété (JSON)?</li><br /> <li>Le modèle MVC ne se marie pas toujours très bien avec les contraintes actuelles du développement Web: JavaScript sur les navigateurs n'est pas assez standardisé et formalisé pour pouvoir bâtir des applications web par composants. Les étapes de chargements des éléments de la page par exemple sont difficilement modélisables avec une API.</li><br /></ul><br />S'il se prête moins à la réalisation d'applications Web, surtout les<br />applications Web appellées «2.0» où l'utilisateur interagit avec le<br />serveur de manière beaucoup plus fine que sur des applications web<br />classiques qui se contentent de <em>servir des pages</em>, l'environnement Java reste particulièrement intéressant pour une équipe de développement qui construit des bibliothèques de composants comportant des traitements métiers complexes, accessibles par exemple à travers une API Rest.<br /><br />Cependant, même si on le cantonne au <em>backend</em>, la JVM prend du retard sur ses concurrents, et du fait de son aspect «fermé», Java n'obtient pas nécessairement les contributions qui permettraient de moderniser lae language. Ses concurrents Stackless Python, Erlang et Scala suivent la tendance actuelle de distribuer les applications sur plusieurs serveurs et de profiter du nombre croissant de processeurs. Tirer intelligemment profit de la puissance de traitement des clusters de machine, c'est le <em>challenge</em> à relever pour les applications Web d'aujourd'hui.<br /><br />Pourquoi Java est-il donc si populaire dans le monde des services informatiques? Il répond à une demande forte de la part des professionnels, et l'ensemble de la filière s'adapte à cette demande. Java est devenue la référence commune admise dans le monde des grandes entreprises, la technologie «politiquement correcte» dans laquelle on peut s'engager les yeux fermés. Bien souvent la montée en charge n'est pas une problématique exprimée initialement. L'exercice d'optimisation <em>a posteriori</em> est donc bien souvent fastidieux. Avec Java il est tellement facile de construire des cathédrales, en réutilisant des librairies par ci par là, que le développeur (ou même l'architecte) oublie bien souvent l'aspect <em>efficacité</em> au profit des <em>fonctionnalités</em>.<br /><br />Par contre quand on a la <strong>liberté</strong> de créer une application web en dehors de ce monde <em>corporate</em>, on aura intérêt à utiliser des technologies plus «abordables» pour rester dans la course: abordables en termes de développement, qui n'a pas eu régulièrement des problèmes de <em>classpath</em>, mais aussi de déploiement: redémarrage en cas de <em>crash</em>, <em>monitoring</em>, exploitation des fichiers de <em>logs</em>, etc. Les technologies «abordables» donc, avec des cycles de développement<br />courts, permettent de garder un avantage compétitif non négligeable sur ce web en perpétuelle évolution.<br /><br />Quelques articles intéressants (en anglais):<br /><ul><br /> <li>http://www.process-one.net/en/blogs/article/web_20_shifting_from_get_fast_to_get_massive/</li><br /> <li>http://public.yahoo.com/bfrance/radwin/talks/yahoo-phpcon2002.htm</li><br /> <li>http://raibledesigns.com/rd/entry/php_vs_java_which_is</li><br /> <li>http://www.epicserve.com/blog/69/python-and-django-ruby-on-rails-and-php</li><br /></ul><br />Et une spéciale dédicace à <a href="http://bluxte.net">Sylvain Wallez</a>, qui m'a aiguillé sur toutes ces pistes de réflexion intéressantes depuis 2004.Jean-Baptiste Quenothttp://www.blogger.com/profile/05009942829916672633noreply@blogger.com0tag:blogger.com,1999:blog-1754257998136697631.post-90789075218034832452007-10-15T09:44:00.002+02:002008-11-05T11:52:24.556+01:00Using the older Subversion working copy format with HudsonUsually I don't let <strong>Hudson</strong> check out my projects, as I arrange my projects workspaces to be symbolic links to a specific location in a manually-checked-out working copy of a full repository, so that relative paths expressed in the build files resolve properly. But beware that if the Subversion client installed is pre-1.4, Hudson will automatically upgrade the working copies to the new Subversion 1.4 format. This can be a problem as the Subversion client on the command-line cannot be used anymore. If you can't afford to upgrade to Subversion 1.4 or later, you need to turn on compatibility with the older Subversion working copy format (before 1.4), using the <a href="https://hudson.dev.java.net/servlets/ProjectDocumentList?folderID=8171&expandFolder=8171&folderID=5818"><strong>Hudson's svncompat13</strong> plugin</a> I just released.<br /><br /><p>Internally, Hudson uses SVNKit, a Java library to access Subversion repositories. The trick used by the <strong>svncompat13</strong> plugin is described in the </span><a href="https://wiki.svnkit.com/SVNKit_FAQ" rel="nofollow" target="_top"><span><strong class="highlight">SVNKit FAQ</strong></span></a><span></span><span>.<br /></p>Jean-Baptiste Quenothttp://www.blogger.com/profile/05009942829916672633noreply@blogger.com0tag:blogger.com,1999:blog-1754257998136697631.post-7711880521195225002007-10-10T08:47:00.001+02:002008-11-05T11:52:04.283+01:00Better SCM polling for HudsonHave you already experienced this problem with Hudson? When project <strong>Bar</strong> depends on project <strong>Foo</strong> and a commit spans both, Hudson rebuilds the two in an undefined order, thus sometimes leading to a failure in <strong>Bar</strong>, whereas one is expecting that <strong>Foo</strong> is built first, then <strong>Bar</strong>. Let's give a concrete example to precisely understand what happens:<br /><ol><br /> <li>The developer adds a new feature in <strong>Foo</strong>, and makes use of it in <strong>Bar</strong>. Both <strong>Foo</strong> and <strong>Bar</strong> are committed at the same time</li><br /> <li>Hudson polls the projects for changes in no particular order (alphabetical in my experience), thus <strong>Bar</strong> is appended to the build queue, then <strong>Foo</strong></li><br /> <li>Build for <strong>Bar</strong> fails because <strong>Foo</strong> has not been rebuilt yet and the new feature has not made it yet</li><br /> <li><strong>Foo</strong> is updated and built successfully, and all projects depending on <strong>Foo</strong> (namely <strong>Bar</strong>) are added to the build queue</li><br /> <li><strong>Bar</strong> now builds successfully</li><br /></ol><br />This is a simplified explanation however, in practice the results can be different because Hudson provides every project its own thread to poll the SCM for changes, so depending on the number of projects and the response time of the SCM, projects may be queued in a different order. In no way Hudson will guarantee that Bar will be added to the build queue before Foo.<br /><br />On the contrary, processing the set of projects in the order of dependencies (least-dependent first), and performing polling synchronously using a single thread for all projects will make sure Bar is queued before Foo, so that a commit spanning both Foo and Bar will result in a success in both Bar, and Foo. To achieve this I have added an experimental piece of code starting from version 1.141 that you can activate by adding the following highlighted XML element in <tt class="filename">hudson.triggers.SCMTrigger.xml</tt>, and make sure to request <strong>only one thread</strong> for polling:<br /><pre class="code xml"><hudson.triggers.scmtrigger_-descriptorimpl> <strong><synchronouspolling>true</synchronouspolling></strong> <maximumthreads>1</maximumthreads></hudson.triggers.scmtrigger_-descriptorimpl></pre><br />Reload Hudson configuration and watch your projects build. Please report your experience with this improved polling algorithm. If it happends to be useful, we might add it to the SCM configuration screen.<br /><br /><em>P.S. in theory, even with the improved SCM polling algorithm, there is still a possibility to fail if the commit is made <strong>during</strong> polling, between Foo and Bar, but the overall probability to fail is much lower than with the genuine algorithm.</em>Jean-Baptiste Quenothttp://www.blogger.com/profile/05009942829916672633noreply@blogger.com0tag:blogger.com,1999:blog-1754257998136697631.post-39743590211198743212007-09-10T15:16:00.000+02:002008-11-05T11:51:50.824+01:00Test-driven development for HudsonRecently I convinced myself to implement the various features that I was missing in Hudson: <a href="https://hudson.dev.java.net/issues/show_bug.cgi?id=747" title="null">cumulative notifications</a>, <a href="https://hudson.dev.java.net/issues/show_bug.cgi?id=806" title="null">build in the right order when commits span multiple projects</a>, <a href="https://hudson.dev.java.net/issues/show_bug.cgi?id=763" title="null">gracefully handle project deleted in SVN</a>. But as it involved hacking the guts of Hudson and I didn't want to click-click the whole day in the configuration screens, I wrote the various usecases as tests. Up to now in Hudson, the only existing tests were true unit-tests in the sense of checking that an isolated method returns the proper result. But there was no test to actually verify the complete lifetime of a project: configure, build, delete, or to verify behavior with Subversion.<br /><br />Writing integration tests for Hudson was hard, here are the ugly bits:<br /><ul><br /> <li>Subversion local repository is created with <tt>svnadmin create</tt> in a temporary directory</li><br /> <li>Actual builds are done with shell script, this will only work on *nix machines</li><br /> <li>There are various reflection hacks. The Hudson API could be more consistent in that area and allow to set field values programmatically</li><br /></ul><br />See for yourself and run <tt>mvn test</tt> in <a href="http://fisheye5.cenqua.com/browse/hudson/hudson/extras/tester/" title="null">Hudson Tester</a>. There's a long way to go to be able to<br />easily write tests for Hudson, but the proof of concept works. I hope<br />this test-driven approach can help Hudson developers implement new features more<br />efficiently and prevent regressions across releases.Jean-Baptiste Quenothttp://www.blogger.com/profile/05009942829916672633noreply@blogger.com0