February 2007 - Posts

Incorporating a ClickOnce Application into your Team Build

I found out last week that the Team Build server doesn't perform the essential Publish step that makes a ClickOnce app, well, a ClickOnce app!

But this is easy enough to fix.

Let's start with a simple Hello World ClickOnce app that has one shared DLL dependency. If we do a Team Build for this app, we get the following files in the drop folder:

OneClickApp.application
OneClickApp.exe
OneClickApp.exe.manifest
SharedLibrary.dll

Notice anything missing? To be fair, even in the Visual Studio 2005 IDE, Publish is a seperate step from Build that you must manually invoke via a different menu.

Even if you're building and publishing the project remotely on the build server, you must Publish locally at least once, so all the Publish preferences are stored in the project files.

In order to Publish a project on the Team Build server, we have to make a small edit to the tfsbuild.proj file. Under the SolutionToBuild XML element, add a SolutionToPublish element:

<ItemGroup>
  <
SolutionToBuild Include="$(SolutionRoot)\OneClickApp.sln" />
  <
SolutionToPublish Include="$(SolutionRoot)\OneClickApp.sln" />
</
ItemGroup>

Check this change in, then try rebuilding. Now the drop folder contains the Publish results, too:

OneClickApp.application
OneClickApp.exe
OneClickApp.exe.manifest
SharedLibrary.dll
OneClickApp_1_0_0_0\OneClickApp.exe.deploy
OneClickApp_1_0_0_0\OneClickApp.exe.manifest
OneClickApp_1_0_0_0\SharedLibrary.dll.deploy
setup.exe

See all the new files at the bottom? With that small edit, this build is now functionally equivalent to a local Build plus Publish.

posted by jatwood with 7 Comments

Copying Web Files After a Team Build

After building a web project in a Team Build, you probably want to deploy that website to a target server. And what better place to do this than in the build scripts themselves?

This is pretty easy to do; we'll just customize the AfterBuild event to XCOPY the contents of the \_PublishedWebsites folder somewhere. Note that you must grant permission for the Team Build account to the target UNC path first-- otherwise the copy will immediately fail!

There are two slightly different ways to do this, depening on what kind of web project you chose.

If you are using Web Application Projects, you'll need to edit the project file. The easiest way to do this is to right-click the project and select "Unload". After doing that, you can right click the unloaded project and edit the project file directly within Visual Studio:

Edit the project file to include the following command. The variable $(WebProjectOutputDir) conveniently includes the exact path we want:

<Target Name="AfterBuild">
<
Exec Command="xcopy /y /e &quot;$(WebProjectOutputDir)&quot; &quot\\remote\share&quot;"/>
<
Target>

Save this, check it in, then perform a Team Build. You should see the XCOPY command work in the build report, like so:

Target AfterBuild:
xcopy /y /e "c:\builds\Demo\WebApplication1\Binaries\Release\_PublishedWebsites\
WebApplication1" "\\remote\share"
C:\builds\Demo\WebApplication1\Binaries\Release\_PublishedWebsites\WebApplication1\Default.aspx
C:\builds\Demo\WebApplication1\Binaries\Release\_PublishedWebsites\WebApplication1\Web.config
C:\builds\Demo\WebApplication1\Binaries\Release\_PublishedWebsites\WebApplication1\bin\SharedLibrary1.dll
C:\builds\Demo\WebApplication1\Binaries\Release\_PublishedWebsites\WebApplication1\bin\SharedLibrary1.pdb
C:\builds\Demo\WebApplication1\Binaries\Release\_PublishedWebsites\WebApplication1\bin\WebApplication1.dll
C:\builds\Demo\WebApplication1\Binaries\Release\_PublishedWebsites\WebApplication1\bin\WebApplication1.pdb
C:\builds\Demo\WebApplication1\Binaries\Release\_PublishedWebsites\WebApplication1\bin\WebService1.dll
C:\builds\Demo\WebApplication1\Binaries\Release\_PublishedWebsites\WebApplication1\bin\WebService1.pdb
8 File(s) copied

If you are using Web Site Projects, you must have one Web Deployment project for each Web Site. Until you do, there's nothing to edit, and nothing to deploy. You'll be editing the Web Deployment Project directly: right click it and select "Open Project File" to begin.

