August 12, 2008
Creating a TFS 2008 with SP1 Slipstreamed ISO image
Now that TFS 2008 SP1 is here, time to create a version of the TFS installer media that just contains the bits with SP1 applied. This is essential for installations targeting SQL Server 2008, but also makes the installation process onto Windows Server 2008 much easier and any installation faster (otherwise you have to install TFS 2008, then apply the service pack). Note that this is only required for new TFS installations - if you already have TFS installed then you are best of simply running the excellent service pack installer and it will do the business. Hopefully in a few weeks Microsoft will make a TFS 2008 with SP1 ISO image available, but in the meantime I thought I would write up the process of creating your own as I did mine.
Update: After creating the patched install of everything and running it, there were errors for the Team Build and Proxy installers. Talking with fellow MVP Etienne Tremblay this is apparently a known issue, documented as such (d'oh, I should really RTFM) and that slipstreaming of the Build and Proxy stuff is not supported at this present time. I've therefore updated this post to include the TFS SP1 rather than patched Build and Proxy installations so that you can do it the old fashioned way of installing, then patching...
Pre-requisites
- TFS 2008 Installation DVD (Workgroup, Trial or Full)
- TFS 2008 Service Pack 1
- An iso creating tool (I will use ISORecorder because it is good, free and works on Windows Vista x64).
- A couple of gigs worth of spare hard disk space to work in.
Slipstreaming the TFS Installation Files
- First, you must copy the contents of the TFS installation media onto a temporary folder on your hard drive. In my case I have created a folder called D:\tfs_sp1\source and copied the contents there.
- Extract the contents of the TFS installer executable by running the following command:
en_visual_studio_team_system_2008_team_foundation_server_service_pack_1_x86_x64wow.exe /extract:<location>
- Run the following command to apply the patch to the contents of the main TFS application installation folder (AT):
msiexec /a <RTM Source Dir>\AT\vs_setup.msi /p TFS90sp1-KB949786.msp TARGETDIR=<SP1 Target Dir>\AT
- Note that slipstreaming the Build and Proxy installations is not supported at this time. Also, the sharepoint extensions folder (wssExt) does not need patching so we can just copy these over.
- Because slipstreaming the Build and Proxy is not supported, you will also want to copy over the original service pack .exe file so that you can run it after installing them.
- Also, the Team Foundation Server client (Team Explorer) requires Visual Studio 2008 SP1, not the service pack for TFS. If you installed Team Explorer without the service pack onto a SP1 server then bad things can happen (I've seen class serialization errors but you might see other symptoms) - therefore you might want to exclude the TFC folder from this SP1 disc so that you have to install it from a Visual Studio Team Suite disc instead - hopefully remembering to run Visual Studio SP1 afterwards. However if, like me, you frequently install Team Explorer onto your TFS servers so that you can manage them directly from the server then you might want to also include the offline installation for Visual Studio on your new ISO image, that way you can quickly get access to the service pack. To get hold of the offline installer, download the Visual Studio 2008 SP1 iso image, mount the image and then copy the vs90sp1 folder.
- While you are at it, you might as well download the latest copy of the TFS Install Guide. If you are really fancy you can copy all the files over from the root of the RTM source and edit the setup.ini file to point to the new version of the document (mine is TFSInstall-RTM-v080811.chm).
- Now we have a nice little package that contains all the bits we need to install TFS SP1 onto a server. Mine looks like this:
If we go look inside the AT folder and check the file versions, we can see which assemblies were patched. The TFS2008 RTM versions of the assemblies were 9.0.21022.8 but the TS 2008 SP1 versions are 9.0.30729.1
- You could just burn the contents of your SP1 folder to a DVD, but I personally like to have it as an ISO image so that I can easily archive it and point to it from a Virtual PC. To create an ISO image using the excellent ISORecorder is very easy - just right click on your SP1 folder and select "Create ISO Image".
And there you have it. A handy ISO image that should speed up your TFS installations no end. Happy installing!
August 11, 2008
Visual Studio Team System 2008 SP1 Now Available!
That's right folks, 2008 SP1 is now available for everyone to download. Get it while it's hot!
Visual Studio 2008 SP1 (needed for Visual Studio and Visual Studio Team System client, including Team Explorer), (exe version) (iso image version)
Visual Studio Team Foundation Server 2008 SP1 (needed for the server, build server and proxy server)
Note that to install TFS 2008 SP1 on Windows Server 2008 with SQL Server 2008, you must create a slipstreamed install. Also - if you have installed any of the pre-release bits on a client machine then you should run the Visual Studio 2008 Service Pack Preparation Tool first.
For more information on what is in this release from a TFS perspective, see Brian Harry's blog post or listen to Radio TFS Episode #6.
August 5, 2008
Radio TFS is back with Version Control 101
In this first episode back from the summer break we talk about the features available in TFS Version Control and talk about some of the fundamental concepts that you should understand to make your life easier. Don't forget to stay tuned all the way to the end when I offer up a brainteaser for everyone and Paul goes crazy and offers a FULL copy of Microsoft Visual Studio 2008 Team Suite to a lucky listener drawn at random who provides a correct answer to radiotfs@gmail.com before the end of August 2008.
If that is not enough for you, Mickey is running a competition over at Team System Rocks where you could win a years MSDN Premium Subscription with Visual Studio Team Suite.
Don't forget that you can subscribe to the show using the RSS Feed in iTunes or Zune. You can also listen to the show direct.
For feedback or suggestions for future shows please contact us at radiotfs@gmail.com or leave a comment over at the Radio TFS web site.
July 23, 2008
Behind Brian the Build Bunny
Since posting about Brian the Build Bunny, I have been getting a steady stream of emails from people who would like to know more about how it works.
Team Foundation Server provides a mechanism by which you can subscribe to events when certain things happen. This is actually how many of the components in TFS are integrated between themselves and like the rest of the TFS glue - Microsoft make these events available so you can customize your TFS instance.
At a high level, I purchased a Nabaztag bunny and decorated it with the Visual Studio logo and Brian the Build Bunny was born. The code behind Brian the Build Bunny is actually very simple, a web service (in my case sat in IIS on my TFS server) listens for the events and then converts this into a string on text which it sends to the Nabaztag servers. Nabaztag run this text through a Text to Speech engine and generate an MP3 file with the results, they then notify my build bunny about the MP3 file and he downloads the file over his WiFi connection and plays it on his speakers.
But if you want the full gory details then read on.
Brian the Build Bunny Project.
Pre-requisites:
- 1 Team Foundation Server instance (a VPC one to play with first is recommended)
- 1 Nabaztag bunny
- 1 Wifi connection to the internet for the bunny to use
- 1 sheet of clear labels (to print the Visual Studio logo as Brian's bow tie). I used Avery J8560, but then again I'm a crazy European with metric pages.
Preparation:
- Ensure your bunny is registered with Nabaztag and you have your serial number and API token.
- Ensure your Team Foundation Server instance (or where-ever you want to host the "bunny listener") code has internet access so that it is able to talk to the Nabaztag server. Try pasting the following URL into Internet Explorer on the TFS instance to make sure you can get out and talk to Nabaztag:
http://api.nabaztag.com/vl/FR/api.jsp?sn=SERIAL&token=TOKEN&voice=UK-Mistermuggles&tts=Hello+my+name+is+Brian+the+Build+Bunny.
Remembering to insert your build bunny's serial number and API token obtained when you registered your bunny with Nabaztag. - Download the image teamsystem.png (70 KB) and print out the Team System logo on a clear label. Cut around the logo and stick onto your bunny to turn him into a smart looking Build Bunny.
Code:
You can download the example code using the following link, (VS 2008 project using C# in .NET 2.0):
There are many ways to listen for Team Foundation Server SOAP events. You can have a "proper" web service living on IIS, or you have have a hosted WCF process listening to a particular port. In my particular case I was happy with the IIS approach and so used the excellent Team Foundation Server Notification Event project template from Howard van Rooijen at Conchango as that's the fastest way to get up and running when doing this type of project. If you wanted to go down the WCF route then take a look at Martin Hinshelwood's TFS Event Handler project up on CodePlex.
I created a quick class library that is a thin wrapper over the Nabaztag API, and exposes functionality via the Bunny class including building and sending of the HTTP message to the Nabaztag servers.
I then created a TFS Web Services Notification project (using Howard's template). This gives me ready-made code to hook into various events - but I just wanted the build completion event and the check-in event for this example.
Build completion event:
Check-in event:
As you can see, I really don't do that much - just build up a string based on the data in the TFS event and then send that string to the bunny.
Subscribing the Bunny Listener to the TFS Events
Finally, once the web services are ready and listening - I must now tell TFS to send a SOAP message to my web services at the appropriate time. To do this, I used the command line tool BisSubscribe.exe. This can be found on your TFS server in the %ProgramFiles%\Microsoft Visual Studio 2008 Team Foundation Server\TF Setup directory.
If you open up a command prompt and cd to it then you can type:
bissubscribe /eventType BuildCompletionEvent /address http://localhost:8181/bunnylisener/BuildCompletionEndpoint.asmx /deliveryType Soap /server http://localhost:8080 bissubscribe /eventType CheckinEvent /address http://localhost:8181/bunnylisener/CheckInEndpoint.asmx /deliveryType Soap /server http://localhost:8080
Where the webservices are running at http://localhost:8181/bunnylistener
.
Exercises for the Reader:
As you can see, the code above is just a starter. There are many things that you can get this new notification device to do and the code above is designed more of a sample that I can use in a talk rather than full on production code. That said, if anyone is interested in starting up a CodePlex project around this then please drop me a line. However, the following things are on my TODO list:
- Subscribe to BuildCompletionEvent2 which has some more relevant information
- Use the GlobalSecurityService API to obtain the users real names and speak those rather than the userid.
- Store the bunny config data in web.config rather than hard-coding bunny details in BunnyConstants class
- Allow multiple bunnies to be registered (perhaps with a UI), allow each voice to be separately configured
- Make sending of messages to Nabaztag asynchronous
Additionally - rather than using the eventing service to trigger Brian on build completition events, it might be interesting to write the "Bunny Link" as a windows service and get it to subscribe to the build queue using the same API as demonstrated by my Build Wallboard sample. That way you could make Brian talk when a build was queued or many other things.
If anyone has any questions or would like more details then please drop me a line.
July 9, 2008
Teamprise 3.1 is now available
At the Microsoft Worldwide Partner Conference in Houston today, Corey Steffen (General Manager of Teamprise and the guy that pays my wages) announced the public availability of Teamprise 3.1.
This is a maintenance release free to everyone with a valid Teamprise 3.0 license and includes several bug fixes along with a few new features. For the proper release notes, take a look here. However I just wanted to point out a few highlights.
Improved Offline Support
With-out doubt, the biggest new feature in Teamprise 3.1 for most people will be the improved offline support. If you right click on a project in the Eclipse IDE, you are now presented with a "Go Offline" option which allows you to tell Teamprise not to bother trying to talk to TFS for a while (previously you had to restart Eclipse for Teamprise to ask you if you wanted to go offline, and only then after it had tried *really* hard to connect).
While offline, you can still perform all the file operations like you expect -- you can add, edit, move and delete files just like if you were online.
When you want to come back online (say when you have stepped back out of the plane, bus, meeting room), you can right click on the project and say "Return Online" and Teamprise will do it's best to detect what changes have happened while you were away, giving you the option to pend those changes.
There is even "return online" capability in the stand-alone client Teamprise Explorer which is very neat and makes working with tools that are not TFS aware a little easier too. The actual algorithm used by the return online feature is more sophisticated than just checking for read/write status in your local workspace, we also do some magic and compare checksums of the file contents etc. Fellow Teamprise blogger Ed Thomson was the lead developer for the offline work and he has some more details on his blog.
TFS 2008 SP1 New Feature Support
Service Pack 1 of Team Foundation Server 2008 is hopefully due out soon, and with it come lots of lovely new features. We took advantage in the timing of our 3.1 release to update Teamprise to support some of the new server capabilities so that they are available to our customers as soon as TFS 2008 SP1 arrives. These include:
- Last check-in date/time column (see my previous post about this feature)
- Support for work item meta-data filtering option (note that this option is already being used on some of the CodePlex servers so if you use Teamprise to talk to the work item functionality in CodePlex then you probably want to upgrade to Teamprise 3.1, after all the price is right :-) ).
tf branch -checkin
command, the fastest way to create large branches and perform the check-in at the same time.
Command Line Client Improvements
In this release we are making publicly available a bunch of improvement and new features that we added to our command line client after some great feedback from one particular customer who has un-questionably the largest and most demanding Team Foundation Server install base on the planet. In particular we have added the "-format:xml" option for most commands in addition to the usual -format:brief and -format:detailed. The -format:xml option will output data from the command line client in a format easily XML parseable without truncating output which makes it much easier to use and parse command line output in scripting scenarios. However, there have been many more improvements so check out the release notes for more information.
64-bit support on Windows.
Not really worth calling out separately only to say that Teamprise is now the world's first commercially available x64 TFS client for Windows :-)
While we are a Java application, as mentioned before, we have a bit of JNI code to do the stuff not possible from all the JRE versions that we support (such as native authentication on Windows or making a file writable). Also we use SWT to give us a native look and feel on all platforms and SWT works by using lots of JNI to do the presentation calls. This meant that if you tried to run Teamprise under a x64 Java runtime we died pretty quickly. We've had x64 support available for other platforms for a while (including Linux), but with Windows x64 support coming in Eclipse 3.4 we took the opportunity to compile our JNI code over to the Win32 x64 architecture and it works great.
On a personal note, during this activity I had to fix some bits in the core Eclipse 3.4 codebase (specifically PDE for people that are interested) and the small patches that I submitted have been applied into the main Eclipse project which is a nice feeling. Eclipse is a poster-child of open source projects and it is with some pride that I can tell people I have contributed code into it. It's also nice that as Teamprise is a commercial company that uses a lot of the Eclipse code and is an Eclipse Foundation member, we are able to do our bit and contribute something back for the benefit of all.
As you can see, we've been busy. We've hopefully cleared some adoption blockers for some of our customers, done quite a few bug fixes and performance improvements and thrown in some new features along the way. While the headline grabbing features of interest to most people are probably the offline support and the 64-bit support, I'm very proud of this as a solid "point" release and I would encourage everyone with a valid 3.0 license to upgrade.
June 5, 2008
Last Check-in Date Explained
I've been doing a lot of work with the Team Foundation Server 2008 SP1 Preview, and even recorded a podcast about it (also see Brian Harry's blog post for more details on TFS 2008 SP1 features).
One out of the many new features introduced in TFS 2008 SP1 is the "Last Check-in" column in the source control explorer. It is a handy little thing that I think a lot of people will find useful.
However just a couple of warnings for you for behaviour that you might not expect at first.
- The date shown for folders is the date that the folder was added, not the last date that any contents of that folder where checked in. That means you cannot use it to drill down onto the most recently changes files - to find that out you should still do a "View History" on the parent folder and look at the changesets.
- If you are using a Visual Studio 2008 SP1 client (or Teamprise 3.1 for that matter when it is released) and you point it at a server prior to TFS 2008 SP1 (i.e. TFS 2005 or the RTM release of TFS 2008) then you do not get any data in this column because the server doesn't send back that data to the client.
Otherwise it works pretty much as you expect. Most useful is that you can obviously sort the column to find the recently changed files in a big list of files.
May 30, 2008
Brian the Build Bunny Backgrounds
It turns out that the little video I posted yesterday has taken on a bit of a life of it's own. Last time I checked, it was in the top 10 Science and Technology posts for YouTube in Ireland. It's funny how it is always the posts that you do more for your own entertainment that take off.
Anyway, there is no doubting that Brian is a bit of a character, he's already recorded his first TV appearance as a guest on this weeks, "This Week in Channel 9" (to be broadcast soon). I wish that Nabaztag had an affiliate program as it sounds like I may have sold a few rabbits for them.
Anyway, if you can't afford your own bunny, then you can have the next best thing for free. Your very own Brian the Build Bunny Background on the desktop of a computer near you (standard and widescreen versions available). Click here to chose a image size that suits you.
May 28, 2008
Brian the Build Bunny
I'm always keen try new and novel ways to keep in touch with the status of my software projects. Fortunately, Team Foundation Server provides many ways to do this. While the Build Wallboard is fun if you have a spare monitor and machine lying around, I wanted to experiment with some inexpensive dedicated devices, and so Brian the Build Bunny was born.
Brian is a Nabaztag smart rabbit. He reads out details of check-ins and builds. If a build has failed then his ears go down to show how sad he feels, but if you fix the build his ears will soon pick up again.
I've had Brian for about a year now waiting to do this project, but when I tried it in the past I always found the response times from the rabbit to be too slow. However earlier this year, the Nabaztag developers updated the code running the rabbits so that they are now using the XMPP (Jabber) protocol to receive updates and the service now seems pretty good.
Brian is now sitting on my desk chattering away and letting me know what is happening in TFS. If you want to find out more about how he works and see him in action then take a look at the video. If your company blocks YouTube but you have Silverlight installed then you can view a higher quality version of the video courtesy of the Windows Live Streaming service. I'll go through the code behind Brian in a later post if there is any interest, but it is pretty much a standard TFS event listener that then sends text to the rabbit using the Nabaztag API.
May 23, 2008
Radio TFS 05: Common Team System Questions
I've just posted the latest installment of Radio TFS. I'm actually a show behind on editing so expect to see episode 6 up soon. However, in episode 5 Paul, Mickey and I attempt to answer some of the common questions we hear people ask about Team System including:
- What is Team System?
- Which edition is right for me?
- Why can't I find Team Foundation Server on MSDN?
- What is Team Foundation Server Workgroup Edition?
- Is VSTS 2005 compatible with TFS 2008?
- Why can't I see Team Foundation Server when I install Team Suite?
- What are my options for migrating from my old system(s) to TFS?
- Can I use TFS with VB6, .NET 1.1, Eclipse etc?
- What is a Team Project - how should it be scoped?
- I deleted a file locally, I do a "Get Latest" and TFS doesn't download it - why?
As well as the usual sprinkling of tangents along the way.
Click here for a direct link to this episode.
If you have any questions that you would like answered, or if you have any comments and feedback about the show then please contact us at radiotfs@gmail.com or visit the website at http://radiotfs.com for quick links to subscribe to the feed in iTunes, Zune etc.
April 21, 2008
TFS Build API by Example #1: Queue a build.
As we all know by now - the build system in TFS2008 was substantially improved. Along with the many improvements came an official API for talking to the build system. This is the same API that the Team Foundation Build UI in Visual Studio uses, however there are many additional methods that were added that were not for the UI at all but for potential consumers of the Build API.
In talking with folks at community events, and on the MSDN forums I have realized that there isn't a huge awareness of this API. Having written a parallel implementation of the build API, but in Java for the Teamprise 3.0 release I have spent a great deal of time with the .NET API and have a few examples lying around of how to accomplish certain common tasks - so I thought I would run through a some of them. If you have an example of something you would like to see with the build API then please leave a comment for this post or drop me a line.
So - let's start with a basic one. How to queue a build. This will introduce us to a few concepts with-in the build API that are common across all of the methods.
The Easy Way.
A quick look at the class diagram above will show you that the main interfaces you'll be dealing with in the Build API are the IBuildServer and IBuildDefinition interfaces. To get started with these you'll need to add references to the Microsoft.TeamFoundation.dll, Microsoft.TeamFoundation.Client.dll and Microsoft.TeamFoundation.Build.Client.dll.
C#
TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer("http://tfsserver:8080”); IBuildServer buildServer = (IBuildServer)tfs.GetService(typeof(IBuildServer)); IBuildDefinition buildDef = buildServer.GetBuildDefinition("TeamProject", "Build Name"); buildServer.QueueBuild(buildDef);
VB.NET
Dim tfs As TeamFoundationServer = TeamFoundationServerFactory.GetServer("http://tfsserver:8080") Dim buildServer As IBuildServer = DirectCast(tfs.GetService(GetType(IBuildServer)), IBuildServer) Dim buildDef As IBuildDefinition = buildServer.GetBuildDefinition("TeamProject", “Build Name") buildServer.QueueBuild(buildDef)
I'm afraid that's the last example you'll be seeing in VB.NET. Being a Java developer by day I tend to like my semi-colons. However if you are a VB developer then hopefully you'll be able to follow along as the rest of the examples are more or less just method calls with the occasional bit of casting.
So - what's going on here. Well we first get hold of a TeamFoundationServer object. If this was server side code and we wanted to specify some credentials then we would use a slightly difference mechanism but in this case the constructor works well and this will automatically connect using the credentials of the current thread.
Next we get hold of the build service that implements the IBuildServer interface. Finally we get a hold of the build definition by specifying a team project and build definition name (build definitions are unique per team project in TFS). We then queue a build using the defaults for that build definition. This API call is actually shorthand for the following...
The Hard Way
Supposed you want to queue a build, but you don't want to use the default build agent, priority, drop location etc. Well in that case you need to look at the IBuildRequest interface. Here you will find all the options to customize the build request, you can specify a build agent, drop location, priority etc like you can do from the Visual Studio UI. You will also find other options such as being able to queue the build with a maximum acceptable queue position, pass a custom get version for the build or even queue the build in a postponed status.
In the following example, I am going to find a non-default build agent from the server and then queue a build using it.
TeamFoundationServer tfs = new TeamFoundationServer("http://tfsserver:8080");
IBuildServer buildServer = (IBuildServer)tfs.GetService(typeof(IBuildServer));
String teamProject = "TeamProject";
String buildDefinitionName = "MyBuildDefinition";
String buildAgentName = "MyBuildAgent";
// Find our (non default) build agent.
IBuildAgentQueryResult queryResult = buildServer.QueryBuildAgents(buildServer.CreateBuildAgentSpec(teamProject, buildAgentName));
if (queryResult.Failures.Length > 0 || queryResult.Agents.Length != 1)
{
throw new Exception("Invalid Build Agent");
}
IBuildAgent buildAgent = queryResult.Agents[0];
IBuildDefinition buildDefinition = buildServer.GetBuildDefinition(teamProject,buildDefinitionName);
// Create a build request based on our chosen definition.
IBuildRequest buildRequest = buildDefinition.CreateBuildRequest();
// Optionally set command line args, drop location, priority, custom source version etc here
// in this case just overriding default build agent.
buildRequest.BuildAgent = buildAgent;
buildServer.QueueBuild(buildRequest);
Pretty simple really and very powerful. I encourage you to go take a look at the IBuildServer interface to see some of the stuff that you can do. If you have any suggestions as to what you would like to see as a build API example then leave a comment to this post.
April 3, 2008
IMTC 2008: Automating Builds with Team Foundation Server 2008
Thanks to everyone who attended my session this afternoon at IMTC 2008 in Dublin. As promised, here is a copy of the slide deck.
Also, as mentioned - the Build Wallboard project that we did as a demo is also available at the MSDN Code Gallery here:
April 2, 2008
CruiseControl 2.7.2 Released
Jeffrey Fredrick just announced that CruiseControl 2.7.2 is now available for download: http://tinyurl.com/2zm9mz.
There are lots of bug fixes, lots of changes to the Dashboard and some new plug-ins, but the bit that is of most interest to me was (from the release notes)
TeamFoundationServer source control ---------------------- * Fix compatibility with Microsoft Visual Studio Team Foundation Server 2008 (CC-735). Submitted by Martin Woodward.
This was to work around an issue that came up when using CruiseControl (java version) to talk to a TFS2008 server (TFS2005 worked fine and still does). If you are attempting to use CruiseControl with TFS 2008 then you should go with CruiseControl 2.7.2. For that matter - if you are using CruiseControl.NET with TFS then you should also take a look at the latest release of the integration to TFS - as that contains the same fix allowing you to happily talk to a 2008 version of Team Foundation Server (also using the TFS 2008 client API's).
Anyway, congratulations to the CruiseControl team on the 2.7.2 release!
March 18, 2008
Teamprise 3.0 Ships!
At EclipseCon 2008 this morning, we just announced that Teamprise 3.0 has been released! If you've been wondering why I have been quiet on the blog lately - but also why anything I have been talking about is Team Foundation Build related, then you are about to find out why :-) First of all, I'd encourage you to go visit the shiny new website at http://www.teamprise.com. Our marketing team had too much fun putting that together, including getting a real, live, massive Teamprise power button made up and shipped in a huge crate from New York to be photographed and used as the new site/icon image.
The full release notes are available here, but as has been the tradition for the past few Teamprise releases, I thought I would give you a run down of my favourite new features in the 3.0 release.
At a high level, the features in 3.0 can be summarised as:-
- Full Team Foundation Build integration (including ability to execute Ant based builds)
- Check-in policy support
- Recursive folder compare
- Single sign-on (from Microsoft Windows machines)
- "Destroy" command for version control
- Show deleted items and undelete from Source Control Explorer UI
- much much more (see release notes)
While it is not my area, I should also mention that we've taken this opportunity to make our licensing more affordable for smaller teams. We have been very pleasantly surprised by the number of people buying 1 to 20 licenses at a time. Originally, Teamprise pricing was skewed to the Enterprise customers (i.e. simple, all inclusive and with steep volume discounts). So we have done a couple of things to help out the smaller companies:-
- You can now purchase the various components (Teamprise Plug-in for Eclipse, Teamprise Explorer, Teamprise Command Line Client) individually as well as the Teamprise Client Suite which gives you the lot.
- We have lowered the initial prices for a single seat, meaning that people buying one or two licenses can now get the same discounts that used to only be available to folks purchasing 100.
If you have any licensing issues / queries then feel free to contact me, or you can talk to the sales team direct at sales@teamprise.com. Anyway - back to the part of this release that I do know about - the technology.
The first feature I want to talk about is one that I had no involvement with. It's one of those features that many people will not notice because it just works but anyone who has done any Java to .NET web service interop work will instantly recognise as being a little bit clever.
Single Sign-On
The initial log-in screen has undergone a big overhaul. On Windows machines you are given the option to use "default credentials", i.e. the username and password that you are logged onto windows with. It obviously doesn't know your password, but does some JNI magic to get the native Windows API's to handle the authentication logic with Team Foundation Server. While you are also on the login screen, you may notice the Profile feature. This is an area that many people probably won't use, but we added for our power users and for ourselves. Basically, the profiles feature allows you to store sets of servers/credentials that you commonly use to connect to Team Foundation Server and then you can bring up the details using a simple drop down. Makes it much easier to switch between your production TFS instance and your CodePlex project for example - or switch credentials if you are a TFS administrator.
Check-in Policy Support
In Visual Studio, check-in policies are implemented as a .NET assembly runs every time a policy is evaluated or configured. The policy also has full access to the .NET API's, the Visual Studio API's as well as anything it might want to pinvoke out to on the Win32 API side. As you can imagine, this presented us some problems when we wanted to have check-in policies that ran the same in Eclipse on Windows Vista as Teamprise Explorer on the Mac or Aptana on Ubuntu - therefore we have had to develop a parallel Teamprise check-in policy framework.
As we were doing this, we took the opportunity to learn from some of the feedback folks have been having with the Visual Studio check-in policies. While our framework and SDK will look very familiar to anyone that has developed a custom check-in policy for Visual Studio, you will notice some differences.
Firstly, we supply different policies out of the box. The vast majority of custom check-in polices that people deploy are things like "Check for Comments" etc, so we just shipped the common ones our customers wanted to prevent them from having to write their own.
Secondly, we make use of the Eclipse plug-in framework to implement our policies as extension points. This means that they are easy to deploy (using the Eclipse update site mechanisms built in to the IDE). We have also separated the configuration (stored as a blob of XML data in our framework) from the implementation - represented by the plug-in deployed. The again makes it easier to deploy, especially when it comes to version 2 of a policy...
Thirdly, all of our policies can be scoped by the path in version control to which they correspond - you are not limited to per Team Project scoping and you do not have to wrap your policies in a custom policy to get more detailed scoping like you do with the current Visual Studio framework.
Team Foundation Build Integration
Anyone that has been following this blog for a while, or who attended the Team Build talk I did at TechEd with Brian Randell, will notice that I have been increasingly involved in the inner workings of Team Foundation Build. Now you can see the fruits of that labour.
In Teamprise we now have full integration with the shiny new build functionality in TFS 2008 as well as support for TFS 2005. Backwards compatibility with the TFS 2005 server is very similar to if you were using a Visual Studio 2008 client, accept that ours is slightly more backwards compatible (you can create new builds on a TFS 2005 server as well as manage build qualities etc). However it is with TFS 2008 that you get to see the majority of the features. I could go on about this aspect all day as their are so small things that I am proud of, but at a high level you can:
- View existing build definitions
- Manage builds in Build Explorer
- Queue new builds
- View build report
- Edit Build Quality
- Delete build
- Manage Build Qualities
- Open Drop Folder
- New/Edit Build Definition
- Edit Retention Policies
- Keep Build
- Set Queue Priority
- Postpone Build
- Stop/Cancel Build
- Delete Build Definition
One of the smaller features I will call out is that from the build definition in the Team Explorer, you can right click and do a "View Build Configuration" that will open the Source Control Explorer at the place in which the TFSBuild.proj file is stored so that you can check it out and edit it. A feature that I added solely for my own sanity during dogfooding :-).
All this would be fairly academic, if you didn't have some way to do a cross-platform build using Team Foundation Build. In the current release, we provide a the Teamprise Extensions for Team Foundation Build which basically Ant enables the Team Foundation build server. The Teamprise extensions are a set of MSBuild targets that insert the Ant build process into the standard Team Build mechanism as well as a custom MSBuild We hope to extend this to support in the near future to some of the other common build/test tool-chains in the cross-platform world. However, the Ant integration case will help a lot number of people out there.
Best yet, the Teamprise Extensions for Team Foundation Build are available free of charge for everyone - wether or not you are a Teamprise customer. Also, if you want to see how they work and customize them to meet your own non-standard build system then the source is available under the permissive open source Microsoft Public License (MS-PL).
I would personally like to thank the Team Foundation Build Team (especially Buck Hodges and Aaron Hallberg) who have been incredibly helpful through the development of the build functionality in Teamprise 3.0 while they were also busy working on TFS 2008.
Hopefully that gives you a quick flavour of Teamprise 3.0 and where we are going with this release. If you head over to the new site now and take a look at the many improvements we've made, we'd love to hear what you think.
March 6, 2008
Radio TFS
Paul Hacker, Mickey Gousset and I have recently started a Team System related podcast called Radio TFS.
While it is not going to win any awards any time soon, we've been having a lot of fun so we are going to continue to try and get one or two episodes out a month. If you have 40 or so minutes to kill, then feel free to take a listen and subscribe.
If you have any suggestions for topic or any questions about Team System that you would like answered then please drop us a line at radiotfs@gmail.com or visit the website at http://www.radiotfs.com.
February 5, 2008
Aligning Build Numbers with Assembly Versions in TFS2008.
I like my build numbers to be the same number that my assemblies are versioned with (and my end deliverables). It just makes things easier to track, that way if I get a bug report in from a customer I can look at the version and easily look at the label in source control to see what code that included. In all deliverables provided to the customer, we always output the version obtained from the current assembly somewhere at the start of any diagnostic information, that way you can easily tell what version they are on and instantly track this back. This all helps to make it easy for bugs to be filed against the correct version and reported in which version they have been fixed (using the nice integration between Team Build and the Work Item tracking portion of TFS).
People are often surprised that this feature does not work "out the box" with Team Build, so I thought I would just take the time to document how I made this work for us internally. As you'll be able to see, in TFS2008 all the basic hooks are provided for us to support this way of working.
Firstly, our .NET version numbering uses a slightly different scheme to our Java version numbering. In our Java products, the "build number" portion of the version number is actually the changeset number of TFS at that point in time. In .NET there are 4 components to a typical assembly version number (1.0.1234.5678) and the maximum value for each number is 65535. Our production TFS server is currently at changeset 7698 which means that we would get about 6 years out of such a build numbering scheme for .NET - that would be perfectly satisfactory if you had a changeset epoch after each major release (so you would reset the build number to be current changeset - 7698 if we did a major version today). However Team Build needs a unique name for each build - using a changeset based approach risks having two builds with the same build number. So rather than do a changeset based system, I decided to make the .NET build numbers be a straight-forward incrementing number. I rely of the default functionality of Team Build to create a label for that build number to track the number back to version control. The incrementing number value is stored in a file on the default drop location for the build.
Another thing that I should explain is that I don't personally like the "standard" Microsoft way of versioning assemblies as:-
<Major>.<Minor>.<Build>.<Service>
To me, it reads much easier as:-
<Major>.<Minor>.<Service>.<Build>
Where <Build> is the number that increments every time a build is performed. As far as I am concerned, this difference is mostly cosmetic as it doesn't change the way the CLR resolves the assembly versions, however feel free to correct me in the comments if I am talking rubbish.
So - onto how we accomplish this. Firstly, in TFS2008 there is a convenient target for you to override to generate your custom build numbers called "BuildNumberOverrideTarget". The important thing is that each build number must be unique, therefore a good rule of thumb is to use something like BuildDefinitionName_1.0.0.1234. Inside the BuildNumberOverrideTarget you simply set "BuildNumber" property to be what you want. Here is ours:-
<PropertyGroup> <VersionMajor>1</VersionMajor> <VersionMinor>0</VersionMinor> <VersionService>0</VersionService> <VersionBuild>0</VersionBuild> </PropertyGroup> <Target Name="BuildNumberOverrideTarget"> <!-- Create a custom build number, matching the assembly version --> <Message Text="Loading last build number from file "$(DropLocation)\buildnumber.txt"" /> <IncrementingNumber NumberFile="$(DropLocation)\buildnumber.txt"> <Output TaskParameter="NextNumber" PropertyName="VersionBuild" /> </IncrementingNumber> <PropertyGroup> <BuildNumber>$(BuildDefinitionName)_$(VersionMajor).$(VersionMinor).$(VersionService).$(VersionBuild)</BuildNumber> </PropertyGroup> <Message Text="Build number set to "$(BuildNumber)"" /> </Target>
The first thing I do is call a quick custom task I wrote that increments the build number stored in the passed file. I wanted to do this while keeping a lock on the file itself in case two builds tried to update the same file at the same time. We then take this new number and build the BuildNumber based upon that value. The code for the Incrementing Number task is very simple and is given below:-
using System; using System.IO; using Microsoft.Build.Framework; using Microsoft.Build.Utilities;namespace Teamprise.Tasks
{
/// <summary>
/// A simple task to increment the number stored in a passed file.
/// </summary>
public class IncrementingNumber : Task
{
public override bool Execute()
{
NextNumber = IncrementNumber();
return true;
}
public int IncrementNumber()
{
using (FileStream fs = new FileStream(NumberFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
{
StreamReader reader = new StreamReader(fs);long pos = 0;
String line = reader.ReadLine();// Ignore comments in file
while (line != null && line.StartsWith("#"))
{
pos = pos + line.Length + System.Environment.NewLine.Length;
line = reader.ReadLine();
}int number = -1;
if (line != null)
{
number = Int32.Parse(line);
}
NextNumber = number + 1;// Rewind the file stream back to the beginning of the number part.
fs.Position = pos;StreamWriter writer = new StreamWriter(fs);
writer.WriteLine(NextNumber.ToString());
writer.Flush();
writer.Close();
}
return NextNumber;
}
[Required]
public string NumberFile { get; set; }
[Output]
public int NextNumber { get; set; }
}
}
You compile this code into an assembly of your choice that lives alongside the TFSBuild.proj file in the build configuration folder in source control and is this loaded using the UsingTask call at the begging of your MSBuild project, i.e.
<UsingTask TaskName="Teamprise.Tasks.IncrementingNumber" AssemblyFile="Teamprise.Tasks.dll" />
The next thing that we have to do is to take the new version and force this into the assemblyinfo files. Personally, I prefer the AssemblyInfo files stored in source control to have a certain well defined number for each release branch (i.e. 1.0.0.0), and make it the build server that versions them. Some people like to check these back into source control - if you do that, be sure to check them in with the special comment of "***NO_CI***" to ensure that the check-in does not trigger any CI builds potentially putting you into an infinite loop of building.
So, we modify our assembly version files after they have been downloaded from source control using a technique borrowed from Richard Banks, our interpretation of this is given below:-
<ItemGroup> <AssemblyInfoFiles Include="$(SolutionRoot)\**\assemblyinfo.cs" /> </ItemGroup> <Target Name="AfterGet"> <!-- Update all the assembly info files with generated version info --> <Message Text="Modifying AssemblyInfo files under "$(SolutionRoot)"." /> <Attrib Files="@(AssemblyInfoFiles)" Normal="true" /> <FileUpdate Files="@(AssemblyInfoFiles)" Regex="AssemblyVersion\(".*"\)\]" ReplacementText="AssemblyVersion("$(VersionMajor).$(VersionMinor).$(VersionService).$(VersionBuild)")]" /> <FileUpdate Files="@(AssemblyInfoFiles)" Regex="AssemblyFileVersion\(".*"\)\]" ReplacementText="AssemblyFileVersion("$(VersionMajor).$(VersionMinor).$(VersionService).$(VersionBuild)")]" /> <Message Text="AssemblyInfo files updated to version "$(VersionMajor).$(VersionMinor).$(VersionService).$(VersionBuild)"" /> </Target>
As you can see, we are making use of the custom Attrib task that is provided by the essential MSBuild Community Tasks to set the files to read/write and then we are calling the MSBuild Community Task FileUpdate to do a couple of regular expression search replaces on the appropriate parts of the files.
And that's about all that needs to be done. Now our builds have nice incrementing numbers that have the version number included that is the same as the assembly info files.
January 28, 2008
Upgrading from TFS 2008 Trial to TFS 2008 Full
One of the most frequent questions I get when talking to people about TFS if how to upgrade from the freely downloadable 90-day TFS 2008 trial to a full version of TFS. Our TFS 2008 license key arrived at the weekend, so I thought I would take the opportunity to record the process. The first two steps have nothing to do with installing your license key, just purchasing it - but sadly are often the most complicated. Installing the actual key once received, is pretty trivial as I hope you will see.
Step 1: Purchase a TFS License Key.
This step is frequently mis-understood. You have to pay for the full version of TFS. Just because you are a partner of an MSDN subscriber, doesn't mean that you get TFS included. As an MSDN Subscriber, depending on your subscription type you do get access to a 5-user limited version of TFS called "Team Foundation Server Workrgoup Edition". However only Gold Partners or Partners with an ISV Competency get TFS included in their partner fees - everyone else has to purchase it. When connecting to TFS, you also need to make sure you have enough Team Foundation Server Client Access Licenses (CAL's) to cover your use - if you have a Team Suite flavor of MSDN then that gives you one CAL.
Step 2: Obtain TFS License Key.
The license key for TFS will be printed on a little sticker on the back of the media that it arrives on. If it didn't (perhaps because you purchased TFS via a volume licensing agreement) then there are ways around this to get the license key out of the media (look for a file called setup.sdb and open it in Notepad, look for [Product Key], usually at the bottom of the file, and you will find it there).
Because getting a TFS License Key usually involves the physical delivery of the media, you should make sure that you factor this time into your purchasing decision. If (like most people) your purchasing process takes longer than you budgeted for - then you do have the ability to extend your TFS Trial by an additional 30-days.
Step 3: Enter TFS Maintanence Mode
Go to, Control Panel, Add Remove Programs.
Then find Microsoft Visual Studio 2008 Team Foundation Server in the list and press Change/Remove
Step 4: Enter the Full Key for TFS
At the bottom of the maintenance dialog, you have the option to enter your full license key.
Note that there was a bug in the RTM version of TFS 2008 that means that workgroup edition doesn't have the ability - see this post for details. This is TFS 2008 90-day trial, so it works just fine for us.
Enter your license key and press Next. Note that this will install the license key and fire up a command window for a few seconds while it restarts IIS for the TFS server. Once you are finished, you will get the following dialog.
And you are complete. If you really want to check that the new license key has taken hold, then you can fire up a copy of Brian Harry's VersionDetection tool to set your mind at rest.
And there you go - running the final release (9.0.21022.8) with no expiry date :-)
January 24, 2008
Accessing Team Build logs over the WAN
In a previous post, I talked about how Windows file sharing sucks over the WAN. This is particularly annoying for me when trying to view the log of a TFS Build - especially if that build has failed and I want to know why in a hurry. On my computer (sitting on the end of a VPN nearly 4000 miles from my TFS instance), there is a delay of about 50-70 seconds to view the log file depending on the size and the speed of the link at that moment in time. During that time, Visual Studio is hanging waiting for the file to open. The issue is compounded by the fact that the rest of the Team Build UI - and in fact the whole of TFS access in general - is so speedy over the same VPN link, that I really notice the time delay accessing build logs.
Therefore - it didn't take too many 70-seconds delays for me to fire up a second instance of Visual Studio to create a work-around. In Visual Studio 2008 (and in the upcoming Teamprise 3.0 Team Build integration), if the log location provided is not a UNC style path (i.e. \\server\drop\build\BuildLog.txt) but a http:// address, then it will open the file in a browser instead. Accessing the build log over http helps in two important ways.
- HTTP is much less latency sensitive than accessing a file from a Windows share
- A browser will display the contents of the file before it has finished loading. When accessing the build log directly from a file share, the application (i.e. Notepad) will have to wait until it has recieved the whole file before displaying any of it to you. These log files can get large - so the improvement in perceived speed is significant. (http:// urls are a lot more cross-platform friendly than UNC paths as well, which is nice for us Teamprise folks).
Therefore, I created a quick and dirty ASP.NET page that accessing the build log for a particular build over the network and streams the contents of a build log to the browser. I then add a target into my TFSBuild.proj file that sets the log location to be the http url rather than the default UNC address.
LogView ASP.NET Page
I am by no means an ASP.NET expert - so please feel free to highlight any glaring stupidity on my part if you know better. I know the code below is sub-optimal and presents some security considerations, however it is a quick work-around that I spent a couple of minutes over to solve my issue - so please treat this code as just that.
Create a new Web Application Project, with references to Microsoft.TeamFoundation.Client and Microsoft.TeamFoundation.Build.Client assemblies. Create a new aspx page - mine is called view.aspx. In the page mark-up, ensure it only contains the Page tag, and an output caching directive. Mine (which is in a C# project) looks like this.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="view.aspx.cs" Inherits="LogViewer._Default" %>
<%@ OutputCache Duration="2592000" Location="Server" VaryByParam="*" %>
Now - the work is performed in the code behind.
1 using System; 2 using System.IO; 3 using Microsoft.TeamFoundation.Build.Client; 4 using Microsoft.TeamFoundation.Build.Common; 5 using Microsoft.TeamFoundation.Client; 6 7 namespace LogViewer 8 { 9 public partial class _Default : System.Web.UI.Page 10 { 11 protected void Page_Load(object sender, EventArgs e) 12 { 13 Response.Write("<html>"); 14 string teamFoundationServerUrl = Request.Params["TeamFoundationServerUrl"]; 15 string buildUri = Request.Params["BuildUri"]; 16 17 if (String.IsNullOrEmpty(teamFoundationServerUrl)) 18 { 19 teamFoundationServerUrl = "http://localhost:8080"; 20 } 21 22 if (String.IsNullOrEmpty(buildUri)) 23 { 24 Response.Write("<title>LogViewer Error</title><body>A valid BuildUri must be passed</body></html>"); 25 Response.End(); 26 return; 27 } 28 29 TeamFoundationServer tfs = new TeamFoundationServer(teamFoundationServerUrl); 30 IBuildServer buildServer = (IBuildServer) tfs.GetService(typeof(IBuildServer)); 31 32 IBuildDetail buildDetail = buildServer.GetBuild(new Uri(buildUri)); 33 34 String logFile = Path.Combine(buildDetail.DropLocation, BuildConstants.BuildLogFileName); 35 36 Response.Write("<title>Build Log: " + buildDetail.BuildNumber + "</title><body>\r\n<pre>"); 37 38 StreamReader reader = File.OpenText(logFile); 39 String line = reader.ReadLine(); 40 41 while(line != null) 42 { 43 WriteLine(line); 44 line = reader.ReadLine(); 45 } 46 reader.Close(); 47 48 Response.Write("</pre></html>"); 49 50 Response.End(); 51 52 } 53 54 private void WriteLine(string line) 55 { 56 line = Server.HtmlEncode(line); 57 if (line.StartsWith("Target "")) 58 { 59 line = "<strong>" + line + "</strong>"; 60 } 61 Response.Write(line); 62 Response.Write("\r\n"); 63 } 64 } 65 } 66
As you can see - this is a very quick and dirty example. I am relying on the output caching of the ASP.NET page to provide any performance at all, and this code could be improved in many ways. However to walk you through the code;
I start by checking for a TeamFoundationServerUrl parameter, if not passed, I assume that this application is being installed on the TFS server itself so localhost:8080 will get me there. I show an error if the BuildUri is not passed. Inside the TFSBuild.proj file execution, the MSBuild properties $(TeamFoundationServerUrl) and $(BuildUri) are accessible and provide you all you need to be able to get how of the build that you are building.
Line 29 is me getting hold of the TFS instance, line 30 the build server and finally line 32 the build that I am currently executing. 3 lines to get everything I need to be able to query and modify the build information - you gotta love the TFS2008 build API ;-)
I then (line 34) get the build log path, which is always a file called BuildLog.txt in the drop folder. I then simply stream the file, line by line, into the response stream.
The WriteLine method is used to do this so that I can optionally do a bit of formatting to the lines as I stream them to the client. In this example, I am highlighting and lines that are the beginnings of a Target - just to make the log easier to read.
I then deploy this ASP.NET page onto an IIS server. In my case, I have it running on my production TFS server as a separate application (on a different port) and I have the application set to run as a defined user that has read access to the drop share along with read access to the TFS Build Store. This means that anyone inside the network can anonymously access the web page and view the build log of any build - but that is acceptable in my organization. You might want to make access more secure - but then you will also have to be cleverer in your output caching decisions.
TFSBuild.proj File Customization
In the TFSBuild.proj file, I then override one of the provided hook targets to set the log location property of the build. I'm still un-decided as to which is the best target to put this in, but at the moment I'm going with BeforeGet.
As you can see - it is pretty low tech, but very effective. Clicking on the link starts providing me with log output with-in a second.
I actually have the web site running on port 9090 configured to give me directory browsing of the drop location file share (that is also located on the same server). This means that I can access my drop files using a browser by browsing to http://tfsserver:9090/ - however, you can not update the drop location of the build to be a http:// url. This is because the drop path is specified (and verified) as a UNC path and several parts of code in the build client API itself assume this. You could work around this by extending the ASP.NET page mechanism above to provide access to the files in the drop location - perhaps a future project for me ;-)
Rather a lengthy post this one - but I hope it helps someone else out of the same frustrations I was having. If you've read this far than I guess it was moderately interesting to you :-)
January 23, 2008
Team System 2008 Interview with Brian Harry
Brian Keller has posted a great interview with Brian Harry about Visual Studio Team System 2008, and a lot of detail on the improvements in Team Foundation Server 2008. One of the things that I enjoy about talking with Brian Harry is that you get the information in a direct and clear way, with a complete absence of "marketing speak" or buzzword bingo. At just over 10 minutes in length, this interview is a classic example of that and is densely packed with information - highly recommended.
January 22, 2008
Team Foundation Server over a VPN
I connect to the central Teamprise TFS instance over a VPN connection to the head office in Champaign, IL. It is a direct distance of nearly 4,000 miles - but probably much longer depending what route my packets take - on a good day typical ping times for me are ~150ms, but bandwidth is also a major issue. On my current ADSL line (that is booked in for upgrade before the end of the month) I get 140 kbs upstream and about 400 kbs downstream - on a good day. On a bad day, it can be much worse. Last year someone decided to start shooting at bits of fibre optic cable in Ohio and that really messed up my day.
In many ways, the fact that Teamprise has a couple of the core development team working remotely over the VPN is good for Teamprise the product because we certainly complain if we encounter any performance issues during dogfooding - issues that the folks in the office with a gigabit connection to the TFS server might not feel as strong about :-)
TFS mostly works great for me over my VPN connection. The TFS team did a lot of work to optimise TFS traffic for WAN environments, such as use of standard web based protocols for transport, the heavy use of compression, minimization of the SOAP payload, keeping connections cached and not to forget things like inventing the TFS Proxy Server - you can certainly tell that TFS was designed with this type of environment in mind. (I'm sure it helps that the majority of the TFS team are based in Raleigh, NC while their dogfood TFS instance is based in Redmond, WA ;-) ). The only times where I have issues are on the rare occasion when Windows File Shares are used or the client needs to query Active Directory information.
Obviously - Windows file shares are an issue for Teamprise in general (with around 50% of the team using non-Windows systems as their primary machine). But most modern operating systems can talk to CIFS based file systems pretty well. The issue for me is that Windows file shares suck over a high-latency, low bandwidth connection. This was one of the reasons VSS performed so badly over a WAN. Thankfully there are only a couple of main areas in Team System and TFS where Windows file sharing is currently used:-
- Publishing of test results from the client
- Accessing the TFS build drop location (and most annoyingly, the build log file).
Note, it because of these types of issues (and the Active Directory releated ones) that I always recommend people connecting into TFS remotely use a VPN if they want access to the full functionality. While the Windows file sharing bits may perform really badly - at least they will work over a VPN. Try getting file sharing to work securely between firewalls if TFS is exposed on the internet and a VPN is not used...
In a follow up post, I'm going to be talking about how to modify your builds to make them easier to work with over a WAN / VPN. Despite these minor niggles (which don't come up very often), if you are considering using TFS for use over a VPN or Wide Area Network then I would certainly recommend it.
January 16, 2008
TFS Top Tip #15 - Baseline your Builds
Microsoft Team Foundation Server 2008 has a great new improved build system (often referred to as "Team Build"). An advantage of running your build from Team Build is that (by default) all changes between your current build and the last good one are reported in the build report and any work items that were associated with those changesets are automatically updated with the build number they were fixed in.
However, when demonstrating this feature - the following thing always catches me out and is important to remember when using Team Build in production. The very first time you run a build, it does not associate that build with changesets or update any work items.
Therefore, the first time your create a new build definition you should manually run a build and make sure it is successful.
In fact I would recommend you do this anyway, even if the changeset association thing wasn't the case, after all you need to test that the new build definition you created actually works!
The reason why Team Build 2008 works in this way is that when Team Build successfully completes a build it stores the label applied to that last good build. The next time it runs a build that is successful it will compare the two labels to detect which changesets were included in the build. It will then look over those changesets for any associated work items and update them to include the build number in which they were fixed.
This tip is also true if you upgrade from TFS2005 to TFS2008. Once the upgrade is complete you should run your builds once manually to check that they are all still working fine and to give a baseline from which changes can be detected.
December 3, 2007
Building Ant projects from Team Build
Update: With Teamprise 3.0 we included this work into the freely downloadable Teamprise Extensions for Team Build. The source is also provided under the MS-PL if you are interested. You should definately look at the new version as it contains some fixes and additional features based on feedback during beta testing.
Original Post:
With the recent release of Microsoft Visual Studio 2008 Team Foundation Server we are seeing more and more people looking to use the build capabilities of TFS (often referred to as "Team Build") to manage their Java based builds as well as their .NET ones. We have an MSBuild task available internally that we use to trigger Ant based builds and report the progress back into TFS, and I wanted to share this with a wider audience to get some feeedback. This task is heavily influenced by Aaron Hallberg's Team Build DevEnv task which I encourage you to go look at if you are interested in getting other build systems integration with Team Build.
You can download an early version of the Ant task from here - (TeampriseBuildExtensions 1.1MB). There are two versions of the task included in the zip file - one for TFS2005 and one for TFS2008. Additionally there is a draft set of instructions included on how to get this working today. We hope to make the process much easier with future releases of Teamprise.
The Ant task works by calling Java to run Ant. The task first parses the Ant file to locate the name of the project and the description. It then calls Ant and the resulting output from Ant is then parsed by the task to look for key information (such as javac and junit tasks) as well as to pass the results into the MSBuild log. Options which are normally available via ant launching script are available as additional attributes to the Ant task.
Example Usage
<Ant TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildFile="$(SolutionRoot)\java\HelloWorld\build.xml"
BuildUri="$(BuildUri)"
AntHome="$(ANT_HOME)"
JavaHome="$(JAVA_HOME)"
Flavor="%(ConfigurationToBuild.FlavorToBuild)"
Platform="%(ConfigurationToBuild.PlatformToBuild)"
Properties="BinariesRoot=$(BinariesRoot);BuildNumber=$(BuildNumber);SourceGetVersion=$(SourceGetVersion)" />
Task Reference
The following is a complete list of all the attributes supported by the task, note that many of them are best left to the default values unless a different behavior is explicitly required. Items that are in bold are the ones that are frequently used.
Parameter |
Required |
Description |
AntHome |
No |
Location of Ant on Build Server. If not specified then the value of the ANT_HOME environment variable will be used. |
AutoProxy |
No |
In Java 1.5+, use the OS proxies |
BuildFile |
No |
Name of the build file to use, by default this is "build.xml" in the current directory. |
BuildUri |
Yes |
The team system URI which uniquely represents the instance of the build being run. With-in a Team Build MSBuild script, this is normally available in the MSBuild property $(BuildUri) |
Debug |
No |
Set to “true” to instruct Ant to print debugging information. By default this is set to “false”. |
Flavor |
No |
The flavor of the build i.e. Release, Debug etc. This will default to "Release". In the Team Build MSBuild scripts, this is normally available as the global property %(ConfigurationToBuild.FlavorToBuild) |
InputHandler |
No |
Specifies the Ant class which will handle input requests |
JavaHome |
No |
Location of Java home directory on build server. If not specified then the value of the JAVA_HOME environment variable will be used. |
KeepGoing |
No |
Instruct Ant to execute all targets that do not depend on failed target(s) |
Lib |
No |
Specifies a path for Ant to search for jars and classes. |
Listener |
No |
Add an instance of an Ant class as a project listener |
Logger |
No |
Specify an Ant class to perform logging. |
Main |
No |
Override Ant's normal entry point with specified Ant class. |
NoClasspath |
No |
Run ant without using CLASSPATH |
Noinput |
No |
Do not allow interactive input in Ant script |
NoJavacBuildSteps |
No |
Set to “true” to suppress the reporting of javac steps to TFS. By default javac steps are added as build steps. |
NoUserLib |
No |
Run ant without using the jar files from ${user.home}/.ant/lib |
Platform |
No |
The build platform i.e. Any CPU, x86, x64. This will default to "Any CPU". In the Team Build MSBuild script, this is normally available as the global property %(ConfigurationToBuild.PlatformToBuild) |
Properties |
No |
Properties to pass to Ant in "name=value;name2=value2" syntax. When calling Ant, it is often useful to pass through properties from the originating MSBuild script – for example Properties="BinariesRoot=$(BinariesRoot);BuildDefinitionName=$(BuildDefinitionName);" |
PropertyFile |
No |
Instruct Ant to load all properties from file with -D properties taking precedence |
Target |
No |
Single Ant Target to execute. If not specified then the default target specified in the script will be used. It is often useful to specify a target that is executed by Team Build and leave the default target to be what would get executed by a developer in a local workstation build. |
Targets |
No |
Comma separated list of ant targets to execute. |
TeamFoundationServerUrl |
Yes |
The URL of the Team Foundation Server to talk to. In the Team Build MSBuild script, this is often available in the property $(TeamFoundationServerUrl) |
Verbose |
No |
Set to “true” to instruct Ant to be extra verbose. |
As part of the next version of Teamprise we will be providing integration with Team Build in the UI (i.e. inside Eclipse based IDE's or in the stand-alone Teamprise Explorer client). More on those build capabilities soon - but hopefully you can see that with the ability to run Ant based builds from Team Build and the ability to control/monitor from Eclipse you will have a nice build system that is fully integrated with TFS.
If you find this task useful, then be sure to drop me a line with any feedback you might have. We have not yet decided how to make the source of this task available, either as an open source project of it's own or as part of a larger community of MSBuild tasks. There are pro's and con's to both. In the meantime, if anybody would like the source code then drop me a line.
October 26, 2007
TFS Top Tip #14: The only 2 things you need to know when customizing Team Build
If you are customizing a team build, then there are only two things you need to know to get you started. The rest you can (mostly) figure out from there.
- Your build is defined in a file called TFSBuild.proj for your particular Team Build Type. You probably figured that bit out already. The first thing you need to know is that the TFSBuild.proj file inherits most of its logic from a file called Microsoft.TeamFoundation.Build.targets which lives in
%ProgramFiles%\MSBuild\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets
.
If you open this file in Notepad you can see that it is exceptionally well commented. There is no smoke and mirrors with Team Build, if it happens at all then it triggered by this file. Note that you should never edit the Microsoft.TeamFoundation.Build.targets file. In Team Build 2008 there are so many hook points provided for you to insert your own logic into the build that you should never need to. Microsoft goes to some rather extreme lengths to ensure backwards compatibility with this file (i.e. a TFSBuild.proj file written with the TFS2005 targets file in mind will work just great with the 2008 targets file). If you modify the targets file, not only are you setting your self up for deployment nightmares but you servicing nightmares as well if you attempt to upgrade to later version or even possibly service packs - don't say I didn't tell you ;-) - If you get a bit lost in reading the targets file - the MSDN help for build customizations is pretty good. The best starting point is the page "Understanding Team Foundation Build Configuration Files" - however the pages that this links to that I refer to constantly are Customizable Team Foundation Build Targets and Customizable Team Foundation Build Properties. That said - most of the comments in those MSDN help pages are actually in the targets file itself - just sometimes the property or task you are after is easier to find in the MSDN help.
July 21, 2007
TFS Top Tip #13 - Watch where you drop stuff
With Team Build in Team Foundation Server, you can have multiple build types for multiple projects. For example, we have a build that runs fast that we can have running Continuous Integration on, and a slower build (that does obfuscation, release packaging etc) that is run on demand when we want to perform a release to QA (some of which later go on to become public releases).
A neat feature in the Team Build stuff is that you specify a drop location for your builds and the packaged binaries along with the build log and test results will be placed in a folder corresponding to the build name. In all the demos you see, this will typically be specified as something like \\server\drop and you may well do this yourself at first. Pretty quickly the drop folder will fill up with builds and get confusing.
A better way is a rather simple solution that I almost didn't mention, however when I mentioned this to a friend of mine and it caused some head-slapping, so I thought I'd discuss it here. Apologies if you'd already figured this out this rather basic step.
Simply, put your builds into folders. Something like:-
- \\server\drop\product\buildType\
To give an example:-
- \\jeeves\drop\teamprise\ci
- \\jeeves\drop\teamprise\release
You can obviously take this further if need be. For example we currently have builds going on in two main places, we are putting Teamprise 2.2 through final testing while, in parallel, we work on the next major release in trunk. In the example above, it means we have 4 builds types in total and can follow the convention of \\server\drop\product\branch\buildType to make:-
- \\jeeves\drop\teamprise\2.2\ci
- \\jeeves\drop\teamprise\2.2\release
- \\jeeves\drop\teamprise\trunk\ci
- \\jeeves\drop\teamprise\trunk\release
Pretty simple really, but helps to plan this stuff up front.
January 4, 2007
TFS Top Tip #12 - Specifying a proxy server at the command line
If (like me) you are using the tf.exe command line to access your Team Foundation Server via a Version Control Proxy from a remote office, then the following tip is extremely useful - much more so than my previous registry hack.
There is currently no option with the Microsoft command line to pass a version control proxy server in to TF.exe. It will pick one up if you have one set in the registry if you have used the Team Explorer GUI - but that isn't great for scripted scenarios. James Manning recently pointed me in the direction of an undocumented environment variable called TFSPROXY. If set, the TFS client will use that setting to proxy your requests. Therefore the following will call tf.exe passing a proxy server to use in your connection:-
@echo off setlocal set TFSPROXY=http://your_proxy_server:8081 tf %* endlocal
I saved this in a batch file called "tfvp.cmd" (for "tf via proxy"), therefore if I want to call tf via my proxy and I'm in a shell that doesn't have the environment variable set I can call my script.
The only command that this really useful for is when doing a tfs get command, as only the download is sent via the version control proxy server, the majority of requests go direct to the main Team Foundation Server application tier that you are connected to.
By the way, if you are using the Teamprise command line client then you can use the /proxy:http://your_proxy_server:8081 argument to specify a proxy server to use for the connection, I've just logged a bug so that we will also accept this undocumented environment variable, but we'll make it so that passing one explicitly will override any picked up from the environment variable.
December 12, 2006
TFS Top Tip #11 - Removing source control files from your local file system
One of the questions that came up from one of our users was "how do I delete the files from my local file system - and tell Team Foundation Server that I have done this".
The first thing you might try is to just delete them locally. However, Team Foundation Server (TFS) uses your workspace to keep track of what files you have downloaded and what version you have of them. The reason it does this is so that it can maintain your files without a costly (both in terms of network and CPU processing) sync step. With TFS, when you say "Get latest", you only get the latest version of files that have changed since you last got them. Nothing is downloaded that you don't need (thereby saving you network traffic). A really neat thing about TFS is that if you delete a file on the server and check that delete in, then when somebody does a "Get Latest", the file is deleted on their local system as well - very nice. Moves and renames also exhibit this behavior - really useful for keeping the local file system in sync with the servers.
But, if you have deleted a file locally (using Windows Explorer for example) then do a "Get Latest" the file is not downloaded - because the server thinks you already have it. You can easily download the file by going to "Get Specific Version..." and selecting the "Force get" option - which will download all files, regardless of if the server things you have them or not.
The question I was asked was how to tell the server you have removed the file from your system, without deleting it from the server. In the end, it took me a while to figure out the answer (and the help of Buck Hodges)
The answer is, if you do a "Get Specific Version..." on the files, and select Changeset 1, the files will be deleted locally and the server will know this. The color of the file in the Source Control explorer will go from black to gray and will have the phrase "Not downloaded" in the latest column.
Changeset 1 is a special changeset on your Team Foundation Server instance. It was created as part of the setup routine and only contains one thing - the root node ($/) in your source control tree. If you do a get for changeset 1 on any actual files then they will not exist at that point in time on the system so will be deleted locally and the server will know this.
Anyway, I thought this worthy of a TFS Tip post. Not only because it highlights how to do something that is non-obvious, but also because once you understand how it works, you will understand a large part of what you need to know about Team Foundation Server Source Control.
October 5, 2006
TFS Top Tip #10 - Keep Your Shelves Tidy
Team Foundation Server source control has a great feature called Shelving. Shelving lets you set store a batch of pending changes onto the server and optionally remove them from your local workspace. It comes in really handy for the times when you want to backup your code and store it on the server but don't want to commit it to source control. I also sometimes use it when I would like a remote colleague to take a look at some code I have written before I commit it into the code base. For more information about Shelving, see the MSDN documentation. A Shelveset is identified by developer and the name the developer gave it when shelving.
One of the features of shelving (and how it differs from working in a private developer branch) is that a Shelveset is in itself not versioned. If the same developer saves a Shelveset with the same name, then it will overwrite the previous Shelveset. This comes in really handy when you have a Shelveset that you commonly use for one thing - for example, I have a Shelveset that I normally call "Work In Progress" (actually, I normally call it "wip" because I am lazy when it comes to typing, but you get the idea). If I need to stop work, but I haven't been able to get to a point where I can check-in the code, then I shelve the pending changes and call the Shelveset "Work In Progress". That way, I only have one of these and I know the purpose of it.
However, most of the time when you shelve, it is a temporary thing. You create a Shelveset and then you unshelve it - and then you no longer want it. The old shelvesets will sit on your shelf gathering dust until you tidy them up by deleting them. The unshelve dialog has a "Delete" button that you can use to delete a particular shelveset.
Additionally, if you press "Details", then you get to see more information about the shelveset in question - but you also get a couple of other options controlling the behavior that occurs while the unshelve is being performed:-
If you un-check the "Preserve shelveset on server" check-box then your shelveset will be automatically deleted after you successfully unshelve it - which is a quite handy (if slightly hidden) feature.
It's good practice to delete the shelvesets when you no longer need them. Not only will it get rid of clutter for you, it will also help when another person is trying to unshelve something that you have placed there for them.
August 31, 2006
TFS Top Tip #8 - Where to get help
Recently, I've had a surge of emails asking various questions about Team Foundation Server and how to do certain things. While I always (eventually) respond, it can sometimes take me a while. If you have questions about Team Foundation Server then the best place to go is the MSDN forums. These forums are monitored by the VSTS team as well as by the Team System MVP's and other folks who know stuff about the product. Unlike a lot of newsgroups, these forums are actually a very welcoming and friendly place to ask questions. I personally hang out on the following forums:-
- Team Foundation Server - General
- Team Foundation Server - Setup
- Team Foundation Server - Version Control
- Team Foundation Server - Work Item Tracking
- Team Foundation Server - Administration
- Team Foundation Server - Build Automation
- Team Foundation Server - Process Templates
- Team Foundation Server - Reporting
- Visual Studio Team System - General
- Visual Studio Team System - Developers
As well as the official MSDN forums, you can also try one of the community ones. These don't tend to get the traffic from Team folks but are still good resources. It's also a good place to ask questions that you might not want to ask on a Microsoft resource (such as how does TFS stack up against Product B) and get an answer from somebody who is not paid by Microsoft.
- Team System Rocks (excellent community resource site)
- TeamFoundationServer.org (a new UK based resource)
At Teamprise we also have our own forum and knowledge base that are worth checking out if the question is related to use of Team Foundation Server from Java or on the Unix / Mac / Linux platforms.
Before you ask the question, it is probably worth checking the MSDN documentation first or using your favorite search engine, but don't worry about asking if you can't find your answer - the folks on these forums are always happy to point you to the best resource.
You never know, if you ask a question on these forums then you might still get me answering it if you are unlucky enough. By asking the question in a public forum you are doing a service for the next person that comes down the line with the same problem as they'll be able to see somebody else with the same issue and (hopefully) the answer.
May 19, 2006
TFS Top Tip #5 - What to do if you lock yourself out of your repository
This tip is a follow up from my rather less helpful post “Don’t Do That” where I discussed the “Inherit security settings” option in the security settings tab for the Source Control Explorer when talking to Team Foundation Server. The issue with this check-box is that if you un-tick it then all the settings for every group on that folder are removed meaning that you have to go through every group and make sure that they have the correct permissions before you press the OK button. Even if you are a project administrator and you un-tick this box and press OK without giving project administrators rights to the folder or file you will not be able to go back and adjust the permissions.
However, I have found a work around should you be left stranded in this way. If you log in to TFS with the credentials of somebody who is a local administrator on the server then you will be able to view the folder and reset the security permissions. The easiest way to do this is to log on locally to your TFS application tier machine as the TFSSetup user and use the source control explorer on that machine to rectify the problem. Obviously it is not ideal but at least you are not totally stranded.
Rather embarrassingly, I found this work-around totally by accident while up on stage presenting my recent talk “Top Ten Tips for Team Foundation Server”. My finale was going to be trashing my TFS installation by un-ticking this box and pressing OK without giving the Project Administrators the appropriate rights – however it wasn’t as dramatic as I’d planned. Turns out that this was because I was also the TFSSetup user on the VPC I was using as the TFS server. At least next time I can make it dramatic and then show folks how to fix it!
April 28, 2006
TFS Top Tip #4: The Command Line Client is your friend.
If you are going to be doing more than the basic check-in / check-out options then it pays to get to know the command line client – tf. The command line client is actually the most flexible and powerful client to Team Foundation Version Control.
For more information, consult the MSDN help documentation. Teamprise users will be glad to hear that a large percentage of the commands are implemented in V1.0 of our command line client meaning that you can do these actions from a Mac or Unix box as well (install instructions are posted in the Knowledge Base). If you are running on Windows then you are probably better off sticking to Microsoft’s command line client that gets installed as part of the Team Foundation Server client installation (and accessible via a Visual Studio 2005 command shell).
For example, to show all the check-outs by everyone on a path in the repository:-
tf status /server:http://yourservernamehere:8080 /user:* /recursive $/TeamProject/
For more examples, see an excellent post from James Manning or some of my previous posts:-
- Hatteras Command Line Tips (from back when tf.exe was called h.exe which saved a keypress :-), however the documentation was no where near as good as it is now )
- Unlocking files in VSTS
The command line client is also excellent for scripting purposes (our automated build system relies heavily on it). I urge you to spend a few moments with a strong cup of coffee, the help documentation and just have a play around. Once you realise what commands are available then you’ll know what’s possible if you come across a situation in the future where you reach limitations in the UI.
April 27, 2006
TFS Top Tip #3: Removing the Resolve Check-In Action from a Work Item
I don’t know about you, but I love associating my check-ins with work items using the Pending Changes view, it makes it so easy to maintain requirements traceability and helps me feel less guilty as I’m doing one more thing that I should have been doing for years.
The only problem with the default definitions for Bug and Task are that if you select the item then by default the “Resolve” check-in action is selected. This is annoying for me as it the action I want to take in probably about 5% of check-ins. 95% of the time I just want to associate and 90% of the time I forgot that I have to change the default setting and end up going back to the work item and re-activating it.
The “Resolve” option is displayed when a state transition in the work item’s workflow is defined as having an action defined for check-in. Below is the transition section from the MSF Agile Bug:-
<REASONS>
<DEFAULTREASON value="Fixed" />
<REASON value="Deferred" />
<REASON value="Duplicate" />
<REASON value="As Designed" />
<REASON value="Unable to Reproduce" />
<REASON value="Obsolete" />
</REASONS>
<FIELDS>
<FIELD refname="System.AssignedTo">
<COPY from="field" field="System.CreatedBy" />
</FIELD>
<FIELD refname="Microsoft.VSTS.Common.ActivatedDate">
<READONLY />
</FIELD>
<FIELD refname="Microsoft.VSTS.Common.ActivatedBy">
<READONLY />
</FIELD>
<FIELD refname="Microsoft.VSTS.Common.ResolvedBy">
<COPY from="currentuser" />
<VALIDUSER />
<REQUIRED />
</FIELD>
<FIELD refname="Microsoft.VSTS.Common.ResolvedDate">
<SERVERDEFAULT from="clock" />
</FIELD>
</FIELDS>
<ACTIONS>
<ACTION value="Microsoft.VSTS.Actions.Checkin" />
</ACTIONS>
</TRANSITION>
If you remove the actions section from the transition from Active to Resolved transition then the only check-in action available will be “Associate”. Admittedly, this means that I have to edit the work item after finishing checking in files to move it from Active to Resolved – but I find this works better for me. I usually leave a little comment in the history as I change the state to say (at a high level) what I did and in the case of bugs any help I need to give to help the fix be verified by the test verification team.
April 26, 2006
TFS Top Tip #2: Changing the Logged In User
When you connect to a Team Foundation Server, the Microsoft client API attempts to connect with your current credentials that the process is running with under Windows. If you are on a different domain than your TFS server or the user that you are logged in with does not have access, then you will be prompted to enter your login credentials using the standard login dialog box that you will be familiar with from Internet Explorer.
However, what if you pass credentials and now you would like to pass in a different set. In Teamprise it is easy enough because we have to ask you them every time you connect. With the Microsoft API your credentials are cached in the standard windows store.
To access this store (and remove your cached credentials allowing you to re-authenticate as a different user) then go to Control Panel, User Accounts, Advanced Tab, Manage Passwords. You should then locate your TFS Server instance and select Remove.
April 25, 2006
TFS Top Tip #1 - WIQL Seperators
I thought I’d try and post some quick Top Tips for Team Foundation Server – in no particular order apart from as I think of them. Today, this came up in the forums so I thought I’d elaborate.
WIQL (pronounced Wickle), stands for Work Item Query Language and is what is used when talking to the work item store in Team Foundation Server. It has a SQL like construct and is used to pass queries to the server. Visual Studio 2005 comes with a Query Editor that generate WIQL. While the query editor is straightforward, it is pretty powerful and allows you to do most things.
However, the query editor is region sensitive which sometimes causes confusion. Take the following example where I am using an “IN” statement to list a set of values for the work item status:-
Note that the values are separated by commas. Those of you from a SQL background find this very sensible, but what the query editor is actually doing is taking the list of values and converting them into the following WIQL statement:-
FROM WorkItems
WHERE [System.TeamProject] = @project
AND [System.State] IN ('Active', 'Pending', 'Proposed', 'Requested')
ORDER BY [Microsoft.VSTS.Common.Rank], [System.WorkItemType], [System.Id]
(A top sub-tip is that it is possible to save WIQL files from Visual Studio by editing the query then selecting File, Save Query As.. and then select file. To run a saved query from the file system double click the *.wiq file from explorer)
The comma separator used by the query editor is actually being picked up from the “List separator” of your regional settings (shown below) (Start, Control Panel, Regional Settings, Customize…)
If you are in one of the many regions of the world that use a different list separator then you have to use that in the Visual Studio 2005 Query Editor. For example, if I change my list separator to be a semi-colon and then re-edit the query in the Visual Studio 2005 Query Editor I get the following:-
This behaviour has some interesting side effects. Remember when I said that the Visual Studio 2005 Query Editor “allows you to do most things”. Well, one small problem is forcing the editor to take a character to say that you want the following to be treated as a string. For example, if you have a comma in the text value you are trying to use in an “IN” statement then you are hosed because the query editor assumes that this is a new value in your list. For example, if you try the following:
This actually gets translated by the query editor as the following:
FROM WorkItems
WHERE [System.TeamProject] = @project
AND [System.AssignedTo] IN ('''Woodward', 'Martin''', '''Sell', 'Clark''')
ORDER BY [Microsoft.VSTS.Common.Rank], [System.WorkItemType], [System.Id]
As you can tell, it parses on the commas first, which is not want you wanted at all. If you manually type in the WIQL correctly as IN (‘Woodward, Martin’, ‘Sell, Clark’)
then the query editor will display this as Woodward, Martin, Sell, Clark
– which in turn gets treated as IN (‘Woodward’,’Martin’,’Sell’,’Clark’)
when the WIQL is generated by the editor.
Hey ho – Clark Sell has a post about changing the regional settings to enable to to query assigned to names but be warned it may have nasty side effects in other programs on your machine.
Hmm. When I thought about posting a TFS Tip a day for the next couple of weeks I didn’t intend them to be this long. Expect the next one to be more concise…
Now playing: Carl Franklin - Avalon, AJAX, Vista, and more with Tim Huckaby