How to build a custom check-in policy?
One of the things that can be configured on a per project basis is the set of check-in policies that are enforced for a project. Microsoft ships Team System with a set of four policies. These policies are:
- Clean Build
- Code Analysis
- Testing Policy
- Work Items
There are quite some other interesting policies that you might want to enforce before source code gets checked-in. For example you might want to make sure that certain pragma directives in C# are not used to suppress XML documentation warnings, or you want to make sure the projects are configured to generate XML documentation during compilation. Because Microsoft can’t ship all policies one might think of in a project, they provide an extensibility model instead. In this article I describe how you can implement your own check-in policy using the extensibility features.
Before we can start building our own custom policy we need to have an understanding of how policies are enforced in team system. The policy architecture can be split in two distinct phases. One is the definition of the policies you want to enforce for a project and the second phase is the policy enforcement itself.
Policies are enforced by the policy engine that runs inside the Visual Studio IDE. This is an important observation, because this fact implies that a policy that needs to be enforced needs to be installed on the developer machine before it can be evaluated.
A policy is just an assembly that implements a class that has two interfaces. The first interface is used for the definition of the policy and is called IPolicyDefinition. The second interface is called IPolicyEvaluation and does the real policy evaluation. You can see the definition of the interfaces in the picture below.
The IPolicyDefinition interface is important during the definition phase. When a project manager or development lead wants to define the policies he wants to enforce for his project, he can go to the Visual Studio Team Explorer and select the option to configure Source Control. There he will find a dialog that shows a list of available policies that are registered on the local machine. This list of policies is retrieved from the local registry.
( HKEY_LOCAL_MACHINESOFTWAREMicrosoftVisualStudio8.0TeamFoundationSourceControlCheckin Policies)
So if you would like to use a custom build policy, you need to install the policy locally on the machine.
Note: If your register your assembly in the registry, make sure the name of the key you add exactly matches the name of your assembly. This is the dll name without the dll extension
Once the dialog is shown, the policy assembly will be loaded and the Type and Description methods of the IPolicyDefinition will be invoked to get the information to be displayed to the end user.
When the policy is selected and Ok is clicked, the policy engine will get additional information from the assembly and then will call the CanEdit property to determine if the policy can be edited before activating. If this property returns true the method Edit is called on IPolicyDefinition.
The edit method provides the implementer of the policy the option to show a dialog to the user in which he can specify specific options for the policy. The values of these options are persisted by serialization of your policy class. So make sure all settings can be serialized and that the policy class is marked as Serializable. The settings are serialized into the Team Foundation server once the policy is defined for the project. After the call to the Edit method the engine will call the InstallationInstructions property to get textual information how the policy can be installed on a machine where the policy appears to be absent when the policy engine wants to evaluate the policy. This information can be shown to the developer so he can take the appropriate actions to install the policy on his local machine. Now all this information gets serialized to the server so the information can be used on all machines when someone checks-in sources.
The IPolicyEvaluation interface is used when the policy is activated during a check-in procedure. The interface has, besides a set of methods, also an event that informs the Visual Studio IDE when a policy assembly wants to signal that the status of the policy has been changed. For example: You can write a policy that uses a file watcher to monitor changes in a file. When the file changes and this change will change the state of the policy, it can signal this to Visual Studio using the event, so it can update its UI according to the policy state.
The policy gets activated by the Pending Check-in dialog. (Shown below) When the Check-in or policy icon is clicked for the first time, the Activate method will be called on the IPolicyEvaluate interface. This is the moment where we receive information about the pending changes that can be checked-in. We can also make a subscription to an event that gives us a signal when something in the list of pending changes has been changed. For example: when the user unchecks or checks a file in the pending changes file list, we get a notification if we subscribed to the event. The information of pending changes also contains a list of files that we can use to evaluate the policy. When we create a policy that checks whether the project that is under source control has XML documentation generation enabled, we can use the list to check if the user wants to check in a project file. If so, we can open the project file and search for the XML tag DocumentationFile. If this node tag is available and has a value, we know the documentation is going to be generated during compilation and the policy is satisfied.
In the code sample below I implemented a policy that checks if a C# project that gets checked-in has documentation enabled. If so the policy is satisfied, otherwise a PolicyFailure is reported back to Visual Studio.
Just some quick tips when you try and use the samples below:
- To debug the policy, set the debug properties of the class library project to start a new instance of Visual Studio .NET. When you activate the policy in the IDE, it will break on breakpoints you have set.
- When you change the class in such a way that its serialization stream would change, make sure you first remove the policy from the team project settings and then add them again. Because of the changes in the serialization format of your class the policy engine will try to de-serialize the data back into the class thus resulting in an internal exception. When defining the policy again, the serialized data for the policy on the server will be refreshed.
- The references to the assemblies that implement the IPolicyEvaluation and IPolicyDefinition interfaces can be found in the folder:
c:program filesmicrosoft visual studio 8commonideprivateAssemblies and is called Microsoft.TeamFoundation.VersionControl.Client.dll
You can find the source code here
To make the sample code work take the following steps:
- Just copy past the whole page and place it in a C# file.
- Compile the assembly and register the policy in the registry.
- Enable the policy for your Team System Project
- Create a project that contains a C# project file
- Check in the code into Team Foundation Source Control
- Now you will see a policy violation that the documentation property is not turned on
- Now Enable XML documentation for the project, and save the new configuration.
- Push the check-in button, and you will see the policy is satisfied.
Couple small notes:
1) I would recommend deriving from PolicyBase instead of implementing the 2 interfaces if that’s an option – it does some lifting for you (keeping a copy of the PendingCheckin in a property, handling dispose, etc.)
2) If there’s a problem, the object that’s sent back to the policy framework is a PolicyFailure object, not PolicyViolation
3) typo: IPolicyDefintion
I fixed the Typos and the PolicyViolation 🙂
When I have some more time on my hands the comming weeks I will update the sample to use PolicyBase. I just upgraded the sample and was not aware of the fact that a base class is now provided for the basic plumming.
Marcel de Vries