1

The structure of my PR pipeline looks like this:

trigger: none

resources:
- repo: self

variables:
- template: templates/variables.yaml

pool:
  vmIMage: $(vmImageName)

stages:
- template: templates/changed.yaml
  parameters:
    comparedTo: origin/production
- template: templates/unitTests.yaml
- template: templates/build.yaml
  parameters: 
    services:
    - api
    - admin
    - admin-v2
    - client
    tag: test
    tagVersion: latest
- template: templates/integrationTests.yaml
- template: templates/seleniumTests.yaml

The change.yaml basically determines what microservices have been changed so that it can avoid building, testing, and deploying services if there have been no changes to them.

parameters:
- name: comparedTo
  default: ''

stages:
- stage: Changed
  displayName: Checks for changes in services and configs...
  jobs:
  - job: Changes
    displayName: Checking for changes in services and configs...
    steps:
    - bash: |
        mapfile -t servicesChanged < <(git diff  HEAD ${{ parameters.comparedTo }} --name-only | awk -F'/' 'NF!=1{print $1}' | sort -u)
        echo "Total Changed: ${#servicesChanged[@]}"
        if [[ ${#servicesChanged[@]} > 0 ]]; then
          echo "Any services changed: True"
          echo "##vso[task.setvariable variable=anyServicesChanged;isOutput=true]true"
        fi
      name: detectChanges

There are then job conditions in subsequent stages that determine if the job should run, or not, if anyServicesChanged = true.

What I am currently testing with, this true always.

It gets to the unitTests.yaml and runs normally (it passes):

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

Then it gets to build.yaml and it skips it:

parameters:
- name: services
  type: object
  default: []
- name: tag
  default: ''
- name: tagVersion
  default: ''

stages:
- stage: BuildAndPush
  displayName: Build and Push Docker images of services...
  condition: succeeded()
  dependsOn: UnitTests
  jobs:
  - job: BuildAndPush
    condition: or(eq(stageDependencies.Changed.Changes.outputs['detectChanges.anyServicesChanged'], true), eq(variables['Build.Reason'], 'Manual'))
    displayName: Building and Push Docker images of services...
    steps:
    - ${{ each service in parameters.services }}:
      - task: Docker@2
        displayName: Build and Push ${{ service }} Docker image
        inputs:
          command: buildAndPush
          repository: $(imageRepository)-${{ service }}
          dockerfile: $(dockerfilePath)/${{ service }}/Dockerfile
          containerRegistry: $(dockerRegistryServiceConnection)
          tags: |
            ${{ parameters.tag }}-${{ parameters.tagVersion }}

The job condition: is exactly the same, but this time it says it is NULL:

6 queue time variables used
Expanded: or(eq(Null, True), eq('PullRequest', 'Manual'))
Result: False

I'm not sure why this is as I thought it would persist where every I need to use that variable.

Suggestions for why this is happening and how to fix it?

Hugh Lin
  • 17,829
  • 2
  • 21
  • 25
cjones
  • 8,384
  • 17
  • 81
  • 175

1 Answers1

2

In build.yaml, you need to add depend on Changed stage. Otherwise, the variable set in the Changed stage cannot be obtained in the BuildAndPush stage.

stages:
- stage: BuildAndPush
  displayName: Build and Push Docker images of services...
  condition: succeeded()
  dependsOn:
  - UnitTests
  - Changed
Hugh Lin
  • 17,829
  • 2
  • 21
  • 25