RSS RSS feed | Atom Atom feed

New Continuous Integration Servers

Jeffrey Frederick, who is organizing the CITCON conference in London, sent out a message today asking if folks had seen or used any of the newer CI servers that are becoming available. It is pretty amazing that over the past few years so much has been done in this area. Check out this huge feature comparison matrix.

Personally, I have only used CruiseControl, CC.Net, and at one point I did a trial install of AntHill. Of those, I have been the most impressed with CC.Net. I found it easy to install and configure, powerful, and flexible. It's been a long time since I looked at AntHill, so I bet that has gotten better too. It would be fun to look at some of these new ones. I'm especially intrigued by the new Java tools that can work with Maven. The dependency conglomeration they do seems like it might be a lot more flexible and robust than the system I described in my post on enterprise continuous integration. That system has been working extremely well on my current set of projects, but I think I might have more difficulties if we scaled up to a larger more distributed team. Some of the tools that Jeffrey mentioned specifically include

  • Bamboo from Atlassian (the folks who make Jira)
  • TeamCity from JetBrains, the IntelliJ people
  • AntHill3 from UrbanCode

National Debt

Interesting graph, from Brian Marick's blog. He overlaid democratic/republican presidencies over a graph of the national debt. So much for the theory of tax-and-spend Democrats, although it is true that the Democrats controlled congress for most of the time shown.
Tags :

CITCON London, October 6-7, 2006

Continuous Integration and testing conference

Paul Julius and Jeffrey Fredrick are hosting another version of the Continuous Integration and Testing Conference, this time in London, October 6th and 7th. The first one in Chicago was impossible for me to go to, this one is just rubbing salt in the wound for me!

The last one sounded like a great success. They ran it using the 'open spaces' conference style that I am seeing more and more. Here's hoping that this one gathers lots of folks and leads to lots of good information sharing. If you are interested, register (it's free once you get there!) at the citcon website. Even if you are not going, check out the wiki from the last conference.

Upgraded blog tools

I just upgraded from Pebble 1.9 to Pebble 2.0 RC1. It came with a new theme, as you can see - perhaps I will change that to my own theme at some point. In any case, the upgrade took a while. In the process, I also had to upgrade the version of Tomcat I was running as well. It may be a few days before everything is working properly again!

Tags :

Shipping pdb files in release mode

I was reading an article a couple of weeks ago about shipping pdb files with the release versions of your DLLs, and thought that would be a good idea for us to do. In those cases where something unexcpected does happen (you never expect the unexpected!) having a stack trace with symbols and line numbers can be REALLY helpful. My problem is that I am lazy. I didn't want to go off and fix every single .csproj file we had, which was on the order of 60 or so across several solutions. So I took a look at the .csproj file - it is just XML, and said to myself that if I could just iterate over all the project files, and then do a replace on that one node, I would be able to do this with one command line. So I put together a few tools to make that happen.

Here is the relevant section of a .csproj file:

<VisualStudioProject>
    <CSHARP ...>
        <Build>
            <Settings … >
                <Config
                    Name = "Debug"
                    …
                    DebugSymbols = "true"
                    …
                />
                <Config
                    Name = "Release"
                    …
                    DebugSymbols = "false"
                    …
                />
            </Settings>
...

I love command line tools - they can be really useful in situations like this. In this case, here is the psuedo code for what I wanted to do:

for each .csproj file in this directory and any subdirectory
{
  change the XML at the path /VisualStudioProject/CSHARP/Build/Settings/Config[@Name='Release']/@DebugSymbols  to 'true' 
}

It did take me some time playing around with Visual XPath to get that XPath expression correct, but then all I needed was a command-line version of the nAnt xmlpoke task. So I did just that - a quick batch file called xmlpoke.bat that wraps the xmlpoke task. Another way to do this might be to use the xmlpoke task directly from C#, using something like Jeffrey Palermo's netScripter, but the batch file is my home away from home, so that is what I did. In the xmlpoke.bat, you'll see a reference to setnant.bat - this is just a batch file that adds the nant binary directory to my PATH - I could do that in my system settings, but I prefer to add it when I need it.

Here is the batch file that controls everything. I named it CreatePdbForReleaseBuilds.bat:

@echo off
for /R %%i in (*.csproj) do @xmlpoke %%i "/VisualStudioProject/CSHARP/Build/Settings/Config[@Name='Release']/@DebugSymbols" true

here is xmlpoke.bat:

@echo off
:: this is a batch file to call nant and run the xmlpoke command on an file, changing the existing value at xpath to a new value
:: parameters must be given:
::   path and name of xml file
::   xpath expression
::   new value
:: nant.exe must be on the path or setnant.bat must be on the path
setlocal
set _ERROR=
call setnant.bat > NUL
echo.
if .%1.==.. echo XML input file must be specified & set _ERROR=1
if .%2.==.. echo XPath must be specified, and might need quotes around it & set _ERROR=1
if .%3.==.. echo newvalue must be specified & set _ERROR=1
if defined _ERROR goto ERROR

if not exist %1 echo the XML input file '%1' could not be found & set _ERROR=1 if defined _ERROR goto ERROR

set xpath=%2 set xpath=%xpath:"=%

echo. echo poking value [%3] echo into xpath %xpath% echo inside file %1 echo. nant.exe -D:filename=%1 -D:xpath=%xpath% -D:value=%3 -buildfile:%~dp0xmlpoke.build | findstr /C:" [xmlpoke]" goto :EOF

:ERROR echo. echo syntax: echo xmlpoke ["input.xml"] ["xpath"] ["newvalue"] echo.

xmlpoke.bat uses a nant build file called xmlpoke.build - here is that file:

<?xml version="1.0" encoding="utf-8" ?>
<project name="xmlpoke" default="go" xmlns="http://nant.sf.net/release/0.85-rc3/nant.xsd">

<target name="go" description="run xmlpoke using properties that have to be set on the command line"> <xmlpoke file="${filename}" xpath="${xpath}" value="${value}" /> </target>

</project>

Simple but powerful. Enjoy!

I've attached all these files to this post as a zip file for your coding convenience!

even more FitNesse GoodNesse

running all your suites on a regular basis

We ran into a little hitch not too long ago with our fitnesse tests. We are now up to 4 different suites of fitnesse tests, one for each of 4 different solutions. The solution have dependencies among themsleves. We had been doing work on one solution, doing our TDD red-green-refactor and our fitnesse equivalent. Moving along nicely. And then cam a day and we had to do some work on a different solution that we hadn't made any changes to in a few weeks. Made some simple changes, checked them in, and (as described in a few previous posts) our CC.net build kicked off, followed by the fitnesse tests for that project. What surprised us was that the fitnesse tests broke. In a strange place. Turns out we had an integration breakage. Changes to project B had caused the fitnesse tests in project A to fail, and we hadn't noticed since those tests hadn't been run in a couple of weeks. So after we got the tests passing, we resolved to set up a system to keep us from going that long with breakages we didn't know about.

I set up a new project in my ccnet.config file like this:

<project name="nightly_fitnesse">
      <webURL>http://buildserver/ccnet/Controller.aspx?_action_ViewProjectReport=true&amp;server=buildserver&amp;project=nightly_fitnesse</webURL>

<triggers> <scheduleTrigger time="23:30" buildCondition="ForceBuild" /> </triggers>

<sourcecontrol type="svn"> <executable>c:program filessubversionbinsvn.exe</executable> <trunkUrl>svn://codeserver/fitnesse/trunk/</trunkUrl> <tagBaseUrl>svn://codeserver/fitnesse/tags/</tagBaseUrl> <workingDirectory>d:workfitnesse-trunk</workingDirectory> <tagOnSuccess>false</tagOnSuccess> <autoGetSource>true</autoGetSource> <username>cruisecontrol.net</username> <password>ccnetpassword</password> </sourcecontrol>

<tasks> <nant> <executable>D:workdependencies-trunkexternalnantNAnt.exe</executable> <baseDirectory>d:workfitnesse-trunk</baseDirectory> <buildFile>AustinNightlyFitnesse.build</buildFile> <targetList> <target>fit</target> </targetList> <!-- this build timeout is fairly long, since these tests can get long. --> <buildTimeoutSeconds>1500</buildTimeoutSeconds> </nant> </tasks>

<publishers> <merge> <files> <file>D:workfitnesse-trunkresults*.xml</file> </files> </merge> <xmllogger /> </publishers> </project>

We set up a new build file to run all 4 fitnesse suites - the parts that we call the 'regression' suite, which we expect to always pass. As you can see in the trigger block, it runs every night at 11:30, whether there have been any code changes or not. The build file is where I think the interesting bits are.

<?xml version="1.0" encoding="utf-8" ?>
  <project name="AustinNightlyFitnesse" default="fit" xmlns="http://nant.sf.net/release/0.85-rc3/nant.xsd">

<property name="fit.server" value="localhost" /> <property name="fit.port" value="8080" /> <property name="results.dir" value="results" />

<target name="fit" description="run fit tests"> <property name="FitRegressionFailureSummary" value="0" />

<delete dir="${results.dir}" failonerror="false" /> <mkdir dir="${results.dir}" />

<property name="fit.suite" value="GuideEngine.SuiteRegressionTests" /> <property name="fitlibs.dir" value="c:fitnessedotnetGuideEngine" /> <call target="runFitSuite" />

<property name="fit.suite" value="LookupWeb.SuiteRegressionTests" /> <property name="fitlibs.dir" value="c:fitnessedotnetLookupWeb" /> <call target="runFitSuite" />

<property name="fit.suite" value="FireFly.SuiteRegressionTests" /> <property name="fitlibs.dir" value="c:fitnessedotnetFireFly" /> <call target="runFitSuite" />

<property name="fit.suite" value="ShareDoc.SuiteRegressionTests" /> <property name="fitlibs.dir" value="c:fitnessedotnetShareDoc" /> <call target="runFitSuite" />

<fail message="${FitRegressionFailureSummary} Fitnesse Regression test(s) failed." if="${int::parse(property::get-value('FitRegressionFailureSummary')) != 0}" />

