【2021鐵人賽】CI/CD從這裡:編譯專案與上傳成品

前面的文章都是在介紹Pipeline介面、範本內容,這一篇終於要真正進入正題,將Repo中的ConsoleApp透過Pipeline來編譯,並且將編譯完成的成品放到Pipeline的成品庫(Artifacts,但是這個並不是Project左邊選單的Artifacts)。

首先將Yaml檔案中steps底下的內容刪除:

將steps底下不需要的內容刪除

接著從右邊的Task清單(沒看到請按Show assistant)中選擇「.Net Core」這個Task,之後會出現這個Task的屬性編輯畫面,如果屬性旁邊有個圈圈包起來的i字母,代表點擊之後可以得到一些提示資訊(嗯,好像玩遊戲):

.Net Core task
點擊屬性旁邊的i可以獲得提示

在Command的部份維持build這個選項,底下的Path to project(s)的部份剛好被截圖擋住了,提示內容中提到可以用「**/*.csproj」來代表所有子資料夾底下的c#專案檔(.csproj),不過這裡並不打算這麼做,因為我只想要Build Console app專案,所以輸入ConsoleApp/*.csproj。

下面的Arguments則是輸入-o $(Build.BinariesDirectory),代表Build完的檔案要輸出到$(Build.BinariesDirectory)所代表的位置,下一個Task會使用到它。

輸入完之後,在按下Add按鈕之前,有一個重點…就是左邊的Yaml內容的游標所在位置,因為當按下Add的時候,它會從游標所在位置插入內容,所以請務必確認是在所要插入內容的位置(這裡的範例就是最後一行的最前面,這裡就不另外截圖了,可以自己試試看)。

加入了.Net Core task之後,Pipeline執行到這個Task會利用dotnet cli執行build指令編譯Console app專案,但是我希望在Pipeline執行完之後可以將Build出來的檔案壓縮成zip,所以我再點擊了「Archive files」這個Task,屬性的編輯內容如下圖:

Archive files task
Archive files task的編輯畫面

Root folder or file to archive屬性中的內容是$(Build.BinariesDirectory),這是一個系統內建的變數,代表的是build的輸出目錄,在Azure DevOps中有許多預先定義的變數(官方文件:英文/中文),設定Pipeline的時候會時常用到不同的預先定義的變數,善用它們和不同的Task就可以組合出不同的Pipeline。

Prepend root folder name to archive paths的選項則是代表要不要包含上面所設定的資料夾,也就是$(Build.BinariesDirectory)這個資料夾,視需求決定是否勾選,這邊我將它取消勾選

Archive type選項預設是zip,也可以選擇其它的選項,包含有zip、7z、tar、wim。zip還是最容易使用的格式,所以使用預設值即可。

Archive file to create屬性設定的是壓縮檔要存放的位置和檔名,預設值是$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip, $(Build.ArtifactStagingDirectory) 是預設存放成品(Artifact)的資料夾, $(Build.BuildId) 則是這個Pipeline執行的識別碼(這裡的例子是1,2,3,4,…),之後的文章範例會改用別的,這裡先使用預設值。(這裡的Task屬性都是使用預設值)

確認沒問題之後同樣按下底下的Add將Task的設定加入左邊的Yaml編輯區。

Build的Task有了,壓縮的Task也有了,但是並不代表這樣在Pipeline執行完之後就可以取得壓縮的zip檔案了,Pipeline可沒有那麼聰明!所以我們還需要再加上把zip檔案上傳的Task,就是Publish build artifacts task:

Publish build artifacts task

在Task清單中尋找這個Publish build artifacts的時候會看到許多和它很像的其它Task,其中有一個叫做Publish Pipeline Artifacts的Task:

Publish pipeline artifacts task

這兩個實在是很像,也很容易搞混,所以我決定把這兩個Task都加進來,然後執行之後看一下結果的差異。

首先來看看Publish build artifacts task的屬性編輯畫面:

Publish build artifacts task的編輯畫面

上圖中除了Artifact name不是預設值(drop)之外,其它的都是預設值。Artifact publish location的選項除了Azure Pipelines可以選之外,還可以選擇A file share,不過這個選項是給Self-hosted agent使用的,因為設定的位置必須是agent可以存取的位置,雲端的agent應該沒有其它可以存取的位置。

下面的是Publish Pipeline Artifacts task的屬性編輯畫面:

Publish pipeline artifacts task的編輯畫面

看看,除了使用的變數不同之外,還真的是一模一樣。Artifact name和Artifact publish location都相同,很容易理解,那第一個選項有不同的意義嗎?點擊小i的提示來看看好了:

兩個Task屬性的說明

慘了…從這裡實在看不出來有什麼差異,怎麼辦呢?

上面那幾個Task在按下Add的時候,有沒有注意到它的旁邊有個About this task的字樣?大部份的Task都會有這個About this task連結,可以點擊之後開啟Task的相關說明頁面(通常官方的Task都有,但是第三方製作的也可能沒有)。

點擊Publish build artifacts task的About this task會連到這個頁面,但是如果點擊Publish Pipeline Artifacts task的About this task會跳出一個錯誤連結的頁面,不過因為Publish Pipeline Artifacts這個Task也是官方製作提供的,所以從Publish build artifacts task連結到的頁面中,可以看到左邊的項目就有Publish Pipeline Artifact可以選擇,所以Publish Pipeline Artifacts task的說明頁面應該是這個連結

從官方的說明頁面中找到了答案:

Note
This task is deprecated. If you’re using Team Foundation Server 2017 or newer, we recommend that you use the Pipeline Artifacts task instead.

也就是說Publish build artifacts task是舊的,官方建議TFS2017之後的版本改用Publish Pipeline Artifacts task,難怪兩個Task看起來根本一模一樣。

其實從這裡是要帶出一個觀念,就是碰到問題或不了解的東西不要輕易的舉白旗投降,花一些時間尋找問題點或答案就可以繼續下去。同樣的,雖然在這一系列的文章中可能無法完全的介紹到每一個細節,自己摸索嘗試也可能會碰到一些不同的問題,但是官方的文件其實很詳細的說明了各種資訊,只是通常大家都很懶得去看XD。

寫到這裡岔開話題聊一下碰到問題輕易投降這部份…我曾經碰過一個開發者光是看到Exception吐出的那一長串Stack資訊就直接投降的,看都不太看就直接把訊息貼給我,問我該怎麼解決?殊不知只要花個5-10秒看一下內容,再思考個3-5秒,問題點的答案馬上就出現了…

話題還是再轉回來最終完成的yaml內容吧!

# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml

trigger:
- master

pool:
  vmImage: ubuntu-latest

steps:
- task: DotNetCoreCLI@2
  inputs:
    command: 'build'
    projects: 'ConsoleApp/*.csproj'
    arguments: '-o $(Build.BinariesDirectory)'
- task: ArchiveFiles@2
  inputs:
    rootFolderOrFile: '$(Build.BinariesDirectory)'
    includeRootFolder: true
    archiveType: 'zip'
    archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
    replaceExistingArchive: true
- task: PublishBuildArtifacts@1
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)'
    ArtifactName: 'BuildOutputFiles'
    publishLocation: 'Container'
- task: PublishPipelineArtifact@1
  inputs:
    targetPath: '$(Pipeline.Workspace)'
    artifact: 'ArtifactFiles'
    publishLocation: 'pipeline'

執行之後從下圖紅框中可以看到有2個published,可以對比一下前一篇Starter範本執行的結果,一個是0 published,這個則是2 published。

點擊2 published之後就可以進入此次Pipeline執行結果的成品庫(Artifacts),每次Pipeline執行都可以將執行時產生的檔案放到那一次執行的成品庫,每次執行的成品庫都是獨立的,這個在之後的Release部份也會使用到。

Pipeline run artifacts

從上圖加上Task屬性的編輯畫面對照就可以很容易理解之間的關係了,Task中設定的Artifact name指的就是成品庫(或資料夾概念)的名稱,可以將Pipeline產生的檔案分門別類的放在不同的Artifact名稱底下,例如:Executable programs、Sql scripts、Config files,後續建置的Release pipeline或是比較進階的Build pipeline不同的stage可以下載各別Artifact內的檔案,不需要全部的檔案都下載。

在上圖的ArtifactFiles底下有很多的資料夾與檔案,像是TestResults、a、b、s等資料夾,這是因為Publish Pipeline Artifacts第一個屬性設定的是$(Pipeline.Workspace),也就是把Pipeline中相關的檔案與資料夾都上傳上來了,也就是說 $(Pipeline.Workspace) 是Pipeline執行的根目錄,它與Agent.BuildDirectory是相同的內容。

a、b、s雖然只有一個字母,但是其實很好理解,分別代表Artifacts、Binaries、Sources,也就是ArtifactStagingDirectory、BinariesDirectory、SourcesDirectory的意思,在Build底下有這幾個變數可用(請參考預定義變數),在之後的文章介紹到Self-hosted Agent的時候也可以看得到相關的目錄結構。

發佈留言

%d 位部落客按了讚: