土炮 TFS Check-In Policy - 簽入時強制再使用本機 MSBuild 重建通過後才允許簽入

前言

最近在客戶端遇到一個需求,由於時常發生一種情況就是 Developer 撰寫完程式碼簽入 TFS 時,總是會將無法編譯通過的程式碼簽入,通常,在直覺上想到的就是 TFS Build 的『閘道簽入』

透過閘道簽入可以做到『先提交程式碼』到後端 TFS 的 Build 主機,交由 Build 主機確認該程式碼可以建置通過,當然,一般來說,所謂的完整地 CI 必須經由 Build Server 建置通過 + Unit Test + Source Analysis 完成的才表示程式碼是可交付、且沒有問題,當然,流程上是沒有問題的!標準的 CI 確實是如此,但是…. 有另外的一個問題來了!因為,所謂的閘道簽入它還是將『無法編譯的程式碼』簽入到 TFS 中了,而且,熟悉 TFS 的應該都知道,Developer 還是可以選擇將『在本機保留我的暫止的變更』的勾勾消掉,甚至略過驗證組建… 所以,這根本還是無法達到客戶的需求,客戶雖然希望由 CI 機制通過、出去的程式碼才是標準、可運行的,但是,希望開發人員可以在本機先進行『建置』動作,並確認建置動作是 OK 沒有問題的!才開放讓開發人員簽入程式碼。


TFS Check-In Policy

而這時,客戶提了個需求,說:TFS 有沒有 Check-In Policy 是可以在 Client Visual Studio 端確認建置過才可簽入呢?就像是 StyleCop 可以在 Client 先掃過程式碼,確認符合團隊的 Coding Style 後才允許簽入。

聽到這需求後,我當下想了想,身為 Visual Studio 擴充套件開發魔人的我,寫過各式 VS Extensions 還上架了一個 MyORM Extension ,甚至還推出了『C# Project Templates』的線上課程,當然 VsPackage 是不同的,但是我也寫過 VsPackage,卻沒寫過 TFS Check-In Policy 這…他日恐遭人話柄 XD


需求分析:

使用 C# 撰寫一個 VS 擴充套件,就像 StyleCop 一樣,只是這個套件必須呼叫 Local 端的 MSBuild 進行專案的建置動作,如果建置不過,就不讓 Developer 簽入程式碼,並秀出錯誤訊息。

這個 Check-In Policy 也必須可以被包裝成 VSIX 的可發散的套件,以便可以安裝在團隊的所有開發人員的電腦上。


開始實作 TFS Check-In Policy

這時第一件事,當然就是先爬文了,雖然我有許多撰寫 Visual Studio Extensibility SDK 的相關經驗,但是也沒寫過 TFS Check-In Policy,到底該繼承哪一個 Class、或者該參考哪一個 Team Foundation Client 的套件 等等

第一個參考資料是官方的 MSDN 論壇:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/5b254f63-4199-4977-a387-6430d9c868c3/visual-studio-2017-custom-checkin-policy?forum=tfsgeneral

但是上面只是描述如何註冊 Check-In Policy、如何使用 .pkgdef 檔案註冊登錄檔相關資訊,與 VS 版本注意事項,但是這只要用 VsPackage 即可,所以此資料不太管用。

接著還是在 Stackoverflow 找到 TFS 的 Check-In Policy 的功能由 Microsoft.TeamFoundation.VersionControl.Client.DLL 中的 PolicyBase 類別所提供,也就是說,撰寫 Check-In Policy 得繼承 PolicyBase。

因此,直接搜尋 MSDN 的 PolicyBase 直接找相關參考資料:

https://msdn.microsoft.com/zh-tw/library/microsoft.teamfoundation.versioncontrol.client.policybase.aspx

PolicyBase 屬性:

PolicyBase 相關方法:

注意:由於官方說明已經相當詳細,我就不再說明每一個 Properties 與 Methods 名稱的詳細用途。

從官方的資料發現,我們最重要的其實要實作 Evaluate() 方法,因為該方法就是當你要簽入項目進入 TFS 時,Visual Studio Team Foundation Explorer Client 會去呼叫的評估方法,該方法就是評估目前是否可以進行簽入,並在方法中進行自定義的檢查,不通過則回傳包含錯誤訊息的 PolicyFailure[] 陣列物件,為什麼用陣列呢?因為很多情況你必須回傳多個錯誤訊息,像是:註解必須輸入 + 必須參考 WorkItem 工作項目 等等。

再來是,你可以實作 Edit() 方法以秀出你的 Settings 的對話方塊。

當你在加入 TFS Check-In Policy 的視窗點選『編輯』時,可以秀出自己的設定視窗,如下:

接著,需要實作的屬性是 Type,這個屬性讓 Visual Studio 知道你的 TFS Check-In Policy 的名稱是什麼。

對了,但是 Microsoft.TeamFoundation.VersionControl.Client.dll 這個套件在哪裡呢?怎麼取得?一般來說 NuGet Gellery 上應該要有,當然、也確實有,但是….. 不能用。等等說明為什麼?

NuGet Gellery 上面有的是 All-In One 的套件:

另外,找到更清楚的參考資料,上面甚至有完整的範例,只需要照著做就一定會成功。

http://blogs.microsoft.co.il/shair/2008/09/21/how-to-create-custom-check-in-policy/

但我們還是一步一步來,因為這牽涉到 VsPackage 的註冊問題。


首先、我們依照下面步驟來進行,但我們先寫一個檢核『註解』是否有輸入的 Check-In Policy 來測試看看此機制是否會 Work!


一、建立 VSIX 專案


二、參考 Microsoft.TeamFoundation.VersionControl.Client.DLL & System.Windows.Forms