It's almost the same as the previous XCOPY command, except the variable you want this time is $(WDTargetDir). Unfortunately this path includes a trailing slash, so we have to add a period afterwards to indicate that we want to copy the contents of the folder.

<Target Name="AfterBuild">
<
Exec Command="xcopy /y /e &quot;$(WDTargetDir).&quot; &quot;\\remote\share&quot;"/>
</
Target>

Save this, check it in, then perform a Team Build. You should see the XCOPY command work in the build report, like so:

Target AfterBuild:
xcopy /y /e "c:\builds\Demo\WebSite1\Binaries\Release\_PublishedWebsites\
WebSite1_deploy\." "\\remote\share"
C:\builds\Demo\WebSite1\Binaries\Release\_PublishedWebsites\WebSite1_deploy\.\Default.aspx
C:\builds\Demo\WebSite1\Binaries\Release\_PublishedWebsites\WebSite1_deploy\.\PrecompiledApp.config
C:\builds\Demo\WebSite1\Binaries\Release\_PublishedWebsites\WebSite1_deploy\.\web.config
C:\builds\Demo\WebSite1\Binaries\Release\_PublishedWebsites\WebSite1_deploy\.\bin\App_WebReferences.compiled
C:\builds\Demo\WebSite1\Binaries\Release\_PublishedWebsites\WebSite1_deploy\.\bin\SharedLibrary1.dll
C:\builds\Demo\WebSite1\Binaries\Release\_PublishedWebsites\WebSite1_deploy\.\bin\SharedLibrary1.pdb
C:\builds\Demo\WebSite1\Binaries\Release\_PublishedWebsites\WebSite1_deploy\.\bin\WebSite1_deploy.dll
7 File(s) copied

You could achieve the same effect by modifying a post-build event in the Team Build script, but I find it easier to modify the individual projects.

posted by jatwood with 2 Comments

Building a Web Site Project with Team Build

If you're still using Web Site projects, I strongly urge you to switch to Web Application Projects. Web Application Projects are included in Visual Studio 2005 Service Pack 1. There are lots of good technical and practical reasons to make the switch, especially in Team System. But I digress.

If you're still using Web Site projects, you'll have to make some modifications before you can build these projects using Team Build.

In order to build a Web Site project at all, you must add a new project type to your solution-- the Web Deployment Project. It's a new project type that you'll need to download from Microsoft and install. But don't worry: it's a very small download, and a quick, painless install.

Once you have Web Deployment Projects installed, right click each web site and select "Add Web Deployment Project".

Once you have the deployment projects set up, make sure they're deselected in Configuration Manager for the Debug builds. There's no need to waste compile time on deployment until you're ready to create a release.

After you do a Team Build for this project, you should see a new _PublishedWebsites folder in the drops folder that contains the website and dependencies:

Now, this is the behavior you'll get by default with a Web Application project, but for each Web Site project, you must have one Web Deployment Project in place to get things working.

posted by jatwood with 5 Comments

Speeding up Team Builds

If you're testing a complex new team build, you may find yourself going through a lot of build cycles. This can be painful, because most complex builds also take a while to complete.

To speed up the build process, you can skip some parts of it. You can do this in one of two places. First, in the TFSBuild.proj, you can insert these elements in the PropertyGroup:

<!-- insert in PropertyGroup -->
<SkipClean>true</SkipClean>
<SkipInitializeWorkspace>true</SkipInitializeWorkspace>
<ForceGet>false</ForceGet>
<SkipWorkItemCreation>true</SkipWorkItemCreation>
<SkipLabel>true</SkipLabel>
<SkipPostBuild>true</SkipPostBuild>

Alternately, you can edit the TFSBuild.rsp file to achieve the same results:

# This is a response file for MSBuild
# Add custom MSBuild command line options in this file
/p:SkipClean=true;SkipInitializeWorkspace=true;
ForceGet=false;SkipWorkItemCreation=true;
SkipLabel=true;SkipPostBuild=true 

(Note the above should all be on a single line; I broke it up for ease of display.) What are we skipping here? Well, we skip...

  • .. deleting the source code folder
  • .. doing a "force get" of the source code, so we use the code that's already present on disk
  • .. labelling after completing the build
  • .. compiling a list of work items that changed in the build
  • .. creating a work item when the build fails

