NOTE: This has been updated so I don’t have to hard-code the outside URL, along with updates that work with V1.8.4 of CC.Net
I have not been able to find a simple way to include a link to the created build show up in the web dashboard, nor the email. So I've rolled my own modifications. I'm sure there are better ways of doing it (such as a plug-in), but didn't have time to research the architecture. THIS IS A HACK! The following description has been implemented in CC.Net V1.4.2.14 V1.8.4.

The basic idea is to have the name of the generated build traverse from the build script into the web dashboard. Since my build names include the SVN revision number and the build number, the name can't be hard-coded in the web dashboard. For the benefit of completeness, I will also show how I arrive at the SVN revision number (again, there are probably easier ways of doing this, but I haven't found any).
Step 1: To get the revision number, do an SVN INFO command and parse out the revision number. Done as follows:
<target name="GetLatest">
<exec program="svn.exe"
commandline="info https://server/svn/project/trunk --username readonly --password readonly"
output="./test.txt"
/>
<foreach item="Line" in="./test.txt" property="lineitem">
<if test="${string::get-length(lineitem) > 0}">
<regex pattern="^(?'cmd'.*):\ (?'cmdval'.*)$" input="${lineitem}"/>
<if test="${cmd=='Revision'}">
<property name="revision" value="${cmdval}"/>
</if>
</if>
</foreach>
<echo message="Current revision is ${revision}"/>
<loadfile file="MasterVersion.txt" property="versions"/>
<property name="versionstring" value="${versions}.${revision}.0"/>
<SetFileVersions basedir="." resetversion="false" version="${versionstring}"/>
</target>
The last three lines bear explanation: I have a small text file that contains the first three numbers of my version string, i.e. MajorVersion.MinorVersion.Branch The revision number becomes the fourth number in line 2, above. Line 3 is a NAnt task DLL I wrote which hunts for all known source files where the version number should exist and replaces this field in the found sources. These files include: AssemblyInfo in VB.Net, C# and C++/CLI, resource files for unmanaged C++ and VB6 project files. By modifying the source files before building I essentially stamp the current version string into all my builds. Also doing it this way shows when a developer build is executed, because the version strings will not be stamped, and my default version numbers in all these files is a very noticeable 999.999.999.999.
Step 2: At the end of the NAnt script, package the files into a logical filename. After the packaging output the package name into a small XML File:
<target name="Package">
<zip zipfile="${CCNetArtifactDirectory}/projectname-${versionstring}.zip">
...
</zip>
<echo file="./packagename.xml">
<![CDATA[<output>projectname-${versionstring}.zip</output>
]]></echo>
</target>
In ccnet.config (the script that runs the NAnt build script described above), there is a section in every project called publishers. Merge the packagename in:
<publishers>
<merge>
<files>
<file>.\packagename.xml</file>
</files>
</merge>
<xmllogger/>
...
</publishers>
In the folder c:\program files\CruiseControl.NET\webdashboard\xsl is a file called header.xsl. Near the start of this file a variable is being defined (the variable name being defined is ‘modification.list’). Add the following two variable definitions:
<xsl:variable name="filename" select="/cruisecontrol/build/output"/>
<xsl:variable name="webpath" select="/cruisecontrol/@project"/>
A bit further down is an xsl:if statement that checks if the build was successful. If it is, add the ‘download here’ portion in. The following fragment shows the entire xsl:if statement:
<xsl:if test="not (/cruisecontrol/build/@error) and not (/cruisecontrol/exception)">
<tr><td class="header-title" colspan="2">BUILD SUCCESSFUL</td></tr>
<tr>
<td class="header-title" colspan="2">Download result here: <a href="/{$webpath}/{$filename}"><xsl:value-of select="$filename"/></a></td>
</tr>
</xsl:if>
In the folder c:\program files\CruiseControl.NET\server\xsl is also a file called header.xsl. It is used to build the emails being sent out. We will do more-or-less the same thing here: Add the variables (note a third variable, and add a link. In addition, we need to add a link if the email is sent off-site and we have to expose an externally-visible link to the HTML webdashboard.
First the variables:
<xsl:variable name="filename" select="/cruisecontrol/build/output"/>
<xsl:variable name="webpath" select="/cruisecontrol/@project"/>
<xsl:variable name="weburl" select="/cruisecontrol/integrationProperties/CCNetProjectUrl"/>
A little further down is that same xsl:if statement again. Here I’ve defined it as follows:
<xsl:if test="not (/cruisecontrol/build/@error) and not (/cruisecontrol/exception)">
<tr><td class="header-title" colspan="2">BUILD SUCCESSFUL</td></tr>
<tr>
<td class="header-label">Build output:</td>
<td class="header-data"><a href="{$weburl}/{$webpath}/{$filename}"><xsl:value-of select="$filename"/></a></td>
</tr>
</xsl:if>
Lastly, in IIS add a virtual directory named the same as the project, and point it at the project’s Artifacts folder (typically c:\program files\CruiseControl.NET\server\project\Artifacts.