Thursday, May 29, 2008

Adding an Organization Hierarchy in Team Build


We really like Team Build. Especially Team Build 2008 which has the continuous integration ability. However any good product can always be improved.

To the left you'll see a screen shoot of one of our Team Projects and the builds under it. You can't see them all but there are 42 build definitions setup. Having them all in a flat view can be a bit overwhelming. Adding the ability to group these build types would be very cool. For example, the DownloadServlet builds might be under a folder called \Team Builds\DownloadServlet. Builds that run the Unit tests could be grouped as well.

The feedback I'm expecting is, why didn't we just break these out into separate Team Projects. Separate Team Projects would build the organization that I'm suggesting. While valid, we have hundreds if not thousands of "applications" to support. Most of them are supported by sub-groups in the company. For example, the applications behind these build types are all worked on by one group. We find it easier to group the applications together in one Team Project instead of all having autonomous Team Projects. We do the same for other groups and it works very well.

What are your thoughts? Would adding the ability to "group" build types be advantageous for other teams?

Tuesday, May 27, 2008

Bounce IIS after re-stamping your TFS Databases with InstanceInfo.exe

We saw an interesting scenario today after restoring our TFS PROD instance to a TFS QA environment for testing. Even though we changed the Instance ID for each database (see here for instructions), the App Tier was still rendering the old Instance ID for the TfsVersion control database. This was causing the Source Control view to continue to look at the PROD server even though it said "QA". All other databases were rendering with the new Instance ID.

If you're not familiar with the Instance ID, let me take a second to explain it. The Instance ID is basically a GUID (globally unique identifier) for the TFS databases. While you may refer to the TFS Server as tfs.mycompany.com, your client software (e.g. Team Explorer) prefers to use the GUID.

