Continuous Learning and Sharing of Team Foundation Server and Application Lifecycle Management RSS 2.0
# Friday, May 28, 2010

Today I have released Team Deploy 2010 for Team Foundation Server 2010.  In this post, I am going to give a quick overview of Team Deploy if you haven’t used it before, explain this release and upcoming releases, compare it to Lab Management 2010, and give a walkthrough for setting it up and uninstalling it.

What is Team Deploy?

Team Deploy is a set of custom build activities used to deploy MSIs to multiple client PCs and/or deploy services to servers.  This activities include the ability to kill processes, start/stop services, pass in arguments to the MSIs, provide the service username/password, and uninstall previous versions.  Team Deploy uses SysInternal’s PSTools to remotely execute MSIEXEC to install the MSIs and PSKill to kill processes.  By using Team Deploy, development teams can create automated build and deploy processes for better configuration management.  Deployments can be done on demand or scheduled just like any other build in Team Build.  If you are using Team Foundation Server 2008, Team Deploy 2.1 is the current release to download.  Team Deploy is open source and free to use.  It can be downloaded from http://teamdeploy.codeplex.com

This Release and Future Plans

I am calling this version of Team Deploy 2010, Release 1.  This release is a 1 for 1 port of the MSBuild tasks to Workflow custom activities.  I wanted to release this version without any additional enhancements so development teams can upgrade their build definitions to workflow.  I have a lot ideas for future versions.  Lab Management 2010 has given me some ideas (see comparison below) and there are several other things I want to do.  Here is a list of some:

  • MSI Package for Team Deploy 2010 – For custom build tasks, deployments are easy.  Basically just copy the Dll to the MSBuilds folder and use that path in the build definitions.  For Team Build custom activities, it is a little more complicated.  As you will see in the Setup Walkthrough below, there are several steps that are fairly easy to do manually but are going to be more difficult to do with a custom task.  I have begun working on this but it wasn’t ready for this release.
  • Breakout Deploy activity into Workflow – Currently the Deploy activity does all the work and calls the other activities within code.  I want to create an additional workflow with all of these steps in a workflow.
  • PowerShell capabilities – PowerShell 2.0 has the ability to be run on remote machines.  I want to research this functionality and see if it makes sense to create an addition set of activities that use PowerShell instead of PSTools.
  • Custom Build Definition Screen – Display screen to create the deployment options through the UI instead of creating it in XML today.
  • Change the Threadpool to .Net 4 Tasks for deploying to multiple machines at the same time.
  • Team Deploy build definition to call another definition to  do the build and deploy (Similar to Lab Management)

Team Deploy and Lab Management

Visual Studio 2010 introduces an additional product for Team Foundation Server 2010 called Lab Management 2010.  This product allows virtual environments be created, quickly provisioned, used for manual and automated testing.  Lab Management also includes a new build definition type and activities.  With the build definition type, it allows you to revert the virtual environment to a baseline snapshot, build the application, deploy the application, run the automated tests, and capture the results.  So Lab Management can do what Team Deploy can do and a lot more.   The one area that I have seen Team Deploy used where Lab Management would not be used is for deploying applications to QA and Production.  I have worked with several companies that use Team Deploy to deploy to all of their environments for a consistent deployment process.

Team Deploy Setup

Here are the steps to install Team Deploy and create a simple build.

1. The TeamDeploy2010_R1.zip file contains the following 4 files that are used to install the application.

image

2. Copy TeamDeploy.Activities.* to a location in source control and check in.

image

3. Add TeamDeploy.Activities.dll to the GAC using Gacutil.

image

4. Add source control location of custom assemblies to build controller.  ($/TestBuilds/CustomActivities in this example)

image

5. Copy DeployTemplate.xaml to source control in the BuildProcessTemplates folder.

image

6. Create a new build definition.  In the Process Step click on “New” Template and add the existing DeployTemplate.xaml template that was added to source control.  Click OK.

image

7. Set the build properties to where the PSTools is installed and where the deployment XML is located. Click Save.

image

8. Rename and edit SampleDeployScript.xml to specify applications to install and machines to deploy to.  See http://teamdeploy.codeplex.com website for full list of options available. If you open the DeployTemplate.xaml.  It should look like this. 

image 

If there is an error for the deploy activity.  Delete the AgentScope activity.

1.  Add Team Deploy 2010 Activities by right clicking in the toolbox and select “Choose Items…”. Make sure System.Activities Components tab is selected and select “Browse…” to find the location of the TeamDeploy.Activities.dll and choose it.

image

  2. Once you have added the TeamDeploy.Activities.dll, you will see the activities selected.  Click Ok.

image

