在Azure DevOps中建立共用的Yaml Pipeline Template
建立共用的 ADO yaml pipeline Template
為何有這種需求
最近在上 Azure DevOps 課程,下課後,一群學員突然跑來問了個問題。
「老師,我們公司有三十幾個 Azure DevOps 專案,每個專案都有自己的 repo,每個 repo 都有自己的 pipeline yaml 檔…」他頓了一下,臉上帶著某種「歷盡滄桑」的表情,「有沒有辦法讓這些 pipeline 共用同一份 template?改一次就全部生效的那種?」
我其實急著下課,並沒有聽得很仔細,腦袋裡還在糾結:「為何要讓所有的 repo 都共用同一份 yaml template,每個開發專案不是應該有自己的 pipeline design 嗎?為何要為了統一控管搞得這麼複雜?」
於是我追問:「為什麼會有這種需求呢?」
他苦笑著說「每當公司想要在 pipeline 裡加入一些共用的步驟,比如說安全掃描、程式碼品質檢查、部署前的驗證等等,我們就得一個一個 repo 去改 pipeline yaml 檔。三十幾個專案,每個專案又可能會有好幾個 repo,改起來超級累人。」
教室裡其他學員也紛紛露出「我懂我懂」的表情。這種痛,做 DevOps 的人應該都瞭。
更慘的是,當你好不容易改完二十幾份檔案,過兩個月主管又說:「欸,我們現在還要加入程式碼品質檢查。」然後你又要再改一輪。如果哪天發現其中一個步驟寫錯了,想改?好啊,二十幾個 repo 再巡一次。
這就像是複製貼上了二十幾份同樣的食譜,結果發現鹽放太多了,然後你得把二十幾本筆記本都翻出來,一本一本改。真的累。
「所以…」學員問,「有沒有辦法讓這些 pipeline 共用同一份 template?改一次就全部生效?」
我當下趕著回家,沒多想,也來不及具體回答。
但回家後仔細思量:「這個問題其實挺實際的,應該是有解決方案的吧?」稍微翻了一下,果然發現 Azure DevOps pipeline 原本就支援引用外部的 yaml template。
如何實現?
Azure DevOps 其實早就想到這個問題了。它提供了一個機制,讓你可以把 pipeline 的某些步驟抽出來,放到另一個 repo 裡當作「模板」,然後其他專案的 pipeline 就可以「引用」這個模板。
聽起來很簡單對吧?但實際上第一次設定的時候,總會有那種「欸這樣寫對嗎?」的不確定感。
我也是這樣,看著官方文件,然後照著做,結果 pipeline 一開始是連儲存都失敗,後來是跑起來一直紅燈。改了兩三次才發現是路徑寫法錯了。
所以在好不容易把範例做完之後,順手寫了這篇文章,一步一步紀錄一下整個流程。
不廢話,直接做。
具體實作
我建了兩個 Azure DevOps 專案來示範,分別叫 TestShardYmlA 和 TestShardYmlB。
想像一下,TestShardYmlA 是「模板庫」,專門用來放那些可以共用的 pipeline 步驟。而 TestShardYmlB 是「實際專案」,它的 pipeline 會去引用模板庫裡的東西。
這種設計的好處是,以後如果你的企業有更多專案,它們都可以指向同一個模板。改一次模板庫裡的模板,所有專案全部生效。
第一步:建立模板 Repo
在 TestShardYmlA 專案裡,我建了一個 repo,然後在裡面放了一個共用的 pipeline template,位置是這樣的:
/SharedTemplates/templates/dotnet-unit-test.yml

