程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Team System: 自定義簽入策略

Team System: 自定義簽入策略

編輯:關於.NET

在本專欄的最近三期中,我探討了 Team Foundation Server (TFS) 版本控制和工作項跟蹤 API。我 使用這些 API 構建了一個 Microsoft® Word 2003 加載項,為 Word 文檔的簽入和工作項關聯提供 支持,這類似於 Visual Studio® 2005 中團隊資源管理器的功能。在本期專欄中,我將深入論述簽 入說明和策略。您將了解簽入說明的工作原理以及如何編寫自己的自定義策略實現。在未來的專欄中,我 會將此支持添加到 Word 加載項中。

簽入說明和策略

簽入說明是自由格式的文本字段,可用於向簽入中添加分類的字符串數據。簽 入說明在團隊項目級別定義,並可作為簽入操作的一部分強制實施。Microsoft 提供的默認項目模板定義 了三種簽入說明:代碼審閱者、安全性審閱者和性能審閱者。它們都不是強制性的;您可定義自己的簽入 說明,方法是在創建團隊項目之後使用團隊資源管理器和/或 API,或者在創建團隊項目之前通過修改過 程模板中的 VersionControl.xml 文件。定義簽入說明時,要定義一個最多 64 個字符的標簽,指定簽入 時是否要求提供該說明,並指定該說明在簽入窗體中的顯示位置。簽入時,說明的值最多可以包含 2048 + (230 – 1) 個字符。前 2048 個字符存儲在數據庫的 nvarchar 列中 — 任何超出部分都 將存儲在 ntext 列中。

簽入策略強制執行由團隊項目管理員定義的規則。策略可以很簡單,例如 確保您在簽入時輸入注釋,也可以很復雜,例如執行靜態代碼分析和運行測試。2005 版的 TFS 出廠時附 帶了三項策略。隨後,作為 Microsoft Visual Studio 2005 Team Foundation Server Power Tool 的一 部分,Microsoft 又新增了四項附加簽入策略。圖 1 提供了有關全部七項策略的詳細信息。(當然,是 “Tool”而不是“Tools”。請參見 msdn2.microsoft.com/aa718351。)

Figure 1 Microsoft 提供的策略

策略 發布 策略要求 工作項 RTM 至少在簽入中關聯一個工作項。沒有配置選項。 代碼分析 RTM 在簽入之前運行靜態代碼分析(這意味著要進行干淨的編譯)。您可以控制策略 是否應用於托管代碼和/或 C++ 代碼。 測試 RTM 在簽入之前運行 策略測試列表指定的測試。您可以控制策略運行的測試列表。 注釋關聯 Power Tool 簽入時輸入注釋。沒有配置選項。 禁止模式 Power Tool 允許您定義一組不希望簽入到存儲庫中的文件。通過指定簡單的文件擴展 名或使用正則表達式來配置策略。 工作項查詢 Power Tool 將來自 指定查詢的工作項與簽入相關聯。通過從團隊項目中選擇團隊查詢來配置策略。 自定 義路徑 Power Tool 與另一個策略協同工作。它允許您有選擇地將 “buddy”(密友)策略應用於團隊項目的源代碼管理樹的子集。

實現 策略的方式是創建一個托管的類庫,它將公開一個可序列化的公共類以實現兩個眾所周知的接口: IPolicyDefinition 和 IPolicyEvaluation。要安裝策略程序集,首先將其復制到安裝了團隊資源管理器 軟件的工作站。然後注冊該程序集,方法是在 Windows® 注冊表中的 HKEY_LOCAL_ MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\TeamFoundation\SourceControl\Checkin Policies下 添加一個條目。您需要創建一個字符串值,其中 Name 是策略程序集的文件名但不帶 .dll 擴展名,Data 是該程序集在本地工作站上的完全限定的路徑(請參見圖 2)。

圖 2 注冊 表項