3. Clicking Ok will add the activities to the toolbox. Drag the Deploy activity to the AgentScope container.  The required properties will cause a red error icon to display.  Fill out the properties to where you have the deployment script and PSTools installed. (Sometimes the designer won’t let you drag the Deploy activity to the canvas.  Save the workflow, exit Visual Studio 2010, and reopen the workflow.  It should then.

image

Uninstalling or Updating Team Deploy 2010

1. Close Visual Studio 2010

2. Stop the Visual Studio Team Foundation Build Service Host service.

image

3. To remove Team Deploy from the GAC, browse to c:\windows\microsoft.net\assembly\gac_msil and delete the TeamDeploy.Activities folder.

image

Build Log of Deploy activity in Team Build 2010

Here is an example of the build log for the Deploy activity.

image

I hope you enjoy!  Let me know if you have any ideas or run into problems.

Mike

Friday, May 28, 2010 12:36:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
Team Build | Team Build 2010 | Team Foundation Server | TFS 2010 | Visual Studio 2010

# Thursday, March 25, 2010

Thanks everyone for coming on Tuesday to the Omaha Team System User Group meeting to see my presentation on What’s New for Testing in Visual Studio 2010 and TFS 2010.  It was a great turnout and there were a lot of great dialog and questions asked. 

Here’s the slides from the presentation.
http://www.codesmartnothard.com/content/binary/Whats_New_for_Testing_in_Visual_Studio_2010.zip

If you missed it or know anyone else that wants to see it, I’m doing this presentation again on a Webcast on April 14th at 11:00 (central time).  Here’s the link:
https://www.clicktoattend.com/invitation.aspx?code=146828

Contact us below if you would like more information about implementing Team Foundation Server and/or Visual Studio Lab Management 2010.

Thanks,
Mike

deliveron_banner

Thursday, March 25, 2010 2:30:09 PM (Central Daylight Time, UTC-05:00)  #    Comments [2] -
Deliveron | Lab Management | Team Build | Team Build 2010 | Team Foundation Server | TFS 2010

# Thursday, March 04, 2010

This is Part 4 of my Deployments with TFS series. It has been awhile since I started putting this information together but I haven’t found time to finish this.   A question about building ClickOnce deployments with Team Build 2008 on the MSDN forums was just what I needed to get this posted.

What is ClickOnce?

ClickOnce is a deployment technology that enables you to create self-updating windows based applications that can be installed and run with minimal user interaction.  For a complete overview of ClickOnce, please visit this the ClickOnce Deployment Overview article on MSDN.

Our Requirements

Our requirements contained a couple items that made our deployment more complicated.  The first one is one a configuration management issue that I strongly pushed.  Basically there were two environments, Test and Production.  The ClickOnce was to be built and deployed to the Test server, tested and accepted, and finally pushed to production.  The problem is that Test used different config files than Production.  The thing we didn't want to do is to have to rebuild the application to update the config files, recreate the manifest, and publish to the production.  This had the potential to introduce something different than was tested.Fortunately our Production Release build script was able to copy in the new config files and recreate the manifest and push to production.  The helped ensure the same version of the assemblies tested.  Of course nothing is perfect, we could have still had problems in our config files. :)

The next issue is that we wanted to run Test and Production at the same time to compare if there were problems.  ClickOnce thought these were the same applications so we were unable to run them both.  The solution for this was to use a different key for each environment.  In Test we used a test certificate key that we created, in Production, we used our issued certificate key.  With this, the assemblies could be the same or different versions and ClickOnce treated them as two separate applications.

The Solution

The solution consisted of 4 build scripts

  1. Build and Stage to Test – This builds the application, creates the ClickOnce manifest, and then publishes it to a folder share, ready to be deployed to Test (but not yet).
  2. Deploy to Test – This script copies the ClickOnce application to the web server so the application is available to the testers.  This script also deployed a WCF Windows service.
  3. Stage to Production – This script copied the production config file into the manifest folder, recreated the manifest (with production certificate), and copied the ClickOnce application to a staging folder on a production (so it could be deployed at a later time).
  4. Deploy to Production – This script copies the ClickOnce application from the production staging folder to the production web server.

The “staging” builds could be combined with the “deployment” builds but I believe by separating these it offers the most flexibility.  I will now highlight some of the unique things I had to do to get this all to work.  Unfortunately it wasn’t as simple as using the MSBUILD task.  I think this would work for a demo or simple scenario but not a real world scenario.  There wasn’t any way to change any of the settings.  This offers more options.

Build and Stage To Test

The first part is the properties.  Here I set all of the ClickOnce properties that will be used later on in the build.

<PropertyGroup>
  <TF>&quot;$(TeamBuildRefPath)\..\tf.exe&quot;</TF>
  <PublishUrl>\\myserver\deployments\coolapp\current\ClickOnceStage\</PublishUrl>
  <ClickOnceAppName>MyCompany.CoolApp</ClickOnceAppName>
  <ClickOnceExeFile>MyCompany.CoolApp.exe</ClickOnceExeFile>
  <ClickOnceProduct>Cool Application</ClickOnceProduct>
  <Company>My Company</Company>
  <ClickOnceDescription></ClickOnceDescription>
  <ClickOnceUrl>http://testserver.mycompany.com/</ClickOnceUrl>
  <SigningCert>$(SolutionRoot)\MyCompany.CoolApp\CoolApp_1_TemporaryKey.pfx</SigningCert>
  <SigningCertPassword>1234</SigningCertPassword>
</PropertyGroup>

The first trick is to edit project file and update the publish url.   This allows the build to edit it but not check out the file and need to check it in.  Do this before the build by overriding the BeforeCompile target.

<Target Name ="BeforeCompile">
  <Message Text="Making csproj file writable"/>
  <Exec Command="attrib -R &quot;$(SolutionRoot)\MyCompany.CoolApp\MyCompany.CoolApp.csproj&quot;"/>

  <Message Text="Replacing PublishUrl"/>
  <File.RegEx
    Path="$(SolutionRoot)\MyCompany.CoolApp\MyCompany.CoolApp.csproj"
    RegularExpression="&lt;PublishUrl&gt;(.*?)&lt;/PublishUrl&gt;"
    NewValue="&lt;PublishUrl&gt;$(PublishUrl)&lt;/PublishUrl&gt;"
     />
</Target>

Take the publish.htm file from a manual ClickOnce publish change the version to a tag that can be replaced by the updated version number and check it in to the solution.  After the compile, use the modified version of the publish.htm file copy it to the staging location and then replace it with the version.  MaxVersion is the variable the represents the new version.  I like to keep the assembly version the same as the ClickOnce version.

<Target Name="AfterCompile" Condition="'$(IsDesktopBuild)'!='true'">
  <!-- Copy modified publish htm file to staging publish location -->
  <Copy SourceFiles="$(SolutionRoot)\publish.htm" DestinationFolder="$(PublishUrl)" />

  <ItemGroup>
    <WebPage Include="$(PublishUrl)\publish.htm" />
  </ItemGroup>

  <RegEx
    Condition="Exists(@(WebPage))"
    Path="@(WebPage)"
    RegularExpression="#VERSION#"
    NewValue="$(MaxVersion)"
    Force="true"/>
</Target>

Next, I used a couple of custom tasks I created.  The first was to get the framework version.  I couldn’t figure out a way to do this.  Basically this just returns the .net framework path so I can call Mage.exe.  The second one takes the name of the app and the version to create the manifest folder.  Then it does the heavy lifting to create the ClickOnce application.  We have to do each step to make this work.  There is one thing you will probably noticed is that the setup.exe is renamed to CoolAppSetup.exe.  This was done because there was a policy that users couldn’t run setup.exe. I left out the copying of the files to the staging location before running all of this below.  I had to copy the files individually because this build script also built the WCF service.  You will need to add the appropriate process to copy these.

  1. Generate the application manifest
  2. Sign the application manifest
  3. Rename the source files to .deploy
  4. Generate the deployment manifest
  5. Sign the application manifest (one more time)
  6. Create the bootstrapper

<Target Name="AfterEndToEndIteration">
  <GetFrameworkPath>
    <Output TaskParameter="FrameworkPath" PropertyName="FrameworkPath" />
  </GetFrameworkPath>
  <CreateManifestName ExecutableName="MyCompany.CoolApp" ExecutableVersion="$(MaxVersion)">
    <Output TaskParameter="ManifestName" PropertyName="ManifestName" />
  </CreateManifestName>
  <PropertyGroup>
    <ClickOnceApplicationUrl>$(ClickOnceUrl)$(ClickOnceAppName).application</ClickOnceApplicationUrl>
    <PublishDir>$(PublishUrl)</PublishDir>
    <AppPublishDir>$(PublishDir)Application Files\$(ManifestName)</AppPublishDir>
    <SdkPath>$(FrameworkPath)\</SdkPath>
    <VersionNumber>$(MaxVersion)</VersionNumber>
  </PropertyGroup>

  <Message Text="FrameworkPath = $(FrameworkPath)" />
  <BuildStep
    TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
    BuildUri="$(BuildUri)"
    Message="Building $(ClickOnceAppName) ClickOnce version: $(VersionNumber)">
    <Output TaskParameter="Id" PropertyName="StepId" />
  </BuildStep>

  <!--
************************************************
Generate application manifest
************************************************
-->
  <Exec
  Command="mage.exe -New Application -TrustLevel FullTrust -ToFile &quot;$(AppPublishDir)\$(ClickOnceExeFile).manifest&quot; -Name &quot;$(ClickOnceAppName)&quot; -Version &quot;$(VersionNumber)&quot; -FromDirectory &quot;$(AppPublishDir)"
  WorkingDirectory="$(SdkPath)"/>

  <!--
************************************************
Sign application manifest
************************************************
-->
  <!--<Exec Condition="'$(SigningCertPassword)'==''"
    Command="mage.exe -Sign &quot;$(AppPublishDir)\$(ClickOnceExeFile).manifest&quot; -CertFile &quot;$(SigningCert)&quot;"
    WorkingDirectory="$(SdkPath)"  />-->

  <Exec Condition="'$(SigningCertPassword)'!=''"
      Command="mage.exe -Sign &quot;$(AppPublishDir)\$(ClickOnceExeFile).manifest&quot; -CertFile &quot;$(SigningCert)&quot; -Password &quot;$(SigningCertPassword)&quot;"
      WorkingDirectory="$(SdkPath)"/>

  <!--
************************************************
Rename source files to .deploy
************************************************
-->
  <ItemGroup>
    <SourceFilesToRename Include="$(AppPublishDir)\**\*.*"
                         Exclude="$(AppPublishDir)\*.manifest;$(AppPublishDir)\*.htm"/>
    <SourceFilesToDelete Include="$(AppPublishDir)\**\*.*"
                         Exclude="$(AppPublishDir)\*.application;$(AppPublishDir)\*.manifest;$(AppPublishDir)\*.htm"/>
  </ItemGroup>

  <Copy
      SourceFiles="@(SourceFilesToRename)"
      DestinationFiles="@(SourceFilesToRename->'$(AppPublishDir)\%(RecursiveDir)%(Filename)%(Extension).deploy')"
      />

  <Delete Files="@(SourceFilesToDelete)"/>

  <!--
************************************************
Generate deployment manifest
************************************************
-->
  <CreateItem Include="$(AppPublishDir)\$(ClickOnceExeFile).manifest" AdditionalMetadata="TargetPath=Application Files\$(ManifestName)\$(ClickOnceExeFile).manifest">
    <Output TaskParameter="Include" ItemName="ApplicationManifest"/>
  </CreateItem>

  <Message Text="@(ApplicationManifest)" />

  <GenerateDeploymentManifest
    MapFileExtensions="true"
    AssemblyName="$(ClickOnceAppName).application"
    AssemblyVersion="$(VersionNumber)"
    Description="$(ClickOnceDescription)"
    Product="$(ClickOnceProduct)"
    Publisher="$(Company)"
    SupportUrl="$(SupportUrl)"
    EntryPoint="@(ApplicationManifest)"
    Install="false"
    UpdateEnabled="true"
    UpdateInterval="7"
    UpdateMode="Foreground"
    OutputManifest="$(PublishDir)\$(ClickOnceAppName).application"/>

  <!--
************************************************
Sign application manifest
************************************************
-->
  <!--<Exec Condition="'$(SigningCertPassword)'==''"
      Command="mage.exe -Sign &quot;$(PublishDir)\$(ClickOnceAppName).application&quot; -CertFile &quot;$(SigningCert)&quot;"
      WorkingDirectory="$(SdkPath)"/>-->
  <Exec Condition="'$(SigningCertPassword)'!=''"
      Command="mage.exe -Sign &quot;$(PublishDir)\$(ClickOnceAppName).application&quot; -CertFile &quot;$(SigningCert)&quot; -Password &quot;$(SigningCertPassword)&quot;"
      WorkingDirectory="$(SdkPath)"/>

  <!--
************************************************
Generate Bootstrapper
************************************************
-->
  <ItemGroup>
    <BootstrapperFile Include="Microsoft.Net.Framework.3.5">
      <ProductName>Microsoft .NET Framework 3.5</ProductName>
    </BootstrapperFile>
    <BootstrapperFile Include="Microsoft.Windows.Installer.3.1">
      <ProductName>Windows Installer 3.1</ProductName>
    </BootstrapperFile>
  </ItemGroup>

  <GenerateBootstrapper
    ApplicationFile="$(ClickOnceAppName).application"
    ApplicationName="$(ClickOnceAppName)"
    ApplicationUrl="$(ClickOnceUrl)"
    BootstrapperItems="@(BootstrapperFile)"
    Culture="en"
    FallbackCulture="en-US"
    CopyComponents="true"
    Validate="false"
    OutputPath="$(PublishDir)"/>

  <Copy SourceFiles="$(PublishDir)\Setup.exe" DestinationFiles="$(PublishDir)\CoolAppSetup.exe" />
  <Delete Files="$(PublishDir)\Setup.exe" />

  <BuildStep
    TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
    BuildUri="$(BuildUri)"
    Id="$(StepId)"
    Status="Succeeded"/>

  <OnError ExecuteTargets="MarkBuildStepAsFailed" />
</Target>

<!--
************************************************
Mark the buildstep as failed
************************************************
-->
<Target Name="MarkBuildStepAsFailed">
  <BuildStep
    TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
    BuildUri="$(BuildUri)"
    Id="$(StepId)"
    Status="Failed"/>
</Target>

The Deploy Builds are simply a Copy task to copy the files to the web server.  Do this by creating a share to the same location so you can use a UNC.  The production stage build is basically all of the above again but with the production certificate key and copy the new config file(s) to the production stage location.

Looking back on this it seems complex but this is what the publish wizard is doing behind the scenes and offers complete customizing to fit your needs.  I sliced, diced, and renamed items in the scripts above.  I tried to make sure everything is correct.  If there is a typo or something missing, please let me know and will update it.

Enjoy!

Mike

Thursday, March 04, 2010 11:20:00 AM (Central Standard Time, UTC-06:00)  #    Comments [3] -
ClickOnce | Team Build | Team Foundation Server

# Saturday, December 12, 2009

There is a lot of information about the major updates of TFS 2010 and Team Build 2010 including changing from MSBuild to Workflow and Gated Check-Ins to name a couple.  In using TFS and Team Build 2010 beta 2, there are a lot little features and improvements that help make these two products complete and polished.  Here are a few of the features and I keep discovering new ones each time I use it.

 

New Build Definition will default name and solution to build

if you have a solution open in Visual Studio 2010 when you create a new build definition, the build name will default to the solution name.

image

 

An open solution will also automatically be populated as the Project to Build

image

 

The build retention policy is not set to “Keep All” by default.

Finally, the default retention policy for the builds is not set to “Keep All” anymore.  Primarily all results will default to keep the last 10 builds.  In Visual Studio 2008, I always recommended that this should be changed.

image

 

TFS Build notifies you about successful and failed builds

The Team Foundation Build Notification tool used to be part of the power tools.  Now it is included with the standard installation and alerts you to the success or failure of the build.   This supports continuous integration and gated check-in builds.  The notification dialog window also has an option for unshelving failed gated check-ins.

This dialog displays for a successful Gated Check-in build

GatedCheckinResults

 

This dialog is display when a Gated Check-In fails.  Notice the Unshelve Changes option to retrieve the changeset that was be attempted to be checked-in.

FailedGatedCheckin

Build Parameters are now strongly typed and visible

In Team Build 2008, parameters could be passed in to a build when it was being queued.   However the format was command line argument style passed into a textbox similar to this:

/p:IsThisCool=”false”

In Team Build 2010, the build parameters are displayed as strongly typed properties.   This will allow for type checking and eliminate the misspelling of parameters.

image

 

Enjoy all of the great new features in Team Build 2010 including these smaller but helpful features!

Mike

Saturday, December 12, 2009 6:10:00 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Team Build | Team Build 2010 | Team Foundation Server | TFS 2010

# Saturday, September 26, 2009

In Part 1: The Deployment Process Should Enforce Good Configuration Management Practices, I gave some background on my experiences and how the configuration management process has evolved and some rules and benefits to the automated deployment MSIs.

In Part 2: How to create an automated deployment MSI, I walked through the steps to create an automated deployment MSI in Visual Studio satisfying the rules from Part 1.

In Part 3, I am going to walk through the steps to install Team Deploy.  Then walk through creating a team build, configure Team Deploy, and deploy a MSI with it. 

There is also a great Screencast by Ian Ceicys walking through the entire process of installing TFS, WIX, Team Deploy and deploying a MSI.  I highly recommend watching this.

Installing PS Tools

  • Team Deploy uses the free PSExec and PSKill utilities by Sysinternals (owned by Microsoft).  PSExec allows you to remotely run any command and PSKill can kill any process on a local or remote machine.
  • Download PSTools at http://technet.microsoft.com/en-us/sysinternals/bb896649.aspx and install to a local folder.  I also recommend renaming psexec and pskill to something like psexec2.exe.  Some anti-virus software sees these files as a risk.

 

Installing Team Deploy

  • Browse to the Team Deploy website on CodePlex at http://TeamDeploy.CodePlex.com
  • Click on the Downloads and download the TeamDeploy.MSI.
  • Run the MSI to install Team Deploy to c:\Program Files\MSBuild\TeamDeploy
  • The windows service account that runs Team Build on the build server will need to be a local administrator on all target machines.

 

Creating a Team Deploy TFS Build

  • In Team Explorer, create a new build in your Team Project by right clicking on Builds and choosing “New Build Definition”

New Build Definition

 

  • Give it a name such as “Build and Deploy”
  • Create a workspace (Cloak other folders in your project that don’t need to be part of the build. This helps the speed up your build because otherwise the server will try to get all source files in the project)
  • Leave Project File name as is but click on the “Create” button to create a new TFSBuild.proj file.
  • Next choose the solution you want to build, then the configuration type. I recommend leaving the default.
  • Choose any options that you want to enable for running tests and/or code analysis. I would recommend leaving these unchecked for now and once you verify everything is working then go back and enable the options you want.
  • In TFS 2008, you can choose retention policies. This will help prevent builds from filling up your server disk space quickly. I usually choose Keep 7 latest.
  • the next options are for the Build Defaults. Choose the appropriate build agent. If unsure, just leave the default. Then choose the share where you want to copy the staging files.
  • The last option is “Trigger”. The "build and deploy" build should be separate from the continuous integration build. I recommend leaving the default “Check-ins do not trigger a new build”.

 

Build Trigger

 

  • Finally click “OK” to create the build type.

Creating the build definition will create the TFSBuild.proj that contains the basic options that were selected in the wizard. The following steps will customize the TFSBuild.proj file created. This file is a Xml file based on MSBuild.
  • To modify the TFSBuild.proj file, located the file under Source Control -> $/YourTeamProject/TeamBuildTypes/Build and Deploy (You can also navigate directly to this file by right clicking on the build definition and choosing "View Configuration Folder".)
  • Check out and open the TFSBuild.proj file to configure it to use Team Deploy
  • Find the comment <!—Do not edit this -> and add the following line underneath the one that is already there. It should look something like the screenshot below

<Import Project="$(MSBuildExtensionsPath)\TeamDeploy\TeamDeploy.Tasks.targets" />

AddTeamDeployProject.jpg

 

Scroll to the bottom of the TFSBuild.proj file to the <PropertyGroup>. Overwrite the Property group with the following (Adjust the paths for your specific environment):

<PropertyGroup>
    <KillAppPathFilename>c:\Program Files\PSTools\pskill2.exe</KillAppPathFilename>
    <RemoteExecutePathFilename>c:\Program Files\PStools\psexec2.exe</RemoteExecutePathFilename>
  </PropertyGroup>

  <!-- Deploy MSI  -->
  <Target Name="AfterEndToEndIteration">
    <CallTarget Condition="'$(IsDesktopBuild)'!='true'" Targets="DeployMSITargetVirtuals" />
  </Target>

  <Target Name="DeployMSITargetVirtuals">
    <Deploy DeployScript="$(SolutionRoot)\..\..\Push Scripts\SampleDeploy.xml"
            KillAppPathFilename="$(KillAppPathFilename)"
            RemoteExecutePathFilename="$(RemoteExecutePathFilename)"
            TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
            BuildUri="$(BuildUri)" />
  </Target>

The copied Xml in the TFSBuild.proj should look something like this

FinishedTFSBuild.jpg

  • Save and check-in the TFSBuild.proj file


The Deploy task uses a Xml Deploy Script that contains the information of what Msi(s) to deploy, additional tasks such as starting and stopping the service, and specifies the target machines. The following steps walks you through editing this file.

DeployScript.jpg

 

There are a few things to note here.

  • Team Deploy can
    • Kill 0 to many processes
    • Deploy/Uninstall 0 to many MSIs
    • Deploy MSIs to 0 to many Target Machines
  • Modify the xml appropriately for your environment. The GUID for the uninstalls are the MSI product codes. If the MSI is done correctly it will uninstall previous versions using the upgrade code, however they are usually unable to remove the same version of the MSI, this is why we have the separate step of uninstall.
  • The ExtraArgs element contains the SETUPENV variable that is used by the MSI’s custom action to copy the correct environment’s config file to the project’s. The custom action is a simple VBScript that copies the config file (not included with Team Deploy). See Part 2 for more details on creating the config files folder structure.

 

Once the TFSBuild.proj is checked in and the Deploy script is saved (or also checked in), then you can right click on the build and do a queue new build.   The MSI will be deployed to your target machine(s).

See the Troubleshooting / FAQ section of Team Deploy or contact me if you have any questions or problems.

Saturday, September 26, 2009 1:16:00 PM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
Team Build | Team Deploy | Team Foundation Server

# Wednesday, August 26, 2009

Team Deploy is a free set of custom Team Build tasks for deploying MSIs to client PCs and servers.  Team Deploy 2.1 has been released and includes a number of fixes and a couple new features.

A special thanks to Jeremy Novak for creating the new CleanupPsExec task and multiple other fixes.  Here is the list of the changes:

  • Moved Guidance and Installation from Word document to wiki site (and created a Troubleshooting section)
  • Added -accepteula to all pstools calls so it won’t display the EULA dialog the first time it runs
  • Added new CleanupPsExec task and test to clean up the PSTools service if it becomes stuck.
  • Fixed spelling error in RemoteExecute task
  • Added support to uninstalling 32bit apps on Windows 2008 64bit servers
  • Changed property declarations to be auto-implemented
  • Updated task required properties to have the Required attribute
  • Created Team Deploy Installation Screencast
  • Other misc fixes.

Team Deploy 2.1 can be found on CodePlex at http://teamdeploy.codeplex.com/.

-Mike

Wednesday, August 26, 2009 2:49:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
Team Build | Team Deploy | Team Foundation Server

# Thursday, July 30, 2009

Note:  I slightly changed the title from TFS Deployments to Deployments with TFS in case there was any confusion about whether this is about deploying TFS or doing deployments with TFS.

In Part 1: The Deployment Process Should Enforce Good Configuration Management Practices, I gave some background on my experiences and how the configuration management process has evolved and some rules and benefits to the automated deployment MSIs.

In this Part, I will walk through the steps to create an automated deployment MSI in Visual Studio 2008 satisfying the rules from Part 1.    In this example I will build the MSI for a windows service that will auto assign the username and password.

The first few steps are standard steps for building the MSI.  Then the steps get more interesting when it starts getting into to the automated deployment settings.

Step 1:  Add the Setup Project to existing solution

To add the setup project to the solution, choose “Add New Project” > Other Project Types > Setup Project.

image

 

Step 2:  Add Project Output to MSI

Add the required files for the deployment by adding the project output of the primary project.  To the project output, right click on the setup project in the solution explorer  > View > Project Output.  A dialog box similar to the one below will appear.  Verify that the windows service project is the selected project in the combo box.  Choose Primary Output and click OK.

image

 

Step 3:  Add Custom Actions

Next add the Primary output as a Custom action for the install, uninstall, etc.  To do this, right click on the Custom Actions > Add Custom Action > Application Folder > Primary Output from <your project>.

image

Step 4:  Add Installer to Windows Service project

In the Windows Service project, add an Installer class by double clicking on the Service component to view the designer.  In the Properties window (usually) at bottom right corner, there will be a link to “Add Installer” similar to the image below.  This creates the project installer class containing the service installer and the process installer.  We will be modifying these in some later steps.

image

Step 5:  Create the environment config files and folders in the windows service and add them to the MSI.

If the solution had multiple projects, I usually create a separate project for the config files.  Here I will create the folders in the same project. Create the folders and files as shown below.  Basically environment’s config file will be stored here and checked into source control.  To add these to the MSI, go to the properties of each config file and change the Build Action to Content.  Now go back to the MSI, right click on the project > Add > Project Output > Content Files to add these.

image

Step 6:  Create Custom Action to copy the selected config file to the application folder

Now that environment configs are in the ConfigFiles folder, the selected one needs to be copied to Application folder.  To do this we need to create a custom action.  This is in VBScript but eventually I want to create these custom actions as a helper class in Team Deploy.  To create the custom task, the actual file needs to be created outside the MSI.  I have created a CustomActions folder in the windows service project.   In there I added a copyconfig.vbs file.  This custom action is going to need two values passed into it.  One is the environment and the other is TargetDir.  I couldn’t figure out a way to get current folder inside for the vbscript so I found it easier to just pass it in.  To pass in values into the custom action the name/value pairs are passed into the CustomActionData property.  Also since this is going to overwrite the current app.config files, exclude the app.config by clicking on the Primary Output > Exclude > Add for *.config.

Here is the code for the copyconfig.vbs file.  After you copy and paste this into the vbs file.  Save it and in the properties, change the Build Action to None.   We do not want this copied to the MSI output like the config files. 

on error resume next
dim paramsList
paramsList = split(session.property("CustomActionData"), ",")
dim param1
dim param2

param1 = paramsList(0)
param2 = paramsList(1)

dim filesys
dim path

path = param2 & "configfiles\" & param1 & "\"

dim strEvn, objFile, strLogFile

Set objShell = CreateObject("Wscript.Shell")
strEnv = objShell.ExpandEnvironmentStrings("%temp%")
strLogFile = strEnv & "\MSIInstallLog.txt"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(strLogFile, 8, True)

objFSO.CopyFile path & "*.config", param2, true

if err.number > 0 then
    objFile.WriteLine Now & "::CopyConfigs - " & param1 & err.Description
else
    objFile.WriteLine Now & "::CopyConfigs - Completed Successfully."
end if

objFile.Close

Now add the Custom Action to the MSI by right clicking on the project > View > Custom Actions.  We only want this to run during the installation process, so right click on Install > Add Custom Action > Application Folder > Add File > Browse to Copyconfig.vbs and click OK.  Notice in the setup project that the copyconfig.vbs has the no sign icon on it so it isn’t copied to the application folder.

Next we need to pass in the the values into the Custom Action.  Click on the Copyconfig.vbs in the Custom Actions view.  In the properties window, set CustomActionData = [ENV],[TARGETDIR]. 

Repeat the steps above for AssignService.vbs.   Set CustomActionData = [USR],[PWD],"SampleService"

on error resume next

dim param

param = split(session.property("CustomActionData"), ",")

if len(param(0)) > 0 then

    dim strEvn, objFile, strLogFile

    Set objShell = CreateObject("Wscript.Shell")
    strEnv = objShell.ExpandEnvironmentStrings("%temp%")
    strLogFile = strEnv & "\MSIInstallLog.txt"
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objFile = objFSO.OpenTextFile(strLogFile, 8, True)
    ' ------ SCRIPT CONFIGURATION ------
    strUser     = param(0)
    strPassword = param(1)      
    strSvcName  = cstr(replace(param(2), chr(34), ""))
    strComputer = "."    
    ' ------ END CONFIGURATION ---------
    set objWMI = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
    set objService = objWMI.Get("Win32_Service.Name='" & strSvcName & "'")
    intRC = objService.Change(,,,,,,strUser,strPassword)
    if intRC > 0 then
       objFile.WriteLine Now & "::" & strSvcName & " - " & "Error setting service account: " & intRC
    else
       objFile.WriteLine Now & "::" & strSvcName & " - " & "Successfully set service account"
    end if
    if err.number > 0 then
        objFile.WriteLine Now & "::" & strSvcName & " - " & err.Description
    end if
    objFile.Close

end if

This custom action finds the “SampleService” and changes the username and password.  I had to use the service name literal.  There wasn’t anything I could find where I could use a dynamic value.

Step 7:  Modify Project Installer to handle interactive and silent modes

One of the requirements is that the MSI needs to work when using the wizard and when specifying parameters when installing from the command line or remote utility.  Therefore when the user is installing the MSI through the wizard (we will create this in the next step)   it needs to prompt for the user and password.  This is accomplished by setting the service account of the installer to LocalSystem so it won’t prompt but if the username isn’t passed in it switches the service account to User.  This will then prompt the user.

To implement this, add the following code to the ProjectInstaller.

private string GetContextParameter(string key)
{
    string sValue = "";

    try
    {
        sValue = this.Context.Parameters[key].ToString();

    }
    catch
    {
        sValue = "";
    }

    return sValue;
}       

// Override the 'OnBeforeInstall' method.       
protected override void OnBeforeInstall(IDictionary savedState)
{
    try
    {
    base.OnBeforeInstall(savedState);
    string username = GetContextParameter("usr").Trim();

        if (username == "")
        {
            serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.User;
        }
    }
    catch(Exception e)
    {

  }
}

private string getvalues()
{
    string all = "";

    foreach (DictionaryEntry value in this.Context.Parameters)
    {
        all += value.Value.ToString() + ",";
    }
    return all;
}

Step 8:  Add step to wizard to specify the Environment.

In addition to prompting the user for the user and password, the MSI needs to prompt the user for the environment.  To add a form to the Wizard.  Right click on the setup project > View > User Interface.  Then right click on Start (as shown) and the click Add Dialog.  In this example we have 3 environment, so choose the RadioButtons (3 Buttons) and press Ok.

image

The Dialog window appears below the Confirm Installation box.  If you build the setup project you will notice the warning, “All custom dialogs must precede the 'Installation Folder' dialog”.  Therefore, click on the up arrow twice to move the RadioButton dialog window  above the Installation folder.  Now click on the dialog and view the Properties window.  There are several empty properties we need to set.  Set the following to something similar to these values:

  • BannerText = “Choose Environment”
  • BodyText = “Please select an environment from the list.”
  • Button1Label = “Production”
  • Button1Value = “Prod”
  • Button2Label = “Test”
  • Button2Value “Test”
  • Button3Labal = “Development”
  • Button3Value = “Dev”
  • ButtonProperty = “Env”
  • DefaultValue = “Prod”

 

Step 9:  Change build type to loose uncompressed files

The last step is to change the Package Files option under the setup project’s properties from “In setup file” to “As loose uncompressed files.  You will need to do this for each configuration in the project.

image

 

These are all of the steps.  I hope it helps.  Attached below is the sample solution with the setup project hosted on the MSDN Code Gallery.  You may use this however you would like.  Now that you know how to build the MSI, it is time to deploy it.  I will walk through that in the next post.

Sample Automated Deployment Solution for building a Windows Service MSI (22k)

The other future parts in the series will include:

  1. Building and Deploying ClickOnce applications
  2. Deploying a web application.
  3. Hopefully more!  Let me know what you would like to see.

 

Mike

Thursday, July 30, 2009 5:14:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [1] -
Team Build | Team Deploy | Team Foundation Server

# Tuesday, July 14, 2009

Build automation and more specifically deployments are one of my favorite things to do with Team Build in Team Foundation Server 2008.  Deployments are not an “out of the box” feature but there are several ways to accomplish this.  I am going to cover some of these different approaches and strategies for doing deployments with TFS.  Before I get into doing the actual deployments, I want to give a some background and guidance behind why creating automated deployments are part of a good software configuration management (SCM) process. Then I will go into creating a MSI that supports automated deployments.   The subsequent posts will cover a variety of topics including deploying the MSIs with Team Deploy and deploying Click Once applications.  Lastly I will explore what automated deployments will look like in TFS 2010.  As always, if you have any questions or if there is something you want me to see, send me an email, tweet, or comment.

This deployment process has been an evolution of the past 3 years.  I began getting engaged in this process when I used to be part of a process where the development team created a MSI for test and a couple weeks before production our PC configuration team would create a new MSI with their tools.  We would do some testing of this MSI over those couple weeks before going to production.  Unfortunately this process led to a few mistakes, mostly due to the fact we were changing too much too late in the game.  We began to think of an improved process with better configuration management in mind.  We created a few rules to that we wanted our new process to follow. 

Rules for MSIs

  • Create the MSI early in the development process – As early as a development team has most of the projects for the deployment created, compile the assemblies and create the MSI.  For Agile teams this can be as early as Iteration 1.
  • Create a MSI that is easily updated – MSBuild and Team Build by default do not support building setup projects.   There are some workarounds to being able to build a MSI, but I prefer creating the MSI and choose “As  loose, uncompressed files” for the build in the setup project properties.   This will leave the files out of the MSI and basically add pointers to the files in the MSI.  This makes updating the MSI as easy as using the copy task.  If a new project is added to the solution, add the new dependency, rebuild the MSI, and add the new assembly to the copy process (more on this later)
  • Config files – This could probably be a post by itself.  Basically there are a couple rules I have about config files. 
    • The config files for all of the environments must be checked in to source control.  Usually I will create a library project in the solution and add all of the config files to it.
    • Along with all of the files being in source control, there can be no editing or copying of config files once the application is deployed.  For too long I would always copy the config file from the installed location.  Then I would do the uninstall, reinstall, and then copy/modify the config file based on the one I copied to another file.   I did this because I didn’t know if there were changes or particular production settings that were not in the MSI.  
    • Lastly the MSI should contain all of the config files for all of the environments.  These can be removed after the installation process is done but the key is that the config files are not copied from another location.  This will become more clear when I start to walk through the process.
  • Because the MSI contains the config files for multiple environments, the user must be able to select the appropriate environment to install.  The MSI should allow the the user to specify the environment in the wizard or through a command line argument when installing it in quiet mode.
  • If the MSI is a windows service, the user must also be able to specify the username and password through the wizard or command line.

 

By following these rules, it provides the following enhancements

  • Tested and Proven MSI – Just like we shouldn’t deploy code that we haven’t tested, the same is true for the deployment process.  By using the same MSI in the Test environment, throughout the development cycle, and in production, this will help ensure that that are no surprises when deploying to production.
  • Ready for Automated Deployments – The MSI will include key features that are required for use in automated deployments.   These include the config files for multiple environments, easily updateable from Team Build, and supports passing environment and username/password by command line.

In the next part, I will go over the steps and processes for creating a MSI to support these rules and help enforce your configuration management process.  I will demonstrate how to do all of this within Visual Studio without using any 3rd party tools.

Mike

Tuesday, July 14, 2009 5:13:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [1] -
Team Build | Team Foundation Server

# Thursday, July 02, 2009

In many of my projects I will create a new release branch when our code is ready for production.  This gives me a snapshot of the code when it went to production.  This allows us to make any bug fixes in the production code base but continue adding new features to dev and test branches.  This has always been a manual step to create the branch until i was talking to a client and suggested that I could automate this process.    I hadn’t done a branch from within Team Build but I knew it could be done via the command line so I figured we could automate it.    I used the Exec task to create the branch like this:

<Exec WorkingDirectory="$(SolutionRoot)"
       Command="$(TF) branch $/MyTeamProject/Main/ $/MyTeamProject/Releases/2.1.0.0 /noget"

Next the build needed to check in the branch:

<Exec WorkingDirectory="$(SolutionRoot)"
  Command="$(TF) checkin /comment:&quot;***NO_CI***New Release Created&quot; /noprompt
  /override:&quot;New Release Created&quot; /recursive"
  IgnoreExitCode="true" />

This was the easy part.  One of the challenging parts was the workspace mappings.  Even though I could get this to work by creating a mapping at the root of the team project, I didn’t want to pull down all of the branches under source control.   If I didn’t have the mappings at a high enough level to cover the target, the build will generate an error like this:

No appropriate mapping exists for $/MyTeamProject/Releases/2.1.0.0

Since with each release this problem would compound, I wanted to only pull down the necessary source control.  To solve this problem I created a custom task that takes a parent folder, in this case $/MyTeamProject/Releases/ and either cloaks all of the subfolders to only add a new row, or it will cloak all of the folders except for the one specified.   The second option is beneficial if the source folder for the branch is dynamic also.  This will also create a dynamic mapping between the WorkspaceServerItem and WorkspaceLocalItem.

The task is called CreateDynamicMapping and here is the call:

<CreateDynamicMapping
  TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
  BuildUri="$(BuildUri)"
  WorkspaceServerItem="$/MyTeamProject/Releases/"
  WorkspaceLocalItem="$(SolutionRoot)\Releases"
  WorkspaceName="$(WorkspaceName)"
  WorkspaceOwner="$(WorkspaceOwner)"
  ParentFolder="$/MyTeamProject/Releases/"
</CreateDynamicMapping>

This example uses the ParentFolder to get all of the child folders, loops through each of them and if it finds a match to the WorkspaceServerItem (It won’t in this example because it isn’t a child folder of the ParentFolder) it will keep that folder, otherwise it cloaks the folder so it is not pulled down.

Creating this task was my first opportunity to use the TFS API.  With the help of some great posts by Shai Raiten, this ended up straight forward and even wrapped some unit tests around make sure it worked.  Here’s the main part of the code for the task:

TeamFoundationServer tfs = new TeamFoundationServer(teamFoundationServerUrl);

// Get a reference to Version Control.
VersionControlServer versionControl = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));

