We cobbled together a simple system to provide us a single email each day to tell us if all of our jobs and unit tests were successful on their overnight runs.

An example of our daily build summary email.
Given that we have a dozen different jobs that run tests and a dozen that run builds, it was helpful to have the results of all the jobs in a single email. To implement this similar to what we did, you need the following:
- A database for storing the results of the jobs. We used SQL Server.
- A program to write the build results to the database. The C# program we wrote is provided here.
- A few modifications to your build scripts. Our changes to our Nant builds are shown here.
- Optionally a testing framework that produces output you can read easily. We use nunit.
- A report sent via email. We used an SSRS report to send the email summary each morning.
Here is how we did it:
- We created a table in SQL Server to store the results of each job.

SQL for Creating Table ZBuildJobs
Here is a sample of the table with data:

Sample Of Table ZBuildJobs with data
- We created a program that we could call from a command prompt and pass a few parameters to write into the table. This is our C# program. It is comprised of just 3 methods.
The App entry code reads the command line arguments. If there are more than 2 (> 3 in code), then we also look for a number of tests passed and failed to be passed in.

Code Part 1
The CallDatabase
method simply writes the values passed into the database table.

Code Part 2
The ProcessTestResults
method uses some example code from the Microsoft URL shown to read the results of the nunit test file and count the number of passed and failed tests.

Code Part 3
- We modified our nant build files to call the program written in step 2 and pass in a value of success or failure. For nunit test jobs, we also pass in the XML results file created by our nunit tests.
- Nant includes some built-in properties that we can use to specify a target to call when the nant job succeeds or fails. So we configured those properties like this:
<property name=”nant.onsuccess” value=”BuildSuccess” />
<property name=”nant.onfailure” value=”BuildFailure” />
- For ease of maintenance, we created a property to hold the name of our program from step 2 above that does the logging:
<property name=”param.EventLogPgmName” value=”C:\Dev\BuildLogger\BuildLogger.exe” />
- To include spaces in our job names, we need to surround the parameters we pass to
BuildLogger
with double quotes, and to include double quotes as part of the parameters we needed to create a property to hold the double quotes. We also have a property to hold our current build version number:
<property name=”dq” value='”‘/>
<property name=”project.version.full” value=”15.0″/>
- Then at the start of each build target, we add a name for that build, something like this:
<property name=”param.BuildNameForLog” value=”${dq}${project.version.full} C# .Net Build${dq}” />
- If the job runs unit tests, we include a property to specify the name of the unit test output file:
<property name=”param.TestResultsFile” value=”${dq}c:\program files (x86)\NUnit\bin\TestResult.xml${dq}” />
- The final change to our build files is to declare the targets for
BuildSuccess
and BuildFailure
. These look like this:
<target name=”BuildSuccess”>
<exec program=”${param.EventLogPgmName}” commandline=” ${param.BuildNameForLog} Succeeded ${param.TestResultsFile}” failonerror=”true”/>
</target>
<target name=”BuildFailure”>
<exec program=”${param.EventLogPgmName}” commandline=” ${param.BuildNameForLog} Failed” failonerror=”true”/>
</target>
- The last step is to build the report. If you are already familiar with SSRS, you will probably find this step to be very simple. SSRS is easy to use and is free if you have a Standard Edition of SQL Server. Here, I share the more advanced features for the report.
This is the SQL to get the most results of the most recent execution for each job:

SQL for Report
For each job listed on the report, we want to show the record in green (Lime) if it passes and red (Tomato) if it fails. So I modified the BackgroundColor
property of each row and gave it this expression:
=IIF(Fields!Result.Value = "Succeeded", "Lime", "Tomato")
For the two columns with the number of tests that passed or failed, I had to test if the field had null
first, then apply the color:
=IIF( IsNothing(Fields!TestsFailed.Value), IIF(Fields!Result.Value = "Succeeded",
"Lime", "Tomato"), IIF(Fields!TestsFailed.Value > 0,
"Tomato", "Lime"))
We have been very pleased with the results of our Build Summary process and continue to add more things to it. It was easy to implement and has ran very reliably.