安裝策略後,需要為每個團隊項目啟用它,這通常可以使用團隊資源管理器的“源代碼 管理設置”對話框來完成。要訪問此對話框,請先在團隊資源管理器工具窗口中選擇一個有效的團 隊項目,然後導航到“團隊”|“團隊項目設置”|“源代碼管理”並選 擇“簽入策略”選項卡(請參見圖 3)。

圖 3“簽入策略”選項卡

第一次從“源代碼管理設置”對話框中選擇“ 添加”按鈕時,團隊資源管理器將枚舉本地工作站上的已注冊程序集列表。(完成此操作後,實際 上每次打開“源代碼管理設置”對話框時都會枚舉此列表。)如果單擊“添加”按 鈕,團隊資源管理器將顯示已安裝的策略程序集列表(請參見圖 4)以及所選策略的說明。

圖 4“添加 簽入策略”對話框

在“添加簽入策略”對話框中選擇了簽入策略後,團隊資源管 理器將查詢該策略的 IPolicyDefinition 實現的 CanEdit 屬性。如果 CanEdit 返回 true,則團隊資源 管理器將調用該策略的 Edit 方法,允許該策略顯示一個用於進行附加配置的對話框。配置了策略並關閉 “源代碼管理設置”對話框後,團隊資源管理器將從一個 TeamProject 對象實例中調用 SetCheckinPolicies 方法,以便將有關該策略程序集的若干元數據以及策略類的實例序列化到 Team Foundation Server 數據庫中。

請注意,在為特定團隊項目啟用 TFS 之前,它對您的策略程序集 一無所知。啟用策略之後,您需要在要執行簽入的每個開發人員的工作站上安裝該策略程序集。TFS 當前 未提供任何自動部署機制。

創建自己的策略

若要創建自己的策略,則需要安裝 Microsoft .NET Framework 2.0 和團隊資源管理器。雖然可以在未安裝 Visual Studio 2005 的情況下創建程序集 ,但我將假設您至少使用了 Visual Studio 2005 Professional Edition。首先要創建一個新的 Visual Basic® 類庫項目。對於本例,您要創建一個掃描 .vb 或 .cs 文件中是否存在“壞”詞 的策略程序集。我已經調用了程序集 MSDNMag TeamSystem.CheckinPolicies。創建項目後,將 Class1.vb 重命名為 BadWords.vb 並將該類標記為“可序列化”。

您需要添加一個對 Microsoft.TeamFoundation.VersionControl.Client.dll 程序集的引用。此程序集是作為 Visual Studio 2005 SDK 的一部分安裝的,其中包含 IPolicyDefinition 和 IPolicyEvaluation 的定義。此外 還有一個抽象基類 PolicyBase,利用它可以稍微提高一下開始的工作效率。不過,在本例中,您要在自 己的類中一次顯式實現一個接口。

在 BadWords.vb 類文件中,為 Microsoft.TeamFoundation.VersionControl.Client 添加一個 imports 語句。然後實現 IPolicyDefinition 接口。Visual Studio 2005 將消除此接口的六個成員。圖 5 提供了此接口的實現。

Figure 5 BadWords Policy 的 IPolicyDefinition

Imports Microsoft.TeamFoundation.VersionControl.Client
<Serializable()> _
Public Class BadWords
 Implements IPolicyDefinition
 Public ReadOnly Property CanEdit() As Boolean _
  Implements IPolicyDefinition.CanEdit
  Get
   Return False
  End Get
 End Property
 Public ReadOnly Property Description() As String _
  Implements IPolicyDefinition.Description
  Get
   Return "Checks code for bad words."
  End Get
 End Property
 Public Function Edit(ByVal policyEditArgs As IPolicyEditArgs) _
  As Boolean Implements IPolicyDefinition.Edit
  Throw New NotImplementedException("Edit is not implemented")
 End Function
 Public ReadOnly Property InstallationInstructions() As String _
  Implements IPolicyDefinition.InstallationInstructions
  Get
   Using sw As New IO.StringWriter()
    With sw
     .Write("You need to install ")
     .Write("BrianRandell.MSDNMag.CheckinPolicies.dll ")
     .Write("on your local workstation." & .NewLine())
     .WriteLine("Visit the Team Project for more info.")
    End With
    Return sw.ToString()
   End Using
  End Get
 End Property
 Public ReadOnly Property Type() As String _
   Implements IPolicyDefinition.Type
  Get
   Return "Brian Randell-MSDNMag Check for Bad Words""
  End Get
 End Property
 Public ReadOnly Property TypeDescription() As String _
  Implements IPolicyDefinition.TypeDescription
  Get
   Return "This policy checks .vb and .cs files for bad words."
  End Get
 End Property