看到這個資料夾結構了嗎?這就是模板庫的樣子。
假設,我將片段的 yml 內容撰寫如下:
# 檔案:ProjectA / SharedTemplates / templates/dotnet-unit-test.yml
# 功能:共用的 .NET Unit Test 步驟
steps:
- task: DotNetCoreCLI@2
displayName: Test
inputs:
command: test
projects: $(BuildParameters.TestProjects)
arguments: --configuration $(buildConfiguration)
就這樣。
這個 template 做的事情很單純:跑 .NET 的 unit test。
就這麼幾行,但它未來會做為所有專案共用的標準測試步驟。(假設你的專案是用 .NET Core 開發的)
如果哪天你想在測試後加點什麼,比如說檢查測試覆蓋率,或是輸出測試報告,改這裡就好。所有套用到這個 template 的專案,全部會自動更新。
第二步:在實際專案中引用 Template
接下來,我們看 TestShardYmlB,這是實際在跑的專案。
在它的 repo 裡,我建了一個 azure-pipelines.yml,這是該專案主要的 pipeline 定義檔。內容如下:
# 檔案:ProjectB / <你的 WebApp Repo> / azure-pipelines.yml
# ASP.NET Core
# Build and test ASP.NET Core projects targeting .NET Core.
trigger:
- main
pool:
vmImage: ubuntu-latest
# 👉 引用 ProjectA 的共用 template Repo 👈
resources:
repositories:
- repository: sharedTemplates # 在這個 yml 裡的暱稱
type: git
name: TestShardYmlA/TestShardYmlA # 格式:<ProjectA 名稱>/<Repo 名稱>
#ref: refs/heads/main # 或 main,看你那邊的 branch 名稱
variables:
- name: buildConfiguration
value: 'Release'
- name: BuildParameters.RestoreBuildProjects
value: '**/*.csproj'
- name: BuildParameters.TestProjects
value: '**/*[Tt]ests/*.csproj'
name: $(date:yyyyMMdd)$(rev:.r)
jobs:
- job: Job_1
displayName: Agent job 1
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
- task: DotNetCoreCLI@2
displayName: Restore
inputs:
command: restore
projects: $(BuildParameters.RestoreBuildProjects)
- task: DotNetCoreCLI@2
displayName: Build
inputs:
projects: $(BuildParameters.RestoreBuildProjects)
arguments: --configuration $(buildConfiguration)
# 👉 這裡改成使用 ProjectA 的共用 Unit Test template 👈
- template: SharedTemplates/templates/dotnet-unit-test.yml@sharedTemplates
- task: DotNetCoreCLI@2
displayName: Publish
inputs:
command: publish
publishWebProjects: true
projects: $(BuildParameters.RestoreBuildProjects)
arguments: --configuration $(buildConfiguration) --output $(build.artifactstagingdirectory)
zipAfterPublish: true
- task: PublishBuildArtifacts@1
displayName: Publish Artifact
condition: succeededOrFailed()
inputs:
PathtoPublish: $(build.artifactstagingdirectory)
TargetPath: '\\my\share\$(Build.DefinitionName)\$(Build.BuildNumber)'
關鍵在這兩個地方
第一個是 resources: 區段:
resources:
repositories:
- repository: sharedTemplates
type: git
name: TestShardYmlA/TestShardYmlA
這段是在告訴 Azure DevOps:「嘿,我要用另一個專案裡的 repo,它叫 TestShardYmlA/TestShardYmlA,在這個 pipeline 裡我給它取個暱稱叫 sharedTemplates。」
格式很重要:<專案名稱>/<Repo 名稱>。如果寫錯了,pipeline 會找不到,然後就報錯了。
第二個是引用 template 的那一行:
- template: SharedTemplates/templates/dotnet-unit-test.yml@sharedTemplates
這行的意思是:「去 sharedTemplates 這個 repo(就是我們剛剛取的暱稱),找到 SharedTemplates/templates/dotnet-unit-test.yml 這個檔案,然後把裡面的 steps 插到這裡。」
就是這麼簡單。
但關鍵就藏在這麼簡單的兩個步驟裡。
現在,如果你有十個、二十個、甚至五十個專案,它們都可以用同樣的方式引用這個 template。以後要改測試步驟?改一個地方就好。不用再一個 repo 一個 repo 巡了。
結論
自動化的本質,說穿了就是「減少重複勞動」。
你寫的每一行 code、每一個 pipeline、每一個設定檔,都應該問自己:「這東西會不會重複用到?」如果答案是「會」,那就該想辦法抽出來、共用它。
Azure DevOps 的 pipeline template 就是這種思維的呈現。它並非什麼高深的技術,就是把「可以重複用的東西」拉到一個地方,然後讓所有需要它的人都能輕鬆取用。
但這背後的價值,可不只是「少打幾行 code」這麼簡單。
當你的團隊有了共用的 template,代表著:
- 標準化:所有專案的 pipeline 都遵循同樣的規範,不會有人自己亂搞。
- 可追溯性:template 放在 git 裡,每次改動都有紀錄,出事了可以回溯。
- 快速迭代:要推廣新的實踐(比如安全掃描、測試覆蓋率檢查),改一次就全部生效。
更重要的是,當有一天你的主管又跑來說「我們要在所有 pipeline 加入 XXX 步驟」的時候,你可以淡定地回答:「好,我五分鐘改給你。」
然後看著他驚訝的表情,內心暗爽。
這才是 DevOps 該有的樣子。😁
留言