Workspace[] workspaces = versionControl.QueryWorkspaces(workspaceName, workspaceOwner, null);

if (!parentFolder.Contains("*.*"))
{
    parentFolder += "/*.*";
    parentFolder = parentFolder.Replace("//", "/");
}
RecursionType recursion = RecursionType.OneLevel;
ItemSet itemSet = versionControl.GetItems(parentFolder, recursion);
foreach (Item item in itemSet.Items)
{
    if (workspaceServerItem.ToUpper() != item.ServerItem.ToUpper())
    {
        workspaces[0].Cloak(item.ServerItem);
    }
}

workspaces[0].CreateMapping(new WorkingFolder(workspaceServerItem, workspaceLocalItem));

This is what you need to automate the branching process from with a TFS Build.   I didn’t talk about how to get your version number to branch to.  This will depend on your projects requirements.  You may want to use get the assembly version of one of the assemblies you compiled to use this as the folder or in this instance the source folder was dynamic too.

Mike

Thursday, July 02, 2009 5:01:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
Team Build | Team Foundation Server

# Wednesday, May 20, 2009

Last week I added a MSI for installing Team Deploy.   I am excited about this because this simplifies the installation process.  Simply run the MSI and it installs all of the components for Team Deploy.  Team Deploy still requires the separate download and installation of PSTools.   Also, if you have an older version of PSTools, download the latest version to help ensure it works with the latest version of Team Deploy.