End Class

圖 5 中的多數代碼都不言自明。該接口提供了描述策略的字符串值以及必要的關聯 CanEdit 和 Edit ,以便您選擇使用支持附加配置的策略。請注意,Type 屬性表示“添加簽入策略”對話框中 提供的顯示字符串。首先,您需要對壞詞列表進行硬編碼;然後則可以實現 CanEdit 和 Edit 方法。

實現了 IPolicyDefinition 之後,還需要實現 IPolicyEvaluation。此接口定義了四個方法 (Activate、DisplayHelp、Evaluate 和 Initialize)和一個事件 (PolicyStateChanged)。策略評估框 架將在創建了策略類實例之後調用 Initialize。此方法接受一個 IPendingCheckin 引用,後者為該策略 程序集提供對數據的訪問權限(與掛起的簽入相關),例如掛起的更改和工作項。Evaluate 是策略程序 集的核心。當用戶單擊“策略”按鈕或嘗試簽入或擱置操作時,簽入框架將執行此方法。如果 Evaluate 返回非空的 PolicyFailure 實例列表,框架將取消簽入並提供錯誤列表。如果雙擊某個錯誤消 息,框架將調用 Activate,從而允許策略類顯示有關該錯誤的詳細信息。最後,如果選中某個錯誤並按 F1,框架將執行 DisplayHelp 方法,它可以顯示一個幫助文件、導航到某個網頁或提供自定義 UI。策略 類將使用 PolicyStateChanged 事件來通知框架已發生更改,它應該再次調用 Evaluate。圖 6 提供了 IPolicyEvaluation 的初始實現。

Figure 6 BadWords Policy 的 IPolicyEvaluate

Imports Microsoft.TeamFoundation.VersionControl.Client
Imports System.IO
<Serializable()> _
Public Class BadWords
 Implements IPolicyDefinition, IPolicyEvaluation
' IPolicyDefinition elided (see Figure 5)
 Private badWords() As String = {"Dag", "Darn", "Doh"}
 <NonSerialized()> _
 Private m_pendingCheckin As IPendingCheckin = Nothing
 Public Sub Activate(ByVal failure As PolicyFailure) _
  Implements IPolicyEvaluation.Activate
  ' Do Nothing for Now
 End Sub
 Public Sub DisplayHelp(ByVal failure As PolicyFailure) _
  Implements IPolicyEvaluation.DisplayHelp
  ' Do Nothing for Now
 End Sub
 Public Function Evaluate() As PolicyFailure() _
  Implements IPolicyEvaluation.Evaluate
  Dim fileContents As String = Nothing
  Dim failureMessage As String = Nothing
  Dim failures As New List(Of PolicyFailure)
  For Each pc As PendingChange In _
   m_pendingCheckin.PendingChanges.CheckedPendingChanges
   ' Only check .vb and .cs files
   If pc.FileName.EndsWith(".vb") OrElse _
    pc.FileName.EndsWith(".cs") Then
    Using sr As New StreamReader(pc.FileName)
     fileContents = sr.ReadToEnd()
    End Using
    For Each s As String In badWords
     If fileContents.Contains(s) Then
      failureMessage = pc.FileName & " has a bad word."
      Dim pf As New PolicyFailure(failureMessage, Me)
      failures.Add(pf)
      Exit For
     End If
    Next
   End If
  Next
  Return failures.ToArray()
 End Function
 Public Sub Initialize(ByVal pendingCheckin As IPendingCheckin) _
  Implements IPolicyEvaluation.Initialize
  If (Not Me.m_pendingCheckin Is Nothing) Then
   Throw New InvalidOperationException("Already Active")
  End If
  If disposedValue Then
   Throw New ObjectDisposedException(Nothing)
  End If
  Me.m_pendingCheckin = pendingCheckin
 End Sub
 Public Event PolicyStateChanged(ByVal sender As Object, _
  ByVal e As PolicyStateChangedEventArgs) _
  Implements IPolicyEvaluation.PolicyStateChanged
 ' IDisposable implementation elided
