TFS

PowerShell TFS WorkSpace Initialization Script

I needed a way to set up developer machines quick for access to a new Team Foundation Server rollout awhile back. Normally to set up a workspace on each dev’s machine I’d have to do the following:

A. Teach everyone how to set up workspaces

B. Show them how to get latest

C. Hope they don’t accidentally (or purposely) mess it up. 

 

“C” is my main problem. 80% of users mess it up the first time. I cant blame them, TFS is a paradigm shift and it takes some getting used to. Just like going from VSS to SVN, it takes some getting used to.

To mitigate the risk of going well over my estimated time for installation of the system across the teams I decided to write a PowerShell script to do it for me. However, it is, well clunky. I’ve since improved it to the version below … however, it still has is "clunkiness" to it. But the best part about it is… IT WORKS. So fee free to use it to set up your TFS environments.

 

Prereq’s to get the script to run

  • Users must have .NET 3.5 installed.
  • Users must have Visual Studio 2008 installed
  • Users must have the Team Foundation Client Installed
  • Users must have been added to the proper security groups in TFS (if you’ve set up TFS already this should be easily accomplished through AD and groups).
  • Users must have PowerShell (sometimes this is a sticking point with clients, but IMHO I truly believe EVERY developer should have PowerShell – more on this later)
  • PowerShell must have the execution policy set to RemoteSigned/AllSigned (if you plan to sign this script, which I have not) or you can set it to Unrestricted (which is what I prefer, just be safe in what you run – read the script before you run it). More on execution policies here.

 

After that is taken care of, have the user can download this script, run it, and their workspace will be mapped and files will be downloaded. Note, this script may take awhile to run because it downloads all workspace files to your machine.

# Script that will:
# 1. Create a workspace. Workspacce Name: <ComputerName>_Root
# 2. Get the latest code from repository

$tfsServer = "TFSRTM08"
$userName = [system.environment]::UserName;
$computerName = [system.environment]::machinename
$workspaceName = $computerName + "_" + $userName +"_WS"   #Use 'WS' as an acronym for "WorkSpace"
$folderName = "C:\TFS"; 

# Set up the TF Alias</pre>
# Find where VS is installed.
$key = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\VisualStudio\9.0
$dir = [string] (Get-ItemProperty $key.InstallDir)
Set-Alias tf "$dir\tf.exe"

Write-Progress -Activity "Initializing Tree" -Status "Percentage Complete" -PercentComplete(10)

# Create the folder
echo "Creating folder: $folderName"
new-item -itemtype directory -path $folderName -force
echo "Completed Creating folder: $folderName"

[system.environment]::newline
Write-Progress -Activity "Initializing Tree" -Status "Percentage Complete" -PercentComplete(20)

# Move to folder
echo "Navigating to $folderName..."
cd $folderName
echo "Arrived at $folderName"

[system.environment]::newline
Write-Progress -Activity "Initializing Tree" -Status "Percentage Complete" -PercentComplete(30)

# Delete the workspace if it exists.
echo "Deleting workspace (if exists): $workspaceName"
$expr = "tf workspace /delete " + $workspaceName + " /noprompt"
$output = invoke-expression $expr
echo "Done deleting workspace."

[system.environment]::newline
Write-Progress -Activity "Initializing Tree" -Status "Percentage Complete" -PercentComplete(40)

# Create the workspace
echo "Creating workspace: $workspaceName"
$expr = [system.string]::Format("tf workspace /new /computer:{0} /server:{1} /noprompt {2}", $computerName, $tfsServer, $workspaceName)
$output = invoke-expression $expr
echo "Done Creating workspace: $workspaceName"

[system.environment]::newline
Write-Progress -Activity "Downloading Entire Source Tree" -Status "% Complete - this bar will not move for awhile. This process takes awhile." -PercentComplete(50)

# Get the latest
echo "Getting the latest code from: $tfsServer. This could take awhile..."
$expr = "tf get";
$output = invoke-expression $expr
echo "Done getting latest." 