Here is the listing of installed files with the MSI.

image

I have also included a “real world” type deploy script and TFSBuild.proj file based on some great feedback I have received.  These should show some good examples of how to use it.

As always you can find Team Deploy at http://teamdeploy.codeplex.com.

Mike

Wednesday, May 20, 2009 3:53:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [2] -
Team Build | Team Foundation Server

# Wednesday, April 22, 2009

Today I am excited to announce the release of Team Deploy 2.0.  Additionally, the total download count of Team Deploy has passed 1000 downloads!  

Team Deploy is an open source package of custom build tasks designed for Team Foundation Server 2008 for deploying MSIs to clients and/or servers.  There is also a build notification task for lava lamps or street lights with your builds.

The enhancements and fixes for 2.0 include:

  • Added TaskBase class for common functionality for example,BuildUri and TeamFoundationServerURL. These properties are now optional. It will basically skip the build step display if these properties are not set. All tasks inherit TaskBase.
  • Added check to "StopService" so it only does it if the service exists and it "canstop". (bug)
  • Added new RemoteExecute task. Now call any executable on target machines. This also has a WaitForExit property to be able to wait for the response or not from the target machine.
  • Added setup MSI to install Team Deploy instead of manual steps.
  • Added IgnoreExitCode attribute to the base class. If the task checks for exit code it now will log it as an error if the code > 0 unless IgnoreExitCode == true.
  • Fixed a problem where with the new version of pstools, it wouldn't work unless session zero is specified.
  • Updated the deploy task to ignoreexitcode when calling the Kill task
  • Added logic to stopservice to not try to stop if it isn't installed or started.

    Future updates to Team Deploy will include breaking out the build light notification portion to a new CodePlex project I created called TFSBuildLight.  Also 2.x versions will continue to be for Team Foundation Server 2008.  New 3.x versions of Team Deploy will be developed for Team Foundation Server 2010.

     

    Team Deploy is a free to download and use.  You can find it at http://teamdeploy.codeplex.com.

  • Wednesday, April 22, 2009 4:28:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [5] -
    Team Build | Team Foundation Server

    # Saturday, March 28, 2009

    I created a nightly build to run my data driven unit tests.   When my build finished it was only partially successfully.   I looked at the unit tests and they were all passing yet there was an error on the testing step.  When I looked in the log below is the message I saw.   I thought this was a little confusing because there was only a warning.  I wasn’t sure what caused the TestToolsTask to fail.  I first thought it was a permission or some other issue when it was trying to publish but the results did publish.   Next I looked at the warning VSP1030 message.   Then I remembered I enabled code coverage but the build configuration was set to Release.  I changed the build to Debug and everything worked!   Before I figured out the issue I tried searching the web but I didn’t find any solutions.   I hope this helps.

    Summary
      -------
      Test Run Warning.
        Passed  6
        ---------
        Total   6
      Results file:      t:\builds\MyTeamProject\Nightly Build\TestResults\builduser_MYSERVER_2009-03-25 23_56_46_Any CPU_Release.trx
      Run Configuration: Local Test Run
      Run has the following issue(s):
      Instrumentation error while processing file BusinessObjects.dll:
    TESTTOOLSTASK : warning VSP1030: Invalid, mismatched, or no PDB file was found for t:\builds\MyTeamProject\Nightly Build\Binaries\Release\BusinessObjects.dll.
      The previous error was converted to a warning because the task was called with ContinueOnError=true.
      Waiting to publish...
      Publishing results of test run builduser@MYSERVER 2009-03-25 23:56:46_Any CPU_Release to http://tfsserver:8080/Build/v1.0/PublishTestResultsBuildService2.asmx...
      ....Publish completed successfully.
      The command exited with code -1.
      Build continuing because "ContinueOnError" on the task "TestToolsTask" is set to "true".
    Done executing task "TestToolsTask" -- FAILED.

     

    Mike

    Saturday, March 28, 2009 2:23:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
    Team Build | Team Foundation Server | Unit Testing

    # Saturday, December 20, 2008

    Doing builds and running unit tests after every check-in has become a standard practice in Agile and non-Agile development teams.  Continuous Integration is the term that describes this process and helps ensure that that nothing has been broken since the last check-in.    One area that is sometimes overlooked is the code generation.    Continuous Generation, the re-generating of code / stored procedures for every check-in and build, should be considered for your TFS builds.  I believe there is a lot of benefit to generating the code with every build.  There are several benefits:

    • Table schema and stored procedure parameters can change.  How do you know that your generated objects are up to date and match the database?
    • It keeps everyone honest so they don’t break the golden rule of never editing generated code.  If the build is going to re-generate the code there’s no way to sneak the semicolon or curly bracket that you manually fix each time after generation.
    • Sometimes we generate just the class we are working on and keep the rest checked in so it doesn’t overwrite the current object.  If any changes are made to the template only this new class will have them.  The other classes will be based on the older version.
    • It gives more credibility to the generation process.  There is sometimes a feeling of nervousness when generating all of the objects.  Since generated code is never edited, you can generate the code as often as you wish.

    CodeSmith Tools is the code generation tool that we use.  It offers an easy way to accomplish this from your Team Foundation Builds.   With version 3.2 and newer there is a custom build task included with the Professional edition.  Here is the online help with some information about it.

    There are a couple things to consider.  The generated classes will be checked-in to source control.  You will need to check them out before you call the task and then check them back in afterwards. Also, the user guide instructions do not work quite right in the link above.  The guide explains how to use it within your visual studio project.  However, the task needs to be called from Team Build.  Here’s the basic steps and changes for your CI tfsbuild.proj file.

    Step 1: Install CodeSmith Professional on your TFS build server.  This will install the MSBuild task and Targets file.   I contacted CodeSmith Tool’s sales department and this does require an additional license for the server.

    Step 2: Import the CodeSmith Targets file.  Add the following line just below the import element below the <!-- Do not edit this --> comment.

    <Import Project="$(MSBuildExtensionsPath)\CodeSmith\CodeSmith.targets" />

    Step 3:  Add TF property in the PropertyGroup element specifying the tf.exe location to be used throughout the process.

    <PropertyGroup>
      <
    TF>&quot;$(TeamBuildRefPath)\..\tf.exe&quot;</TF>
    </
    PropertyGroup>

    Step 4:  Override the AfterGet target to check out the generated file(s).  This must be called before the CodeSmith task or it will return an access denied error.  This example demonstrates one file but you can use a subfolder or naming prefix for the generated files and recursively check out all of the necessary files.

    <Target Name="AfterGet">

        <Exec WorkingDirectory="$(SolutionRoot)\Main\TestCodeSmithMSBuild\"

              Command="$(TF) checkout Measure.cs"  />

    Step 5:  Call the CodeSmith task to execute the CodeSmith project(s) in your solution.  This will also be called in the AfterGet target.  Currently the example in the documentation incorrectly shows the CodeSmith task using the ProjectFile attribute.  As the usage description shows, the attribute is actually ProjectFiles. 

    <CodeSmith ProjectFiles="$(SolutionRoot)\Main\TestCodeSmithMSBuild\Test.csp"  />

    Step 6:  Override the AfterCompile target to check in the updated generated files.  Fortunately TFS will only check in the file if there is a change.  This is good because most of the time re-generating the code should generate the same thing each time.  However, tf.exe returns a code of 1 instead of 0 and results in a partial success of the build.  Use the IgnoreExitCode=”true” to ignore this.  You could additionally update this to create a work item for any other return code.  Here’s a post with a good example of this.

    <Target Name="AfterCompile" Condition="'$(IsDesktopBuild)'!='true'">

        <Exec WorkingDirectory="$(SolutionRoot)\Main\TestCodeSmithMSBuild"

              IgnoreExitCode="true"

              Command="$(TF) checkin /comment:&quot;***NO_CI***Auto-Generate&quot; /noprompt /override:&quot;Auto Generate&quot; measure.cs "/>

      </Target>

    Step 7:  Undo the check out if the build fails by overriding the BeforeOnBuildBreak.

    <Target Name="BeforeOnBuildBreak" Condition="'$(IsDesktopBuild)'!='true'">

        <Exec WorkingDirectory="$(SolutionRoot)\Main\TestCodeSmithMSBuild"

              Command="$(TF) undo /noprompt measure.cs"/>

      </Target>

    That is it.  Enjoy!

    Mike

    Saturday, December 20, 2008 10:49:44 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
    Code Generation | Team Build | Team Foundation Server

    # Wednesday, November 12, 2008
    Last week I released some minor updates to Team Deploy.  When you use Team Deploy to deploy your MSIs, it will update the build steps and show that it is deploying.  The second feature will now write any non-zero return code to the build log.  Before this, these return codes were being lost.  In the next release of Team Deploy I will build upon this and allow you to pass in an XML file contain return codes.  If they are an error, warning or information.  For errors, I will make it so it will actually fail the build.

    Here's the release
    http://www.codeplex.com/teamdeploy

    Mike

    Wednesday, November 12, 2008 10:36:56 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
    Team Foundation Server | Team Build

    Visual Studio ALM MVP
    Microsoft Visual Studio ALM MVP
    Archive
    <February 2012>
    SunMonTueWedThuFriSat
    2930311234
    567891011
    12131415161718
    19202122232425
    26272829123
    45678910
    Blogroll
    About the author/Disclaimer

    Disclaimer
    The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

    © Copyright 2012
    Mike Douglas
    Sign In
    Statistics
    Total Posts: 76
    This Year: 0
    This Month: 0
    This Week: 0
    Comments: 52
    All Content © 2012, Mike Douglas
    DasBlog theme 'Business' created by Christoph De Baene (delarou)