如何正確使用 Azure Pipelines 內建的 FTP Upload Task 上傳與刪除檔案 | The Will Will Web

The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

如何正確使用 Azure Pipelines 內建的 FTP Upload Task 上傳與刪除檔案

這幾天在 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 上傳部分的設定。

  1. 使用 PowerShell 工作,建立 app_offline.htm 檔案

    echo "" > App_Offline.htm
    
  2. 使用 FTP Upload v2 工作,上傳 App_Offline.htm 檔案

    Root folder: $(Build.SourcesDirectory)

    File patterns: App_Offline.htm

  3. 使用 PowerShell 工作,等待 15 秒讓網站確實關閉

    Start-Sleep -s 15
    
  4. 使用 FTP Upload v2 工作,上傳所有需要發布的檔案檔案

    Root folder: $(Build.ArtifactStagingDirectory)

    File patterns: **

  5. 使用 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
    
  6. 使用 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

相關連結