Write-Progress -Activity "Initializing Tree" -Status "Percentage Complete" -PercentComplete(100)

echo "Tree initialization is complete."

You can copy the above into a InitializeScript.ps1 file and then run it from any machine that has the pre-req’s installed. Enjoy.

TFS Dependency Replicator Gotcha

Recently I was working with Dependency Replicator and I ran into an issue.  

 

Background

The file that is the “dependency” does not exist in the destination yet. Therefore the replicator fails. If it does not find the destination file in the dependent project location it will fail. In English: In $/TeamProject/Main/SharedBinaries/ there was no “utility.dll”. However, this is EXACTLY where I wanted replicator to PUT the file. Since the file did not exist at the destination, I would receive this error in the event log:

———————————–
Error in Event Viewer:

Dependency Repliccator error:
Event Type: Error
Event Source: DependencyReplicator
Event Category: None
Event ID: 0
Date:  10/23/2008
Time:  11:24:30 AM
User:  N/A
Computer: TFSSERVER
Description:
An error occured processing the dependency ‘$/TeamProject/Path/To/Dependency/dependency.dll’ from build ‘TeamProject_CI_20081023.5′ in project ‘TeamProject’. Processing subsequent rules will continue:
System.IO.DirectoryNotFoundException: Could not find a part of the path ‘C:\DependencyReplicator\TeamProject\Path\To\Dependency\Dependency.dll’.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.File.InternalCopy(String sourceFileName, String destFileName, Boolean overwrite)
   at DependencyReplicator.BuildStoreWatcher.ExecuteDependencyReplicationRule(Rule rule, Workspace workspace)
For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

———————

 

Steps to recreate:

  1. Create two team projects
    1.   -  One being the one which will propogate the dependency (ex: shared utility dll or something). This would output a file called “utility.dll” which would then
        be propogated to other locations.
    2. Other being the project which utilizes the dependency “MytestProject”. DO NOT put the dependency (utility.dll) in this team project anywhere.
  2. Set Depdenency Replicator xml to have the utility.dll file placed somewhere in “MyTestProject”.
  3. Kick off a build for the utililty project. This should kick off the dependency repliccator.
  4. Error (above) occurs.

 

Work Around

Place a copy of the “utility.dll” into the destination where the dependency should be. From that point forward you will not have a problem.

 

The Real Fix

The real fix is to fix the source code. Since I’m not a developer on the project I whipped up this code (which please note is only POC at this time).

 

string fullyQualifiedSource = Path.Combine(rule.Event.Data.DropLocation, rule.Source);
string fullyQualifiedDestination = workspace.GetLocalItemForServerItem(rule.Destination);
if(status.NumOperations != 0)
{
workspace.PendEdit(rule.Destination);
File.Copy(fullyQualifiedSource, fullyQualifiedDestination, true);
}
else
{
File.Copy(fullyQualifiedSource, fullyQualifiedDestination, true);
workspace.PendAdd(rule.Destination);
}

I’m going to build this hopefully later this week and submit it to the project. :)

TFS – Disabling Continuous Integration with NO-CI

This is kind of a “known thing” to those of us who automate team builds tasks in TFS with build scripts but there are still a lot of people who don’t know this… so here we go…

The 2008 version of TFS implemented Continuous Integration. In TFS, a build is triggered upon every check-in (if you have that option enabled in TFS). This can be setup in the build definition as shown below:

 

image

 

When anyone  checks a file into the workspace that is defined within the build (see the “Workspace” area in the image above), TFS will kick off a build.

Sometimes you’ll need to automate your build through some custom MSBuild tasks and during that time you may need to check a file back into TFS after a build. Why? Perhaps you’re generating some code on the fly, based upon the output of the  build. Perhaps you’re generating Xml documentation and you need to drop that back into the source repository for whatever reason. When you check code back into TFS, TFS will kick off a build, quickly leading to an endless loop.

 