But despite all the skipping, the essential part of the build still completes. And that's usually what we're trying to troubleshoot.

(thanks to Dave McKinstry and Omar Villarreal for these tips!)

posted by jatwood with 26 Comments

Making Team Builds More Verbose

Build engineering is one of the most challenging parts of Team System. It can be difficult to get a Team Build working exactly the way you want. When you're troubleshooting a build, the build log output file, BuildLog.txt, is your best friend.

The build log is fairly helpful as-is, but it's possible to make it even more verbose in those times when you're troubleshooting an obscure build problem.

Visual Studio, like Team Build, also uses MSBuild under the hood to compile solutions. In Visual Studio, you can set the build verbosity via a handy IDE setting. It's under Tools, Options, Projects and Solutions, Build and Run. The "MSBuild project build output verbosity" field offers four values: Quiet, Minimal (the default), Normal, Detailed, and Diagnostic.

You can also set verbosity via a Team Build script.

MSBuild.exe supports the verbosity command line parameter:

/verbosity:<level> Display this amount of information in the event log.
                   The available verbosity levels are: q[uiet], m[inimal],
                   n[ormal], d[etailed], and diag[nostic]. (Short form: /v)
                   Example:
                     /verbosity:quiet

All we need to do is edit the TFSBuild.rsp file in our TeamBuildTypes folder to pass this parameter on to MSBuild.exe:

# This is a response file for MSBuild
# Add custom MSBuild command line options in this file
/v:diag

Check TFSBuild.rsp in, and then do a build. The resulting BuildLog.txt will be much more.. er.. verbose!

Here are the results of running a build for each verbosity level:

diagnostic825 KB
detailed288 KB
normal48 KB
minimal12 KB
quiet0 KB

The default verbosity level for Team Build is normal, so be careful when ratcheting it all the way up to diagnostic!

posted by jatwood with 1 Comments

Team Foundation Server Event Subscription Tool

The Team Alerts dialog lets you subscribe to basic email alerts on a Team Project.

But the dialog is severely limited; the full richness of the email subscription mechanism in Team Foundation Server isn't available. To set up advanced subscriptions, you needed to use bissubscribe.exe. This is problematic, because the command-line syntax is complicated, and also because bissubscribe.exe is only available on the Team Foundation Server.

To address this deficiency Naren posted a GUI tool that lets you create Work Item Event Subscriptions in July 2006. I took that tool and modified it so that it supports all event subscriptions, along with a bunch of other enhancements The new Team Foundation Server Event Subscription Tool is up on CodePlex now.

Some examples of subscriptions you might want to create are:

Email me when any new work item is created:

PortfolioProject = 'TeamProject1' AND ChangeType = 'New'

Email me when any work item moves to the resolved state:

"CoreFields/StringFields/Field[ReferenceName='System.State']/NewValue\" = 'Resolved'

Email we when a specific (string) field changes in a work item:

PortfolioProject = 'TeamProject1' AND "ChangedFields/StringFields/Field[ReferenceName='field']/NewValue" <> null

Email me when someone checks source code into a specific folder:

'Artifacts/Artifact[starts-with(@ServerItem, \"$/TeamProject1/A\")]' <> null

Email me when someone overrides a checkin policy:

TeamProject = 'TeamProject1' AND PolicyOverrideComment <> ''

The queries are all based on XPath applied to the Event XML. As you can see, it's quite powerful-- far more powerful than the simplistic Team Alerts dialog would lead you to believe!

posted by jatwood with 9 Comments

The path <path> is already mapped in workspace <workspace>

I recently ran into a problem with a local TFS server. Although I was logged in as my local account, TFS insisted that I was logged in as Administrator. Since the server thought I was a different user, it prevented me from opening my local workspace with this error:

The path is already mapped in workspace

The path conflict error reminds us of one important fact about workspaces: only one local path can be mapped, per user, per team foundation server. Two users can't share the same filesystem path. And two different team foundation servers can't share the same filesystem path, either.

The system caches your workspace mappings in this location, so if you're having workspace-related problems, it's worth clearing this cache file out:

C:\Documents and Settings\[user]\Local Settings\Application Data\Microsoft\Team Foundation\1.0\Cache\VersionControl.config

