Check-in policies help enforce your organization's development methodology and ensure that the code being checked in passes the necessary quality requirements. In addition to using the pre-defined check-in policies, you can create custom ones.
Team Foundation Server 2008 in Action
EARLY ACCESS EDITION
MEAP Release: March 2008
Softbound print: November 2008 (est.); 375 pages ISBN: 1933988592
This article is taken from the book Team Foundation Server 2008 in Action. This segment looks at how to use TFS Power Tools to apply custom policies at the branch level.
Using the Custom Path Policy
After installing TFS Power Tools, you'll find a number of new policies available in the Add Check-in Policy dialog box (see Figure 1). One of the newly available policies is the Custom Path policy. In the Custom Path Policy configuration dialog box, you have the option to associate a single check-in policy with one or more folders in TFVC (see Figure 2).
TIP: in order to be able to select a policy from the Select Child Policy drop-down, a child policy should already be added to the team project. You also need to mark the policy as disabled. Otherwise, the child policy will be executed twice. Moreover, due to a bug in TFS 2005, after adding a child policy to a team project, you need to close the Source Control Settings dialog box (click OK) and then launch the dialog box again. The newly added child policy will be available for use in Custom Path policy.
Figure 1 TFS Power Tools provides additional check-in policies.
Figure 2 You can associate a check-in policy with specific folders using the custom path policy.
Creating a Custom Policy
In this section, we create a custom policy for associating check-in policies with branches. The motivation for creating a custom policy is to improve the user experience associated with defining, executing, and maintaining branch-specific child policies.
The Custom Path policy (available in TFS Power Tools) allows you to associate a single child policy with a folder, at a time. To associate multiple policies with one or more branches, the Custom Path policy has to be invoked multiple times. If there are many combinations of policies and branches, the Source Control Settings dialog box (in the Check-in Policy tab) becomes cluttered, and you can not tell which instance belongs to which policy and branch.
The custom policy that we create allows you to view branches and corresponding child policies in a single form. Only a single instance of the custom policy appears in the Source Control Settings dialog box (Check-in Policy tab). This approach enhances usability and makes it easier to create and maintain branch-specific policy associations.
Moreover, the Custom Path policy executes the child policies in the alphabetical order of their names, no matter which order you added them in. For example, if you specify Work Items, Testing Policy, and Code Analysis policies, in that order, the policies will be executed in the following order:
- Code Analysis
- Testing Policy
- Work Items
This behavior is similar to the policy execution order when check-in policies are defined at the team project level.
The custom policy that we create provides the flexibility to define the policy execution order. The policies are executed in the order that they are added. This feature gives you greater control, as sometimes it becomes necessary to execute check-in policies in a specific order, due to their logical dependencies.
Our functional approach is as follows:
- Create a custom policy which is invoked when a developer attempts to check in code. Just like the Custom Path policy in TFS Power Tools, the child policies are defined at the team-project level and marked disabled (see Figure 3). However, unlike the Custom Path policy in TFS Power Tools, as discussed, only a single instance of the policy appears in the Source Control Settings dialog box (in the Check-in Policy tab), and the child policies are executed in the order that they are added.
- When you add the custom policy, a dialog box is displayed where you can specify branches and associated policies (see Figure 4). Click Add Branch to specify a branch. Click Add Policy to select the policies associated with the branch. When you click Add Policy, a dialog box is displayed containing the list of policies installed in the client machine.
Figure 3 A single instance of the custom check-in policy appears in the Source Control Settings dialog box (in the Check-in Policy tab).
Figure 4 The custom policy displays branches and associated policies in a single form.
In order to create a custom check-in policy, you need to understand two interfaces—IPolicyDefinition and IPolicyEvaluation (see Figure 5). When creating your custom policy class, you can either directly implement these two interfaces, or simply inherit from the PolicyBase base class (see Figure 6). The PolicyBase class provides default implementations for IPolicyDefinition and IPolicyEvaluation interfaces.
IPolicyDefinition is used when defining a policy—it contains simple properties to specify the description, installation information etc, as well as a method to edit the configuration options (if any) for the policy. When you install a policy for the first time, as well as when you subsequently click Edit in the Check-In Policy tab, TFVC evaluates the value of the CanEdit property. If the value is true, the Edit method is invoked. If you have configurable properties for your custom policy, display a dialog box in the Edit method. The user-specified selections are stored as object properties. The properties are serialized to storage in TFS. The framework deserializes the configuration properties when the policy class is instantiated during policy execution or when you click Edit in the Check-In Policy tab. That is why, the policy class needs to be marked as [Serializable]. Serialization and deserialization of the configuration options take place behind the scenes by TFVC—you only need to mark the policy class as [Serializable] (as well as ensure that you are using serializable classes for storing the configuration options) and proceed normally.
IPolicyEvaluation is used when executing a policy during check-in. TFVC calls the Initialize method first, passing information about the files that are about to be checked in; the parameter type is IPendingCheckin. IPendingCheckin contains a number of useful properties, such as associated work items, check-in notes, and the changes that are ready for check-in (see Figure 7). The pending changes are encapsulated in an object of type IPendingCheckInPendingChanges. IPendingCheckInPendingChanges also contains a number of useful properties such as the name of the workspace, all changes available in the workspace, the changes which have been marked for inclusion in the current check-in, the team projects which will be affected by the current check-in (remember, you can map multiple team projects to a single workspace), and the check-in comment (see Figure 8). You could for example, create a policy to make sure that developers do not check in files without writing comments associated with the check-ins—a common-sense best practice for change management.
The Evaluate method does the main work—it executes the policy rules. The method returns a collection of PolicyFailure objects (see Figure 9) if errors are encountered during rules processing. If a non-empty PolicyFailure collection is returned from the Evaluate method, TFVC cancels the check-in and displays the errors to the user (based upon the Message property of the PolicyFaulure objects in the return collection). If the user double-clicks a policy failure message, TFVC calls the Acivate method of the corresponding PolicyFailure object. Notice from Figure 9 that the PolicyFailure object, via its Policy property, knows which policy produced the error. Consequently, when the Activate method is invoked on a PolicyFailure object, the call is delegated to the Activate method of the actual policy object. If the user presses F1 for help, TFVC calls the DisplayHelp method of the PolicyFailure object; this call is also delegated to the DisplayHelp method of the corresponding policy object.
Figure 5 The IPolicyDefinition and IPolicyEvaluation interfaces; these interfaces are needed for defining a custom policy.
Figure 6 The PolicyBase class provides a useful base class for creating custom policies; it also contains some default implementations.
Figure 7 The IPendingCheckIn interface provides properties methods to obtain information associated with a check-in.
Figure 8 The IPendingCheckinPendingChanges interface provides granular information about the changes as well as other relevant information.
Figure 9 The PolicyFailure class contains methods and properties to display policy violations as well as to launch screens for help and remediation purposes.