End Class

初始實現非常簡單 — 策略實例存儲一個對 IPendingCheckin 引用(通過 Initialize 方法提供)的引用。在 Evaluate 方法中,代碼將枚舉 CheckedPendingChanges 集合,為每 個已檢查的項檢索 PendingChange 實例。如果該項是以 .vb 或 .cs 擴展名結尾的文件,則該代碼會使 用 StreamReader 將文件內容加載到一個字符串變量中。然後,代碼將使用 String.Contains 方法掃描 文件內容中的每個壞詞,每次掃描一個。如果代碼找到一個壞詞,則會創建一個新的 PolicyFailure 實 例,並將其添加到失敗 List(Of T) 集合中,然後繼續下一個掛起的更改。掃描了所有文件後,它將失敗 集合返回給調用 Evaluate 的策略框架。

添加了必要代碼以支持 IPolicyEvaluation 後,您需要編譯該程序集。您必須向注冊表中添加必要數 據,以便當您在“源代碼管理設置”對話框中單擊“添加”按鈕時 Visual Studio 能夠加載該程序集。只要對已安裝的策略列表進行修改,就需要重新啟動任何正在運行的 Visual Studio 實例,以便它能夠看到這些更改。完成所有配置後,啟用該策略,然後修改源代碼管理下的某個文件,在 其中輸入一個壞詞。然後打開“掛起的更改”對話框並單擊“策略警告”按鈕或嘗 試一個簽入。您會看到類似圖 7 所示的內容。

圖 7策略沖突警告

調試策略程序集

調試自定義策略實際上非常簡單。首先,如果已經啟用了自定義策略,則 請將其從“團隊項目”列表中刪除。僅在私有團隊項目中處理策略程序集會使事情變得簡單得 多。當然,有時您可能希望用您選擇啟用的任何其他策略來測試您的策略程序集,但是在進行調試時縮小 問題范圍無疑大有好處。刪除了全部策略後,關閉所有正在運行的 Visual Studio 2005 實例並保留先前 在注冊表中添加的數據。然後,重新啟動一個 Visual Studio 2005 實例。加載自定義策略項目並訪問項 目設計器(在解決方案資源管理器中右鍵單擊項目並選擇“屬性”)。激活“調試 ”選項卡並將“啟動操作”更改為“啟動外部程序”。您需要提供 Visual Studio 2005 的完全限定的路徑,通常為 C:\Program Files\Microsoft Visual Studio 8\Common7 \IDE\devenv.exe。保存所做的更改並關閉設計器。

完成上述操作後,在 IPolicyDefinition.Type 和 IPolicyEvaluate.Evaluate 上設置斷點。通過按 F5 或從菜單中選擇“調試”|“啟動調試”來啟動調試。第二個 Visual Studio 實例將啟動,在此實例中,打開用於進行壞詞測試的項目。然後,訪問“源代碼管理設置”對 話框並添加您的自定義策略。當單擊“添加”按鈕時,第一個 Visual Studio 實例將成為活 動窗口,因為 Visual Studio 將訪問策略的 Type 屬性。當關閉設置對話框時,策略框架將執行策略的 Evaluate 方法,後者將隨之重新激活 Visual Studio 的第一個實例並允許您啟動調試。在調試會話的最 後,返回到“源代碼管理設置”對話框並刪除您的策略。在此您可以調試 IDisposable 實現 。

完成後,關閉 Visual Studio 的第二個實例。此時可以修改您的策略程序集並重復該過程。策 略只應在 Visual Studio 的第二個實例中啟用。否則就需要下載所有實例並重新編譯。

