Sunday, June 15, 2008

Configuring check-in policies

One interesting moment to be aware of in custom check-in policies implementation is how to implement the policy configuration. Overall, it is rather easy to implement custom check-in policies and there is a whole lot of documentation available (I especially like and highly recommend excellent article in MSDN Magazine by Brian Randell).

The configuration is initially performed when you define check-in policy; later, the configuration can be performed using “Edit” button in Source Control Settings dialog. Good example of configurable check-in policy is Code Analysis policy, since there user has to specify set of rules applicable in check-in policy evaluation.

When you create your custom policy, you override Edit method from IPolicyDefinition interface to display your own custom configuration dialog. But what do you do with the values specified by the user in the dialog (no matter whether it is elementary type or custom data structure)?

None of IPolicyXXX interfaces you implement in your custom policy provides any special methods for storage or retrieval of configuration. TFS serializes the instance of your custom check-in policy class, so to make sure your custom configuration is available, you need to expose it as the class member variable. Since you policy class  is marked as Serializable, the value will be persisted and available when the policy is evaluated at check-in time.

The mechanism is very simple; only caveat is that you must make sure that any internal variables that are not to be persisted are marked as NonSerializable. Here is small example:

[Serializable]
public class Policy : 
   Microsoft.TeamFoundation.VersionControl.Client.PolicyBase
{
    // Configuration to be serialized – may be used in Evaluate etc.
    private string _configuration;
 
    // Required for internal logic; do not serialize
    [NonSerialized]        
    private _DTE _dte;
 
    //...
}

Now, what if the policy configuration should be global and easily changeable? Then the mechanism above for all its simplicity is not very suitable (since once you change the configuration, the data gets serialized into bowels of TFS and is not readily available). Another problem you might face is the versioning of the policy – since the policy is defined on per Team Project basis, when you release a new version and it contains breaking changes to configuration, you will not only have to redeploy it on all client workstations, but also re-add it in all Team Projects.

What I did in such cases (and mind you, this is only one possible approach) is to make configuration file external to the policy implementation (meaning that policy will only consume the configuration but will not edit it). It may be tempting to make that configuration local, but that will actually make things worse (think about synchronizing configuration across all workstations). The easiest solution I have found so far is to put the configuration files somewhere on TFS server and make them accessible over http (using URL similar to “http://tfsserver01/configuration/policy_v1.xml”). Since in most cases TFS server URI is readily available from insides of check-in policy code, this location can be considered well-known. Of course, you would not want to put any security related data (passwords etc.) in that configuration file, but for most custom check-in policies that should not be a concern.

Using that approach, to change the configuration for your policy, you’d modify the external XML file. It may be one global file, or one per version of the policy or even one per Team Project (depending on your custom logic) – but since it will be external to serialized policy, the configuration is easily versioned and changeable without ever touching serialized data in TFS database.

No comments: