Při vývoji projektu v týmu určitě existují nějaké standardy pro psaní kódu, tedy aby jeden vývojář dokázal v pohodě přečíst kód po někom jiném. Např. kontrolovat kde kdo píše složené závorky apod. asi moc nepůjde, ale můžeme zajistit, že se budou dodržovat správné názvy jmnených prostorů (namespace). Tolik jednoduchý úvod a teď co Vám dnes ukážu.

Vytvořil jsem si jednoduchou knihovnu, která před provedením Check-in operace (než dojde k uložení změněných souborů na TFS) zkontroluje, zda-li upravené soubory splňují “namespace polititku". K tomu abych mohl provádět kontrolu, musím mít jednotlivé politiky někde uložené. Podle mě je nejlepší způsob xml soubor, i když někoho možná jako první varianta napadla pamět, ale to je nemyslitelné, protože by se veškeré údaje ztratily při restartu serveru ! Můj XML soubor, ve kterém mám definované jednotlivé politiky vypadá následovně:

<?xml version="1.0" encoding="utf-8"?>
<policy>
  <projects>
    <project name="Test">
      <namespace name="Contoso" pattern="^Contoso(\.[a-zA-Z0-9]+)*$">
        <examples>
          <example>Contoso</example>
          <example>Contoso.Core</example>
          <example>Contoso.Core.Repository</example>
        </examples>
      </namespace>
    </project>
  </projects>
</policy>

Pod hlavním uzlem Policy se nachází uzel Projects obsahující jednotlivé projekty nacházející se na TFS (Pro každý projekt může být politika pro namespace jiná). Uvnitř uzlu Project je definice namespace (projekt může mít více definic pro namespace). No a na závěr je uvntř definice namespace uzel zobrazující ukázky, jaká jsou např. povolená použití namespace.

K tomu abych mohl reagovat na událost check-in si potřebuju vytvořit třídu, kterou podědím ze třídy PolicyBase, která se nachází v assembly “Microsoft.TeamFoundation.VersionControl.Client.dll”. V naší třídě přepíšeme pár vlastností a metod.

Description - udává text, který je zobrazen v okně Source Control Settings (záložka Check-in policy)

#region | Description
/// <summary>
/// Řetězec, který je zobrazen u přidané policy
/// </summary>
public override string Description
{
   get { return "Remind users to write correct namespaces"; }
}
#endregion

Type – viz. summary

#region | Type
/// <summary>
/// Řetězec, který se zobrazí v dialogu pro přidání nové Policy
/// </summary>
public override string Type
{
   get { return "Custom Namespace Policy"; }
}
#endregion

TypeDescription – viz. summary

#region | TypeDescription
/// <summary>
/// Řetězec, který se zobrazí jako popis když v dialogu pro přidání Policy zvolím mojí policy
/// </summary>
public override string TypeDescription
{
   get { return "Checks the namespaces by regex pattern"; }
}
#endregion 

Edit(…) – Pokud si zvolím (nebo edituju) mojí policy, dojde k vykonání této metody – zobrazím si nový formulář, ve kterém provedu nastavení. O formuláři více později

#region | Edit
/// <summary>
/// Metoda je volána při vytvoření nové policy, nebo při její editaci
/// </summary>
/// <param name="policyEditArgs"></param>
/// <returns>Pokud dialog uzavřu tlačítkem Ok, vrátí true, jinak false</returns>
public override bool Edit(IPolicyEditArgs policyEditArgs)
{
   CustomNamespacePolicyEditDialog dlg = new CustomNamespacePolicyEditDialog();
   if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
       return true;
 
   return false;
}
#endregion

