check-tfspolicies.ps1 – sanity check your checkin policy types


I thought I would have done something more significant for my 100th blog post.  Ah, well, maybe for the 200th.


I was coding an example checkin policy from scratch last night (1am insomnia, and I had already caught up on my TiVo’d Colbert Report episodes) and had screwed up something in the authoring.  I had registered the dll fine, but it wasn’t showing up when I went to Add a new policy to a team project.  Unfortunately, the V1 code doesn’t give you any feedback about why your assembly (well, the policies within it) don’t show up.


Rather than do the checks manually, I decided it was a good opportunity to write a dorky little script that will do it for me.


It’s a quick-and-dirty approach, especially as it’s the kind of thing I don’t expect people (myself or others) to need to run very often.



  • It doesn’t hook assembly resolve, so any dependent assemblies (besides WinForms or VersionControl.Client) will need to already be loaded before running it
  • It doesn’t use the ReflectionOnly* api’s currently, mainly because of IsAssignableFrom behavior when one of the types is loaded through ReflectionOnly but the other isn’t (and the current/default app domain may already have VersionControl.Client loaded)
  • It doesn’t create and use a new app domain for the loading.

A “v2” version of the script would create a separate appdomain for the testing, use ReflectionOnly for loading, and hook assembly resolve and find/load dependent assemblies.


For the curious, I had forgotten to mark the class as Serializable.  Hey, it was 1am – give me a break!


 

# load a couple of assemblies we expect to need
[void][reflection.assembly]::loadwithpartialname(‘microsoft.teamfoundation.versioncontrol.client’)
[void][reflection.assembly]::loadwithpartialname(‘system.windows.forms’)

function check_assemblies_at_path([string] $registryPath)
{
if (!(test-path $registryPath))
{
write-debug “skipping registry path which does not exist: $registryPath”
return
}
$policiesKey = get-item $registryPath

$policyAssemblyNames = $policiesKey.GetValueNames()

foreach ($policyAssemblyName in $policyAssemblyNames)
{
$policyAssemblyPath = $policiesKey.GetValue($policyAssemblyName)
if ($policyAssemblyPath)
{
$pathBaseName = $policyAssemblyPath -replace ‘.*\\(.*).dll’,‘$1’
if ($pathBaseName -ne $policyAssemblyName)
{
write-warning (‘Registry value {0} should be changed to match assembly base name {1}’ -f $policyAssemblyName, $pathBaseName)
}
if (!(test-path $policyAssemblyPath))
{
write-warning “File location specified for $policyAssemblyName is missing: $policyAssemblyPath”
continue
}
$policyAsm = [reflection.assembly]::LoadFrom($policyAssemblyPath)
}
else
{
$policyAsm = [reflection.assembly]::Load($policyAssemblyName)
}
$policyTypes = @($policyAsm.gettypes() |
?{ [microsoft.teamfoundation.versioncontrol.client.ipolicydefinition].isassignablefrom($_) -and
[microsoft.teamfoundation.versioncontrol.client.ipolicyevaluation].isassignablefrom($_) })

write-debug (‘Found {0} policy type(s) in assembly {1}: {2}’ -f $policyTypes.Length, $policyAssemblyName, $policyTypes)
foreach ($policyType in $policyTypes)
{
write-debug “Examining $policyType”
if ([attribute]::getcustomattribute($policyType, [SerializableAttribute]) -eq $null)
{
write-warning “Policy type $policyType fails the check for [Serializable]”
}
if (!$policyType.IsClass)
{
write-warning “Policy type $policyType fails the check for being a class”
}
if ($policyType.IsAbstract)
{
write-warning “Policy type $policyType fails the check for not being abstract”
}
if (!$policyType.IsPublic)
{
write-warning “Policy type $policyType fails the check for being public”
}
if ($policyType.GetConstructor(@()) -eq $null)
{
write-warning “Policy type $policyType fails the check for having a zero-arg constructor”
}
}
}
}

check_assemblies_at_path(‘HKLM:\SOFTWARE\Microsoft\VisualStudio\8.0\TeamFoundation\SourceControl\Checkin Policies’)
check_assemblies_at_path(‘HKCU:\SOFTWARE\Microsoft\VisualStudio\8.0\TeamFoundation\SourceControl\Checkin Policies’)

check-tfspolicies.ps1