How To Get Around This

Place the following into the comment field: ***NO_CI*** You can also use the $(NoCICheckinComment) Property in a build file. This property is set at runtime when MSBuild is started from the TFS Build agent.

The “***NO_CI***” comment will instruct team build to ignore this changeset as a trigger for continuous integration.

The Importance of Incremental Builds

With continuous integration being integrated into Team Foundation Server 2008 we can sometimes run into unexpected results. Such as extended build times.  By default the build file is set up to inform the TFS Build Agent that it should perform a “Full Build”.

What is a “Full Build”?

By default, when TFS performs a build the following events happen:

  1. Clean – When Team Foundation Build performs a full build, it cleans the files in the intermediate build folder and sources folder in Clean target.
  2. Workspace Initialization – As a part of a full build, Team Foundation Build deletes the old, and recreates a new workspace to clean and synchronize the source files.
  3. Get – In the Get task, Team Foundation Build by default retrieves new source files and overwrites the existing files in the build workspace.

 

A Possible Issue

slow A full build can have consequences that can be considered “non-optimal” (this is very subjective in many circles), but in my opinion coming from a Cruise Control .NET background, I DO NOT want full builds.

If my source code repository is very large, cleaning the files, re-creating the workspace and then getting all the files again can be a real pain in the “time” category. I’ve had builds on my local dev machine (which is in a VPC) take 15-20 seconds to build using Visual Studio. I’ve even run the same build from the command line using MSBuild using the .proj file and its taken less than 15 seconds for he same build (~15 Visual Studio Projects in the solution).

However, this same build using TFS would take nearly 20+ minutes!

Wowzers… The reason the build took so long in TFS is because it was utilizing the FULL Build.

 

 

 

 

Core Concept

One of the core concepts is “Keep The Build Fast“. We’re looking for frequent feedback. 20 minutes (IMO) is not quick feedback (if the build takes 15-20 seconds on my machine). We should be able to get this down to under 10 minutes at the worst, but my goal is to get it well under 5 minutes (closer to 2 minutes).

 

Enabling TFS 2008 Incremental Builds

In Team Foundation Server 2008, we can change this behavior with a simple change in the TfsBuild.proj file under the TeamBuildTypes directory in TFS. Here’s how:

  1. Open the TfsBuild.proj file for the build in question
  2. Add the following XML:

image

This will enable Team Build to treat this build as an incremental build.

What does this exactly do behind the scenes?

IncrementalBuild is a convenience property that sets SkipClean to true, SkipInitializeWorkspace to true, and ForceGet to false.

Since clean is skipped, the files will not be deleted. Since SkipInitailizeWorkspace is skipped, the workspace will not be deleted and recreated and since ForceGet is disabled, the files will not be overwritten with the files from Source Control. What this will do is perform a regular “Get” on the existing workspace which will only get the changed files.

 

Result

A huge performance improvement. The previous ~20 minute build dropped to about 4 minutes. There are some other improvements I could do to improve the performance,but at this time its 5 times as fast. Excellent. :) Why did it improve this much? Simple: Less disk I/O. Disk I/O = slow.

 

Disclaimer: This may not be desired. For example, build that is specified for “Production” you might want a “Full Build” in order to simulate a production release. You may have tasks in your build file that need a clean build. But for Continuous integration on the project, this setting is highly desirable.

TFS Users & Groups with Active Directory Policies

I forgot where I learned this (perhaps the TFS Admin guide) but … this is a good little tip…

 

When you add users to groups in AD and those groups are present in the Team Foundation Server Security groups it can take up to 60 minutes for the policy to be pushed down into TFS.

To jumpstart this, restart the “Visual Studio Team Foundation Server Task Scheduler”.

 

After the service restarts it kicks off the services that are responsible for refreshing the security policies. Now, if you add a user and they need immediate access, you can do it. Beware of other users though, this could impact your system if someone is checking in files, or working with TFS. So take caution in doing this.