建議不要使用 NuGet Gellery 上的 All-In One 套件,因為那是 v12 版的,除非你製作的是支援 Visual Studio 2012 的,否則在 Visual Studio 2017 上市抓不到這個 Check-In Policy 的,這非常重要!

這邊我建議自行去下載 Microsoft.TeamFoundation.VersionControl.Client.dll 檔案,因為官方提供的只有 Microsoft.TeamFoundation.VersionControl.All 的 All-In One 套件,他只提供 12.0 版。

這邊我是在 StyleCop Check-In Policy 的 Sources Code 裡找到 15.0 版的 Microsoft.TeamFoundation.VersionControl.Client.dll 檔案。

StyleCop Check-In Policy 的 Github 連結:

https://github.com/LockTar/StyleCop-Check-in-Policy

註:參考 System.Windows.Forms 是為了等一會可以測試 Edit() 方法,並顯示自訂設定視窗看看。


三、建立 TFSBuildSuccessCheckInPolicy.cs 類別,並繼承自 PolicyBase

先使用自動實作,查看必須、一定得實作的屬性與方法有哪一些。


四、撰寫程式碼

前面提到,我們只需要撰寫三個地方,Type 屬性、Edit() 方法、Evaluate() 方法,Description 則是在實際使用此 Check-In Policy 時,可以在【描述】欄位顯示一些說明

Type 的部分比較容易,提供一個顯示的名稱即可,這邊我直接秀出 class name:

Edit 部份我們看第五步驟。


Evaluate() 這個是最重要的實作方法,是否允許簽入的評估運算式皆在此執行,若不允許簽入也在此回傳錯誤訊息,交由 Team Explorer 擋下來。

我們先撰寫測試 Comment 註解是否有輸入,以測試機制是否會正常執行,程式碼如下:


五、加一個 AboutAndSettings 的 Windows Form 視窗

直接在 VSIX 專案中加入一個 AboutAndSettings 的 Windows Form,並簡單設計一下,在 Edit() 方法裡加入如下程式碼。


六、簽署 VSIX

簽署的部分,官方建議與 Assembly Name 一樣,理論上不一樣也可以建置通過,只為避免花時間處裡鬼打強問題,所以還是依照官方作法。


七、建立 .pkgdef 註冊資訊檔案

可使用 CreatePkgDef.exe 公用程式建立,此公用程式路徑在 C:\Program Files (x86)\Microsoft Visual Studio 14.0\VSSDK\VisualStudioIntegration\Tools\Bin; 建立加入『Visual Studio 2017 命令提示字元內』

產生的 .pkgdef 檔內容如下:


八、設定為安裝到所有使用者


九、加入 VsPackage 資產


十、開啟實驗性執行個體進行測試

在實驗性執行個體中,我們可以在『擴充套件管理員』中發現,它有自動被 Deploy 過來。

接著,我們點選 Team Explorer => 設定 => Team Project => 原始盪控制,會看到如下視窗,如果 Add 的時候可以看見自訂的 Check-In Policy 就算是成功一半了。

測試的結過,非常成功!當我要簽入時,若不輸入註解資訊,TFS Team Explorer 會直接秀處定義的『簽入時請輸入註解資訊!』的錯誤訊息,如下:

並阻擋簽入,這部分的測試算是成功了。


但是,還沒完,別忘了我們的目標是要做出一個可以在本機電腦先以 Developer 環境的 MSBuild 先 Build 過之後,才允許簽入,為了達成這個目標,我們得多增加三個關於『Microsoft.MSBuild.Framework』與『Microsoft.MSBuild.Engine』的參考,這是為了讓 C# 可以可程式化的去呼叫 MSBuild 甚至進行專案的建置動作。


首先,加入 Microsoft.MSBuild.Framework:


加入 Microsoft.MSBuild 與 Microsoft.MSBuild.Engine (這在擴充功能裡):


再修改一下 Evaluate() 的程式碼,並加入 Microsoft.MSBuild.Framework 提供的建置功能,如下程式碼:


接著,我們在測試一下,故意將程式碼改壞,再試著簽入看看。

果然,我們的 TFSBuildSuccessCheckInPolicy 立刻發揮功用,將程式碼擋了下來!打完收工!!

最後再補充說明一下,因為目前在 PolicyBase 裡面,我無法取得當前的 .csproj 的專案檔名稱,所以我使用了一個偷步的方式,就是直接在檔案系統裡面找,若找不到就往上一層找,因為通常會有一個專案檔,當然這部分還是有微調的空間。


結語:

使用這個與 CI/CD 其實是兩件事情,CI 本來就是由 CI Server 來確保程式碼跑過測試、Sources Code 的掃瞄 與 CI 各種環節,只是 TFS 的組建並無法要求 Developer 一定要在本機的 MSBuild 建置過後才允許簽入,因為 TFS 由 Build Server 來執行 CI 的確認動作,所以 Source Code 還是到 CI Server 上了。而本篇則阻止了 當 Code 無法編譯時,我根本不讓你進到 Build Server 中。


參考資料:

https://blog.devart.com/creating-tfs-custom-check-in-policy-part-1.html

http://blogs.microsoft.co.il/shair/2008/09/21/how-to-create-custom-check-in-policy/

https://social.msdn.microsoft.com/Forums/vstudio/en-US/9a2a8e51-66e8-4606-ae5e-5bfdea8d1062/create-custom-checkin-policy-to-restrict-large-files-checkin-to-tfs?forum=tfsadmin

https://msdn.microsoft.com/zh-tw/library/microsoft.teamfoundation.versioncontrol.client.policybase.aspx

留言

這個網誌中的熱門文章

什麼是 gRPC ?

什麼是 gRPC(二)- 來撰寫第一個 Hello World 吧!

常見的程式碼壞味道(Code Smell or Bad Smell)