Evaluate(…) – Tato metoda, se zavolá těsně před provedení check-in operace. Z objektu PendingCheckin si získám název projektu a pak projdu všechny soubory u kterých došlo k nějaké změně. Když je budu procházet, tak je budu číst po řádcích a když řádek začíná slovem namespace, tak ověřím, jestli pro tento namespace existuje nastavená politika. To ověřím tím, že zavolám Repository.IsMatch(..), kde předám název projektu a název namespace. Metoda, projde soubor Policy.xml a pokud zde u konkrétního projektu není definice ověřovaného namespace, tak do kolekce failures přidám svojí chybovou hlášku. Na závěr metody vrátím již zminovanou kolekci failures (všechny chyby budou zobrazeny ve VS v error listu)

#region | Evaluate
/// <summary>
/// Metoda je volána před provedním check-in operace
/// </summary>
/// <returns></returns>
public override PolicyFailure[] Evaluate()
{
    IList<PolicyFailure> failures = new List<PolicyFailure>();
    // aktuální projekt
    string projectName = PendingCheckin.PendingChanges.CheckedPendingChanges[0].ServerItem.Split('/')[1];
    // získám si všechny soubory, ve kterých došlo ke změně
    foreach (var pendingChange in PendingCheckin.PendingChanges.AllPendingChanges)
    {
        // otevřu si potřebný soubor
        using (StreamReader reader = new StreamReader(pendingChange.LocalItem))
        {
            // a začnu číst jednotlivé řádky
            string line = string.Empty;
            while ((line = reader.ReadLine()) != null)
            {
                // pokud řádek začíná na slovo namespace
                if (line.StartsWith("namespace"))
                {
                    // tak provedu ověření jestli namespace odpovídá hodnotám zadaným v konfiguračním souboru
                    if (!Repository.IsMatch(projectName, line.Replace("namespace", " ")))
                    {
                        // pokud neodpovídá, do kolekce chyb přidám další
                        failures.Add(new PolicyFailure("Please correct the namespace in the file " + pendingChange.LocalItem, this));
                    }
                } // end if
            }
        } // end StreamReader
    } // end foreach
 
    return failures.ToArray();
}
#endregion

Activate(…) – viz. summary. Zobrazením nápovědy je myšleno zobrazení jednotlivých ukázek povolených namespace které jsou uvedeny v souboru Policy.xml

#region | Activate
/// <summary>
/// Pokud nejsou policy splněny, jsou zobrazeny jednotlivé chyby. Pokud uživatel dvakrát klikne na tuto chybu
/// je vyvolána metoda Activate, ve které pouze zobrazím nápovědu, jak odstranit tyto chyby
/// </summary>
/// <param name="failure"></param>
public override void Activate(PolicyFailure failure)
{
    // aktuální projekt
    string projectName = PendingCheckin.PendingChanges.CheckedPendingChanges[0].ServerItem.Split('/')[1];
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("Your namespace is incorrect. Please look at the following namespace policies and then correct it");
    foreach (var project in Repository.Projects.Where(p=>p.Name == projectName))
    {
        foreach (var policy in project.Policies)
        {
            sb.AppendLine("- " + policy.Name);
            foreach (var example in policy.Examples)
            {
                sb.AppendLine("  " + example);
            }
        }
    }
    System.Windows.Forms.MessageBox.Show(sb.ToString(), "How to fix your policy failure");
}
#endregion 

Pokud jsme přepsali všechny metody a vlastnosti, které jsme potřebovali, tak nesmíme zapomenout označit třídu atributem Serializable!

Dále se už jenom podíváme na můj formulář jak vypadá (rozebírat ho tu podrobně nebudu, ale na konci článku bude odkaz na stažení – jedná se tam jenom o práci s XML souborem). Formulář pro správu namespace policy vypadá následovně:

image

Abychom mohli naší politiku nasadit na TFS serveru musíme provést následující kroky:

  1. Otevřeme daný klíč HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\TeamFoundation\SourceControl\Checkin Policies
  2. Na pravé straně vidíme již zaregistrované policy. Klikneme pravým a dáme New – String value
  3. Nastavíme name na název assembly bez přípony dll (CustomNamespacePolicy)
  4. Nastavíme value na cestu k assembly

To je vše. Zde je ukázkový projekt