增強自定 義策略

雖然自定義策略的當前實現可以完成任務,但其功能還不是很豐富。它沒有提供有關壞詞 所在位置的任何詳細信息。此外,您還對壞詞列表進行了硬編碼。若要糾正此問題,首先需要提供一個更 豐富的 IPolicyEvaluate.Evaluate 實現。然後,才可以為 Activate 方法提供一個實際的實現。最後, 您需要提供一個 IPolicyDefinition.Edit 實現。圖 8 提供了 Evaluate 的新代碼。

Figure 8 更新的 Evaluate 實現

Imports Microsoft.TeamFoundation.VersionControl.Client
Imports System.IO
<Serializable()> _
Public Class BadWords
 Implements IPolicyDefinition, IPolicyEvaluation
 Private badWords() As String = {"Dag", "Darn", "Doh"}
 <NonSerialized()> _
 Private m_pendingCheckin As IPendingCheckin = Nothing
 <NonSerialized()> _
 Private badWordFound As Boolean = False
 ' IPolicyDefinition elided
 ' IPolicyEvaluation Activate and DisplayHelp elided
 Public Function Evaluate() As PolicyFailure() _
  Implements IPolicyEvaluation.Evaluate
  Dim fileContents As String = Nothing
  Dim failureMessage As String = Nothing
  Dim wordData As List(Of WordInfo)
  Dim failures As New List(Of PolicyFailure)
  For Each pc As PendingChange In _
   m_pendingCheckin.PendingChanges.CheckedPendingChanges
   ' Only check .vb and .cs files
   If pc.FileName.EndsWith(".vb") OrElse _
    pc.FileName.EndsWith(".cs") Then
    badWordFound = False
    wordData = GetWordInfo(pc.LocalItem)
    If badWordFound Then
     failureMessage = _
      String.Format("{0} has at least one bad word." & _
      " Double-click for details", pc.FileName)
     Dim bpf As New BadWordPolicyFailure(failureMessage, _
      Me, New CheckedFileInfo(pc.FileName, wordData, pc))
     failures.Add(bpf)
    End If
   End If
  Next
  Return failures.ToArray()
 End Function
 ' IPolicyEvaluation Initialize elided
 ' IDisposable implenation elided
 Friend Function GetWordInfo(ByVal fileName As String) _
  As List(Of WordInfo)
  Dim lines As String() = File.ReadAllLines(fileName)
  Dim wordData As New List(Of WordInfo)
  For Each word As String In badWords
   Dim item As WordInfo = New WordInfo(word)
   wordData.Add(item)
   For i As Integer = 0 To lines.Length - 1
    If lines(i).Contains(word) Then
     item.LineList.Add(i + 1)
     item.LineData.Add(lines(i))
    End If
   Next i
   If item.LineList.Count > 0 Then
    badWordFound = True
   End If
  Next
  Return wordData
 End Function
End Class

Evaluate 的新實現使用了一組新的幫助器類,如圖 9 所示。WordInfo 存儲了包含壞詞的行號和實際 代碼行。該代碼會為每個壞詞創建一個 WordInfo 實例。CheckedFileInfo 包含所檢查文件的名稱、一個 WordInfo 對象集合和一個對 PendingChange 項的引用。最後,派生自 PolicyFailure 的 BadWordPolicyFailure 具有對 CheckedFileInfo 實例的引用。存在此類的原因是默認的 PolicyFailure 類未提供任何簡單的方法以將策略失敗與支持元數據(Activate 方法需要用它來提供豐富的用戶體驗) 相關聯。

Figure 9 Evaluate 的幫助器類

Public Class WordInfo
  Public Word As String
  Public LineList As New List(Of Integer)
  Public LineData As New List(Of String)
  Public Sub New(ByVal word As String)
    Me.Word = word
  End Sub
  Public Overrides Function ToString() As String
    Return Word
  End Function
