打通自動化雲端部署到地端-VSTS用MSBuild產生部署Web Site所需要的Files

用VSTS去部署Web Site有什麼難的?尤其部署到Azure Web Site更是沒有難度,基本上只要用Azure web app deployment這個Task就可以。但是,部署到地端的IIS裡面,就沒有這樣簡單,尤其是要從雲到地的部署,沒有這樣簡單的原因,不外乎幾點:

  1. IIS沒有辦法裝Web deploy套件
  2. 地端Agent可能沒有用Web deploy的權限
  3. VSTS並沒有類似Azure web app deployment簡單操作的Task可用

如果真的要做到第三點,可以用這兩種做法去做,不過,前提都是必須要克服第一和第二點問題

  • Visual Studio Build Task內的MSBuild Arguments,透過MSBuild參數做Web deploy,直接部署到IIS,不過,這樣流程就不容易拆分Build和Release

  • Visual Studio Build Task內的MSBuild Arguments,透過MSBuild參數做Web deploy Package,再用IIS Web App Deployment的Task,取得Web deploy package的Zip檔作部署動作

所以,除了Web Deploy外,另一個作法就是產生要Publish檔案,透過File Copy方式放到IIS中Application資料夾內,這樣也可以同時把Build和Release流程分開

Build 的流程


設定Build流程其實不難,如下圖這樣做法就可以

其中,有一個需注意的就是Copy Publish Artifact或是Publish Artifact這兩個Task,它們並不會自動把你Build完的專案,產出只需要Publish的檔案,它只會把Build完畢的專案資料夾內檔案原封不動放到$(Build.ArtifactStagingDirectory)資料夾內,所以,還是會把像是.cs檔案一併取出來。如果,要在$(Build.ArtifactStagingDirectory)存放真正要部署的檔案,必須在Visual Studio Build的Task去下MSBuild Arguments的Command,其指令為

1
/p:OutDir=$(Build.StagingDirectory) /P:TransformConfigFiles=true

然後,把Build完畢檔案放到$(Build.StagingDirectory)內,在Copy Publish Artifact的Task設定如下:

不過,這樣產生部署的檔案,還會有一點點問題,這問題會在你的專案中如果有加入這兩個Package

  • Microsoft.Net.Compilers 1.1.1
  • Microsoft.CodeDom.Providers.DotNetCompilerPlatform 1.0.1

它會產生一個Roslyn的資料夾在Bin資料夾內,這樣Build後會發現要Publish的資料夾中,在Roslyn中內的dll或是其他程式會放在跟Bin同一層目錄下

當你執行這個Web site就會發生這種錯誤,

找不到\bin\roslyn\csc.exe的錯誤資訊

如果沒有用到這兩個Package就把它移除吧,但是,如果真的有在使用的話,就必須在.csproj裡面加入下面指令

1
2
3
4
5
6
7
<Target Name="CopyRoslynFiles" AfterTargets="AfterBuild" Condition="!$(Disable_CopyWebApplication) And '$(OutDir)' != '$(OutputPath)'">
<ItemGroup>
<RoslynFiles Include="$(CscToolPath)\*" />
</ItemGroup>
<MakeDir Directories="$(WebProjectOutputDir)\bin\roslyn" />
<Copy SourceFiles="@(RoslynFiles)" DestinationFolder="$(WebProjectOutputDir)\bin\roslyn" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" />
</Target>

或是

1
2
3
4
5
6
7
8
9
10
11
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
<PostBuildEvent>
if not exist "$(WebProjectOutputDir)\bin\Roslyn" md "$(WebProjectOutputDir)\bin\Roslyn"
start /MIN xcopy /s /y /R "$(OutDir)roslyn\*.*" "$(WebProjectOutputDir)\bin\Roslyn"
</PostBuildEvent>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.1.3.2\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.1.3.2\build\Microsoft.Net.Compilers.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.2\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.2\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props'))" />
</Target>

又或者是在Visual studio中加入也可以

1
2
if not exist "$(WebProjectOutputDir)\bin\Roslyn" md "$(WebProjectOutputDir)\bin\Roslyn"      
start /MIN xcopy /s /y /R "$(OutDir)roslyn\*.*" "$(WebProjectOutputDir)\bin\Roslyn"

這樣就會在bin裡面多一個roslyn資料夾,且裡面也會有相關檔案,不過,這樣做法,其實是把原本Publish資料夾外的roslyn資料夾,拷貝一份到bin裡面

原始在bin資料夾內的roslyn資料夾中的檔案還是存在bin資料夾內

Release流程


在Release部分就相當簡單,就是把產生的Publish資料夾內的檔案複製到你IIS

所以,使用Copy Files toTask完成,在Source Path中,選_PublishedWebsites內的檔案就可以,如果還有多一層目錄,就選到那個目錄內的檔案即可,而Target Folder就是IIS Web Site資料夾路徑位置,這樣就可以透過Copy File方式部署檔案了