unethical blogger - Hudson http://unethicalblogger.com/taxonomy/term/17/0 Hudson-related blog topics en Sometimes Software as a Service Sucks http://unethicalblogger.com/posts/2010/03/sometimes_software_service_sucks <p>Being a big fan of "continuous integration", particularly with <a id="aptureLink_PmOzQb3Bo7" href="http://twitter.com/hudsonci">Hudson</a>, I've often thought about the possibilities of turning it into a business. It's no surprise really, my first commercial application as a rogue Mac software developer was a product called <a href="http://bleepsoft.com/buildfactory/">BuildFactory</a> which, while fun to build, never sold all that many licenses. With the advent of Amazon's <a id="aptureLink_SLPMEfLHeR" href="http://en.wikipedia.org/wiki/Amazon%20Elastic%20Compute%20Cloud">EC2</a> service and the transition of these cloud computing resources into a building block for many businesses, I've long thought about the idea of building "continuous integration as a service."</p> <p>At face value the idea sounds incredibly fun to build, I'll build a service that integrates with <a id="aptureLink_q5Kr8iq6a2" href="http://twitter.com/gIthub">GitHub</a>, <a id="aptureLink_BLDvLKGYwy" href="http://www.crunchbase.com/product/google-code">Google Code</a>, <a id="aptureLink_z9njtjnyXs" href="http://en.wikipedia.org/wiki/SourceForge">SourceForge</a> and private source control systems. The end (paying) user would "plug-in" to the "continuous integration grid", they'd work throughout the day, committing code and then the CI grid would pick up those changes, build releases and run tests against a number of different architecture, automatically detecting failures and reporting them back to the developers. It involves some of my favorite challenges in programming:</p> <ul> <li>Scaling up</li> <li>Efficiently using cycles, and only when needed</li> <li>Building and testing cross-architecture and cross-platform</li> </ul> <p>Unfortunately, it's a crap business idea, I now have second-hand confirmation from a group of guys who've attempted the concept. The folks behind <a id="aptureLink_Yb3agfhs2a" href="http://runcoderun.com/">RunCodeRun</a> are <a href="http://blog.runcoderun.com/post/463439385/saying-goodbye-to-runcoderun">shutting down the service</a>. In the post outlining why they're shutting down, they've hit the nail on the head on why "continuous integration as a service" can <strong>never</strong> work:</p> <blockquote> <p>Large scale hosted continuous integration is consumed as a commodity but built as a craft, and the rewards, both emotional and financial, are insufficient to support the effort.</p> </blockquote> <p>Elaborating further on their point, continuous integration by itself is a relatively basic task: build, test, repeat. The biggest problem with continuous integration as a service however, is that no two projects are alike. My build targets or requirements might be vastly different from project to project, let alone customer to customer, making the amount of tweaking and customization per-job too large such that at some point the only benefit that one derives from such a service is the hosting of the machines to perform the task. If you're just taking care of that, why wouldn't your customers just use Hudson in "the cloud" themselves? The CI grid at that point offers no exceptional value.</p> <p>As much as I regret letting a fun idea die, I think I'll have to file this one under "To do after becoming so rich I'll care about capital gains taxes."</p> http://unethicalblogger.com/posts/2010/03/sometimes_software_service_sucks#comments Hudson Opinion Software Development Tue, 23 Mar 2010 14:00:00 +0000 R. Tyler Croy 275 at http://unethicalblogger.com Mourning Sun http://unethicalblogger.com/posts/2010/01/mourning_sun <p>Some users of Hudson have already started to notice a subtle addition to the latest release, 1.343, a new background watermark image.</p> <p><center><a href="http://agentdero.cachefly.net/scratch/hudson_1343.png"><img width="600" src="http://agentdero.cachefly.net/scratch/hudson_1343.png" border="0"/></a></center></p> <p>The commit message (<a href="http://github.com/kohsuke/hudson/commit/7e1602415ce86fb6ed3630a9e8d6b86a99f6477e">r26728</a>) from <a id="aptureLink_beuDMdQyLf" href="http://twitter.com/kohsukekawa">Kohsuke</a>, the incredibly talented founder and maintainer of the <a id="aptureLink_iq9IUqnvlG" href="http://twitter.com/hudsonci">Hudson project</a>, adds a bit of sadness to the whole affair:</p> <blockquote>In tribute to Sun Microsystems and all my colleagues who had to go today. I hope the community would forgive me for doing this. </blockquote> <p>Given the incredible speed at which the tech industry grows and moves, it's easy to forget that there are a number of talented engineers that have spent their careers at Sun building technologies that have helped change the face of modern computing, regardless of whether or not Sun could figure out how to sell them: <a id="aptureLink_lV3vSmeDpY" href="http://en.wikipedia.org/wiki/SunOS">SunOS</a>/<a id="aptureLink_eXvarQ2fAp" href="http://en.wikipedia.org/wiki/Solaris%20%28operating%20system%29">Solaris</a>, <a id="aptureLink_FpOrTgoKGX" href="http://en.wikipedia.org/wiki/Java%20%28programming%20language%29">Java</a>, <a id="aptureLink_FwpJ7pdCVJ" href="http://en.wikipedia.org/wiki/DTrace">DTrace</a>, <a id="aptureLink_p5b1rnrCvM" href="http://www.slideshare.net/pavelanni/sun-sparc-systems-historic-view">SPARC</a> 64-bit chips, <a id="aptureLink_vax0xgKGzx" href="http://en.wikipedia.org/wiki/Sun%20Grid%20Engine">Sun Grid Engine</a>, <a id="aptureLink_yqKUNGZA08" href="http://en.wikipedia.org/wiki/JRuby">JRuby</a>, the W3C <a id="aptureLink_BX8fOLXg1h" href="http://en.wikipedia.org/wiki/XML">XML</a> specification, <a id="aptureLink_6pAltRfGgE" href="http://en.wikipedia.org/wiki/ZFS">ZFS</a>, <a id="aptureLink_g6Nq6uBwMs" href="http://en.wikipedia.org/wiki/OpenOffice.org">OpenOffice</a> (acquisition), <a id="aptureLink_cmRhH6JoZP" href="http://en.wikipedia.org/wiki/MySQL">MySQL</a> (acquisition), and <a id="aptureLink_OS2nUnWdtm" href="http://en.wikipedia.org/wiki/VirtualBox">VirtualBox</a> (acquisition).</p> <p>As a corporation, I personally think Sun was a failure, as a foundation of engineering in Silicon Valley, I think Sun has been quite successful.</p> <p>To those that are being pushed out as part of the merger with Oracle, I want to sincerely thank you for your contributions to computing and wish you the best of luck. <!--break--> Here's the "full" version of the image, which I found via <a href="http://twitter.com/jtnl" target="_blank">@jtnl</a>'s TwitPic stream: <center><a href="http://agentdero.cachefly.net/scratch/ripsun.jpg"><img width="600" src="http://agentdero.cachefly.net/scratch/ripsun.jpg" border="0"/></a></center></p> http://unethicalblogger.com/posts/2010/01/mourning_sun#comments Hudson Opinion Software Development Sun, 31 Jan 2010 03:51:52 +0000 R. Tyler Croy 263 at http://unethicalblogger.com Pre-tested commits with Hudson and Git http://unethicalblogger.com/posts/2009/12/pretested_commits_hudson_and_git <p>A few months ago <a id="aptureLink_yMRaEAQt6P" href="http://twitter.com/kohsukekawa">Kohsuke</a>, author of the <a id="aptureLink_gay9zt4yuf" href="http://twitter.com/hudsonci">Hudson continuous integration server</a>, introduced me to the concept of the "pre-tested commit", a feature of the <a id="aptureLink_h8ICO1PttT" href="http://en.wikipedia.org/wiki/TeamCity">TeamCity</a> build management and continuous integration system. The concept is simple, the build system stands as a roadblock between your commit entering trunk and only after the build system determines that your commit doesn't break things does it allow the commit to be introduced into version control, where other developers will sync and integrate that change into their local working copies. The reasoning and workflow put forth by TeamCity for "pre-tested commits" is very dependent on a centralized version control system, it is solving an issue <a id="aptureLink_IXcu5r11no" href="http://en.wikipedia.org/wiki/Git%20%28software%29">Git</a> or <a id="aptureLink_cPtvZ5XxiP" href="http://en.wikipedia.org/wiki/Mercurial%20%28software%29">Mercurial</a> users don't really run into. Those using Git can commit their hearts out all day long and it won't affect their colleagues until they <strong>merge</strong> their commits with others.</p> <p>In some cases, allowing buggy or broken code to be <em>merged</em> in from another developer's Git repository can be worse than in a central version control system, since the recipient of the broken code might perform a knee-jerk <a id="aptureLink_N7GE0Q9soz" href="http://www.kernel.org/pub/software/scm/git/docs/git-revert.html">git-revert(1)</a> command on the merge! When you revert a merge commit in Git, what happens is you not only revert the merge, you revert the commits associated with that merge commit; in essence, you're reverting <em>everything</em> you just merged in when you likely just wanted to get the broken code out of your local tree so you could continue working without interruption. To solve for this problem-case, I utilize a "pre-tested commit" or "pre-tested merge" workflow with Hudson.</p> <p>My workflow with Hudson for pre-tested commits involves three separate Git repositories: my local repo (local), the canonical/central repo (origin) and my "world-readable" (inside the firewall) repo (public). For pre-tested commits, I utilize a constantly changing branch called "pu" (potential updates) on the world-readable repo. Inside of Hudson I created a job that polls the world-readable repo (public) for changes in the "pu" branch and will kick off builds when updates are pushed. Since the content of <code>public/pu</code> is constantly changing, the <a id="aptureLink_O9LMHblU7c" href="http://www.kernel.org/pub/software/scm/git/docs/git-push.html">git-push(1)</a> commands to it must be "forced-updates" since I am effectively rewriting history every time I push to <code>public/pu</code>.</p> <p>To help forcefully pushing updates from my current local branch to <code>public/pu</code> I use the following <a id="aptureLink_jO9JAsy1Sm" href="http://git.or.cz/gitwiki/Aliases">git alias</a>:</p> <pre><code>% git config alias.pup "\!f() { branch=\$(git symbolic-ref HEAD | sed 's/refs\\/heads\\///g');\ git push -f \$1 +\${branch}:pu;}; f" </code></pre> <p>While a little obfuscated, thie <code>pup</code> alias forcefully pushes the contents of the current branch to the specified remote repository's <code>pu</code> branch. I find this is easier than constantly typing out: <code>git push -f public +topic:pu</code></p> <p>In list form, my workflow for taking a change from inception to <code>origin</code> is:</p> <ul> <li><em>hack, hack, hack</em></li> <li>commit to <code>local/topic</code></li> <li><code>git pup public</code></li> <li>Hudson polls <code>public/pu</code> </li> <li>Hudson runs potential-updates job</li> <li>Tests fail? <ul> <li><strong>Yes</strong>: Rework commit, try again</li> <li><strong>No</strong>: Continue</li> </ul></li> <li>Rebase onto <code>local/master</code></li> <li>Push to <code>origin/master</code></li> </ul> <p>Using this pre-tested commit workflow I can offload the majority of my testing requirements to the build system's cluster of machines instead of running them locally, meaning I can spend the <strong>majority</strong> of my time writing code instead of waiting for tests to complete on my own machine in between coding iterations.</p> http://unethicalblogger.com/posts/2009/12/pretested_commits_hudson_and_git#comments Git Hudson Software Development Thu, 31 Dec 2009 23:22:16 +0000 R. Tyler Croy 254 at http://unethicalblogger.com IronWatin; mind the gap http://unethicalblogger.com/posts/2009/10/ironwatin_mind_gap <p>Last week <a id="aptureLink_UJbJnwQvgk" href="http://twitter.com/admc">@admc,</a> despite being a big proponent of <a id="aptureLink_mV2RK9dLaN" href="http://twitter.com/windmillproject">Windmill</a>, needed to use WatiN for a change. <a id="aptureLink_sf9oXnu3uF" href="http://watin.sourceforge.net/">WatiN</a> has the distinct capability of being able to work with Internet Explorer's HTTPS support as well as frames, a requirement for the task at hand. As adorable as it was to watch <a id="aptureLink_zccUSsrvlx" href="http://twitter.com/admc">@admc,</a> a child of the dynamic language revolution, struggle with writing in C# with Visual Studio and the daunting "Windows development stack," the prospect of a language shift at Slide towards C# on Windows is almost laughable. Since <a id="aptureLink_oR2hGjfmlx" href="http://www.crunchbase.com/company/slide">Slide</a> is a Python shop, IronPython became the obvious choice.</p> <p>Out of an hour or so of "extreme programming" which mostly entailed Adam watching as I wrote IronPython in his Windows VM, <a href="http://github.com/rtyler/IronWatin"><strong>IronWatin</strong></a> was born. IronWatin itself is a <strong>very</strong> simple test runner that hooks into Python's <a id="aptureLink_SpWkHjDZgq" href="http://en.wikipedia.org/wiki/PyUnit">"unittest"</a> for creating integration tests with WatiN in a familiar environment.</p> <p>I intended IronWatin to be as easy as possible for "native Python" developers, by abstracting out updates to <code>sys.path</code> to include the Python standard lib (adds the standard locations for Python 2.5/2.6 on Windows) as well as adding <code>WatiN.Core.dll</code> via <code>clr.AddReference()</code> so developers can simply <code>import IronWatin; import WatiN.Core</code> and they're ready to start writing integration tests. When using IronWatin, you create test classes that subclass from <code>IronWatin.BrowserTest</code> which takes care of setting up a browser (WatiN.Core.IE/WatiN.Core.FireFox) instance to a specified URL, this leaves your <code>runTest()</code> method to actually execute the core of your test case.</p> <p>Another "feature"/design choice with IronWatin, was to implement a <code>main()</code> method specifically for running the tests on a per-file basis (similar to <code>unittest.main()</code>). This main method allows for passing in an <code>optparse.OptionParser</code> instance to add arguments to the script such as "--server" which are passed into your test classes themselves and exposed as "self.server" (for example). Which leaves you with a fairly straight-forward framework with which to start writing tests for the browser itself:</p> <div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;">#!/usr/bin/env ipy</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># The import of IronWatin will add a reference to WatiN.Core.dll</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># and update `sys.path` to include C:\Python25\Lib and C:\Python26\Lib</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># so you can import from the Python standard library</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> IronWatin</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> WatiN.<span style="color: black;">Core</span> as Watin</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">optparse</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">class</span> OptionTest<span style="color: black;">&#40;</span>IronWatin.<span style="color: black;">BrowserTest</span><span style="color: black;">&#41;</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> url = <span style="color: #483d8b;">'http://www.github.com'</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">def</span> runTest<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;"># Run some Watin commands</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">assert</span> <span style="color: #008000;">self</span>.<span style="color: black;">testval</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> opts = <span style="color: #dc143c;">optparse</span>.<span style="color: black;">OptionParser</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> opts.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'--testval'</span>, dest=<span style="color: #483d8b;">'testval'</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'Specify a value'</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> IronWatin.<span style="color: black;">main</span><span style="color: black;">&#40;</span>options=opts<span style="color: black;">&#41;</span></div></li></ol></pre></div> <p>Thanks to IronPython, we can make use of our developers' and QA engineers' Python knowledge to get the up and running with writing integration tests using WatiN rapidly instead of trying to overcome the hump of teaching/training with a new language.</p> <p><strong>Deployment Notes:</strong> We're using IronPython 2.6rc1 and building WatiN from trunk in order to take advantage of some recent advances in their Firefox/frame support. We've not tested IronWatin, or WatiN at all for that matter, anywhere other than Windows XP.</p> http://unethicalblogger.com/posts/2009/10/ironwatin_mind_gap#comments Hudson Mono Slide Software Development Tue, 13 Oct 2009 21:57:49 +0000 R. Tyler Croy 231 at http://unethicalblogger.com Doing more with less; very continuous integration http://unethicalblogger.com/posts/2009/09/doing_more_less_very_continuous_integration <p>Once upon a time I was lucky enough to take an "Intro to C++" class taught by none other than <a href="http://en.wikipedia.org/wiki/Bjarne_Stroustrup">Bjarne Stroustrop</a> himself, while I learned a lot of things about what makes C++ good and sucky at the <em>same</em> time, he also taught a very important lesson: great engineers are lazy. It's fairly easy to enumerate functionality in tens of hundreds of lines of poorly organized, inefficient code, but (according to Bjarne) it's the great engineers that are capable of distilling that functionality into it's most succinct form. I've since taken this notion of being "ultimately lazy" into my professional career, making it the root answer for a lot of my design decisions and choices: "Why bother writing unit tests?" I'm too lazy to fire up the whole application and click mouse buttons, and I can only do that so fast; "Why do you only work with <a id="aptureLink_qYcERvYA4N" href="http://en.wikipedia.org/wiki/Vim%20%28text%20editor%29">Vim</a> in <a id="aptureLink_m0DuZkisMf" href="http://en.wikipedia.org/wiki/GNU%20Screen">GNU/screen</a>?" I can't be bothered to set up a new slew of terminals when I switch machines, and so on down the line.</p> <p>Earlier this week I found another bit of manual work that <strong>I</strong> shouldn't be doing and should be lazy about: building. The local build is something that's common to every single software developer regardless of language, Slide being a <a id="aptureLink_dkAoFOcNyd" href="http://en.wikipedia.org/wiki/Python%20%28programming%20language%29">Python</a> shop, we have a bit more subtle of a "build", that is to say, developers implicitly run a "build" when they hit a page in <a id="aptureLink_FWKNbGJPnm" href="http://en.wikipedia.org/wiki/Apache%20HTTP%20Server">Apache</a> or a test/script. I found myself constantly switching between two terminal windows, one with my editor (<a href="http://www.vim.org">Vim</a>) and one for running tests and other scripts.</p> <p>Being an avid <a id="aptureLink_youtxiRCtA" href="http://twitter.com/hudsonci">Hudson</a> user, I decided I'd give the <a href="http://wiki.hudson-ci.org/display/HUDSON/File+System+SCM">File system SCM</a> a try. Very quickly I was able to set up Hudson to poll my working directory and <em>watch</em> for files to change every minute, and then run a "build" with some tests to go with it. Now I can simply sit in Vim <strong>all</strong> day and write code, only context-switching to commit changes.</p> <p>Setting up Hudson for <em>local</em> continuous integration is quite simple, by visiting <a href="http://www.hudson-ci.org">hudson-ci.org</a> you can download <a href="http://hudson-ci.org/latest/hudson.war">hudson.war</a> which is a <strong>fully self contained</strong> runnable version of Hudson, you can start it up locally with <code>java -jar hudson.war</code>. Once it's started, visit <a href="http://localhost:8080">http://localhost:8080</a> and you've find yourself smack-dab in the middle of a fresh installation of Hudson.</p> <p>First things first, you'll need the File System SCM plugin from the Hudson Update Center (left side bar, "Manage Hudson" > "Manage Plugins" > "Available" tab)</p> <p><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/fsscm_updatecenter.jpeg" alt="Installing the plugin" /></p> <p>After installing the plugin, you'll need to restart Hudson, then you can create your job, configuring the File System SCM to poll your working directory:</p> <p><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/fsscm1.jpeg" alt="Configuring FS SCM" /></p> <p>Of course, add the necessary build steps to build/test your software as well, and you should be set for some good local continuous integration. Once the job is saved, the job will poll your working directory for files to be modified and then copy things over to the job's workspace for execution.</p> <p>After the job is building, you can hook up the RSS feed (<a href="http://localhost:8080/rssLatest">http://localhost:8080/rssLatest</a>) to <a id="aptureLink_X0ly5HgFWB" href="http://growl.info/">Growl</a> or some other form of desktop notifier so you don't even have to move your eyes to know whether your local build succeeded or not (I use the "hudsonnotify" script for Linux/libnotify below).</p> <p>By automating this part of my local workflow with Hudson I can take advantage of a few things:</p> <ul> <li>I no longer need to context switch to run my tests</li> <li>I can make use of Hudson's nice UI for visually inspecting test results as they change over time</li> <li>I have near-instant feedback on the validity of the changes I'm making</li> </ul> <p>The only real downside I can think of is no longer having any excuse for checking in code that "breaks the build", but in the end that's probably a good thing.</p> <p>Instead of relying on commits, you can get near-instant feedback on your changes before you even get things going far enough to check them in, tightening the feedback loop on your changes even further, very-very continuous integration. Your mileage may vary of course, but I recommend giving it a try.</p> <h2>hudsonnotify.py</h2> <script src="http://gist.github.com/179286.js"></script> http://unethicalblogger.com/posts/2009/09/doing_more_less_very_continuous_integration#comments Hudson Miscellaneous Software Development Wed, 02 Sep 2009 08:42:02 +0000 R. Tyler Croy 226 at http://unethicalblogger.com Jython, JGit and co. in Hudson http://unethicalblogger.com/posts/2009/07/jython_jgit_and_co_hudson <p>At the <a href="http://wiki.hudson-ci.org/display/HUDSON/BayAreaMeetup">Hudson Bay Area Meetup/Hackathon</a> that <a href="http://slideinc.github.com">Slide, Inc.</a> hosted last weekend, I worked on the <a href="http://wiki.hudson-ci.org/display/HUDSON/Jython+Plugin">Jython plugin</a> and released it just days after releasing a strikingly similar plugin, the <a href="http://wiki.hudson-ci.org/display/HUDSON/Python+Plugin">Python plugin</a>. I felt that an explanation might be warranted as to why I would do such a thing.</p> <p>For those that don't know, <a href="http://hudson-ci.org">Hudson</a> is a Java-based continuous integration server, one of the best CI servers developed (in my humblest of opinions). What makes Hudson so great is a <strong>very</strong> solid <a href="http://wiki.hudson-ci.org/display/HUDSON/Extend+Hudson">plugin architecture</a> allowing developers to extend Hudson to support a wide variety of scripting languages as well as notifiers, source control systems, and so on (<a href="http://weblogs.java.net/blog/kohsuke/archive/2009/06/growth_of_hudso.html">related post</a> on the growth of Hudson's plugin ecosystem). Additionally, Hudson supports <em>slaves</em> on any operating system that Java supports, allowing you to have a central manager (the "master" Hudson server/node) and a vast network of different machines performing tasks and executing jobs. Now that you're up to speed, back to the topic at hand.</p> <p><strong>Jython</strong> versus <strong>Python</strong> plugin. Why bother with either, as <a href="http://twitter.com/gboissinot">@gboissinot</a> pointed out in <a href="http://twitter.com/gboissinot/status/2619505521">this tweet</a>? The <em>interesting</em> thing about the Jython plugin, particularly when you use a large number of slaves is that with the installation of the Jython plugin, suddenly you have the ability to execute Python script on <strong>every</strong> single slave, regardless of whether or not they actually have Python installed. The more "third party" that can be moved into Hudson by way of the plugin system means reduced dependencies and difficulty setting up slaves to help handle load.</p> <p>Take the "git" versus the "git2" plugin, the git plugin was recently criticized on the <a href="irc://irc.freenode.net/hudson">#hudson channel</a> because of it's use of the <a href="http://www.jgit.org/">JGit</a> library, versus "git2" which invokes <a href="http://git-scm.org">git(1)</a> on the command line. The latter approach is flawed for a number of reasons, particularly the reliance on the git command line executables and scripts to return consistent formatting is specious at best even if you aren't relying on "porcelain" (git community terminology for front-end-ish script and code sitting on top of the "plumbing", the breakdown is detailed <a href="http://www.kernel.org/pub/software/scm/git/docs/">here</a>). The command-line approach also means you now have to ensure every one of your slaves that are likely to be executing builds have the appropriate packages installed. One the flipside however, with the JGit-based approach, the Hudson slave agent can transfer the appropriate bytecode to the machine in question and execute that without relying on system-dependencies.</p> <p>The Hudson Subversion plugin takes a similar approach, being based on <a href="http://svnkit.com/">SVNKit</a>.</p> <p>Being a Python developer by trade, I am certainly not in the "Java Fanboy" camp, but the efficiencies gained by incorporating Java-based libraries in Hudson plugins and extensions is a no brainer, the reduction of dependencies on the systems incorporated in your build farm will save you plenty of time in maintenance and version woes alone. In my opinion, the benefits of JGit, Jython, SVNKit, and the other Java-based libraries that are running some of the most highly used plugins in the Hudson ecosystem continue to outweigh the costs, especially as <a href="http://slideinc.github.com">we</a> find ourselves bringing more and more slaves online. <!--break--></p> http://unethicalblogger.com/posts/2009/07/jython_jgit_and_co_hudson#comments Git Hudson Miscellaneous Opinion Software Development Wed, 22 Jul 2009 06:16:38 +0000 R. Tyler Croy 219 at http://unethicalblogger.com Using Glib's gtester Results in Hudson http://unethicalblogger.com/posts/2009/06/using_glibs_gtester_results_hudson <p>For one of the projects I've been working on lately, I've been making use of <a href="http://library.gnome.org/devel/glib/unstable/glib-Testing.html">Glib's Testing</a> functionality to write unit tests for a C-based project. I'm a fairly big fan of the <a href="http://hudson-ci.org">Hudson Continuous Integration Server</a> and I wanted to run tests for my C-based project.</p> <p>Unfortunately, the <code>gtester</code> application generates XML in a custom format that Hudson cannot understand (i.e. JUnit formatted XML). In order to come up with some JUnit XML, I spent about an hour and a half toying with XSLT for transforming the XML <code>gtester</code> generates (see the snippet below).</p> <p>I added a shell script build step to the end of the build process that runs:</p> <pre><code>gtester test_app --keep-going -o=Tests.xml xsltproc -o tests.junit.xml gtester.xsl Tests.xml </code></pre> <p>Then I just specified "tests.junit.xml" in the <strong>Publish JUnit test result report</strong> section of the <strong>Post-build Actions</strong> and then Hudson would properly process and post the test results when the job was finished.</p> <script src="http://gist.github.com/131727.js"></script> http://unethicalblogger.com/posts/2009/06/using_glibs_gtester_results_hudson#comments Hudson Linux Miscellaneous Software Development Thu, 18 Jun 2009 08:02:02 +0000 R. Tyler Croy 218 at http://unethicalblogger.com Do not fear continuous deployment http://unethicalblogger.com/posts/2009/03/do_not_fear_continuous_deployment <p>One of the nice things about living in Silicon Valley is that you have relatively easy access to a number of the developers you may work with through open source projects, mailing lists, IRC, etc. Today <a href="http://weblogs.java.net/blog/kohsuke/" target="_blank">Kohsuke Kawaguchi</a> of Sun Microsystems, the founder of the <a href="http://hudson.dev.java.net" target="_blank">Hudson project</a>, stopped by the <a href="http://www.slide.com" target="_blank">Slide</a> offices to discuss Hudson and the "cloud", continuous deployment and our workflow with Hudson here at Slide. Continuous deployment being the most interesting topic for me, and the most relevant in terms of the importance of Hudson in our current infrastructure.</p> <p>Since reading Timothy Fitz's <a href='http://timothyfitz.wordpress.com/2009/02/10/continuous-deployment-at-imvu-doing-the-impossible-fifty-times-a-day/' target="_blank">post on the setup for "continuous deployment"</a> at <a href="http://www.imvu.com/" target="_blank">IMVU</a>, I've become obsessed to a certain degree with pushing Slide in that direction as an engineering organization. Currently we push a number of times a day as necessary, and it's almost as if we have manual-continuous-deployment as it is right now, there's just a lot of room for optimizations and automation to cut down on the tedium and allow for more beer drinking. </p> <blockquote><p>@agentdero continuous deployment = when build is green, autoship? sounds terrifying...</p></blockquote> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<a href="http://twitter.com/tlipcon" target="_blank">@tlipcon</a>)</p> <p>As a concept, continuous deployment can be quite scary "wait, some robot is going to deploy code to my production site, wha!" It's important to remember that the concept of continuous deployment doesn't necessarily mean that no QA is involved in the release process, it is however ideal to have enough good test cases such that you can do a fully automated unit/integration/system test run. The biggest difficultly with the entire concept of "continuous deployment" however is not writing tests or actually implementing a system to deploy, it forces you to <strong>understand your releases and production environment</strong>; it's about eliminating the guess work from your process and reducing the amount of human error (or potential for human error) involved in deployments. </p> <p>In my opinion, continuous deployment isn't about making a hard switch, firing your QA and writing boat-loads of tests to ensure that you can push the production site straight from "trunk" as much as humanly possible. Continuous deployment is far more about solidifying your understanding of your entire stack, evolving your code base to where it is both more testable and better covered by your tests, then putting your money where your mouth is and relying on those tests. If your codebase moves rapidly, unit/integration/system tests are only going to be up to date and valuable if you actually rely on them. If breaking a single unit test pre-deployment becomes a Big Deal&trade;, then the developer responsible for the code being deployed will make sure that: (a) the test is valid and up to date and (b) the code that the test is covering does not contain any actual regressions.</p> <p>Take the typical repository layout for most companies which is, as far as I've seen, made up of a volatile trunk, stable release branch and then a number of project branches. In an engineering department QA would be responsible for ensuring that projects are properly vetted before merging from project branches (also called "topic branches" in the Git community) into the more volatile trunk branch. Once the CI server (i.e. Hudson) picks up on changes in trunk, the testing process would begin at that particular revision. Provided the test suites passed with flying colors Hudson would start to kick up the process to do a slow/sampled deploy as <a href='http://timothyfitz.wordpress.com/' target="_blank">Timothy</a> describes in <a href='http://timothyfitz.wordpress.com/2009/02/10/continuous-deployment-at-imvu-doing-the-impossible-fifty-times-a-day/' target="_blank">his post</a>. If the tests failed however, alarms would start beeping, sirens would wail and there would be much gnashing of teeth, somebody has now broken trunk and is blocking any other deployments coming down the pipe. In this "disaster scenario" the QA involved in the process would be thoroughly shamed (obviously) but then given the choice to block future pushes while the developer(s) create a fix or revert their changes out of trunk and take them back to a project branch to correct the deficiencies. This attention to detail will have an larger benefit in that developers won't become <a href="http://www.squarefree.com/2009/02/19/continuous-integration-at-mozilla/" target="_blank">numb to test failures</a> to where they're no longer important.</p> <p>What good is writing tests if there aren't <em>have real-consequences for them failing?</em> Releases shouldn't be a scary time of the day/week/month, you should certainly be nervous (keeps you sharp), but if you <strong>fear</strong> releases then it is probably an error in your release process that allows for too much uncertainty: inadequate test coverage, insufficient blackbox testing, poor release practices, etc. Continuous deployment might not be the magic solution to your woe of shipping software but the practice of moving <em>towards</em> continuous deployment will greatly improve your release process whether or not you ever actually make the switch over to a fairly automated deployment process as the engineers at IMVU have.</p> <p>How confident are you in your test coverage?</p> http://unethicalblogger.com/posts/2009/03/do_not_fear_continuous_deployment#comments Hudson Slide Software Development Fri, 13 Mar 2009 08:13:29 +0000 R. Tyler Croy 214 at http://unethicalblogger.com Head in the clouds http://unethicalblogger.com/posts/2009/03/head_clouds <p>I've spent the entire day thinking about "cloud computing", which is quite a twist for me. Seeing <a href="http://itcloudconference.com/">"impressive" conferences</a> centered around "cloud computing" I've ridiculed the concept mercilessly, it has a phenomenally high buzzword/usefulness ratio, which makes it difficult to take seriously. It tends to have an air of idiocy attached to it of the same style that the re-invention of thin-clients did a few years back. That said, I think the concept is sound, and useful for a number of companies and uses (once distilled of the buzz).</p> <p>Take <a href="http://slide.com">Slide</a> for example, we have a solid amount of hardware, hundreds of powerful machines constantly churning away on a number of tasks: serving web pages, providing back-end services, processing database requests, recording metrics, etc. If I start the work-week needing a new pool of machines either set up or allocated for a particular task, I can usually have hardware provisioned and live by the end of the week (depending on my Scotch offering to the Operations team, I can get it as early as the next day). If I can have the <em>real thing</em> I clearly have no need for cloud computing or virtualization.</p> <p>That's what I thought, at least, until I started to think more about what would be required to get Slide closer to the lofty goal of <a href="http://timothyfitz.wordpress.com/2009/02/10/continuous-deployment-at-imvu-doing-the-impossible-fifty-times-a-day/">continuous deployment</a>. As I was involved in pushing for and setting up our <a href="http://hudson.dev.java.net">Hudson CI server</a>, I constantly check on the performance of the system and help make sure jobs are chugging along as they should be, I've become the defacto Hudson janitor.</p> <p>Our current continuous integration setup involves one four-core machine running three separate instances of our server software as different users, processing jobs throughout the day. One "job" typically consists of a full restart of the server software (Python) and running literally <strong>every</strong> test case in the suite (we walk the entire tree aggregating tests). On average the completion of one job takes close to 15 minutes, and executes around 400+ test cases (and growing). Fortunately, and unfortunately, our Hudson machine is no longer able to service this capacity during development peak in the middle of the day, this is where the "cloud" comes in.</p> <p>We have a few options at this point: <ul> <li>Setup another one or more machines</li> <li>Rethink how we provision hardware for continuous integration</li> </ul> <p>The fundamental problem with provisioning resources for continuous integration, at least at Slide, is that the requirements are bursty at best. We typically queue a job for a particular branch when a developer executes a <span class="geshifilter"><code class="python geshifilter-python">git push</code></span> (via the Hudson API and a post-receive hook). From around 9 p.m. until 9 a.m. we don't need but maybe two actual "executors" inside Hudson to handle the workload the night-owl developers tend to place on Hudson, from 12 p.m. until 7 p.m. however our needs fluctuate rapidly between needing 4 executors, and 10 executors. To exacerbate things further, due to "natural traffic patterns" in how we work, mid-afternoon on Wednesday and Thursday require even more resources as teams are preparing releases and finishing up milestones.</p> <p>The only two possible solutions to solve the problem are to: build a continuous integration farm with full knowledge capacity will remain unused for large amounts of time, <strong>or</strong> look into "cloud computing" with service provides like Amazon EC2 which will allow for Hudson slaves to be provisioned <strong>on demand</strong>. The maintainer of Hudson, <a href="http://weblogs.java.net/blog/kohsuke/">Kohsuke Kawaguchi</a> has already started work on "cloud support" for Hudson via the <a href="http://fisheye4.atlassian.com/browse/hudson/trunk/hudson/plugins/ec2">EC2 plugin</a> which makes this a real possibility. (Note: using EC2 for this at Slide was <a href="http://stuffonfire.com/">Dave's</a> idea, not mine :))</p> <p>Using Amazon EC2 isn't the only way to solve this "bursty" problem however, we could just as easily solve the problem in house with provisioning of <a href="http://www.xen.org/">Xen</a> guests across a few machines. The downside of doing it yourself is amount of time between when you know you need more capacity and when you can actually add that capacity to your own little "cloud". Considering Amazon has an <a href="http://docs.amazonwebservices.com/AWSEC2/2006-10-01/DeveloperGuide/">API</a> for not only running instances but terminating them, it certainly provides a compelling reason to "outsource" the problem to Amazon's cloud.</p> <p>I recommend following Kohsuke's development of the EC2 plugin for Hudson closely, as continuous integration and "the cloud" seem like a match made in heaven (alright, that pun was unnecessary, it sort of slipped out). At the end of the day the decision comes down to a very fundamental business decision: which is more cost effective, building my own farm of machines, or using somebody else's?</p> <p>(<small><em>footnote:</em> I'll post a summary of how and what we eventually do to solve this problem</small>)</p> http://unethicalblogger.com/posts/2009/03/head_clouds#comments Hudson Slide Software Development Fri, 06 Mar 2009 07:42:03 +0000 R. Tyler Croy 211 at http://unethicalblogger.com But Who Will Write The Tests? http://unethicalblogger.com/posts/2009/01/but_who_will_write_the_tests <p>In addition to frothing at the mouth about <a href="http://unethicalblogger.com/blog_categories/git">Git</a>, I've been really getting into the concept of automated unit tests lately (thus my interest in <a href="http://unethicalblogger.com/blog_categories/hudson">Hudson</a>). Just like code comments however, tests are good, no tests is bad, wrong tests is worse. That means once you give in to the almighty power of unit testing, you are saddled with the curse of knowing that you will have to update them, forever.</p> <p>Taking up <a href="http://en.wikipedia.org/wiki/Test-driven_development">Test-driven Development</a> is like having a child, if you are at a point in your life where you're ready to accept that kind of responsibility, it can be wonderful, a lot of work, but ultimately you will feel satisfied with your new role as a Responsible Developer (tm). If you're not prepared to take on the burden that TDD will present you with, you will likely regret it or neglect your tests (Deadbeat Developer, I like this metaphor). </p> <p>In the Top Friends Team at <a href="http://slide.com">Slide</a>, we practice the more "loose" definition of TDD; tests are not written before functionality is written, but rather functionality is written, and then as part of the QA and release process, the appropriate and accompanying tests are written. Our basic workflow is usually as follows: <ul> <li>Tickets are written and assigned to milestones and developers in Trac</li> <li>Branch is created in central Git repository</li> <li>General plan-of-action is discussed between developers</li> <li>Hack-hack-hack</li> <li>Code complete is reached, QA starts to test milestone</li> <li>Developers write tests if needed for functionality</li> <li>Once QA signs off, and tests look solid, code is shipped live</li> </ul> </p><p>There are two primary flaws with this workflow, the first and most obvious one is that it is far to easy to "forget to write the tests." That is, the project scheduled to start development tends to "flow forward" into the allotted test-writing time. As important as test coverage is, at the end of the day Slide did not raise funding on having solid test coverage, and our priorities lie in shipping software, first and foremost. Solving the flow-forward of scheduled projects into any available space is something that can be worked on, but never solved, it really comes down to discipline between those in charge of setting up any given project's particular roadmap. </p> <p>The second, more subtle flaw in this workflow, and I think all Test-driven Devleopment workflows, revolves around the writer of the tests. The fundamental nature of almost all bugs in software is human error, our natural tendency to make mistakes means that nothing we do will ever be perfect, including our tests. If Developer A is writing a couple new methods to handle data validation prior to that data going into the database. Chances are that Developer A's life is going to be made far easier by writing some test cases to run through some predefined user-input, and pass his validation code over it. Therein lies the problem, if the developer doesn't think of a particular edge case when he's writing the code to handle the data validation, the chances he'll remember and account for that particular edge case while he's working on the unit tests is nil.</p> <p>How do you really ensure that tests are of high enough quality to actually catch errors and regressions?</p> <p>I think a certain extent of intra-team test writing and code review, depending on the level of communication between developers, can really help. In this case less developer communication is better. If Developer A <strong>tells</strong> Developer B how his code works, Developer B is now going to have an unnecessary expectation when he starts to write tests for Developer A's code. If Developer B reviews the code for what it actually is, instead of what Developer A thinks it is, the tests that will ultimately be written will be more thorough than if Developer A had written the whole suite himself.</p> <p>This <em>still</em> isn't sufficiently fool-proof to where I feel all that confident in test coverage, the tests being written are subject to the availability, thoroughness and understanding that Developer B brings to the table. Inside a small team like this one, one of those is almost always in short supply (usually availability). </p> <p>One approach I'm anxious to try is the more active involvement of QA engineers in the test writing process, both in the pre-fail and post-fail scenarios. The pre-fail scenario being one like that which I detailed above, where new code is being written. In this case a QA engineer's experience can help guide the developer on what sets of user-input have typically caused issues in the past. The second case, post-fail, is actually already occuring at Slide; a live issue, data validity bug, or regression is caught by QA engineers who detail the reproduction case in Trac and as a result a regression test can be written for that specific issue.</p> <p>This still is subject to the three things I cited above: availability, thoroughness and understanding of those involved. I still have a lot of unanswered questions about the ideal QA and Dev workflow however, how does this scale to a team of tens or hudnreds? Who writes the tests for large teams? What about a team of 1 Dev and a 1 QA, what about the lone-hacker? How do you write quality code, without getting bogged down in the mush of writing thousands of tests for everything you can imagine could go wrong?</p> <p>Who writes the tests?</p> http://unethicalblogger.com/posts/2009/01/but_who_will_write_the_tests#comments Hudson Slide Software Development Mon, 12 Jan 2009 16:43:02 +0000 R. Tyler Croy 206 at http://unethicalblogger.com Reliable Locks in Hudson http://unethicalblogger.com/posts/2008/11/reliable_locks_hudson <p>There has been some amount of discussion on the Hudson user's list recently about the status of the "<a href="http://hudson.gotdns.com/wiki/display/HUDSON/Locks+and+Latches+plugin" target="_blank">Locks and Latches</a>" plugin. The plugin allows for one to create "locks" for Jobs in a similar manner to how "locks" work in a multithreaded programming environment. The need for such a plugin becomes very clear once you start to run multiple jobs that depend on some set of shared resources, take the following example: <ul> <li>Jobs A,B,C must run unit tests that fetch data from a test site</li> <li>Slave #1 can only run one instance of Apache at a time</li> </ul> <p>How one would accomplish this with the Locks and Latches plugin would be to create a lock like "Site Lock" in the Hudson configuration, and then bind Jobs A, B, C to that Lock. Making the (large) assumption that the plugin works correctly and locks properly in order to prevent A and B from running concurrently, this would be enough to satisfy the requirements we have for the scenario above. Unfortunately it seems the plugin is largely unmaintained and buggy; in the past couple weeks of experimenting with such a set up on a variety of different slaves <a href="http://slide.com">we've</a> noticed that the locks aren't always respected, causing some locked jobs to execute in parallel spewing bad test results and build failures (the crux of this issue seems ot have been reported by Sergio Fernandes in <a href="https://hudson.dev.java.net/issues/show_bug.cgi?id=2450">#2450</a>).</p> <p><big>The Loopback Slave</big><br /> The easiest way I found to work around the short-comings of the Locks and Latches plugin was to "break up" the Locks. Locks are only really useful if you have more than one "executor" on a Hudson node, in order to allow Hudson to execute jobs simultaneously. In essence, if you only have one executor, the Hudson queueing system will technically perform your "lock" for you by default. And thus the "loopback slave" was born! When explaining this to a co-worker, I likened my workaround to the fork(2) call, whereas the Locks and Latches plugin is much more of a pthread_mutex_lock(2) call. According to the "<a href="http://hudson.gotdns.com/wiki/display/HUDSON/Distributed+builds">Distributed Builds</a>" page on the Hudson wiki, you can start slave agent headlessly on <em>any</em> machine, so why not the master node?<br /> <div style="width: 680px; overflow: auto;"><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/hudson_loopback_slave.jpeg"/></div> <p>Above is the configuration of one such "loopback slave" that took the place of one of the executors on the master node.<center><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/hudson_tie_to_node.jpeg"/></center>After setting up the loopback slave, it's just a matter of tying the Job to that node for building.</p> <p>In short our set up was before: Jobs A, B, C all use the Lock "Site Job" in order to queue properly. With this change, now there is no lock, and Jobs A, B, C are all bound to the loopback slave in place of the lock on the master node. While certainly not ideal, given the frustrations of the Locks and Latches plugin going unmaintained this is the best short-term solution I've come up with thus far.<br/></p> <hr/> <em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p> http://unethicalblogger.com/posts/2008/11/reliable_locks_hudson#comments Hudson Slide Thu, 06 Nov 2008 05:23:41 +0000 R. Tyler Croy 195 at http://unethicalblogger.com Hudson Build Bookmarklet http://unethicalblogger.com/posts/2008/10/hudson_build_bookmarklet <p>During the usual friday-frenzy I sat down and wrote a quick 10 minute little bookmarklet to start a Hudson job. Unlike most bookmarklets that "do things" this one actually "does things" but doesn't take you away from your current page. Using the <a href="http://hudson.gotdns.com/wiki/display/HUDSON/Remote+access+API" target="_blank">Hudson Remote Access API</a> you can query information from Hudson programmatically, but you can also kick off <a href="http://hudson.gotdns.com/wiki/display/HUDSON/Building+a+software+project#Buildingasoftwareproject-Configuringautomaticbuilds" target="_blank">builds remotely</a> with nothing more than a simple HTTP request to the properly formed URL. </p> <p>By dragging the link below to your bookmark bar, and updating the URL within ("http://hudson/") to the URL of your Hudson instance, you can queue a Hudson build from any page at any time (without leaving the page).<br /> <br/></p> <h3>The Bookmarklet</h3> <div style="text-align:center;font-size: 20px; font-weight: bold; border: 1px solid #666; width: 300px; margin-right: auto; margin-left: auto; padding: 5px;"><a href="javascript:var b = prompt('Enter a job you would like to build:', 'main'); if (!b) { } else { var i = document.createElement('img'); i.src = 'http://hudson/job/' + b + '/build'; i.height = 1; i.width = 1; document.body.appendChild(i);}; alert('Build ' + b + ' queued');">Build Hudson Job</a></div> <p><br/></p> <h3>The Code</h3> <p><span class="geshifilter"><code class="python geshifilter-python"><span style="color: #66cc66;">&lt;</span>a href=<span style="color: #483d8b;">&quot;javascript:var b = prompt('Enter a job you would like to build:', 'main'); if (!b) { } else { var i = document.createElement('img'); i.src = 'http://hudson/job/' + b + '/build'; i.height = 1; i.width = 1; document.body.appendChild(i);}; alert('Build ' + b + ' queued');&quot;</span><span style="color: #66cc66;">&gt;</span>Build Hudson Job<span style="color: #66cc66;">&lt;</span>/a<span style="color: #66cc66;">&gt;</span></code></span><br /> <br/></p> <h3>How it actually works</h3> <p>After talking the concept of making cross-domain HTTP requests over with <a href="http://overcaffeinated.net/" target="_blank">Sergio</a>, he suggested just using an "IMG" tag (or "IFRAME") to accomplish the task. The bookmarklet doesn't actually have to send any form parameters or receive any data, Hudson just needs to receive <em>any</em> HTTP request to the right URL. By creating the IMG object in JavaScript, and appending it to the body of the current page the user is on, it'll effectively con the browser into making the HTTP request without needing to pull off any XmlHttpRequest hacks. One of the more interesting things that we found out when playing with the end of the bookmarklet, was that if we returned "false" or tried to wrap the whole thing in a closure, the link would still execute and the browser would change pages. However, if we stuck an "alert()" call into the tail end of the bookmarklet JavaScript, execution would stop and the link wouldn't change the page in the browser (tested this in Firefox 3).<br /> <br/><br /> Happy Hudsoning :)</p> http://unethicalblogger.com/posts/2008/10/hudson_build_bookmarklet#comments Hudson Miscellaneous Software Development Sat, 04 Oct 2008 09:36:06 +0000 R. Tyler Croy 193 at http://unethicalblogger.com Hudson notifications with libnotify http://unethicalblogger.com/posts/2008/09/hudson_notifications_with_libnotify <p>I've been using a Gnome-based desktop for about the past 8-9 months and one of the things I've come to really appreciate is that most Gnome applications integrate with "<a href="http://www.galago-project.org/news/index.php" target="_blank">libnotify</a>". <a href="http://www.galago-project.org/news/index.php" target="_blank">Libnotify</a> is a simple Windows taskbar-like notification system that presents status messages at the bottom of your screen. Like all great pieces of software, it has a solid Python interface which allows for incorporating it in those little 10-minutes scripts I find myself writing every now and again.</p> <p>One of the things I wanted to script was the notification of the build status of the numerous jobs that we're running in our <a href="http://hudson.dev.java.net" target="_blank">Hudson</a> instance here at <a href="http://www.slide.com" target="_blank">Slide</a>. Using the <a href="http://feedparser.org/" target="_blank">Universal Feed Parser</a> and <a href="http://www.galago-project.org/downloads.php" target="_blank">pynotify</a> (listed under "notify-python"), I had a good little Gnome Hudson Notifier running in less than 10 minutes.<br /> <center><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/hudson_more.jpeg"/></center> </p> <p>Source code after the jump.<br /> <!--break--><br /> <div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> feedparser</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> pynotify</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">time</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">BASE_TITLE = <span style="color: #483d8b;">'Hudson Update!'</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> success<span style="color: black;">&#40;</span>job<span style="color: black;">&#41;</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> n = pynotify.<span style="color: black;">Notification</span><span style="color: black;">&#40;</span>BASE_TITLE, </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #483d8b;">'&quot;%s&quot; successfully built :)'</span> <span style="color: #66cc66;">%</span> job,</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #483d8b;">'file:///usr/share/pixmaps/gnome-suse.png'</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> n.<span style="color: black;">set_urgency</span><span style="color: black;">&#40;</span>pynotify.<span style="color: black;">URGENCY_LOW</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">return</span> n</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> unstable<span style="color: black;">&#40;</span>job<span style="color: black;">&#41;</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">return</span> pynotify.<span style="color: black;">Notification</span><span style="color: black;">&#40;</span>BASE_TITLE, </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #483d8b;">'&quot;%s&quot; is unstable :-/'</span> <span style="color: #66cc66;">%</span> job,</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #483d8b;">'file:///usr/share/pixmaps/gnome-suse.png'</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> failure<span style="color: black;">&#40;</span>job<span style="color: black;">&#41;</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> n = pynotify.<span style="color: black;">Notification</span><span style="color: black;">&#40;</span>BASE_TITLE, </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #483d8b;">'&quot;%s&quot; failed!'</span> <span style="color: #66cc66;">%</span> job,</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #483d8b;">'file:///usr/share/pixmaps/gnome-suse.png'</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> n.<span style="color: black;">set_urgency</span><span style="color: black;">&#40;</span>pynotify.<span style="color: black;">URGENCY_CRITICAL</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">return</span> n</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> pynotify.<span style="color: black;">init</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Hudson Notify'</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> old_items = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> feed = feedparser.<span style="color: black;">parse</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'http://hudson/rssLatest'</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> items = <span style="color: black;">&#91;</span>t<span style="color: black;">&#91;</span><span style="color: #483d8b;">'title'</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">for</span> t <span style="color: #ff7700;font-weight:bold;">in</span> feed<span style="color: black;">&#91;</span><span style="color: #483d8b;">'entries'</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> new_items = <span style="color: #008000;">list</span><span style="color: black;">&#40;</span><span style="color: #008000;">set</span><span style="color: black;">&#40;</span>old_items<span style="color: black;">&#41;</span>.<span style="color: black;">difference</span><span style="color: black;">&#40;</span>items<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> new_items:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> i = i.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">' '</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> job, build, status = <span style="color: black;">&#40;</span>i<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>, i<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>, i<span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> status = status.<span style="color: black;">replace</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'('</span>, <span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span>.<span style="color: black;">replace</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">')'</span>,<span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">if</span> status == <span style="color: #483d8b;">'SUCCESS'</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> success<span style="color: black;">&#40;</span>job<span style="color: black;">&#41;</span>.<span style="color: black;">show</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">elif</span> status == <span style="color: #483d8b;">'UNSTABLE'</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> unstable<span style="color: black;">&#40;</span>job<span style="color: black;">&#41;</span>.<span style="color: black;">show</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">elif</span> status == <span style="color: #483d8b;">'FAILURE'</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> failure<span style="color: black;">&#40;</span>job<span style="color: black;">&#41;</span>.<span style="color: black;">show</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> old_items = items</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">60</span><span style="color: black;">&#41;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">&nbsp;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></li></ol></pre></div></p> <p>It's pretty basic right now, but does everything I really wanted it to do. I may add it into a public Git repository in the near future if I spend any more time on the project. Hope you like it :)</p> http://unethicalblogger.com/posts/2008/09/hudson_notifications_with_libnotify#comments Hudson Linux Miscellaneous Software Development Fri, 19 Sep 2008 06:40:20 +0000 R. Tyler Croy 189 at http://unethicalblogger.com Don Quixote's new side-kick, Hudson http://unethicalblogger.com/posts/2008/09/don_quixotes_new_sidekick_hudson <p>I recently wrote about "<a href="http://www.unethicalblogger.com/posts/2008/08/oneline_automated_testing" target"_blank">one-line automated testing</a>" by way of <a href="https://hudson.dev.java.net/"><strong>Hudson</strong></a>, a Java-based tool that helps to automate building and test processes (akin to Cruise Control and Buildbot). If you were to read this blog regularly, you'd be well aware that I work primarily with Python these days, at a <a href="http://www.slide.com" target="_blank">web company</a> no less! What does a web company need with a continuous integration tool? Especially if they're not using a compiled language like Java or C# (heresy!). </p> <p>As any engineering organization grows, it's bound to happen that you reach a critical mass of developers and either need to hire an equitable critical mass of QA engineers, or start to approach quality assurance from all sides. That is to say, automated unit testing and automated integration testing becomes a requirement for growing both as a engineering organization but as a web application provider (user's don't like broken web applications). With web products like <a href="http://www.topfriends.com" target="_blank">Top Friends</a>, <a href="http://www.superpoke.com" target="_blank">SuperPoke!</a> and <a href="http://apps.facebook.com/crazyfunpix/" target="_blank">Slide FunSpace</a> we have a large amount of ever-changing code, that has been in a constant state of flux for the past 16-18 months. We can accomodate for ever-changing code on the backend for the past year and half with <a href="http://pyunit.sourceforge.net/" target="_blank">PyUnit</a> and development discipline. </p> <p>How do you deal with months of ever changing code for the aforementinoned products' front-ends? Your options are pretty slim, you can hire a legion of black-box QA engineers to manually go through regression tests and ensure your products are in tip-top shape, or you can hire a few talented black-box QA engineers to conscript a legion of robots to go through regression tests and ensure your products are in tip-top shape. Enter <a href="http://www.getwindmill.com" target="_blank"><strong><big>Windmill</big></strong></a>. Windmill is a web browser testing framework not entirely unlike <a href="http://selenium.openqa.org/" target="_blank">Selenium</a> or <a href="http://wtr.rubyforge.org/" target="_blank">Watir</a> with two major exceptions: Windmill is written in Python and Windmill has a great <strong>recorder</strong> (and lots of other <a href="http://www.getwindmill.com/features">features</a>). One of my colleagues at Slide, <a href="http://adamchristian.com/" target="_blank">Adam Christian</a> has been working tirelessly to push Windmill further and prepare it for enterprise adoption, the first enterprise to use it, Slide.</p> <p>Adam and I have been working on bringing the two ends of the testing world together with Hudson. About half of the jobs currently running inside of our Hudson installation are running PyUnit tests on various Subversion and Git branches. The other half of the jobs are running Windmill tests, and reporting back into Hudson by way of Adam's <a href="http://www.getwindmill.com/archives/207" target="_blank">JUnit-compatible reporting</a> code. Thanks to the innate flexibility of PyUnit and Windmill's reporting infrastructure we were able to tie all these loose ends together with a tool like <a href="https://hudson.dev.java.net/" target="_blank">Hudson</a> that will handle Jabber-notifications or email notifications when test-runs fail and include details in it's reports.</p> <p>We're still working out the kinks in the system, but to date this set up has helped us fix at least one critical issue a week (with a numerous other minor issues) since we've launched the Hudson system, more often than not before said issues reach the live site and real users. If you've got questions about Windmill or Hudson you can stop by the <strong>#windmill</strong> or the <strong>#hudson</strong> channels on <a href="http://freenode.net/" target="_blank">Freenode</a>.</p> <p>Automated testing is like a really good blend of coffee, until you have it, you think "bah! I don't need that!" but after you start with it you can't help but wonder how you could tolerate the swill you used to drink.<br/></p> <hr/> <em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p> http://unethicalblogger.com/posts/2008/09/don_quixotes_new_sidekick_hudson#comments Hudson Slide Software Development Sun, 07 Sep 2008 06:04:53 +0000 R. Tyler Croy 185 at http://unethicalblogger.com One-line Automated Testing http://unethicalblogger.com/posts/2008/08/oneline_automated_testing <p>For about as long as my development team has been a number larger than one, I've been on a relatively steady "unit test" kick. With the product I've worked on for over a year gaining more than one cook in the kitchen, it became time to start both writing tests to prevent basic regressions (and save our QA team tedious hours of blackbox testing), but also to automate those tests in order to quickly spot issues.</p> <p>While I've been on this pretty steadily lately, I'm proud to say that automated testing was one of my first pet projects at <a href="http://www.slide.com" target="_blank">Slide</a>. If you ever crack into the Slide corporate network you can find my workstation under the name "ccnet" which is short for <a href="http://ccnet.thoughtworks.com/" target="_blank">Cruise Control.NET</a>, my first failed attempt at getting automated testing going on our now defunct Windows desktop client. As our development focus shifted away from desktop applications to social applications the ability to reliably test those systems plummeted; accordingly our test suite for these applications became paltry at best. As the organization started to scale, this simply could not stand much longer else we might not be able to efficiently push stable releases on a near-nightly schedule. As we've started to back-fill tests (test-after development?) the need to automate these tests has arisen to which I started digging aronud for something less painful to deal with than <a href="http://cruisecontrol.sourceforge.net/" target="_blank">Cruise Control</a>, enter <a href="https://hudson.dev.java.net/" target="_blank"><strong>Hudson</strong></a>.</p> <h3>Holy Hudson Batman!</h3> <p>I was absolutely astounded that I, nor anybody I knew, was aware of the Hudson project. Hudson is absolutely amazing as far as continuous integration systems go. The only major caveat is that the entire system is written in Java, meaning I had to beg one of our sysadmins to install Java 1.5 on the unit test machine. Once that was sorted out, starting the Hudson instance up was incredibly simple:<br /> <span class="geshifilter"><code class="python geshifilter-python">java -jar hudson.<span style="color: black;">war</span></code></span><br /> In our case the following to keep the JVM within manageable virtual memory limits:<br /> <span class="geshifilter"><code class="python geshifilter-python">java -Xmx128m -jar hudson.<span style="color: black;">war</span> --httpPort=<span style="color: #ff4500;">8888</span></code></span></p> <p>Once the Hudson instance was up and rnuning, I simply had to browse to <strong>http://unittestbox:8888/</strong> and the entire rest of the configuration was set up from the web UI. Muy easy. Muy bueno.</p> <h3>Plug-it-in, plug-it-in!</h3> <p>One of the most wonderful aspects of Hudson is it's extensible plugin architecture. Adding plugins like "Git", "Trac" and "Jabber" means that our Hudson instance is now properly linking to Trac revisions, sending out Jabber notifications on "build" (read: test run) failures and monitoring both Subversion and Git branches for changes. From what I've seen from their plugin architecture, it would be absolutely trivial to extend Hudson with Slide-specific plugins as the needs arise.</p> <p>With the integration of the PyUnit XMLTestRunner (<a href="http://www.rittau.org/python/cruisecontrol/" target="_blank">found here</a>) and working an XML output plugin into <a href="http://windmill.osafoundation.org/" target="_blank">Windmill</a> we can easily automate testing of both our back-end code and our front-end.</p> <p><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/slidehudson.jpeg" rel="lightbox"><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/slidehudson.jpeg" width="500"/></a><br/><strong>Hudson in action</strong></p> <p>And all with one simple java command :)<br/></p> <hr/> <em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p> http://unethicalblogger.com/posts/2008/08/oneline_automated_testing#comments Hudson Opinion Slide Software Development Wed, 20 Aug 2008 08:16:26 +0000 R. Tyler Croy 184 at http://unethicalblogger.com