End Class
Public Class CheckedFileInfo
  Public FileName As String
  Public WordData As List(Of WordInfo)
  Public PendingItem As PendingChange
  Public Sub New(ByVal FileName As String, _
    ByVal WordData As List(Of WordInfo), _
    ByVal PendingItem As PendingChange)
 
    Me.FileName = FileName
    Me.WordData = WordData
    Me.PendingItem = PendingItem
  End Sub
End Class
Public Class BadWordPolicyFailure
  Inherits PolicyFailure
  Public FailureData As CheckedFileInfo
 Public Sub New(ByVal Message As String, _
    ByVal Policy As IPolicyEvaluation, _
    ByVal FailureData As CheckedFileInfo)
    MyBase.New(Message, Policy)
    Me.FailureData = FailureData
  End Sub
End Class

憑借這些新類,新的 Evaluate 方法的工作方式會略有不同。該代碼仍會枚舉 CheckedPendingChanges 集合,只搜索 .vb 和 .cs 文件。但是,GetWordInfo 幫助器方法將承擔實際加 載源文件並搜索壞詞的工作。此方法會構建一個集合,其中包含一個針對此列表中每個壞詞的 WordInfo 。如果 GetWordInfo 至少找到一個壞詞,Evaluate 方法將創建 CheckedFileInfo 實例並將其傳遞給新 的 BadWordPolicyFailure 實例,後者將被添加到失敗集合中。然後,它將繼續檢查每個文件。

接下來,您需要為 Activate 方法提供一個實現。雙擊某個策略沖突消息時,需要該策略顯示一個對 話框,其中列出壞詞、它們所在的行以及包含壞詞的實際代碼行。示例代碼下載將提供一個簡單窗體 frmFileDetails 以幫助您開始這項工作。

圖 10 顯示了 Activate 方法的新代碼。該代碼所做的第一件事就是對所提供的針對派生 BadWordPolicyFailure 類型的 PolicyFailure 引用進行 TryCast。如果此操作成功,代碼會將 frmFileDetails 顯示為一個對話框(有關示例,請參見圖 11)。

Figure 10 Activate 方法實現

Public Sub Activate(ByVal failure As PolicyFailure) _
  Implements IPolicyEvaluation.Activate
  Dim bpf As BadWordPolicyFailure = _
    TryCast(failure, BadWordPolicyFailure)
  If bpf IsNot Nothing Then
    Using frm As New frmFileDetails(bpf)
      frm.ShowDialog()
    End Using
  Else
    MsgBox("The Policy Failure object provided is invalid.", _
      MsgBoxStyle.Exclamation, "Warning")
  End If
End Sub

圖 11 策略失敗消息

添加編輯支持

需要添加的最後一個功能是允許項目管理員定義壞詞列表的對話框。這裡需要完成三項任務。首先需 要為壞詞列表提供一個存儲機制。然後必須提供用於編輯壞詞列表的 UI。最後需要修改策略以便從存儲 位置加載壞詞,而不是使用硬編碼的數組。這些任務都不是策略程序集所特有的 — 它們都是標准代碼。 我在代碼示例中已提供了一個實現。該示例將壞詞列表存儲在加載項所在目錄的一個文本文件中。在編輯 時,項目管理員必須對壞詞文件擁有讀/寫訪問權限,盡管策略的使用者只需要讀取權限。

現在,您應該了解簽入說明和策略了吧。Microsoft 為簽入策略提供的框架非常豐富和靈活。將來, 我想 Microsoft 將會改進安裝體驗。在下一期專欄中,我將深入探討如何以編程方式在自定義代碼和 Word 加載項中使用簽入說明和策略。

請將您想詢問的問題和提出的意見發送至 [email protected].

Brian A. Randell是 MCW Technologies LLC 的一名高級顧問。Brian 從事 Microsoft 技術方面的演 講、培訓和寫作工作。他是 Pluralsight 的 Applied Team System 課程的作者,同時也是一名 Microsoft MVP。您可以通過 Brian 的博客與他聯系,網址是:mcwtech.com/cs/blogs/brianr。

本文配套源碼:http://www.bianceng.net/dotnet/201212/830.htm

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved