Tuesday, November 14, 2006

Creating custom tasks for Team Build

When you create task for Team Build, the usual rules of custom MSBuild task apply, but there are little differences.

Probably, when you create custom task for Team Build you will want to use TFS object model to access version control. For that purpose, first you will need to establish connection to TFS server.

Most obvious approach would be to implement task with server name parameter and establish connection using that parameter:


public class SimpleTfsTask : Task
{
    private string _serverName;


    [Required]
    public string ServerName
    {
        get { return _serverName; }
        set { _serverName = value; }
    }


    public override bool Execute()
    {


        TeamFoundationServer server =
         TeamFoundationServerFactory.GetServer(_serverName);
        // ...
    }
}



Now, the downside of the approach is obvious - the server name parameter should be either placed inside the build script or in somewhat better way supplied as external parameter for the build.

But wait a minute - how come that most of Team Build predefined tasks (for example, those in Microsoft.TeamFoundation.Build.Tasks.VersionControl assembly) do not have that parameter? Whence the Team Foundation server url is retrieved?

The solution is rather simple - Team Build tasks that do not have server parameter must be used in context of workspace (that is it is supposed that build workspace exists before those tasks are called). Tasks that cannot have such context (for example, CreateWorkspaceTask) have TeamFoundationServerUrl parameter.

Let's have a look at how to get Team Foundation server url given valid workspace.
The code below first gets workspace in which specified local path is mapped (not Workspace class but rather WorkspaceInfo that uses local cache) and then Team Foundation server data is retrieved from that workspace properties.

public class AnotherTfsTask : Task
{
    private string _localPath;
    [Required]
    public string LocalPath
    {
        get { return _localPath; }
        set { _localPath = value; }
    }
 
    public override bool Execute()
    {
        WorkspaceInfo workspace =             Workstation.Current.GetLocalWorkspaceInfo(_localPath);
        TeamFoundationServer server =             TeamFoundationServerFactory.GetServer(workspace.ServerUri.AbsoluteUri);
        // ...
    }
}



Obvious advantage of the second approach is that if you have workspace (retrieved either by path or by workspace name), you may always create Team Foundation server instance to access version control or its other services.

In conclusion, I would like to note that it is wise to use TeamFoundationServerFactory rather than constructor to create TeamFoundationServer instance, as factory approach uses caching and given that usually single build script connects to only one TFS server that may result in noticeable performance boost.

1 comment:

Anonymous said...

Thanks eugenez - very useful yet again.

Grant
www.holliday.com.au | www.oztfs.com