OK, using CruiseControl.NET's API I was finally able to solve this problem. Woot!
First I created a C# console application to get the list of failed projects. The log file for each failed project is inspected, and if the project failed due to error 8000000A, a build is forced on that project.
//Due to a race condition in Visual Studio 2010, CCNet builds often fail with error 8000000A.
//See http://stackoverflow.com/questions/8648428/an-error-occurred-while-validating-hresult-8000000a
//
//The build failure can almost always be fixed by manually forcing another build on the failed project.
//
//This program, along with a companion CCNet project, will automate the process of forcing the builds on the failed projects.
// 1. For each CCNet project whose IntegrationStatus == Failure
// 2. Search the most recent log file for the string "8000000A"
// 3. If string found in log file, force a build on the project
using System;
using ThoughtWorks.CruiseControl.Remote;
namespace ForceBuildForError8000000A
{
class Program
{
static void Main(string[] args)
{
CruiseServerRemotingClient ccnetClient = new CruiseServerRemotingClient("tcp://localhost:21234/CruiseManager.rem");
ProjectStatus[] prjStatusList = ccnetClient.GetProjectStatus();
foreach (ProjectStatus prjStatus in prjStatusList)
{
if (prjStatus.BuildStatus == IntegrationStatus.Failure)
{
string latestBuildName = ccnetClient.GetLatestBuildName(prjStatus.Name);
string log = ccnetClient.GetLog(prjStatus.Name, latestBuildName);
if (log.Contains("ERROR: An error occurred while validating. HRESULT = '8000000A'"))
{
Console.WriteLine("Forcing build for " + prjStatus.Name);
ccnetClient.ForceBuild(prjStatus.Name);
}
}
}
}
}
}
Next I created another project in my ccnet.config file. This project uses a Multi-trigger to monitor all the other projects that can fail with the 8000000A error. When any of the trigger projects has a failure, the C# program is run to force a build on any projects that failed due to 8000000A.
<cb:scope ProjectName="$(ForceBuildForError8000000A.Run_ProjectName)">
<project name="$(ProjectName)" >
<cb:ProjectHeaderMacro projectName="$(ProjectName)" />
<cb:AssemblyVersionLabellerMacro Major="1" Minor="0" Build="0" />
<triggers>
<multiTrigger>
<triggers>
<projectTrigger project="$(ProjectName1)" triggerStatus="Failure" />
<projectTrigger project="$(ProjectName2)" triggerStatus="Failure" />
<projectTrigger project="$(ProjectName3)" triggerStatus="Failure" />
<projectTrigger project="$(ProjectName4)" triggerStatus="Failure" />
<projectTrigger project="$(ProjectName5)" triggerStatus="Failure" />
<projectTrigger project="$(ProjectName6)" triggerStatus="Failure" />
</triggers>
</multiTrigger>
</triggers>
<tasks>
<exec>
<executable>$(ForceBuildForError8000000A.exe)</executable>
<baseDirectory>$(MyWorkingTrunk)</baseDirectory>
<buildTimeoutSeconds>120</buildTimeoutSeconds>
</exec>
</tasks>
</project>
</cb:scope>
Note I didn't end up using a conditional task block as I originally thought would be needed, but rather a conditional trigger.
There's probably a way to do this using NANT instead of C# but I could never figure out how.