From what Grant Holiday says, basically what happens is your client will send the URL (e.g. tfs.mycompany.com_ to the server's Registration.asmx web service to get the GUID (e.g. 2dbgh947-049b-7z6c-94y2-4b0767ggf790). Once the client has the GUID it will use that for future communication.

In our case, we properly changed the GUID using the InstanceInfo.exe utility and directions in this forum. However, the App Tier must have been caching the TfsVersionControl GUID as both PROD and QA were responding with the same GUID name, even though the database GUID was restamped. Here are the PROD and QA sections of our VersionControl.config file stored under %USERPROFILE%\Local Settings\Application Data\Microsoft\Team Foundation\2.0\Cache. Interesting enough, the repositoryGuid would stay the same, but the uri would change when we switched TFS Servers. Bad!!!

ServerInfo uri="http://tfsqa.mycompany.com:8080/" repositoryGuid="2dbgh947-049b-7z6c-94y2-4b0767ggf790"
ServerInfo uri="http://tfsprod.mycompany.com:8080/" repositoryGuid="2dbgh947-049b-7z6c-94y2-4b0767ggf790"

The fix was to bounce IIS on the QA App Tier. This reset the App Tier cache and thus resolved our issue. The lesson learned here is to bounce IIS after you re-stamp your TFS databases with new GUIDs.

Thursday, May 15, 2008

Team Build's 'Get' always writes files with the current date

We use Team Build for all of our builds including the packaging of our Static Content application. When Team Build does a 'Get' of Sources it writes the files with the creation/modify date of the current build time. For example if I checked in a file (e.g. Mac.jpeg) yesterday (5/14/08), but didn't run a Team Build until today, the file date would be today (5/15/08). For most things this is not an issue, but for Static Content it is.

The reason having the current date/time on Static Content is an issue is because of the way the browser caches things. When a request is made to our site(s), the browser will see that we have images (e.g. Mac.jpeg). Before grabbing the image from the web server the browser will check its local cache. If the image in the local cache has the same modify date as the image on the web server, the image is not requested, thus improving performance. However, if the local cached version of the image file is older than the web server's version, the new image is sent to the requested user.

The issue is, because Team Build always writes the files with the current date (e.g. 5/15/08), when we deploy them to the Static Content servers they all look like updated image files. This is bad as it invalidates ever user's cache and thus slows down our application as every image is sent to the user.

I've read a bunch of things on this and from what I've heard, the TFS Product Team might be looking into a fix for this. I've dropped our local Microsoft rep a note asking him if he had any details about that. I've also found this post from Cory Foy which looks like a custom task we might be able to write in order to get the behavior we need. If I get any word back from my Microsoft contact, I'll let everyone know. In the mean time, we are looking into Cory Foy's approach.

I'm also going to drop a note to the good folks over at Teamprise to get their input. We're using their Ant interfaces to TFS for some of our non-Microsoft builds and thus could have a similar issue.

Thursday, May 08, 2008

Bug in the TFS 2008 Uninstall on Standby App Tier

A colleague of mine, who has taken over many of my former TFS duties, ran into an interesting problem today. For reasons I won't get into, we were trying to uninstall TFS 2008 from our Standby App Tier.

The error we ran into was as follows. We found this error in the VSMsiLog which is created for installs and uninstalls.

05/08/08 11:35:14 DDSet_Status: --- STATUS: Found Reports.ReportsService=http://tfs.mycompany.com/ReportServer/ReportService.asmx
05/08/08 11:35:14 DDSet_Status: --- STATUS: Writing VSTF_RS_SERVER=tfs.mycompany.com into C:\Documents and Settings\appwesttfnqasetup\Local Settings\Temp\TfsCurrentConfig.ini section Config
05/08/08 11:35:16 DDSet_Error: *** ERROR: Failed to call WMI on the RS server. The most likely cause is that the firewall is blocking WMI calls or that the RS server is not reachable: The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
05/08/08 11:35:16 DDSet_Status: Process exited with exit code: 16

05/08/08 11:35:16 DDSet_Error: GetCurrentTfsProperties failed with exit code: 16
MSI (s) (B4!A4) [11:35:22:083]: Product: Microsoft Visual Studio 2008 Team Foundation Server - ENU -- There is a problem with this Windows Installer package. Please refer to the setup log for more information.

You'll notice that the uninstall was looking for the value ReportService which is found in the TfsIntegration database's tbl_service_interface table. The value in that table is a FQDN which we use because we have Primary and Standby App Tiers. As defined in the TFS documentation, only one is active at a time.

The Bug in the uninstall is, if you are setup behind a FQDN the value of ReportService will be the FQDN (e.g. tfs.mycompany.com) instead of the value of the Standby App Tier, which is what you're trying to uninstall.

A quick Google search led me to this blog post by Nick Berardi on his Coder Journal. He ran into a similar issue during an upgrade. He got around the error by changing the ReportService value to match his App Tier.

While I never recommend messing with the TFS databases, being we're in a QA environment I gave Nick's idea a shot. Sure enough, the uninstall finished successfully. I then changed the value back to our FQDN so the Primary App Tier could be functional.

I'm not sure why the uninstall would need these values for Reporting Services. From what the uninstall documentation says, you must uninstall Reporting Services (and Share Point) as an independent step? Anyway, again while I never recommend manually changing values in the TFS databases, we ended up needing to as to get around the uninstall issue.

Thursday, May 01, 2008

Deployment to IIS 7.0 using Power Shell

Yes, I'm still here. Sorry for not writing anything lately on TFS. I've been in one of those two week Agile (with a capital A) "Iterations" which is when we get all the work done.

I don't really have anything important to share right now on our TFS adoption. Everything is going pretty good. Knock on wood!

While not really related to TFS though, I've fallen in love with Power Shell 1.0. Everyday I find another problem to solve with "commandlets" and all the great things a full featured shell offers. Along with the AppCMD command which ships with IIS 7.0, we're using Power Shell scripts to drive our deployment mechanism. Here is a simple example of what our scripts look like. It's actually working very well for us and far less overhead than creating an msi or Install Shield package.

You'll notice that we take the nuke and pave approach to deployment. That is, we check to see if the application directory is there, and if it is, we purge it. Then we copy over the new application version's code.


#Always get the script root. Per Microsoft's recommendation, each script should know their respected script root.
$SCRIPT_ROOT = Split-Path (Resolve-Path $myInvocation.MyCommand.Path)

#check to see if these values are already set. If not, set them to defalts.
if ($ENV_SITE_NAME -eq $null) {sv ENV_SITE_NAME -value Website}
if ($ENV_APPPOOL_NAME -eq $null) {sv ENV_APPPOOL_NAME -value WebsiteAppPool}
if ($ENV_PORT_NUMBER -eq $null) {sv ENV_PORT_NUMBER -Value "8080"}

sv APPCMD -value $env:systemroot\system32\inetsrv\AppCmd.exe
sv SITE_INSTALL_PATH -value "D:\Web Sites\$ENV_SITE_NAME"
sv APP_SERVICES -Value "aspnet_state", "W3SVC"
# ***************************************************
# Step 1)
# Clean up old install.
Invoke-Expression "$APPCMD delete site $ENV_SITE_NAME"
Invoke-Expression "$APPCMD delete apppool $ENV_APPPOOL_NAME"
if (Test-Path $SITE_INSTALL_PATH) {rmdir -Recurse -Force $SITE_INSTALL_PATH}
# ***************************************************# Step 2)
# Make dirs
New-Item $SITE_INSTALL_PATH -type directory
# ***************************************************
# Step 3)
# Create the AppPool for this site
Invoke-Expression "$APPCMD add apppool /name:$ENV_APPPOOL_NAME"
Invoke-Expression "$APPCMD set apppool /apppool.name:$ENV_APPPOOL_NAME /processModel.identityType:NetworkService"
# ***************************************************
# Step 4)
# This will add the site on port 81. This does not create an App or Virtual Director by default. *****
Invoke-Expression "$APPCMD add site /name:$ENV_SITE_NAME /bindings:`"http/*:${ENV_PORT_NUMBER}:`""
# ***************************************************
# Step 5)
# Craete App with the physical path and set the Apppool we need ******
Invoke-Expression "$APPCMD add app /site.name:$ENV_SITE_NAME /path:/ /physicalPath:`"$SITE_INSTALL_PATH`" /applicationPool:$ENV_APPPOOL_NAME"
# ***************************************************
# Step 6)
# Copy over files recursively (/E and /I), overwrite read only files (/R), surpress comfirmation (/Y), quietly *****
echo "Copy Files to WebSite"
xcopy $SCRIPT_ROOT\Website $SITE_INSTALL_PATH /E /I /R /Y /Q
# ***************************************************
# Step 7)
# Make sure any needed services are started before finishing and spits out their status.
Invoke-Expression "$APPCMD start apppool /apppool.name:$ENV_APPPOOL_NAME"
Invoke-Expression "$APPCMD start site /site.name:$ENV_SITE_NAME"
$APP_SERVICES Start-Service
$APP_SERVICES Get-Service
# ***************************************************
# Step 8)
# Make sure we didn't get any errors at deployment.
if ($error.count -lt 1)
{
Write-Output "" "Deployment Successful!" ""

}
else
{
Write-Output "" "Deployment Failed!" ""
$error
Write-Output ""
}