公司的專案版控由VSS換到TFS版已經很久了,自動組建(Build)專案的工作原本靠CruiseControl.NET搞定,一直沒研究如何改接TFS,但漸漸陷入危機,CCNET主機裝在Windows 2003,註定與.NET 4.5專案無緣,非升級不可。一不做二不休,索性棄守CCNET,另建TFS Build Service處理新專案。

第一次玩TFS Build Service,一路不斷踩到水坑跌進洞裡,耗費多時,終於Build第一個專案! 記錄斑斑血淚於後:

比較之下,安裝TFS Build Service反而是程序最單純的部分,細節可參考Franma的圖形化安裝手冊(連結為2012版,2013版在這裡),在此不多贅述,僅筆記我遇到的大小狀況及心得。

TFS Build Service裝好,下一步是要在Visual Studio的Team Explorer中建立組建定義(Build Definition),做法安裝手冊有介紹,一般小專案應該都能一舉成功。但不巧我試作的解決方案專案結構複雜,人生歷練豐富(.sln搬過家),Build Service的環境特殊,簡單任務頓成天堂路~

問題1:

設好組建定義,第一次建置時冒出The imported project "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\WebApplications\Microsoft.WebApplication.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk. 訊息。檢查TFS Build Service主機,有找到\MSBuild\Microsoft\VisualStudio\v11.0\,但其下沒有WebApplications目錄,猜想是TFS Build Service安裝的MSBuild版本不若Visual Studio安裝的版本齊全,未支援WebApplication型別。從已安裝VS的機器複製補足檔案是一種做法,但我決定在TFS Build Service上安裝Visual Studio 2013以求省事。

問題2:

裝好Visual Studio 2013,在MSBuild資料夾裡看到WebApplications目錄,心想這下OK了。沒想到繼續冒出The imported project "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\WebApplications\Microsoft.WebApplication.targets" was not found.錯誤!

定神一看,才發現安裝VS2013後,MSBuild一併升級,原本的v11.0目錄變成v12.0,而TFS 2010 Build Service仍傻傻去找v11.0撲了空。爬文沒找到強制Build Agent改用v12.0的做法,但組建定義有地方可以指定MSBuild參數,如下圖補上/p:VisualStudoVersion=12.0可以解決。猜想應該可以從Build Controller設定,但我沒找出方法,十方大德若有人知曉請再補充。

補充一點,組建定義預設在每次組建失敗會建立一個新任務進行追蹤,在實務上很有用,理論上程式碼得通過編譯才能簽入版控,編譯失敗意味著有人犯錯,就該追蹤到問題解決為止。但像是第一次嘗試TFS組建,過程難免會有些問題需要排除,此設定會產生一堆無意義任務(就還搞不定咩!有什麼好追蹤的?)及一波Mail通知轟炸,建議可先關閉,正常運作後再打開。(上圖的Create WorkItem on Failure)

問題3:

TFS Build Service所處的網段被禁止存取網際網路,故連不上NuGet主機,NuGet Package Restore功能宣告破功,Log冒出一堆找不到參照元件錯誤。最簡單的解決方法是將整個packages資料夾也簽入TFS,但缺點是未來每次新増或更新NuGet Package得一併簽入packages資料夾的異動;但這樣也有優點,Build Service不需要每次建置都重新用NuGet取檔,節省時間也節省網路傳輸,感覺更務實。packages資料簽入後,參照錯誤問題排除,另一個專案卻爆出錯誤:This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is D:\Works\1\APP\MyWeb\src\\.nuget\NuGet.targets.

比對發現出錯的是後期建立的新專案,csproj多了以下片段,應是NuGet升級後增加的檢查:

  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>This project references NuGet package(s) that are missing 
on this computer. Enable NuGet Package Restore to download them. 
 For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. 
