<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>The Tranxition Developer Blog &#187; Mats</title>
	<atom:link href="http://tranxcoder.wordpress.com/author/matslanner/feed/" rel="self" type="application/rss+xml" />
	<link>http://tranxcoder.wordpress.com</link>
	<description>Random musings from the developers at Tranxition</description>
	<lastBuildDate>Tue, 24 Mar 2009 18:14:16 +0000</lastBuildDate>
	<generator>http://wordpress.com/</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<cloud domain='tranxcoder.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://www.gravatar.com/blavatar/a141c5ce8e6e7831514f7c306752642f?s=96&#038;d=http://s.wordpress.com/i/buttonw-com.png</url>
		<title>The Tranxition Developer Blog &#187; Mats</title>
		<link>http://tranxcoder.wordpress.com</link>
	</image>
			<item>
		<title>Upgrading to Team Foundation Server 2008</title>
		<link>http://tranxcoder.wordpress.com/2008/06/23/upgrading-to-team-foundation-server-2008/</link>
		<comments>http://tranxcoder.wordpress.com/2008/06/23/upgrading-to-team-foundation-server-2008/#comments</comments>
		<pubDate>Mon, 23 Jun 2008 01:09:49 +0000</pubDate>
		<dc:creator>Mats</dc:creator>
				<category><![CDATA[Tools]]></category>
		<category><![CDATA[TFS]]></category>
		<category><![CDATA[TFS Build]]></category>
		<category><![CDATA[TFS2008]]></category>

		<guid isPermaLink="false">http://tranxcoder.wordpress.com/?p=26</guid>
		<description><![CDATA[This weekend I finally pulled the trigger on upgrading our TFS environment from TFS 2005 to 2008. By and large this was a pretty painless upgrade with the notable exception of TFS Build which has turned out to be a royal pain.
Don&#8217;t get me wrong, feature-wise TFS Build 2008 is far superior to the 2005 [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tranxcoder.wordpress.com&blog=3688975&post=26&subd=tranxcoder&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>This weekend I finally pulled the trigger on upgrading our TFS environment from TFS 2005 to 2008. By and large this was a pretty painless upgrade with the notable exception of TFS Build which has turned out to be a royal pain.</p>
<p>Don&#8217;t get me wrong, feature-wise TFS Build 2008 is far superior to the 2005 version, but it was pretty discouraging to find that the build types created for a set of Visual C++ 2005 solutions just completely stopped working. I eventually gave up on the existing build types and started over with a fresh build type and by numerous searches, I&#8217;ve been able to get most things working.</p>
<h3>Building VS 2005 Solutions</h3>
<p>For various reasons, we have one project that we can&#8217;t upgrade to Visual Studio 2008 quite yet, so I had to keep the solution files as VS 2005 solution files, but still be able to build them with TFS Build 2008. After poking around a bit, these blog posts told me how to configure that:</p>
<ul>
<li><a href="http://blogs.msdn.com/aaronhallberg/archive/2008/03/25/overriding-the-toolsversion-for-your-projects-in-team-build.aspx">Overriding the ToolsVersion for Your Projects in Team Build</a> </li>
<li><a href="http://blogs.msdn.com/aaronhallberg/archive/2007/10/30/building-vc-2005-projects-with-team-build-2008.aspx">Building VC++ 2005 Projects With Team Build 2008</a> </li>
</ul>
<p>In my TFSBuild.proj, I specified the solutions to build as:</p>
<p><span style="font-family:consolas;font-size:x-small;">&lt;SolutionToBuild Include=&quot;&lt;solution path&gt;&quot;&gt;      <br />&#160;&#160;&#160; &lt;Targets&gt;&lt;/Targets&gt;       <br />&#160;&#160;&#160; &lt;Properties&gt;ProjectToolsVersion=2.0;VCBuildToolPath=$(ProgramFiles)\Microsoft Visual Studio 8\vc\vcpackages&lt;/Properties&gt;       <br />&lt;/SolutionToBuild&gt;</span></p>
<p>The property ProjectToolsVersion tells TFS Build to target .NET 2.0 and the VCBuildToolPath tells it to use the specified path to find VCBuild.exe which is used to build Visual C++ projects.</p>
<div id='extendedEntryBreak'></div>
</p>
<h3>Generating Custom Build Numbers</h3>
<p>We already had functionality to automatically update the product and file versions of our binaries as part of the build so that every build generated binaries with a unique version number. TFS Build 2008, however, offers the possibility to customize the build number. I thought it would be useful to let the build number match the file version generated by the build, making it easier to identify a particular build just by looking at the version number.</p>
<p>Again, there were several blog posts that talked about how to generate custom build numbers:</p>
<ul>
<li><a href="http://www.woodwardweb.com/tfs/000417.html">Aligning Build Numbers with Assembly Versions in TFS2008</a> </li>
<li><a href="http://blogs.msdn.com/aaronhallberg/archive/2008/05/12/orcas-sp1-tfs-build-changes-part-2.aspx">Orcas SP1 TFS Build Changes, Part 2</a> </li>
</ul>
<p>I wound up using a slightly different approach than the above. I&#8217;m still using a file containing the version number of the previous build (e.g. 1.2.3.4), but I wanted my file to be version controlled which posed a problem since the build number must be generated at the very beginning of the build, before all the source files to use for the build have been retrieved.</p>
<p>Since all I had available to me were the files that are located in my build type directory, I simply moved my version file to the build type directory which ensures it&#8217;s available to me. Next, I used the Version task from the <a href="http://msbuildtasks.tigris.org/">MSBuild Community Tasks</a> project to update this file:</p>
<p><font size="1"><span class="kwrd">&lt;</span><span class="html">Target</span> <span class="attr">Name</span><span class="kwrd">=&quot;BuildNumberOverrideTarget&quot;</span><span class="kwrd">&gt;</span></font></p>
<p><span class="rem"><font color="#008000" size="1">&lt;!&#8211; Update the product version number &#8211;&gt;</font></span></p>
<p><font size="1"><span class="kwrd">&lt;</span><span class="html">Version</span> <span class="attr">VersionFile</span><span class="kwrd">=&quot;$(TxProductVersionFile)&quot;</span> <span class="attr">BuildType</span><span class="kwrd">=&quot;None&quot;</span> <span class="attr">RevisionType</span><span class="kwrd">=&quot;Increment&quot;</span><span class="kwrd">&gt;        <br /></span></font><font size="1"><span class="kwrd">&#160; &lt;</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">=&quot;Major&quot;</span> <span class="attr">PropertyName</span><span class="kwrd">=&quot;TxMajorVersion&quot;</span> <span class="kwrd">/&gt;        <br /></span></font><font size="1"><span class="kwrd">&#160; &lt;</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">=&quot;Minor&quot;</span> <span class="attr">PropertyName</span><span class="kwrd">=&quot;TxMinorVersion&quot;</span> <span class="kwrd">/&gt;        <br /></span></font><font size="1"><span class="kwrd">&#160; &lt;</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">=&quot;Build&quot;</span> <span class="attr">PropertyName</span><span class="kwrd">=&quot;TxPatchVersion&quot;</span> <span class="kwrd">/&gt;        <br /></span></font><font size="1"><span class="kwrd">&#160; &lt;</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">=&quot;Revision&quot;</span> <span class="attr">PropertyName</span><span class="kwrd">=&quot;TxBuildVersion&quot;</span> <span class="kwrd">/&gt;        <br /></span></font><font size="1"><span class="kwrd">&lt;/</span><span class="html">Version</span><span class="kwrd">&gt;</span></font></p>
<p><font size="1"><span class="kwrd">&lt;</span><span class="html">CreateProperty</span> <span class="attr">Value</span><span class="kwrd">=&quot;$(TxMajorVersion).$(TxMinorVersion).$(TxPatchVersion).$(TxBuildVersion)&quot;</span><span class="kwrd">&gt;        <br />&#160; </span></font><font size="1"><span class="kwrd">&lt;</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">=&quot;Value&quot;</span> <span class="attr">PropertyName</span><span class="kwrd">=&quot;TxCompleteVersion&quot;</span><span class="kwrd">/&gt;        <br /></span></font><font size="1"><span class="kwrd">&lt;/</span><span class="html">CreateProperty</span><span class="kwrd">&gt;</span></font></p>
<p><font size="1"><span class="kwrd">&lt;</span><span class="html">CreateProperty</span> <span class="attr">Value</span><span class="kwrd">=&quot;$(TxMajorVersion),$(TxMinorVersion),$(TxPatchVersion),$(TxBuildVersion)&quot;</span><span class="kwrd">&gt;        <br />&#160; </span></font><font size="1"><span class="kwrd">&lt;</span><span class="html">Output</span> <span class="attr">TaskParameter</span><span class="kwrd">=&quot;Value&quot;</span> <span class="attr">PropertyName</span><span class="kwrd">=&quot;TxCompleteVersionRc&quot;</span><span class="kwrd">/&gt;        <br /></span></font><font size="1"><span class="kwrd">&lt;/</span><span class="html">CreateProperty</span><span class="kwrd">&gt;</span></font></p>
<p><font size="1"><span class="rem"><font color="#008000">&lt;!&#8211; Define the build version number –&gt;          <br /></font></span><span class="kwrd">&lt;</span><span class="html">PropertyGroup</span></font><font size="1"><span class="kwrd">&gt;        <br />&#160; </span><span class="kwrd">&lt;</span><span class="html">BuildNumber</span><span class="kwrd">&gt;</span>$(TeamProject)_$(BuildDefinitionName)_$(TxCompleteVersion)<span class="kwrd">&lt;/</span><span class="html">BuildNumber</span></font><font size="1"><span class="kwrd">&gt;        <br /></span><span class="kwrd">&lt;/</span><span class="html">PropertyGroup</span><span class="kwrd">&gt;</span></font></p>
<p><font size="1"><span class="kwrd">&lt;/</span><span class="html">Target</span><span class="kwrd">&gt;</span></font></p>
<p>This gives me my custom build number, but I also need to check the changed version number file back in. The easiest way to do this was to override the AfterGet target:</p>
<p><font size="1"><font face="con"><span class="kwrd">&lt;</span><span class="html">Target</span> <span class="attr">Name</span><span class="kwrd">=&quot;AfterGet&quot;</span><span class="kwrd">&gt;          <br />&#160; </span>&#160;<span class="kwrd">&lt;</span><span class="html">Message</span> <span class="attr">Text</span><span class="kwrd">=&quot;Updating the version-controlled product version file&quot;</span><span class="kwrd">/&gt;          <br />&#160; </span>&#160;<span class="kwrd">&lt;</span><span class="html">CallTarget</span> <span class="attr">Targets</span><span class="kwrd">=&quot;UpdateVersionedProductVersionFile&quot;</span><span class="kwrd">/&gt;</span>         <br />&#160; </font></font><font size="1"><font face="con"><span class="kwrd">&lt;</span><span class="html">Message</span> <span class="attr">Text</span><span class="kwrd">=&quot;Updating the product version information in preparation for the build.&quot;</span> <span class="kwrd">/&gt;</span>         <br />&#160; <span class="kwrd">&lt;</span><span class="html">CallTarget</span> <span class="attr">Targets</span><span class="kwrd">=&quot;SetProductVersionForBuild&quot;</span> <span class="kwrd">/&gt;</span>         <br /><span class="kwrd">&lt;/</span><span class="html">Target</span><span class="kwrd">&gt;</span></font></font></p>
<p>Where the definition of UpdateVersionedProductVersionFile is:</p>
<p><font size="1"><font face="Consolas"><span class="kwrd">&lt;</span><span class="html">PropertyGroup</span><span class="kwrd">&gt;</span>         <br />&#160; <span class="kwrd">&lt;</span><span class="html">UpdateVersionedProductVersionFileDependsOn</span><span class="kwrd">&gt;</span>         <br />&#160; <span class="kwrd">&lt;/</span><span class="html">UpdateVersionedProductVersionFileDependsOn</span><span class="kwrd">&gt;</span>         <br /><span class="kwrd">&lt;/</span><span class="html">PropertyGroup</span><span class="kwrd">&gt;</span>         <br /><span class="kwrd">&lt;</span><span class="html">Target</span> <span class="attr">Name</span><span class="kwrd">=&quot;UpdateVersionedProductVersionFile&quot;</span> <span class="attr">DependsOnTargets</span><span class="kwrd">=&quot;$(UpdateVersionedProductVersionFileDependsOn)&quot;</span><span class="kwrd">&gt;</span>         <br />&#160; <span class="rem"><font color="#008000">&lt;!&#8211; Check out the product version file we now have available to us after the source files have been retrieved. –&gt;</font></span>         <br />&#160; <span class="kwrd">&lt;</span><span class="html">Exec</span> <span class="attr">WorkingDirectory</span><span class="kwrd">=&quot;$(SolutionRoot)&quot;</span> <span class="attr">Command</span><span class="kwrd">=&quot;$(TxTf) checkout &amp;quot;$(TxVersionedProductVersionFile)&amp;quot;&quot;</span><span class="kwrd">/&gt;</span> </font></font></p>
<p><font size="1" face="Consolas">&#160; <font color="#008000"><span class="rem">&lt;!&#8211; Copy the previously updated product version file to the version-controlled product version file –&gt;</span>         <br /></font>&#160; <span class="kwrd">&lt;</span><span class="html">Copy</span> <span class="attr">SourceFiles</span><span class="kwrd">=&quot;$(TxProductVersionFile)&quot;</span> <span class="attr">DestinationFiles</span><span class="kwrd">=&quot;$(TxVersionedProductVersionFile)&quot;</span><span class="kwrd">/&gt;</span> </font></p>
<p><font size="1"><font face="Consolas">&#160; <font color="#008000"><span class="rem">&lt;!&#8211; And finally check the file back in so it&#8217;s available for the next build. –&gt;</span>           <br /></font>&#160; <span class="kwrd">&lt;</span><span class="html">Exec</span> <span class="attr">WorkingDirectory</span><span class="kwrd">=&quot;$(SolutionRoot)&quot;</span> <span class="attr">Command</span><span class="kwrd">=&quot;$(TxTf) checkin /override:&amp;quot;Automated build check-in, no associated work items&amp;quot; /comment:&amp;quot;Checking in the updated product version file for build $(BuildNumber)&amp;quot; &amp;quot;$(TxVersionedProductVersionFile)&amp;quot;&quot;</span><span class="kwrd">/&gt;</span>         <br /><span class="kwrd">&lt;/</span><span class="html">Target</span><span class="kwrd">&gt;</span></font></font></p>
<p>TxProductVersionFile defines the fully qualified path of the version number file in the BuildType directory created by TFS Build when the build is initiated and TxVersionedProductVersionFile the fully qualified path of the same file in the Sources directory (which of course means that I have configured my build type so that the build type itself is included in the workspace, this is easy to do in the new build type editor).</p>
<h3>More Stuff</h3>
<p>The above is what I&#8217;ve come up with so far. I still have to figure out why my code signing is failing (this is my fault, I changed the user account used to run TFS build, so I have to move the certificate to the appropriate certificate store) and my unit tests don&#8217;t appear to run properly yet.</p>
<p>I&#8217;ll post an update once I get that sorted out.</p>
<h3>Update</h3>
<p>I did manage to get the code signing sorted out. It turns out I was trying to import the code-signing certificate in a .spc format which isn&#8217;t good enough for code-signing purposes. The certificate needed to be in .pfx format to be imported and used properly for code-signing (the certificate would still show up in the Certificates MMC console, but SignFile task would fail with the error message that no certificate was found that matched all criteria).</p>
<p>Another item I found is that there now are several new targets you can override for the build, see <a href="http://msdn.microsoft.com/en-us/library/aa337604.aspx">Customizable Team Foundation Build Targets</a> for more information. For our needs, the AfterCompileConfiguration target comes in handy since that lets us conveniently create a single build that builds the code, signs the binaries, builds the installer and then finally sings the installer.</p>
<p>That said, I still haven&#8217;t figured out why my unit tests won&#8217;t run&#8230;</p>
<p><strong>Another Update</strong></p>
<p>After having upgraded our solution to VS2008, the unit tests are now running just fine on our build server. I didn&#8217;t spend a whole lot of time researching the issue while the solution was still using VS2005 as moving it to VS2008 was one of our short term goals anyway.</p>
<p>Here are the definitions of the TxProductVersionFile and TxVersionedProductFile properties.</p>
<p><span style="color:#0000ff;font-size:x-small;"><span style="color:#0000ff;font-size:x-small;">&lt;!&#8211;        <br /></span></span></p>
<div><span style="color:#008000;font-size:x-small;"><span style="color:#008000;font-size:x-small;">Define the names of the files containing the product version information.        <br />TxProductVersionFile is the file stored in the build type directory. This file is         <br />copied to the build directory before all the source files are copied to the temporary         <br />workspace. This file has the version number incremented and the resulting version         <br />number is then used to generate the build number.<span style="color:#008000;font-size:x-small;"><span style="color:#008000;font-size:x-small;">TxVersionedProductVersionFile is only available to us after we&#8217;ve retrieved the            <br />source files required for the build. This file is checked out, updated so that             <br />it contains the same information as TxProductVersionFile and then checked back in             <br />so that we&#8217;re assured of having the updated version number available to us for             <br />the next time the build type is invoked.</span></span></span></span><span style="color:#0000ff;font-size:x-small;"><span style="color:#0000ff;font-size:x-small;">&#8211;&gt;        <br />&lt;</span></span><span style="color:#a31515;font-size:x-small;"><span style="color:#a31515;font-size:x-small;">TxProductVersionFile</span></span><span style="color:#0000ff;font-size:x-small;"><span style="color:#0000ff;font-size:x-small;">&gt;</span></span><span style="font-size:x-small;">$(SolutionRoot)\..\BuildType\ProductVersion.txt</span><span style="color:#0000ff;font-size:x-small;"><span style="color:#0000ff;font-size:x-small;">&lt;/</span></span><span style="color:#a31515;font-size:x-small;"><span style="color:#a31515;font-size:x-small;">TxProductVersionFile</span></span><span style="color:#0000ff;font-size:x-small;"><span style="color:#0000ff;font-size:x-small;">&gt;        <br />&lt;</span></span><span style="color:#a31515;font-size:x-small;"><span style="color:#a31515;font-size:x-small;">TxVersionedProductVersionFile</span></span><span style="color:#0000ff;font-size:x-small;"><span style="color:#0000ff;font-size:x-small;">&gt;</span></span><span style="font-size:x-small;">$(SolutionRoot)\TeamBuildTypes\Main\ProductVersion.txt</span><span style="color:#0000ff;font-size:x-small;"><span style="color:#0000ff;font-size:x-small;">&lt;/</span></span><span style="color:#a31515;font-size:x-small;"><span style="color:#a31515;font-size:x-small;">TxVersionedProductVersionFile</span></span><span style="color:#0000ff;font-size:x-small;"><span style="color:#0000ff;font-size:x-small;">&gt;</span></span></div>
<p><strong>More Build Definitions</strong></p>
<p>In response to <a href="http://tranxcoder.wordpress.com/2008/06/23/upgrading-to-team-foundation-server-2008/#comment-64">Stephen&#8217;s questions</a>:</p>
<p>1. $(TxTf) is defined as:</p>
<pre class="code">    <span style="color:blue;">&lt;!-- </span><span style="color:green;">The fully qualified path of tf.exe, used to check in/out items during the build </span><span style="color:blue;">--&gt;
    &lt;</span><span style="color:#a31515;">TxTf</span><span style="color:blue;">&gt;</span><span style="color:red;">&amp;quot;</span>$(TeamBuildRefPath)\..\tf.exe<span style="color:red;">&amp;quot;</span><span style="color:blue;">&lt;/</span><span style="color:#a31515;">TxTf</span><span style="color:blue;">&gt;
</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Basically it defines the fully qualified path of tf.exe which I use to run TFS-related commands that don&#8217;t have a convenient existing MSBuild target (keep in mind that I did fairly minimal work on this build script as part of the update, there may have been new targets added that I just haven&#8217;t looked for yet in TFS 2008).</p>
<p>2. That was a typo I introduced when copying the code from the .targets file to WordPress, the extra quotes have to be XML encoded, I&#8217;ve updated that code above.</p>
<p>3. The definitions of SetProductVersionForBuild target (and dependent targets) are:</p>
<pre class="code">  <span style="color:blue;">&lt;!--
    </span><span style="color:green;">SetProductVersionForBuild increments the version number in the global version number
    file and updates the product version information in all .rc files accordingly.
  </span><span style="color:blue;">--&gt;
  &lt;</span><span style="color:#a31515;">PropertyGroup</span><span style="color:blue;">&gt;
    &lt;</span><span style="color:#a31515;">SetProductVersionForBuildDependsOn</span><span style="color:blue;">&gt;
      </span>IncrementRcProductVersions;
      IncrementRcFileVersions;
    <span style="color:blue;">&lt;/</span><span style="color:#a31515;">SetProductVersionForBuildDependsOn</span><span style="color:blue;">&gt;
  &lt;/</span><span style="color:#a31515;">PropertyGroup</span><span style="color:blue;">&gt;
  &lt;</span><span style="color:#a31515;">Target </span><span style="color:red;">Name</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">SetProductVersionForBuild</span>&quot; <span style="color:red;">DependsOnTargets</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">$(SetProductVersionForBuildDependsOn)</span>&quot;<span style="color:blue;">&gt;
    &lt;</span><span style="color:#a31515;">Message </span><span style="color:red;">Text</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">Updated the version information in preparation for the build.</span>&quot;<span style="color:blue;">/&gt;
  &lt;/</span><span style="color:#a31515;">Target</span><span style="color:blue;">&gt;
</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code">  <span style="color:blue;">&lt;!--
    </span><span style="color:green;">IncrementRcProductVersions updates the ProductVersion information in all
    .rc files in the project.
  </span><span style="color:blue;">--&gt;
  &lt;</span><span style="color:#a31515;">PropertyGroup</span><span style="color:blue;">&gt;
    &lt;</span><span style="color:#a31515;">IncrementRcProductVersionsDependsOn</span><span style="color:blue;">&gt;
      </span>CheckOutRcFiles;
    <span style="color:blue;">&lt;/</span><span style="color:#a31515;">IncrementRcProductVersionsDependsOn</span><span style="color:blue;">&gt;
  &lt;/</span><span style="color:#a31515;">PropertyGroup</span><span style="color:blue;">&gt;
  &lt;</span><span style="color:#a31515;">Target </span><span style="color:red;">Name</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">IncrementRcProductVersions</span>&quot; <span style="color:red;">DependsOnTargets</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">$(IncrementRcProductVersionsDependsOn)</span>&quot;<span style="color:blue;">&gt;
    &lt;</span><span style="color:#a31515;">FileUpdate </span><span style="color:red;">Files</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">@(TxRcFiles)</span>&quot; <span style="color:red;">Encoding</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">ASCII</span>&quot; <span style="color:red;">Regex</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">PRODUCTVERSION\s+\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*\d+</span>&quot; <span style="color:red;">ReplacementText</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">PRODUCTVERSION $(TxMajorVersion),$(TxMinorVersion),$(TxPatchVersion),$(TxBuildVersion)</span>&quot;<span style="color:blue;">/&gt;
    &lt;</span><span style="color:#a31515;">FileUpdate </span><span style="color:red;">Files</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">@(TxRcFiles)</span>&quot; <span style="color:red;">Encoding</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">ASCII</span>&quot; <span style="color:red;">Regex</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">VALUE\s+</span><span style="color:red;">&amp;quot;</span><span style="color:blue;">ProductVersion</span><span style="color:red;">&amp;quot;</span><span style="color:blue;">\s*,\s*</span><span style="color:red;">&amp;quot;</span><span style="color:blue;">\d+\s*[\.,]\s*\d+\s*[\.,]\s*\d+\s*[\.,]\s*\d+\s*(\)*</span><span style="color:red;">&amp;quot;</span>&quot; <span style="color:red;">ReplacementText</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">VALUE </span><span style="color:red;">&amp;quot;</span><span style="color:blue;">ProductVersion</span><span style="color:red;">&amp;quot;</span><span style="color:blue;">, </span><span style="color:red;">&amp;quot;</span><span style="color:blue;">$(TxMajorVersion).$(TxMinorVersion).$(TxPatchVersion).$(TxBuildVersion)</span><span style="color:red;">&amp;quot;</span>&quot;<span style="color:blue;">/&gt;
  &lt;/</span><span style="color:#a31515;">Target</span><span style="color:blue;">&gt;

  &lt;!--
    </span><span style="color:green;">IncrementRcFileVersions updates the ProductVersion information in all
    .rc files in the project.
  </span><span style="color:blue;">--&gt;
  &lt;</span><span style="color:#a31515;">PropertyGroup</span><span style="color:blue;">&gt;
    &lt;</span><span style="color:#a31515;">IncrementRcFileVersionsDependsOn</span><span style="color:blue;">&gt;
      </span>CheckOutRcFiles;
    <span style="color:blue;">&lt;/</span><span style="color:#a31515;">IncrementRcFileVersionsDependsOn</span><span style="color:blue;">&gt;
  &lt;/</span><span style="color:#a31515;">PropertyGroup</span><span style="color:blue;">&gt;
  &lt;</span><span style="color:#a31515;">Target </span><span style="color:red;">Name</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">IncrementRcFileVersions</span>&quot; <span style="color:red;">DependsOnTargets</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">$(IncrementRcFileVersionsDependsOn)</span>&quot;<span style="color:blue;">&gt;
    &lt;</span><span style="color:#a31515;">FileUpdate </span><span style="color:red;">Files</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">@(TxRcFiles)</span>&quot; <span style="color:red;">Encoding</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">ASCII</span>&quot; <span style="color:red;">Regex</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">FILEVERSION\s+\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*\d+</span>&quot; <span style="color:red;">ReplacementText</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">FILEVERSION $(TxMajorVersion),$(TxMinorVersion),$(TxPatchVersion),$(TxBuildVersion)</span>&quot;<span style="color:blue;">/&gt;
    &lt;</span><span style="color:#a31515;">FileUpdate </span><span style="color:red;">Files</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">@(TxRcFiles)</span>&quot; <span style="color:red;">Encoding</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">ASCII</span>&quot; <span style="color:red;">Regex</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">VALUE\s+</span><span style="color:red;">&amp;quot;</span><span style="color:blue;">FileVersion</span><span style="color:red;">&amp;quot;</span><span style="color:blue;">\s*,\s*</span><span style="color:red;">&amp;quot;</span><span style="color:blue;">\d+\s*[\.,]\s*\d+\s*[\.,]\s*\d+\s*[\.,]\s*\d+\s*(\)*</span><span style="color:red;">&amp;quot;</span>&quot; <span style="color:red;">ReplacementText</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">VALUE </span><span style="color:red;">&amp;quot;</span><span style="color:blue;">FileVersion</span><span style="color:red;">&amp;quot;</span><span style="color:blue;">, </span><span style="color:red;">&amp;quot;</span><span style="color:blue;">$(TxMajorVersion).$(TxMinorVersion).$(TxPatchVersion).$(TxBuildVersion)</span><span style="color:red;">&amp;quot;</span>&quot;<span style="color:blue;">/&gt;
  &lt;/</span><span style="color:#a31515;">Target</span><span style="color:blue;">&gt;

  &lt;!--
    </span><span style="color:green;">CheckOutRcFiles checks out all .rc files in the Source directory in preparation
    for them to be updated with new version information.
  </span><span style="color:blue;">--&gt;
  &lt;</span><span style="color:#a31515;">PropertyGroup</span><span style="color:blue;">&gt;
    &lt;</span><span style="color:#a31515;">CheckOutRcFilesDependsOn</span><span style="color:blue;">&gt;
      </span>FindAllRcFiles;
    <span style="color:blue;">&lt;/</span><span style="color:#a31515;">CheckOutRcFilesDependsOn</span><span style="color:blue;">&gt;
  &lt;/</span><span style="color:#a31515;">PropertyGroup</span><span style="color:blue;">&gt;
  &lt;</span><span style="color:#a31515;">Target </span><span style="color:red;">Name</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">CheckOutRcFiles</span>&quot; <span style="color:red;">DependsOnTargets</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">$(CheckOutRcFilesDependsOn)</span>&quot;<span style="color:blue;">&gt;
    &lt;!-- </span><span style="color:green;">Note that this command is run in our Source directory so we don't check out files from the Extern directory </span><span style="color:blue;">--&gt;
    &lt;</span><span style="color:#a31515;">Exec </span><span style="color:red;">WorkingDirectory</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">$(TxSourceDir)</span>&quot; <span style="color:red;">Command</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">$(TxTf) checkout /recursive $(TxRcSpec)</span>&quot;<span style="color:blue;">/&gt;
  &lt;/</span><span style="color:#a31515;">Target</span><span style="color:blue;">&gt;

  &lt;!--
    </span><span style="color:green;">CheckOutRcFiles checks in all .rc files in the Source directory that were
    updated with the new version information.
  </span><span style="color:blue;">--&gt;
  &lt;</span><span style="color:#a31515;">PropertyGroup</span><span style="color:blue;">&gt;
    &lt;</span><span style="color:#a31515;">CheckInRcFilesDependsOn</span><span style="color:blue;">&gt;
    &lt;/</span><span style="color:#a31515;">CheckInRcFilesDependsOn</span><span style="color:blue;">&gt;
  &lt;/</span><span style="color:#a31515;">PropertyGroup</span><span style="color:blue;">&gt;
  &lt;</span><span style="color:#a31515;">Target </span><span style="color:red;">Name</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">CheckInRcFiles</span>&quot; <span style="color:red;">DependsOnTargets</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">$(CheckInRcFilesDependsOn)</span>&quot;<span style="color:blue;">&gt;
    &lt;!-- </span><span style="color:green;">Note that this command is run in our Source directory so we don't check out files from the Extern directory </span><span style="color:blue;">--&gt;
    &lt;</span><span style="color:#a31515;">Exec </span><span style="color:red;">WorkingDirectory</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">$(TxSourceDir)</span>&quot; <span style="color:red;">Command</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">$(TxTf) checkin /override:</span><span style="color:red;">&amp;quot;</span><span style="color:blue;">Automated build check-in, no associated work items</span><span style="color:red;">&amp;quot; </span><span style="color:blue;">/comment:</span><span style="color:red;">&amp;quot;</span><span style="color:blue;">Checking in the updated resource files for build $(BuildNumber)</span><span style="color:red;">&amp;quot; </span><span style="color:blue;">/recursive $(TxRcSpec)</span>&quot;<span style="color:blue;">/&gt;
  &lt;/</span><span style="color:#a31515;">Target</span><span style="color:blue;">&gt;

  &lt;!--
    </span><span style="color:green;">The FindAllRcFiles target finds all .rc files under $(SolutionRoot) and
    stores that information in the $(TxRcFiles) property.
  </span><span style="color:blue;">--&gt;
  &lt;</span><span style="color:#a31515;">PropertyGroup</span><span style="color:blue;">&gt;
    &lt;</span><span style="color:#a31515;">FindAllRcFilesDependsOn</span><span style="color:blue;">&gt;
    &lt;/</span><span style="color:#a31515;">FindAllRcFilesDependsOn</span><span style="color:blue;">&gt;
  &lt;/</span><span style="color:#a31515;">PropertyGroup</span><span style="color:blue;">&gt;
  &lt;</span><span style="color:#a31515;">Target </span><span style="color:red;">Name</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">FindAllRcFiles</span>&quot; <span style="color:red;">DependsOnTargets</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">$(FindAllRcFilesDependsOn)</span>&quot;<span style="color:blue;">&gt;
    &lt;</span><span style="color:#a31515;">CreateItem </span><span style="color:red;">Include</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">$(TxSourceDir)\**\$(TxRcSpec)</span>&quot;<span style="color:blue;">&gt;
      &lt;</span><span style="color:#a31515;">Output </span><span style="color:red;">ItemName</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">TxRcFiles</span>&quot; <span style="color:red;">TaskParameter</span><span style="color:blue;">=</span>&quot;<span style="color:blue;">Include</span>&quot;<span style="color:blue;">/&gt;
    &lt;/</span><span style="color:#a31515;">CreateItem</span><span style="color:blue;">&gt;
  &lt;/</span><span style="color:#a31515;">Target</span><span style="color:blue;">&gt;
</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/tranxcoder.wordpress.com/26/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/tranxcoder.wordpress.com/26/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tranxcoder.wordpress.com/26/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tranxcoder.wordpress.com/26/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tranxcoder.wordpress.com/26/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tranxcoder.wordpress.com/26/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tranxcoder.wordpress.com/26/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tranxcoder.wordpress.com/26/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tranxcoder.wordpress.com/26/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tranxcoder.wordpress.com/26/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tranxcoder.wordpress.com/26/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tranxcoder.wordpress.com/26/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tranxcoder.wordpress.com&blog=3688975&post=26&subd=tranxcoder&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://tranxcoder.wordpress.com/2008/06/23/upgrading-to-team-foundation-server-2008/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Mats</media:title>
		</media:content>
	</item>
		<item>
		<title>Generating Custom TFS Check-in E-mails &#8211; Part 3.5</title>
		<link>http://tranxcoder.wordpress.com/2008/06/16/generating-custom-tfs-check-in-e-mails-part-35/</link>
		<comments>http://tranxcoder.wordpress.com/2008/06/16/generating-custom-tfs-check-in-e-mails-part-35/#comments</comments>
		<pubDate>Mon, 16 Jun 2008 18:12:57 +0000</pubDate>
		<dc:creator>Mats</dc:creator>
				<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://tranxcoder.wordpress.com/?p=23</guid>
		<description><![CDATA[I realized that I haven&#8217;t talked anything about how the whole TFS Spam system hangs together, something that may be useful as I go on with this series. In my best managerial style I, of course, broke out PowerPoint to create a pretty picture that shows the major components of the system (click the image [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tranxcoder.wordpress.com&blog=3688975&post=23&subd=tranxcoder&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>I realized that I haven&#8217;t talked anything about how the whole TFS Spam system hangs together, something that may be useful as I go on with this series. In my best managerial style I, of course, broke out PowerPoint to create a pretty picture that shows the major components of the system (click the image to see a larger version):</p>
<p><a href="http://tranxcoder.files.wordpress.com/2008/06/tfs-spam-overview1.png"><img style="border-right:0;border-top:0;border-left:0;border-bottom:0;" border="0" alt="TFS Spam Overview" src="http://tranxcoder.files.wordpress.com/2008/06/tfs-spam-overview-thumb.png?w=244&#038;h=181" width="244" height="181"></a> </p>
<p>I apologize for the use of PowerPoint and promise that I&#8217;ll try not to use it too often.</p>
<p>Everything starts with a check-in to TFS (as I&#8217;ve mentioned, we&#8217;re still on TFS 2005 but will upgrade to TFS 2008 in the not too distant future). Any time something is checked-in, the TFS server raises an event to any interested parties. You can read more about the event system in TFS <a href="http://msdn.microsoft.com/en-us/library/bb130154(VS.80).aspx">here</a>.</p>
<p>In our case, we have a web service that sits and listens for these events and when one is received, it parses the notification to get the id of the changeset from the notification data. The notification comes in the form of a relatively simple XML document:</p>
<blockquote><p><font size="2" face="Lucida Console">&lt;?xml version=&#8221;1.0&#8243; encoding=&#8221;utf-16&#8243;?&gt;<br />&lt;CheckinEvent xmlns:xsi=&#8221;</font><a href="http://www.w3.org/2001/XMLSchema-instance&quot;"><font size="2" face="Lucida Console">http://www.w3.org/2001/XMLSchema-instance&#8221;</font></a><font size="2" face="Lucida Console"> xmlns:xsd=&#8221;</font><a href="http://www.w3.org/2001/XMLSchema&quot;"><font size="2" face="Lucida Console">http://www.w3.org/2001/XMLSchema&#8221;</font></a><font size="2" face="Lucida Console">&gt;<br />&nbsp; &lt;AllChangesIncluded&gt;true&lt;/AllChangesIncluded&gt;<br />&nbsp; &lt;Subscriber&gt;user name&lt;/Subscriber&gt;<br />&nbsp; &lt;CheckinNotes&gt;<br />&nbsp;&nbsp;&nbsp; &lt;CheckinNote xsi:type=&#8221;NameValuePair&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name=&#8221;Code Reviewer&#8221; val=&#8221;" /&gt;<br />&nbsp;&nbsp;&nbsp; &lt;CheckinNote xsi:type=&#8221;NameValuePair&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name=&#8221;Performance Reviewer&#8221; val=&#8221;" /&gt;<br />&nbsp;&nbsp;&nbsp; &lt;CheckinNote xsi:type=&#8221;NameValuePair&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name=&#8221;Security Reviewer&#8221; val=&#8221;" /&gt;<br />&nbsp; &lt;/CheckinNotes&gt;<br />&nbsp; &lt;PolicyFailures&gt;<br />&nbsp;&nbsp;&nbsp; &lt;PolicyFailure xsi:type=&#8221;NameValuePair&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name=&#8221;[Invalid Policy]&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val=&#8221;Internal error in Changeset Comments Policy&#8221; /&gt;<br />&nbsp; &lt;/PolicyFailures&gt;<br />&nbsp; &lt;CheckinInformation /&gt;<br />&nbsp; &lt;Artifacts&gt;<br />&nbsp;&nbsp;&nbsp; &lt;Artifact xsi:type=&#8221;ClientArtifact&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ArtifactType=&#8221;Changeset&#8221; ServerItem=&#8221;"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;Url&gt;</font><font size="2" face="Lucida Console">changeset link to standard TFS web UI</font><font size="2" face="Lucida Console">&lt;/Url&gt;<br />&nbsp;&nbsp;&nbsp; &lt;/Artifact&gt;<br />&nbsp;&nbsp;&nbsp; &lt;Artifact xsi:type=&#8221;ClientArtifact&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ArtifactType=&#8221;VersionedItem&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Item=&#8221;item name&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Folder=&#8221;item folder path&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TeamProject=&#8221;project name&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ItemRevision=&#8221;revision number&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ChangeType=&#8221;edit&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServerItem=&#8221;item server path&#8221;&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;Url&gt;</font><font size="2" face="Lucida Console">item</font><font size="2" face="Lucida Console"> URL&lt;/Url&gt;<br />&nbsp;&nbsp;&nbsp; &lt;/Artifact&gt;<br />&nbsp;&nbsp;&nbsp; &lt;Artifact xsi:type=&#8221;ClientArtifact&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ArtifactType=&#8221;VersionedItem&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Item=&#8221;item name&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Folder=&#8221;item folder&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TeamProject=&#8221;project name&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ItemRevision=&#8221;revision number&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ChangeType=&#8221;edit&#8221; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServerItem=&#8221;item server path&#8221;&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;Url&gt;<font face="Trebuchet MS">item URL</font></font><font size="2" face="Lucida Console">&lt;/Url&gt;<br />&nbsp;&nbsp;&nbsp; &lt;/Artifact&gt;<br />&nbsp; &lt;/Artifacts&gt;<br />&nbsp; &lt;Title&gt;<br />&nbsp;&nbsp;&nbsp; event title (the same one you&#8217;d see in the standard e-mail <br />&nbsp;&nbsp;&nbsp; subject header).<br />&nbsp; &lt;/Title&gt;<br />&nbsp; &lt;ContentTitle&gt;<br />&nbsp;&nbsp;&nbsp; Changeset 43690: Changed the action and namespace <br />&nbsp;&nbsp;&nbsp; information for the Notify method. Also changed&#8230;<br />&nbsp; &lt;/ContentTitle&gt;<br />&nbsp; &lt;Owner&gt;user name&lt;/Owner&gt;<br />&nbsp; &lt;Committer&gt;user name&lt;/Committer&gt;<br />&nbsp; &lt;Number&gt;43690&lt;/Number&gt;<br />&nbsp; &lt;CreationDate&gt;2/12/2008 11:06:08 AM&lt;/CreationDate&gt;<br />&nbsp; &lt;Comment&gt;check-in comment&lt;/Comment&gt;<br />&nbsp; &lt;TimeZone&gt;Pacific Standard Time&lt;/TimeZone&gt;<br />&nbsp; &lt;TimeZoneOffset&gt;-08:00:00&lt;/TimeZoneOffset&gt;<br />&nbsp; &lt;TeamProject&gt;project name&lt;/TeamProject&gt;<br />&nbsp; &lt;PolicyOverrideComment&gt;Policy issue.&lt;/PolicyOverrideComment&gt;<br />&lt;/CheckinEvent&gt;</font></p>
</blockquote>
<p>You&#8217;ll recognize much of the information in this notification document from the standard check-in e-mail that TFS can produce.</p>
<p>Once we have the id of the changeset, we pass that to a class that processes the changeset. At a high level, this class generates a XML document that contains the information about the changeset, including all the code file differences and then processes this document with a XSLT transform which generates a HTML document that gets e-mailed to the configured subscribers.</p>
<p>All-in-all, there&#8217;s nothing particularly fancy about this, but it&#8217;s still useful to know how things hang together.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/tranxcoder.wordpress.com/23/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/tranxcoder.wordpress.com/23/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tranxcoder.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tranxcoder.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tranxcoder.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tranxcoder.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tranxcoder.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tranxcoder.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tranxcoder.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tranxcoder.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tranxcoder.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tranxcoder.wordpress.com/23/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tranxcoder.wordpress.com&blog=3688975&post=23&subd=tranxcoder&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://tranxcoder.wordpress.com/2008/06/16/generating-custom-tfs-check-in-e-mails-part-35/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Mats</media:title>
		</media:content>

		<media:content url="http://tranxcoder.files.wordpress.com/2008/06/tfs-spam-overview-thumb.png" medium="image">
			<media:title type="html">TFS Spam Overview</media:title>
		</media:content>
	</item>
		<item>
		<title>Generating Custom TFS Check-in E-mails &#8211; Part 3</title>
		<link>http://tranxcoder.wordpress.com/2008/06/09/generating-custom-tfs-check-in-e-mails-part-3/</link>
		<comments>http://tranxcoder.wordpress.com/2008/06/09/generating-custom-tfs-check-in-e-mails-part-3/#comments</comments>
		<pubDate>Mon, 09 Jun 2008 19:44:53 +0000</pubDate>
		<dc:creator>Mats</dc:creator>
				<category><![CDATA[Tools]]></category>
		<category><![CDATA[Team Foundation Server]]></category>
		<category><![CDATA[TFS]]></category>

		<guid isPermaLink="false">http://tranxcoder.wordpress.com/?p=20</guid>
		<description><![CDATA[When I started this project, I already had some limited experience with programming against the TFS object model since I had written a utility to migrate bugs from our old bug tracking system to TFS. That said, there were still some new areas to explore since my previous experience was all with the WorkItemStore portion [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tranxcoder.wordpress.com&blog=3688975&post=20&subd=tranxcoder&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>When I started this project, I already had some limited experience with programming against the TFS object model since I had written a utility to migrate bugs from our old bug tracking system to TFS. That said, there were still some new areas to explore since my previous experience was all with the <a href="http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.workitemtracking.client.workitemstore(VS.80).aspx">WorkItemStore</a> portion of TFS and nothing with the <a href="http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.versioncontrol.client.versioncontrolserver(VS.80).aspx">VersionControlServer</a> portion (I&#8217;ve linked to the TFS 2005 version of the documentation since we haven&#8217;t yet upgraded to TFS 2008).</p>
<p>To make life a little bit easier for myself I started out by cleaning up the code from my previous utility and refactoring it in a way that made it a bit more broadly useable.</p>
<p><span id="more-20"></span>The base of my two utility classes (one for work items and one for version control) is a very basic class that just takes care of providing access to a <a href="http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.client.teamfoundationserver(VS.80).aspx">TeamFoundationServer</a> object</p>
<div class="csharpcode">
<pre><span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Text;
 
<span class="kwrd">using</span> Microsoft.TeamFoundation.Client;
 
<span class="kwrd">namespace</span> Tranxition.Tools.Tfs
{
    <span class="rem">/// &lt;summary&gt;</span>
    <span class="rem">/// The ServiceBase class serves as an abstract base class for the</span>
    <span class="rem">/// service-specific TFS utility classes.</span>
    <span class="rem">/// &lt;/summary&gt;</span>
    <span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">class</span> ServiceBase
    {
        <span class="preproc">#region</span> -- Constructor
        <span class="rem">/// &lt;summary&gt;</span>
        <span class="rem">/// Standard constructor for the class, connects to the</span>
        <span class="rem">/// specified TFS server.</span>
        <span class="rem">/// &lt;/summary&gt;</span>
        <span class="rem">/// &lt;param name="serverUrl"&gt;The URL of the TFS server to connect to.&lt;/param&gt;</span>
        <span class="rem">/// &lt;exception cref="ArgumentNullException"&gt;Thrown if &lt;paramref name="serverUrl"/&gt; is &lt;c&gt;null&lt;/c&gt;.&lt;/exception&gt;</span>
        <span class="kwrd">protected</span> ServiceBase( Uri serverUrl )
        {
            <span class="kwrd">if</span> ( serverUrl == <span class="kwrd">null</span> )
            {
                <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException( <span class="str">"serverUrl"</span> );
            }
            <span class="kwrd">else</span>
            {
                _server = TeamFoundationServerFactory.GetServer( serverUrl.ToString() );
            }
        }
        <span class="preproc">#endregion</span>
 
        <span class="preproc">#region</span> -- Protected properties
        <span class="rem">/// &lt;summary&gt;</span>
        <span class="rem">/// The actual TFS server we're connected to.</span>
        <span class="rem">/// &lt;/summary&gt;</span>
        <span class="kwrd">protected</span> TeamFoundationServer Server
        {
            get { <span class="kwrd">return</span> _server; }
        } <span class="kwrd">private</span> TeamFoundationServer _server;
        <span class="preproc">#endregion</span>
    }
}</pre>
</div>
<p>As you can see, there&#8217;s nothing particularly fancy in the base class. The VersionControlService that wraps the basic features needed by TFS Spam is a slightly more interesting:</p>
<div class="csharpcode">
<pre><span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Diagnostics.CodeAnalysis;
<span class="kwrd">using</span> System.Text;
 
<span class="kwrd">using</span> Microsoft.TeamFoundation.VersionControl.Client;
<span class="kwrd">using</span> Microsoft.TeamFoundation.WorkItemTracking.Client;
 
<span class="kwrd">namespace</span> Tranxition.Tools.Tfs
{
    <span class="rem">/// &lt;summary&gt;</span>
    <span class="rem">/// The VersionControlService class represents the TFS version</span>
    <span class="rem">/// control functionality and provides support for the major</span>
    <span class="rem">/// version control features needed by TFS Spam.</span>
    <span class="rem">/// &lt;/summary&gt;</span>
    <span class="kwrd">public</span> <span class="kwrd">class</span> VersionControlService : ServiceBase
    {
        <span class="preproc">#region</span> -- Constructor
        <span class="rem">/// &lt;summary&gt;</span>
        <span class="rem">/// Standard constructor for the class. Connects to the version</span>
        <span class="rem">/// control service on the specified TFS server.</span>
        <span class="rem">/// &lt;/summary&gt;</span>
        <span class="rem">/// &lt;param name="serverUrl"&gt;The URL of the TFS server to connect to.&lt;/param&gt;</span>
        <span class="kwrd">public</span> VersionControlService( Uri serverUrl )
            : <span class="kwrd">base</span>( serverUrl )
        {
            _versionControl = (VersionControlServer) Server.GetService( <span class="kwrd">typeof</span>( VersionControlServer ) );
        }
        <span class="preproc">#endregion</span>
 
        <span class="preproc">#region</span> -- Public properties
        <span class="rem">/// &lt;summary&gt;</span>
        <span class="rem">/// The version control service on the TFS server</span>
        <span class="rem">/// we're connected to.</span>
        <span class="rem">/// &lt;/summary&gt;</span>
        <span class="kwrd">public</span> VersionControlServer VersionControl
        {
            get { <span class="kwrd">return</span> _versionControl; }
        } <span class="kwrd">private</span> <span class="kwrd">readonly</span> VersionControlServer _versionControl;
        <span class="preproc">#endregion</span>
 
        <span class="preproc">#region</span> -- Public methods
        <span class="rem">/// &lt;summary&gt;</span>
        <span class="rem">/// Retrieves the specified changeset with all information</span>
        <span class="rem">/// included.</span>
        <span class="rem">/// &lt;/summary&gt;</span>
        <span class="rem">/// &lt;param name="changesetId"&gt;The id of the changeset that should be retrieved.&lt;/param&gt;</span>
        <span class="rem">/// &lt;returns&gt;The requested changeset with complete information.&lt;/returns&gt;</span>
        <span class="kwrd">public</span> Changeset GetChangeset( <span class="kwrd">int</span> changesetId )
        {
            <span class="kwrd">return</span> _versionControl.GetChangeset( changesetId, <span class="kwrd">true</span>, <span class="kwrd">true</span> );
        }
 
        <span class="rem">/// &lt;summary&gt;</span>
        <span class="rem">/// Downloads the version of a file associated with the specified</span>
        <span class="rem">/// changeset it.</span>
        <span class="rem">/// &lt;/summary&gt;</span>
        <span class="rem">/// &lt;param name="itemId"&gt;The id of the file item to download.&lt;/param&gt;</span>
        <span class="rem">/// &lt;param name="changesetId"&gt;The changeset version of the file item that should be downloaded.&lt;/param&gt;</span>
        <span class="rem">/// &lt;param name="targetFile"&gt;The fully qualified path of the file the file item should be saved as locally.&lt;/param&gt;</span>
        <span class="kwrd">public</span> <span class="kwrd">void</span> DownloadFile( <span class="kwrd">int</span> itemId, <span class="kwrd">int</span> changesetId, <span class="kwrd">string</span> targetFile )
        {
            Item item = _versionControl.GetItem( itemId, changesetId );
 
            DownloadFile( item, targetFile );
        }
 
        <span class="rem">/// &lt;summary&gt;</span>
        <span class="rem">/// Downloads the specified file item to a local file.</span>
        <span class="rem">/// &lt;/summary&gt;</span>
        <span class="rem">/// &lt;param name="item"&gt;The file item that should be downloaded.&lt;/param&gt;</span>
        <span class="rem">/// &lt;param name="targetFile"&gt;The fully qualified path of the file the file item should be saved as locally.&lt;/param&gt;</span>
        <span class="kwrd">public</span> <span class="kwrd">void</span> DownloadFile( Item item, <span class="kwrd">string</span> targetFile )
        {
            <span class="kwrd">if</span> ( item == <span class="kwrd">null</span> )
            {
                <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException( <span class="str">"item"</span> );
            }
            <span class="kwrd">else</span> <span class="kwrd">if</span> ( <span class="kwrd">string</span>.IsNullOrEmpty( targetFile ) )
            {
                <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException( <span class="str">"targetFile"</span> );
            }
            <span class="kwrd">else</span>
            {
                ChangesetVersionSpec itemVersion = <span class="kwrd">new</span> ChangesetVersionSpec( item.ChangesetId );
 
                _versionControl.DownloadFile( item.ServerItem, item.DeletionId, itemVersion, targetFile );
            }
        }
 
        <span class="rem">/// &lt;summary&gt;</span>
        <span class="rem">/// Retrieves the project the specified changeset belongs to.</span>
        <span class="rem">/// &lt;/summary&gt;</span>
        <span class="rem">/// &lt;remarks&gt;</span>
        <span class="rem">/// If the changeset contains items from multiple projects, only</span>
        <span class="rem">/// the project the first item in the changeset belongs to is </span>
        <span class="rem">/// returned.</span>
        <span class="rem">/// &lt;/remarks&gt;</span>
        <span class="rem">/// &lt;param name="changeset"&gt;The changeset whose project should be retrieved.&lt;/param&gt;</span>
        <span class="rem">/// &lt;returns&gt;The TFS project the changeset belongs to.&lt;/returns&gt;</span>
        <span class="rem">/// &lt;exception cref="ArgumentNullException"&gt;Thrown if &lt;paramref name="changeset"/&gt; is &lt;c&gt;null&lt;/c&gt;.&lt;/exception&gt;</span>
        <span class="kwrd">public</span> TeamProject GetTfsProject( Changeset changeset )
        {
            <span class="kwrd">if</span> ( changeset == <span class="kwrd">null</span> )
            {
                <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException( <span class="str">"changeset"</span> );
            }
            <span class="kwrd">else</span>
            {
                <span class="kwrd">foreach</span> ( Change change <span class="kwrd">in</span> changeset.Changes )
                {
                    <span class="kwrd">if</span> ( change.Item != <span class="kwrd">null</span> )
                    {
                        <span class="kwrd">return</span> GetProject( change.Item );
                    }
                }
 
                <span class="kwrd">return</span> <span class="kwrd">null</span>;
            }
        }
 
        <span class="rem">/// &lt;summary&gt;</span>
        <span class="rem">/// Retrieves the TFS project the specified item belongs to.</span>
        <span class="rem">/// &lt;/summary&gt;</span>
        <span class="rem">/// &lt;param name="item"&gt;The item whose project should be retrieved.&lt;/param&gt;</span>
        <span class="rem">/// &lt;returns&gt;The TFS project the item belongs to.&lt;/returns&gt;</span>
        <span class="rem">/// &lt;exception cref="ArgumentNullException"&gt;Thrown if &lt;paramref name="item"/&gt; is &lt;c&gt;null&lt;/c&gt;.&lt;/exception&gt;</span>
        <span class="kwrd">public</span> TeamProject GetProject( Item item )
        {
            <span class="kwrd">if</span> ( item == <span class="kwrd">null</span> )
            {
                <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException( <span class="str">"item"</span> );
            }
            <span class="kwrd">else</span>
            {
                <span class="kwrd">return</span> (TeamProject) _versionControl.GetTeamProjectForServerPath( item.ServerItem );
            }
        }
        <span class="preproc">#endregion</span>
    }
}</pre>
</div>
<p>The primary method used by TFS Spam is GetChangeset which (as the name implies) retrieves a <a href="http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.versioncontrol.client.changeset(VS.80).aspx">Changeset</a> with a specified id. The <a href="http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.versioncontrol.client.changeset(VS.80).aspx">Changeset</a> instance is the starting point for all of the operations performed by TFS Spam.</p>
<p>Starting with the changeset id is convenient since the id can (obviously) easily be extracted from the check-in notification provided by TFS, but this approach also makes it possible to provide a feature where a user could just provide a changeset id and then get the corresponding check-in e-mail delivered on an on-demand basis. This is something I have yet to actually implement, but something that I think might be handy down the road.</p>
<p>The next post will cover some more details about how we go about getting some interesting information out of the changeset.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/tranxcoder.wordpress.com/20/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/tranxcoder.wordpress.com/20/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tranxcoder.wordpress.com/20/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tranxcoder.wordpress.com/20/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tranxcoder.wordpress.com/20/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tranxcoder.wordpress.com/20/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tranxcoder.wordpress.com/20/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tranxcoder.wordpress.com/20/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tranxcoder.wordpress.com/20/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tranxcoder.wordpress.com/20/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tranxcoder.wordpress.com/20/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tranxcoder.wordpress.com/20/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tranxcoder.wordpress.com&blog=3688975&post=20&subd=tranxcoder&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://tranxcoder.wordpress.com/2008/06/09/generating-custom-tfs-check-in-e-mails-part-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Mats</media:title>
		</media:content>
	</item>
		<item>
		<title>Generating Custom TFS Check-in E-mails &#8211; Part 2</title>
		<link>http://tranxcoder.wordpress.com/2008/05/28/generating-custom-tfs-check-in-e-mails-part-2/</link>
		<comments>http://tranxcoder.wordpress.com/2008/05/28/generating-custom-tfs-check-in-e-mails-part-2/#comments</comments>
		<pubDate>Wed, 28 May 2008 23:40:34 +0000</pubDate>
		<dc:creator>Mats</dc:creator>
				<category><![CDATA[Tools]]></category>
		<category><![CDATA[Team Foundation Server]]></category>
		<category><![CDATA[TFS]]></category>

		<guid isPermaLink="false">http://tranxcoder.wordpress.com/?p=17</guid>
		<description><![CDATA[As discussed in Part 1, I wanted to provide the functionality of CVSspam for TFS in order to give everyone on the team a better understanding of the code being checked in. The first step in this was, obviously, to find a way to compare text files so that I could present the differences between [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tranxcoder.wordpress.com&blog=3688975&post=17&subd=tranxcoder&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>As discussed in <a href="http://tranxcoder.wordpress.com/2008/05/13/team-foundation-server-customizations/">Part 1</a>, I wanted to provide the functionality of <a href="http://www.badgers-in-foil.co.uk/projects/cvsspam/">CVSspam</a> for TFS in order to give everyone on the team a better understanding of the code being checked in. The first step in this was, obviously, to find a way to compare text files so that I could present the differences between two versions of a file.</p>
<p>I was much too lazy to actually implement a difference algorithm myself and instead went to search for an existing library to do this. After a bit of searching I came across the article <a href="http://www.mathertel.de/Diff/">An O(ND) Difference Algorithm for C#</a> which after writing some test code seemed like it would do the trick nicely.</p>
<p><span id="more-17"></span></p>
<p>The article provides the Diff class (I won&#8217;t list the source here, but you can get it through the link above) which provides a pretty straight-forward set of methods for comparing the contents of two strings as this trivial sample code shows:</p>
<div class="csharpcode">
<pre><span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">void</span> CompareFiles( <span class="kwrd">string</span> oldFile, <span class="kwrd">string</span> newFile )
{
    <span class="kwrd">string</span>[] oldLines;
    <span class="kwrd">string</span>[] newLines;
    <span class="kwrd">string</span> oldContent;
    <span class="kwrd">string</span> newContent;
    Diff differ = <span class="kwrd">new</span> Diff();
    Diff.Item[] differences;

    GetFileContent( oldFile, <span class="kwrd">out</span> oldContent, <span class="kwrd">out</span> oldLines );
    GetFileContent( newFile, <span class="kwrd">out</span> newContent, <span class="kwrd">out</span> newLines );

    differences = differ.DiffText( oldContent, newContent );
    <span class="kwrd">foreach</span> ( Diff.Item difference <span class="kwrd">in</span> differences )
    {
        Console.WriteLine( <span class="str">"Difference:"</span> );
        Console.WriteLine( <span class="str">"\tStartA:   {0}"</span>,
                           difference.StartA );
        Console.WriteLine( <span class="str">"\tDeleted:  {0}"</span>,
                           difference.deletedA );
        Console.WriteLine( <span class="str">"\tStartB:   {0}"</span>,
                           difference.StartB );
        Console.WriteLine( <span class="str">"\tInserted: {0}"</span>,
                           difference.insertedB );
    }
}</pre>
</div>
<p>Each difference between the two files is reported as a Diff.Item instance which tells us:</p>
<ul>
<li>The line number in the old file where the difference starts (StartA)</li>
<li>The number of lines deleted from the old file (deletedA)</li>
<li>The line number in the new file where the difference starts (StartB)</li>
<li>The number of lines from the new file that were added to the resulting file (insertedB)</li>
</ul>
<p>With this information it&#8217;s easy to generate the relevant information from the two versions of the file.</p>
<p>With this in hand, the next step was to get information out of TFS about the changeset, the items included in the changeset and the new and previous versions of each item. This will the topic for my next post.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/tranxcoder.wordpress.com/17/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/tranxcoder.wordpress.com/17/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tranxcoder.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tranxcoder.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tranxcoder.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tranxcoder.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tranxcoder.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tranxcoder.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tranxcoder.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tranxcoder.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tranxcoder.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tranxcoder.wordpress.com/17/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tranxcoder.wordpress.com&blog=3688975&post=17&subd=tranxcoder&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://tranxcoder.wordpress.com/2008/05/28/generating-custom-tfs-check-in-e-mails-part-2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Mats</media:title>
		</media:content>
	</item>
		<item>
		<title>Generating Custom TFS Check-in E-mails &#8211; Part 1</title>
		<link>http://tranxcoder.wordpress.com/2008/05/13/team-foundation-server-customizations/</link>
		<comments>http://tranxcoder.wordpress.com/2008/05/13/team-foundation-server-customizations/#comments</comments>
		<pubDate>Tue, 13 May 2008 20:28:12 +0000</pubDate>
		<dc:creator>Mats</dc:creator>
				<category><![CDATA[Tools]]></category>
		<category><![CDATA[Team Foundation Server]]></category>
		<category><![CDATA[TFS]]></category>

		<guid isPermaLink="false">http://tranxcoder.wordpress.com/?p=5</guid>
		<description><![CDATA[We do the vast majority of our development in Visual Studio 2005 and 2008, so when we were picking a new source code management tool last year, getting something that integrates nicely with Visual Studio was obviously a priority.
We had all worked with a relatively wide variety of tools in the past (Visual SourceSafe, ClearCase, [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tranxcoder.wordpress.com&blog=3688975&post=5&subd=tranxcoder&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>We do the vast majority of our development in Visual Studio 2005 and 2008, so when we were picking a new source code management tool last year, getting something that integrates nicely with Visual Studio was obviously a priority.</p>
<p>We had all worked with a relatively wide variety of tools in the past (Visual SourceSafe, ClearCase, CVS, Perforce, etc.) with most experiences being good (Visual SourceSafe being an obvious exception <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> . We also wanted a nicely integrated solution with bug tracking, build management and so on. Given that we&#8217;re a pretty small team we wanted something that would be easy to maintain.</p>
<p>After having looked at a few alternatives, we decided to go with Microsoft&#8217;s Team Foundation Server mostly because it seemed to provide most of the features we wanted with a minimum of fuss.</p>
<p>We&#8217;re currently running TFS 2005, but will be upgrading to TFS 2008 in the not too distant future (meaning in the next 2 months or so).</p>
<p>In a previous job I worked on a project that used <a href="http://en.wikipedia.org/wiki/Concurrent_Versions_System">CVS</a> for source code control (along with <a href="http://ant.apache.org/">Ant</a> and <a href="http://maven.apache.org/">Maven</a> to round things out), but the tool that really made a difference (especially considering that the developers on the project were scattered across the country) was <a href="http://www.badgers-in-foil.co.uk/projects/cvsspam/">CVSspam</a> which sends out e-mails with highlighted code differences for every check-in (see an example <a href="http://www.badgers-in-foil.co.uk/projects/cvsspam/example-simple.html">here</a>).</p>
<p>I found these e-mails to be an excellent way of getting an understanding of what was going on in the code base, not to mention that they worked as a (very) informal code-review. The standard TFS check-in notification e-mails leave quite a bit to be desired and although you can customize them by modifying the XSLT files, I couldn&#8217;t see any obvious way of replicating CVSspam using just XSLT so I decided to write my own tool (cleverly called TFS Spam) to accomplish this.</p>
<p>Over the next couple of posts I will detail how this tool was created and sometime later this summer we&#8217;re going to put the source code up on <a href="http://www.codeplex.com">CodePlex</a> just in case anyone else finds it useful. In the meantime, here&#8217;s a screen shot of one of our test check-in notifications:</p>
<p><a href="http://tranxcoder.files.wordpress.com/2008/05/check-in-email1.jpg"><img style="border-width:0;" src="http://tranxcoder.files.wordpress.com/2008/05/check-in-email-thumb1.jpg?w=217&#038;h=244" border="0" alt="Check-in Email" width="217" height="244" /></a></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/tranxcoder.wordpress.com/5/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/tranxcoder.wordpress.com/5/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tranxcoder.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tranxcoder.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tranxcoder.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tranxcoder.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tranxcoder.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tranxcoder.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tranxcoder.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tranxcoder.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tranxcoder.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tranxcoder.wordpress.com/5/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tranxcoder.wordpress.com&blog=3688975&post=5&subd=tranxcoder&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://tranxcoder.wordpress.com/2008/05/13/team-foundation-server-customizations/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Mats</media:title>
		</media:content>

		<media:content url="http://tranxcoder.files.wordpress.com/2008/05/check-in-email-thumb1.jpg" medium="image">
			<media:title type="html">Check-in Email</media:title>
		</media:content>
	</item>
		<item>
		<title>Introducing the TranxCoders</title>
		<link>http://tranxcoder.wordpress.com/2008/05/09/introducing-the-tranxcoders/</link>
		<comments>http://tranxcoder.wordpress.com/2008/05/09/introducing-the-tranxcoders/#comments</comments>
		<pubDate>Fri, 09 May 2008 21:03:58 +0000</pubDate>
		<dc:creator>Mats</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Welcome to the TranxCoders blog. This blog is written by the development team at Tranxition, a small software company in Beaverton, Oregon, USA.
This is mostly a developer-oriented blog where we talk about things we&#8217;re currently working on, tools we use (or create) and other things that might be of general interest for developers. The goal [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tranxcoder.wordpress.com&blog=3688975&post=1&subd=tranxcoder&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Welcome to the TranxCoders blog. This blog is written by the development team at Tranxition, a small software company in Beaverton, Oregon, USA.</p>
<p>This is mostly a developer-oriented blog where we talk about things we&#8217;re currently working on, tools we use (or create) and other things that might be of general interest for developers. The goal is to keep up a fairly regular posting schedule, so feel free to yell at us if we start to slack off.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/tranxcoder.wordpress.com/1/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/tranxcoder.wordpress.com/1/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/tranxcoder.wordpress.com/1/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/tranxcoder.wordpress.com/1/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/tranxcoder.wordpress.com/1/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/tranxcoder.wordpress.com/1/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/tranxcoder.wordpress.com/1/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/tranxcoder.wordpress.com/1/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/tranxcoder.wordpress.com/1/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/tranxcoder.wordpress.com/1/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/tranxcoder.wordpress.com/1/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/tranxcoder.wordpress.com/1/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=tranxcoder.wordpress.com&blog=3688975&post=1&subd=tranxcoder&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://tranxcoder.wordpress.com/2008/05/09/introducing-the-tranxcoders/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">Mats</media:title>
		</media:content>
	</item>
	</channel>
</rss>