</target>

<target name="runFitSuite" > <!-- same basic steps, but these must all pass, or the build fails --> <property name="fit.testrunner" value="${fitlibs.dir}TestRunner.exe" /> <echo message="running fit regression tests at http://${fit.server}:${fit.port}/${fit.suite} using ${fit.testrunner}" /> <exec program="${fit.testrunner}" commandline="-results ${results.dir}Fit-${fit.suite}.html ${fit.server} ${fit.port} ${fit.suite}" failonerror="false" resultproperty="FitRegressionResult" /> <!-- now transform those results from html to xml, and then fail if there were any errors. Fitnesse helpfully returns that as a number. --> <exec program="java.exe" commandline="-cp binfitnessefitnesse.jar fitnesse.runner.FormattingOption ${results.dir}Fit-${fit.suite}.html xml ${results.dir}Fit-${fit.suite}.xml ${fit.server} ${fit.port} ${fit.suite}" failonerror="false"/>

<property name="FitRegressionFailureSummary" value="${int::parse(property::get-value('FitRegressionFailureSummary')) + int::parse(property::get-value('FitRegressionResult'))}" /> <echo message="${FitRegressionResult} Fitnesse Regression test(s) in ${fit.suite} failed." /> <echo message="So far, a total of ${FitRegressionFailureSummary} Fitnesse Regression test(s) failed." /> </target> </project>

Since properties in nAnt are mutable, it makes it pretty easy to do paramaterized targets. The strangest bit about this build file is how it adds up the number of faiures. Fitnesse TestRunner is nice enough to return an integer that indicates the number of failures and exceptions. When we exec the TestRunner, we set failonerror to false, and collect the result into a property. As mentioned before, we use the java tools to format the XML output into html. Then, we do a little nAnt expression voodoo to add up all the failures, so that failing one suite doesn't prevent the other suites from running. It is only at the very end of the 'fit' target that we fail with a message if there were any failures in any of the suites. Since we already have the fitnesse-details.xsl stylesheet in place, we can then see what the problems were right in our CC.Net dashboard.

Pretty cool.

ThinkGeek :: Bluetooth Laser Virtual Keyboard

Remember when you were promised all those amazing future tech innovations? Just around the corner was supposed to be a shining technology utopia with flying cars, personal space travel to distant galaxies, and bio-implantable cell phones. It's almost disappointing enough to make you sit at home and watch old episodes of "Space 1999". Don't lose hope! An amazing glimpse of this promised future has just arrived at ThinkGeek in the form of the Bluetooth Laser Virtual Keyboard. This tiny device laser-projects a keyboard on any flat surface... you can then type away accompanied by simulated key click sounds. It really is true future magic at its best. You'll be turning heads the moment you pull this baby from your pocket and use it to compose an e-mail on your bluetooth enabled PDA or Cell Phone. With 63 keys and and full size QWERTY layout the Laser Virtual Keyboard can approach typing speeds of a standard keyboard... in a size a little larger than a matchbook. ThinkGeek :: Bluetooth Laser Virtual Keyboard

Derek Cicerone Installing...: heat.exe - making setup easier

Ran across a cool new tool today! If you are releasing your software on Windows using MSI files, you really ought to know about WiX. And this looks like a most excellent tool to get you started in that direction. Derek Cicerone Installing...: heat.exe - making setup easier

Read more...

interesting new service

Just ran across an interesting new service called email2face that allows you to just register an email address and upload a photo, so that folks you work with over email might see what you look like. Maybe get past that old "on the internet, no one knows you're an alien" phase.

Capistrano: Automating Application Deployment

Capistrano: Automating Application Deployment
Application deployment is one of those things that becomes more and more complicated as the scale of your application increases. With just a single box running your database and your application, it’s quite simple. But when you start putting your database on a different server, and then separating your web servers from your app servers, and eventually splitting your database into master and slave servers… It can get to where you almost don’t want to deploy your application any more. SwitchTower is a standalone utility that can also integrate nicely with Rails. You simply provide SwitchTower with a deployment “recipe” that describes your various servers and their roles, and voila! You magically have single-command deployment. It even allows you to roll a bad version out of production and revert back to the previous release. It should be stated that the concepts that SwitchTower uses and encourages are not specific to Rails, or even Ruby. They are common-sense practices that are applicable to a variety of environments. In fact, you’ll find that there is very little that is Rails-specific about SwitchTower, aside from the fact that it is in Rails that it found its genesis. No matter where you are or what environment you are using, SwitchTower can probably help ease your deployment pains.
We have done some interesting things around automating our deployment, but this project looks interesting. Here is what we currently do though. Currently we have our CruiseControl build creating 2 zip files at build time (one for the App server and one for the web server) that have the correct directory structure. We also include a nAnt build file. The zip file is then checked into subversion. To get those installed, We have CruiseControl.net installed on the servers, and have a project set up so that by triggering a build (it has to be done manually), CC downloads the latest zip files from subversion, unzips them, and then runs the nant build to install them. It has taken us from what was sometimes a 2 hour ordeal to being able to deploy the latest version in less than a minute.