As Buck Hodges points out, you can also clear the local cache file at the command-line:

tf workspaces /remove:*

But clearing the cache didn't help in my case. No matter how much I cleared, how many times I rebooted or logged off, I kept showing up as "Administrator", which means I was conflicting with myself! I could map to a new folder, but that's not what I want. How is this possible?

Well, it's possible if you physically log in to the Team Foundation Server using a different set of credentials. Those credentials are cached deep in the bowels of Windows, and retrieved automatically the next time you contact the server. That's our problem!

To clear the credential cache, try Start, Run, then type

control userpasswords2

And press enter. On the resulting dialog, click the advanced tab, and click Manage Passwords. Remove all cached credentials from this list.

Once I cleared the cached credentials, I was able to access the Team Foundation Server as myself again. Problem solved.

With this in mind, if you must log in to the Team Foundation Server, try to do so under your own credentials. It'll cause less problems later.

posted by jatwood with 1 Comments

Modifying the default Team Build

One annoying thing about Team Builds created through the Team Build wizard is that they pull down all the source code in the entire Team Project by default. This is almost never what I want. But it's easy enough to fix.

First, check out the TFSBuild.proj and WorkspaceMapping.xml files from the TeamBuildTypes folder in source control.

Remember, the build user is just a user, exactly like you. And it has workspace mappings, just like you do. But in this case the workspace mapping is too broad. It's at the root of the Team Project. Let's fix that by editing WorkspaceMapping.xml. Change it from this..

<Mappings>
  <InternalMapping ServerItem="$/TeamProject1" LocalItem="C:\does-not-matter" 
    Type="Map" />
</Mappings>

To this..

<Mappings>
  <InternalMapping ServerItem="$/TeamProject1/DemoBuildWebApp"
    LocalItem="C:\does-not-matter"
    Type="Map" />
</Mappings>

All we're doing is adding the Solution folder to the Team Project path. This is the key fix-- until we do this, the build user will get latest on the entire source tree. That's no good.

Two additional notes about the build user workspace mapping file:

  • If you do have a legitimate need to pull down a giant source tree, you can make it a little less painful by cloaking (aka hiding) some of the subfolders here.
  • The path specified here is truly irrelevant. I changed it to C:\does-not-matter to make a point. The local path is always the server build folder, so whatever you put here is completely ignored.

Next, we need to update our compilation path to reflect the fact that it's no longer in a subfolder. Edit TFSBuild.proj and modify this section from..

<SolutionToBuild Include="$(SolutionRoot)\DemoBuildWebApp\DemoBuildWebApp.sln" />

To this..

<SolutionToBuild Include="$(SolutionRoot)\DemoBuildWebApp.sln" />

Now check in the changes, re-build, and you'll see in the build report and the build folder that only the specific source in the solution subfolder was retrieved. Big improvement!

posted by jatwood with 2 Comments

Displaying Team System reports in the Team Portal

You can display Team System reports in the Team Portal using the default Page Viewer web part, which redirects to the URL

_layouts/tfsredirect.aspx?IsReport=1&ReportName=name

But there's a better way.

I recommend installing the SQL Server 2005 Reporting Services Web Parts. They're already on your server, but they're not set up. To set them up, run this command on your Team Foundation Server web tier:

C:\Program Files\Common Files\Microsoft Shared\web server extensions\60\BIN\
STSADM.EXE -o addwppack -filename 
"C:\Program Files\Microsoft SQL Server\90\Tools\
Reporting Services\SharePoint\RSWebParts.cab"

Once you do this, you'll have two new Web Parts specifically designed for reporting, so you no longer need to rely on the generic Page Viewer. Note that the new controls are in the "Virtual Server Gallery":

Here's the Report Explorer web part in action

And here's the Report Viewer web part in action:

The report gadgets work better. They offer much more control over the report output. For one thing, if you set any parameters to the report in design mode, those parameters are saved. You can also turn off the toolbar so the report comes up pre-configured to whatever parameters you last set.

Note that you will need to bits of information in the properties for each control:

  • Report Manager URL: http://teamsystem/Reports
  • Start Path (explorer): /teamproject
  • Start Path (viewer): /teamproject/reportname

Remember, friends don't let friends use the generic Page Viewer web part to view Team System reports!

posted by jatwood with 4 Comments