在模板参数中使用任务输出变量

问题描述 投票:0回答:1

我的管道的第一阶段检查哪些服务实际发生了变化。这是为了通过避免在没有更改的情况下重建、重新测试、重新部署服务来加快管道速度。

这是该阶段的

changed.yaml

parameters:
- name: comparedTo
  default: ''

stages:
- stage: Changed
  displayName: Check for changes in services and configs...
  jobs:
  - job: Changes
    displayName: Checking for changes in services and configs...
    steps:
    - bash: |
        mapfile -t changed < <(git diff  HEAD ${{ parameters.comparedTo }} --name-only | awk -F'/' 'NF!=1{print $1}' | sort -u)
        servicesChanged=()
        configChanged=()
        echo ""
        echo "Total Changed: ${#changed[@]}"
        for i in "${changed[@]}"
        do
          echo $i
          if [[ $i == 'admin' ]]; then
            echo "##vso[task.setvariable variable=adminChanged;isOutput=True]true"
            servicesChanged+=("admin")
          elif [[ $i == 'admin-v2' ]]; then
            echo "##vso[task.setvariable variable=adminV2Changed;isOutput=True]true"
            servicesChanged+=("admin-v2")
          elif [[ $i == 'api' ]]; then
            echo "##vso[task.setvariable variable=apiChanged;isOutput=True]true"
            servicesChanged+=("api")
          elif [[ $i == 'client' ]]; then
            echo "##vso[task.setvariable variable=clientChanged;isOutput=True]true"
            servicesChanged+=("client")
          elif [[ $i == 'k8s' ]]; then
            echo "##vso[task.setvariable variable=k8sChanged;isOutput=True]true"
            configsChanged+=("k8s")
          elif [[ $i == 'pipelines' ]]; then
            echo "##vso[task.setvariable variable=pipelineChanged;isOutput=True]true"
            configsChanged+=("pipelines")
          fi
        done
        echo ""
        echo "Services Changed: ${#servicesChanged[@]}"
        for i in "${servicesChanged[@]}"
        do
          echo $i
        done
        echo ""
        echo "Configs Changed: ${#configsChanged[@]}"
        for i in "${configsChanged[@]}"
        do
          echo $i
        done
        if [[ ${#servicesChanged[@]} > 0 ]]; then
          echo ""
          echo "Any services changed: True"
          echo "##vso[task.setvariable variable=anyServicesChanged;isOutput=true]true"
          echo "##vso[task.setvariable variable=servicesChanged;isOutput=true]${servicesChanged[@]}"
        fi
        if [[ ${#configsChanged[@]} > 0 ]]; then
          echo ""
          echo "Any configs changed: True"
          echo "##vso[task.setvariable variable=anyConfigsChanged;isOutput=true]true"
          echo "##vso[task.setvariable variable=configsChanged;isOutput=true]${configsChanged[@]}"
        fi
        echo ""
      name: detectChanges

如您所见,它创建了许多任务输出变量:

# This just indicates if the service has changed: true/false
echo "##vso[task.setvariable variable=<service-name>;isOutput=True]true"

# This should be creating a an output variable that is an array of the services that have changed
echo "##vso[task.setvariable variable=servicesChanged;isOutput=true]${servicesChanged[@]}"

所以我给了自己两个选择:为每个服务只提供一个

true/false
,或者(以某种方式)迭代一系列已更改的服务。

每个阶段基本上有以下形式:

# pr.yaml
...
- template: templates/unitTests.yaml
  parameters: 
    services: 
    - api
    - admin
    - admin-v2
    - client
...
parameters:
- name: services
  type: object
  default: []

stages:
- stage: UnitTests
  displayName: Run unit tests on service...
  dependsOn: Changed
  condition: succeeded()
  jobs:
  - job: UnitTests
    condition: or(eq(stageDependencies.Changed.Changes.outputs['detectChanges.anyServicesChanged'], true), eq(variables['Build.Reason'], 'Manual'))
    displayName: Running unit tests...
    steps:
    - ${{ each service in parameters.services }}:
      - bash: |
          echo "Now running ${{ service }} unit tests..."

这是我迄今为止尝试过的方法以及遇到的错误:

有条件地将每个服务添加到服务数组或添加已更改服务的数组:

- template: templates/changed.yaml
  parameters:
    comparedTo: origin/production
- template: templates/unitTests.yaml
  dependsOn: Changed
  parameters: 
    services: 
    - ${{ if eq(stageDependencies.Changed.Changes.outputs['detectChanges.apiChanged'], true) }}
      - api
    - ${{ if eq(stageDependencies.Changed.Changes.outputs['detectChanges.adminChanged'], true) }}
      - admin
    - ${{ if eq(stageDependencies.Changed.Changes.outputs['detectChanges.adminV2Changed'], true) }}
      - admin-v2
    - ${{ if eq(stageDependencies.Changed.Changes.outputs['detectChanges.clientChanged'], true) }}
      - client

或者...

- template: templates/changed.yaml
  parameters:
    comparedTo: origin/production
- template: templates/unitTests.yaml
  dependsOn: Changed
  parameters: 
    services: 
    - ${{ if eq(dependencies.Changed.outputs['Changes.detectChanges.apiChanged'], true) }}
      - api
    - ${{ if eq(dependencies.Changed.outputs['Changes.detectChanges.adminChanged'], true) }}
      - admin
    - ${{ if eq(dependencies.Changed.outputs['Changes.detectChanges.adminV2Changed'], true) }}
      - admin-v2
    - ${{ if eq(dependencies.Changed.outputs['Changes.detectChanges.clientChanged'], true) }}
      - client

或者...

- template: templates/changed.yaml
  parameters:
    comparedTo: origin/production
- template: templates/unitTests.yaml
  dependsOn: Changed
  parameters: 
    services: 
    - $[ stageDependencies.Changed.Changes.outputs['detectChanges.servicesChanged'] ] 

这会导致:

加载 YAML 构建管道时发生错误。未将对象引用设置为对象的实例。

我知道

variables:
只会接受字符串而不是数组。

一种解决方案是为每个

variables:
变量设置一个
true/false
,然后根据
parameters.services
以及任务输出变量是否为
true
确定条件。

有什么建议吗?


参考:

azure azure-devops azure-pipelines azure-devops-pipelines
1个回答
1
投票

模板表达式

${{}}
在编译时(作业运行之前)求值,这意味着它无法访问在运行时(作业启动之后)动态设置的变量。所以你不能在上面的场景中使用模板表达式
${{}}
。请参阅此处的以下描述。

在模板表达式中,您可以访问包含传入参数值的参数上下文。此外,您还可以访问包含 YAML 文件中指定的所有变量以及许多预定义变量(在该主题中的每个变量)。重要的是,它没有运行时变量,例如存储在管道中或在开始运行时给出的变量。模板扩展在运行的早期发生,因此这些变量不可用

您可以使用条件作为解决方法。您需要添加多个要根据条件执行的任务。请参阅下面的示例:

- template: templates/changed.yaml
  parameters:
    comparedTo: origin/production
- template: templates/unitTests.yaml
  dependsOn: Changed

#unitTests.yaml

stages:
- stage: UnitTests
  displayName: Run unit tests on service...
  dependsOn: Changed
  condition: succeeded()
  jobs:
  - job: UnitTests
    condition: or(eq(stageDependencies.Changed.Changes.outputs['detectChanges.anyServicesChanged'], true), eq(variables['Build.Reason'], 'Manual'))
    displayName: Running unit tests...
    
    variables:
      changedServices: $[stageDependencies.Changed.Changes.outputs['detectChanges.servicesChanged']]
    
    steps:
    - bash: |
         echo "Now running api unit tests..."
      name: Api-unit-test
      condition: contains(variables['changedServices'], 'api')
   
    - bash: |
         echo "Now running admin unit tests..."
      name: admin-unit-test
      condition: contains(variables['changedServices'], 'admin')
    
    - bash: |
         echo "Now running client unit tests..."
      name: client-unit-test
      condition: contains(variables['changedServices'], 'client')

   

另一个解决方法是将管道分成两个管道。运行

Changed
阶段的第一个管道。然后在脚本任务中调用 rest api 来触发第二个管道并传递请求正文中的变量。请参阅this类似的帖子。

© www.soinside.com 2019 - 2024. All rights reserved.