這幾天在 Review 公司內一個 ASP.NET Core 專案的 Azure Pipelines,我發現網站是透過 FTP 進行更新的。然而自動化部署的過程,則是先上傳一個 app_offline.htm
檔案,然後在部署成功後刪除 app_offline.htm
檔案。也因為這個專案,我發現了一些 Azure Pipelines 內建的 FTP Upload task (FtpUpload@2
) 有些魔鬼般的細節,實測之後發現有太多地雷,便花了點時間研究一下。
本篇文章都是以 FtpUpload@2 為主,也是目前的最新版本。
地雷 1 - 忘記勾選 Preserve file paths 選項
當你只想上傳 單一檔案 或 只有一個資料夾下的多個檔案 時,其實不用特別勾選 Preserve file paths 選項。
但是如果你想上傳一整個資料夾,不勾選這一項,就會把所有來源任意子目錄下的檔案,全部上傳到遠端的「目標目錄」下,超雷!
假設你想上傳 /home/user/source/
資料夾到遠端,而來源檔案與路徑如下:
foo/bar/foobar.txt
如果你想上傳到遠端的 /uploads/
目錄下,那麼當你沒勾選 Preserve file paths 選項的時候,會上傳到如下的路徑:
/uploads/foobar.txt
如果有勾選 Preserve file paths 選項,就會上傳到以下路徑:
/uploads/foo/bar/foobar.txt
地雷 2 - 無法正確比對排除上傳的檔名路徑
我非常認真的看了 File matching patterns reference,但我發現 FtpUpload@2 根本認不得 !
這種 Pattern,他會直接把 !
符號當成檔名或目錄名稱的一部份,浪費了我好多時間查這個問題,原來是個存在很久很久的 Bug 啊!
錯誤的文件,比 Bug 還可怕,不支援就不支援,文件幹嘛說有支援啊!(怒)
如果你在 File patterns 欄位輸入以下內容:
**
!.git
或是
**
!.git\**
或是
**
!.git
!.git\**
通通無效!他一樣會把你的 .git
資料夾與完整的內容全部上傳上去!
結論是:你只能正面表列要上傳的檔案或目錄清單!
地雷 3 - 當沒有檔案上傳時不會執行 FTP Commands 命令
如果要透過 FTP Upload task 刪除遠端的檔案,必須送出低階的 FTP 命令,這時你可以在 FTP Commands 欄位這樣寫:
CWD /site/wwwroot
DELE App_Offline.htm
這時有個地方要特別注意,這個動作會在建立 FTP 連線後就先執行,而不是等到 FTP 上傳作業結束後才執行!
也因為這個限制,我們要在上傳完檔案後,就要刪除這個 app_offline.htm
檔案,勢必就要在多新增一個 FTP Upload task 來用,但我們檔案明明就上傳完畢了,現在不是要上傳,而是要透過 FTP 命令刪除一個檔案,但又不需要上傳任何新的檔案。
如果你這樣設定:
- Root folder:
$(Build.SourcesDirectory)
- File patterns:
NOTHING*
你的 File patterns 寫成 NOTHING*
是因為故意讓 FTP Upload task 找不到檔案,這樣就不會上傳檔案了。但悲劇的是,只要沒有任何檔案需要上傳,FTP 也完全就不會建立連線,這也意味著你的 FTP Commands 設定了也沒用,根本不會執行,雷阿~~
所以結論是,你必須重新上傳一個小檔案上去,只為了讓 FTP Commands 可以被順利執行而已。有點髒,但有用!
正確使用 Azure Pipelines 內建的 FTP Upload Task 發行 ASP.NET Core 應用程式
我們專案的需求是這樣的,當 ASP.NET Core 透過 dotnet publish
發行完畢後,要透過 FTP 上傳到目標網站進行自動化更版,但是在正式更版之前,要先上傳一份 app_offline.htm
將網站暫時封閉,然後才能正確正常發行檔案。
以下步驟我直接忽略所有 ASP.NET Core 相關的發行步驟,僅呈現 FTP 上傳部分的設定。
-
使用 PowerShell 工作,建立 app_offline.htm
檔案
echo "" > App_Offline.htm
-
使用 FTP Upload v2 工作,上傳 App_Offline.htm
檔案
Root folder: $(Build.SourcesDirectory)
File patterns: App_Offline.htm
-
使用 PowerShell 工作,等待 15 秒讓網站確實關閉
Start-Sleep -s 15
-
使用 FTP Upload v2 工作,上傳所有需要發布的檔案檔案
Root folder: $(Build.ArtifactStagingDirectory)
File patterns: **
-
使用 PowerShell 工作,將 app_offline.htm
更名為 App_Offline_OFF.htm
檔案
if (Test-Path -Path .\App_Offline_OFF.htm)
{
Remove-Item App_Offline_OFF.htm
}
Rename-Item .\App_Offline.htm -NewName App_Offline_OFF.htm
-
使用 FTP Upload v2 工作,刪除 App_Offline.htm
並上傳 App_Offline_OFF.htm
檔案
Root folder: $(Build.SourcesDirectory)
File patterns: App_Offline_OFF.htm
FTP Commands:
CWD /site/wwwroot
DELE App_Offline.htm
以下是完整的 azure-pipeline.yml
範例:
pool:
name: Azure Pipelines
steps:
- powershell: 'echo "" > App_Offline.htm'
displayName: 'PowerShell: Create App_Offline.htm'
- task: FtpUpload@2
displayName: 'FTP Upload: Add App_Offline.htm'
inputs:
serverEndpoint: mocky.azurewebsites.net
rootDirectory: '$(Build.SourcesDirectory)'
filePatterns: 'App_Offline.htm'
remoteDirectory: /site/wwwroot
- powershell: 'Start-Sleep -s 15'
displayName: 'PowerShell: Sleep for 15 seconds'
- task: FtpUpload@2
displayName: 'FTP Upload: Publish'
inputs:
serverEndpoint: mocky.azurewebsites.net
rootDirectory: '$(Build.SourcesDirectory)'
remoteDirectory: /site/wwwroot
preservePaths: true
- powershell: |
if (Test-Path -Path .\App_Offline_OFF.htm)
{
Remove-Item App_Offline_OFF.htm
}
Rename-Item .\App_Offline.htm -NewName App_Offline_OFF.htm
displayName: 'PowerShell: Rename App_Offline.htm to App_Offline_OFF.htm'
- task: FtpUpload@2
displayName: 'FTP Upload: Remove App_Offline.htm'
inputs:
serverEndpoint: mocky.azurewebsites.net
rootDirectory: '$(Build.SourcesDirectory)'
filePatterns: 'App_Offline_OFF.htm'
remoteDirectory: /site/wwwroot
enableUtf8: true
customCmds: |
CWD /site/wwwroot
DELE App_offline.htm
相關連結