CruiseControl.NET let's the build stop and fail, when one of the tasks fails. I want to have the build completed with all tasks triggered, even if one task failes. Bonus: If one fails, I want the build status on "failed", but without stopping on the first fail event.
So I put all the non-critical ("optional") tasks into an extra MsBuild-Script:
<Project DefaultTargets="CIFull" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MsBuildAgent>"C:\Program Files (x86)\MSBuild\12.0\Bin\MSBuild.exe"</MsBuildAgent>
<MsBuildArguments>msbuild.xml /m /p:BuildInParallel="true";Configuration="Release";Platform="Any CPU";nr="false";ccversionbase=$(ccversionbase);revfile=$(revfile);buildplans=$(buildplans) /verbosity:minimal /nologo /target:CIFull /nr:false</MsBuildArguments>
</PropertyGroup>
<Target Name="Libraries">
<Exec Command="$(MsBuildAgent) $(MsBuildArguments)" WorkingDirectory="$(buildplans)\MyLibraryOne" IgnoreExitCode="false" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="LibExitCode01" />
</Exec>
<Exec Command="$(MsBuildAgent) $(MsBuildArguments)" WorkingDirectory="$(buildplans)\MyLibraryTwo" IgnoreExitCode="false" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="LibExitCode02" />
</Exec>
<Exec Command="$(MsBuildAgent) $(MsBuildArguments)" WorkingDirectory="$(buildplans)\MyLibraryThree" IgnoreExitCode="false" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="LibExitCode03" />
</Exec>
</Target>
<Target Name="Applications">
<Exec Command="$(MsBuildAgent) $(MsBuildArguments)" WorkingDirectory="$(buildplans)\ApplicationOne" IgnoreExitCode="false" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="AppExitCode01" />
</Exec>
<Exec Command="$(MsBuildAgent) $(MsBuildArguments)" WorkingDirectory="$(buildplans)\ApplicationTwo" IgnoreExitCode="false" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="AppExitCode02" />
</Exec>
<Exec Command="$(MsBuildAgent) $(MsBuildArguments)" WorkingDirectory="$(buildplans)\ApplicationThree" IgnoreExitCode="false" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="AppExitCode03" />
</Exec>
</Target>
<Target Name="ValidateExitCodes">
<ItemGroup>
<Failed Condition="$(LibExitCode01) != 0" Include="MyLibraryOne" />
<Failed Condition="$(LibExitCode02) != 0" Include="MyLibraryTwo" />
<Failed Condition="$(LibExitCode03) != 0" Include="MyLibraryThree" />
<Failed Condition="$(AppExitCode01) != 0" Include="ApplicationOne" />
<Failed Condition="$(AppExitCode02) != 0" Include="ApplicationTwo" />
<Failed Condition="$(AppExitCode03) != 0" Include="ApplicationThree" />
</ItemGroup>
<ItemGroup>
<Success Condition="$(LibExitCode01) == 0" Include="MyLibraryOne" />
<Success Condition="$(LibExitCode02) == 0" Include="MyLibraryTwo" />
<Success Condition="$(LibExitCode03) == 0" Include="MyLibraryThree" />
<Success Condition="$(AppExitCode01) == 0" Include="ApplicationOne" />
<Success Condition="$(AppExitCode02) == 0" Include="ApplicationTwo" />
<Success Condition="$(AppExitCode03) == 0" Include="ApplicationThree" />
</ItemGroup>
<Message Importance="High" Text="*** Success: @(Success->Count()) (@(Success, ' '))" />
<Message Importance="High" Text="*** Failed: @(Failed->Count()) (@(Failed, ' '))" />
<Error Text="*** FAILED: @(Failed, ' ') ***" Condition="'@(Failed->Count())' > 0" />
</Target>
<Target Name="CIFull" DependsOnTargets="Libraries;Applications;ValidateExitCodes" />
</Project>
Instead of adding each of the "optional" libraries and applications as Tasks to the CC.NET project, they are now listed in the MsBuild file above. The CC.NET configuration is modified as follows:
<tasks>
<msbuild>
<!-- When this task fails, the build shall stop and fail -->
<buildArgs>msbuild-base.xml /m /p:BuildInParallel="true";Configuration="Release";Platform="Any CPU";nr="false";ccversionbase=15.1.0;NuGetProjectDir=D:\REPO\Repo-Base /verbosity:minimal /nologo /target:PreBuild</buildArgs>
<dynamicValues />
<environment />
<executable>C:\Program Files (x86)\MSBuild\12.0\Bin\MSBuild.exe</executable>
<logger>"C:\Program Files (x86)\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MSBuild.dll"</logger>
<loggerParameters />
<priority>Normal</priority>
<timeout>120</timeout>
<workingDirectory>D:\BaseLibraries</workingDirectory>
</msbuild>
<msbuild>
<!-- This task is the new build file from above. -->
<buildArgs>msbuild-new.xml /m /p:BuildInParallel="true";Configuration="Release";Platform="Any CPU";nr="false";ccversionbase=15.1.0;revfile=D:\REPO\Repo-Base\ci-revision.xml;buildplans=D:\Meta\BuildPlans /verbosity:minimal /nologo /target:CIFull</buildArgs>
<dynamicValues />
<environment />
<executable>C:\Program Files (x86)\MSBuild\12.0\Bin\MSBuild.exe</executable>
<logger>"C:\Program Files (x86)\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MSBuild.dll"</logger>
<loggerParameters />
<priority>Normal</priority>
<timeout>120</timeout>
<workingDirectory>D:\Meta</workingDirectory>
</msbuild>
</tasks>
CC.NET does the first task first. If it fails, no other task is executed and an error is reported. Was it OK, CC.NET goes on with the second task.
Now, all the libs and applications are build - ignoring (but saving) the return value (targets Libraries
and Applications
). The target ValidateExitCodes
now creates a list of successful and failed tasks - after all tasks have run. And if the list for failed tasks contains an element, an error is returned to CC.NET. CC.NET will set the build state to error.