The missing file is {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" 
Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
  </Target>

移除上述設定可避免問題,但既然是新版NuGet行為,後續再開新專案預期仍會有類似困擾,補上.nuget\NuGet.targets才是治本之道。將.nugets資料夾也簽入TFS,錯誤就消失了。關於packages與.nuget簽入TFS的注意事項,可參考前文:NuGet Packages資料夾該不該加入TFS版控?

問題4:

這回花最多時間的地方在這一段,原因是先前解決方案(.sln)曾搬過目錄,由Boo/Web/MyWeb.sln移到Boo/MyWeb.sln,而NuGet Packages目錄跟著.sln,因此部分專案csproj的參數<HintPath>仍指向Boo/Web/packages。Visual Studio有許多奇妙法則能找到替代DLL,例如:所參照其他專案帶來的同Package DLL其他軟體用過的其他版本… packages路徑不對的問題一直被掩蓋,直到改用MSBuild才一次爆出來,必須全部修正才能繼續~

要排除前述packages路徑錯誤,可將解決方案由本機刪除再重新由TFS取回,或另找一台電腦由TFS取得原始碼,重新用Visual Studio編譯就可讓錯誤現形。這也是我推薦在TFS Build Service主機安裝Visual Studio的理由之一,面對這類問題時特別能展現優勢。Build Agent每次執行會由TFS抓取最新版程式碼寫入Build Service主機的工作目錄,用VS開啟工作目錄下的.sln檔,會與MSBuild面對環境一致,較容易重現錯誤狀況。Build Agent的工作目錄可以查這裡:

就這樣,使用Visual Studio開啟D:\Works下的sln檔案,手動組置重現錯鋘再一一排除,總算闖關成功。

另外還有幾個TypeScript編譯失敗的小問題,也在更新VS2013 Update 4、TypeScript 1.4 for VS後陸續排除,在Build Service安裝VS2013確實讓射茶包難度降低許多。

心得5:

常見的專案檔案架構會類似這樣:sln放在/src下,packages目錄也在/src下,src下再分成一個專案一個資料夾。/src可能有多個sln,A.sln包含專案A1、A2、Share,B.sln包含B1、B2、B3、Share,放在同一個src下可共用Share專案及packages。在組建定義設定Source時,若設定Source Control Folder為/src,則每次組建時Build Service將下載src下的所有資料夾,把packages、A1、A2、Share、B1、B2、B3全部都抓回來。當我們只想編譯A.sln,多抓B1、B2、B3只是浪費頻寬,還拖慢組建時間。此時可善用組建定義Source Settings可分別列舉應下載(Active)及不要下載(Cloaked)項目的特性,組合出建置所需的最少檔案,提升效率。

心得6:

第一次使用Build Service編譯MVC後楞了一下,輸出資料夾只有bin下的一堆DLL檔案,Scripts、Contents、Views都不見了。後來才發現可部署的網站內容另外放在輸出資料夾的_PublishedWebsites子目錄下,裡面的內容相當於Visual Studio發佈網站的產出結果。

心得7:

Build Service預設提供了幾種建置流程範本(Build Process Template),決定建置流程有幾個步驟,如何抓Source Code、怎麼執行測試… 等等,還可定義控制參數,顯示於下方供使用者調整,這些細節都定義在Windows Workflow Foundation(WWF)規格的XAML檔案,可用Visual Studio編譯修改,為專案量身打造專屬的建置流程,微軟很貼心地在UI 附上教學。呃… 這下連說「我不會」的機會都沒了,夠狠!(淚)

不過,如果只是要在建置時加入執行外部程式、複製檔案等步驟,可考慮在csproj加入額外步驟,用Condition="'$(BuildingInsideVisualStudio)' == ''" 的技巧,設定成MSBuild建置時才觸發(使用Visual Studio建置時不執行),會比客製建置流程範本簡單。

  <Target Name="Copy Files" Condition="'$(BuildingInsideVisualStudio)' == ''" 
   AfterTargets="Build">
    <Exec 
     Command="xcopy &quot;$(SolutionDir)\Web\Boo&quot; &quot;$(DeployPath)\&quot;" 
     Condition="'$(DeployPath)' != ''">
    </Exec>
  </Target>

【延伸閱讀】


Comments

# by ChrisTorng

我還會訂閱組建通知,有組建錯誤會收到 Email 可以立刻處理。 來源檔案部份,一開始我也很精細地設定要下載哪些,不要下載哪些。但方案內容持續變化,不同方案互相參考專案,NuGet 參考也在變化。後來我就把設定簡化了,其實多下載些檔案,建置時間也沒什麼變化...省下方案架構有變化時又得記得修改組建定義的麻煩! TFS Build 的確不好搞...我從 2005 就開始弄,那時是純文字 xml (再加掛微軟人員提供原始碼的簡單 CI)。2008 變部份 xml 部份 UI,再來 2013 就是 WWF...而 VS 2015 還會有新的 Build engine!!! http://geekswithblogs.net/jakob/archive/2015/01/15/tfs-build-vnext-ndash-a-preview.aspx 還好現有的 2013 WWF 流程還是可以使用... 我也已經導入 Release Management,這裡面也很多眉角...但它也有一個 vNext 也把 WWF 改成 PowerShell 了...看來 WWF 在微軟已經不受青睞了...

# by Jeffrey

to ChrisTorng, 好豐富的TFS Build經歷,前輩好!謝謝分享。

# by Franma

ChrisTorng 大真的是很早就開始玩,不過 Team Build 要好弄的話至少要 2010 的版本開始。 若是有客製化需求的話建立還是用 powershell 比較簡單 ( team build 2013 ) 至於 /p:VisualStudoVersion=12.0 這個目前遇到的話只能硬上。黑大講的我全部都遇過 http://www.dotblogs.com.tw/franma/category/6066.aspx

# by 123

123

Post a comment