やる気駆動型エンジニアの備忘録

WPF(XAML+C#)の話題を中心に.NET/Android/CI やたまに趣味に関するブログです

(Azure Pipelines) どういったデータがループ(each in)処理に対応しているのか検証してみる

Azure Pipelines で複数回登場するようなタスクなどを Template にまとめると再利用できるため大変便利です。 Template ではeach inという構文で反復処理(ループ処理)を行うことができます。 ただ、公式ドキュメントはあまり情報が記載されていないため、どういった方法でループ処理ができるのかよくわからないため、いくつかのパターンでループ処理ができるかどうかを検証してみました。

なお、公式ドキュメントは以下を確認してください。

docs.microsoft.com

parametersに文字列配列を渡してループさせる

テンプレート側のparametersに文字列配列を渡してループさせることができるかどうかを見てみます。

先に結論から言うとこちらは可能です。

反復処理が可能なのは配列やKey-value pairstepsjobsなど)となっており、これを利用することで繰り返しタスクの実行が可能になっています。 (以下参考)

Templates [Iterative insertion] - Azure Pipelines | Microsoft Docs

ループする場合は配列も可能ということですが、例えば文字列配列をparametersでテンプレートに渡してループ処理を実行することは可能なのでしょうか?

まずは通常よくあるサンプルから見ていきます。 次のようにiterative-string-template.ymlparametersで文字列の配列(実際にはobject型)を指定してループするような構成です。 非常に単純な構成で、これは成功します。

parameters:
  - name: values
    type: object
    default:
      - "Azure"
      - "Pipelines"
      - "Learning"

steps:
- ${{ each value in parameters.values }}:
  - script: echo "call ${{ value }}."
    displayName: script ${{ value }}
trigger: none

pool:
  vmImage: windows-latest

jobs:
- job: 
  steps:
  - template: iterative-string-template.yml

f:id:iyemon018:20210926171855p:plain

ただ、この構成だとループする要素の数が限られているため、可変長の要素をループさせたいような要件には対応できません。 可変長の要素をループさせるような場合は以下のようにすれば良いのでは?と思い、試してみました。

テンプレート側は以下のように書き換えました。 変わったのはparametersdefaultです。

parameters:
  - name: values
    type: object
    default: []

steps:
- ${{ each value in parameters.values }}:
  - script: echo "call ${{ value }}."
    displayName: script ${{ value }}

呼び出し元の yaml は、以下のようにparametersに文字列配列を指定するように変更しています。

trigger: none

pool:
  vmImage: windows-latest

jobs:
- job: 
  steps:
  - template: iterative-string-template.yml
    parameters:
      values: ["Azure", "Pipelines", "Learning"]

結果は以下のように成功しています。 スクリプトの呼び出しだけなのでパット見はわかりませんが…

f:id:iyemon018:20210926172912p:plain

ただし、これが成功するのはtypeを指定した場合のみ

こちらの方法はあるパターンにおいては失敗します。 それはテンプレート側でparameterstypeが指定されていない場合です。

具体的には以下のようなものです。

parameters:
  - values: []

steps:
- ${{ each value in parameters.values }}:
  - script: echo "call ${{ value }}."
    displayName: script ${{ value }}

この状態でパイプラインを実行すると以下のようなメッセージが出力されます。yaml Editor で Validate を実行した場合も同様です。

/LearningTemplate/iterative-string-template.yml (Line: 2, Col: 5): Unexpected value 'values'

f:id:iyemon018:20210926231824p:plain

複雑なデータを渡してループすることもできる

文字列だけなど単一のデータを渡すこともあれば、もっと複雑なデータを渡したいケースはあるかもしれません。 例えば key value pair のような構造のデータを渡してループさせることも可能です。

parameters:
  - name: keyValuePairs
    type: object
    default: []

steps:
- ${{ each keyValuePair in parameters.keyValuePairs }}:
    - script: echo "call ${{ keyValuePair.value.Value }}."
      displayName: script ${{ keyValuePair.value.Key }}
trigger: none

pool:
  vmImage: windows-latest

jobs:
- job: 
  steps:
  - template: iterative-string-template.yml
    parameters:
      keyValuePairs:
        pair1:
          Key: "number1"
          Value: "Azure"
        pair2:
          Key: "number2"
          Value: "Pipelines"
        pair3:
          Key: "number3"
          Value: "Learning"

azure-pipelines.ymlからは、KeyValueという構造の変数を複数定義しています。 このとき、keyValuePairs変数は配列のように扱われているようです。

iterative-string-template.ymlでは、eachディレクティブでkeyValuePairsの反復処理を行い、keyValuePair.valueとすることでpair1pair2のデータを参照しています。 つまり、keyValuePair.value.Valueはループ1回目はkeyValuePair.pair1.Valueを参照し、ループ2回目はkeyValuePair.pair2.Valueを参照していることになります。

データ構造が複雑になるとその分 yaml も可読性が下がりますが、こういった機能が必要になるケースもあるかもしれません。


テンプレートを利用して反復処理をしたいようなケースはよくあると思うので、実際に要件にあった方法が実現できるかどうかは検証してみなければわかりません。 今回試した方法であれば割と柔軟に色々なパターンに適応できると思います。