Search code examples
azure-devopsazure-devops-pipelines

Azure DevOps YAML If condition


I am attempting to use the if condition to execute a block of operations but it doesn't seem to be working for me. I'm not entirely sure if I understand the if condition correctly, but this is how I have been using it - or at least a simplified version of it.

# 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

name: MyTest-$(date:yyyyMMdd)$(rev:.r)

trigger: none

resources:
  - repo: self
    clean: false
    fetchDepth: 2

variables:
  - group: TestVG
  - name: EnableSigning
    value: true
  - name: SignDebugBuilds
    value: false

stages:
  - stage: Stage_Build
    displayName: Build Stage
    dependsOn: []

    jobs:
      - job: Job_Build
        displayName: Build Job
        condition: succeeded()
        dependsOn: []
        pool:
          vmImage: 'windows-latest'
        variables:
          BuildPlatform: AnyCPU
        strategy:
          matrix:
            Debug:
              BuildConfiguration: Debug
            Retail:
              BuildConfiguration: Retail
        steps:
          - template: param-test.yml
            parameters:
              Value: 'Build Solution 1'
          #- script: echo 'Build Solution 1'
          - ${{ if and(eq(variables['EnableSigning'], True), or(ne(variables['BuildConfiguration'], 'Debug'), eq(variables['SignDebugBuilds'], True))) }}:
            #- script: echo 'Build Solution 2 IF check'
            - template: param-test.yml
              parameters:
                Value: 'Build Solution 2 IF check'
          - script: echo 'Build Solution 2 COND check'
            condition: and(eq(variables['EnableSigning'], True), or(ne(variables['BuildConfiguration'], 'Debug'), eq(variables['SignDebugBuilds'], True)))

The param-test.yml file is a simple file to echo a line for logging/debugging purposes.

parameters:
  Value: string

steps:
  - script: echo '${{ parameters.Value }}'

My expectation was that for the debug build I would see the first log and the other 2 logs would get skipped, while all 3 logs would be printed for retail build.

However, what I see is that for the debug build, the first and second logs are printed, while only the 3rd log is skipped (the one with the explicit condition in the script step), while all 3 logs are printed for retail build.

To clarify a few items, I used the 3rd log as a control to check if the condition was correct or not. And this is a highly simplified version of the actual pipeline that I have in our repo. We have a primary pipeline YAML file where all the variables are declared including the EnableSigning and SignDebugBuilds. This calls a template file passing in the EnableSigning and SignDebugBuilds flags to the same. This happens a few times around with subtemplates, till one of the sub-templates, the one responsible for the build, uses these from the parameters in the if check. In the actual pipeline, I have the condition use parameters instead of variables, but the result is still the same.

I have looked at a few documentation but it wasn't very clear as to what we could expect from the if statement. And since template tags don't support an explicit condition that we can pass in, this seems to be the only option aside from maintaining 2 separate versions of the template files corresponding to the flags


Solution

  • In your current situation, the 'if' keyword cannot get the variables['BuildConfiguration'], because this variable is created when the job is running. And 'if' key word needs to use the runtime parameters.

    So, every time the job is running, the result of the variables['BuildConfiguration'] under the 'if' keyword is NULL. Because when the job is init, this variable has not been created. This variable will be created when the job is running and then the pipeline will help you create other two jobs under your job.

    At present the work around is to split the matrix to different two jobs and use the parameters to instead of the variables. Here is the demo I create:

    name: MyTest-$(date:yyyyMMdd)$(rev:.r)
    
    trigger: none
    
    resources:
      - repo: self
        clean: false
        fetchDepth: 2
    
    parameters:
      - name: EnableSigning
        type: boolean
        displayName: 'EnableSigning'
        default: true
      - name: SignDebugBuilds
        type: boolean
        displayName: 'SignDebugBuilds'
        default: false
    
    
    variables:
      # - group: TestVG
      # - name: EnableSigning
      #   value: true
      # - name: SignDebugBuilds
      #   value: false
      - name: system.debug
        value: true
    
    stages:
      - stage: Stage_Build
        displayName: Build Stage
        dependsOn: []
    
        jobs:
          - job: Build_Job_Debug
            pool:
              # vmImage: 'windows-latest'
              name: default
            variables:
              BuildPlatform: AnyCPU
              BuildConfiguration: Debug           
            steps:
              - template: param-test.yml
                parameters:
                  Value: 'Build Solution 1'
              
              - ${{ if and(eq(parameters.EnableSigning, true), eq(parameters.SignDebugBuilds, true))}}:
                - template: param-test.yml
                  parameters:
                    Value: 'Build Solution 2 IF check'
              
              - script: echo 'Build Solution 2 COND check and BuildConfiguration is $(BuildConfiguration)'
                name: 'CMD3_check' 
                condition: eq('${{ parameters.EnableSigning }}', true)
          - job: Build_Job_Retail
            pool:
              # vmImage: 'windows-latest'
              name: default
            variables:
              BuildPlatform: AnyCPU
              BuildConfiguration: Retail             
            steps:
              - template: param-test.yml
                parameters:
                  Value: 'Build Solution 1'
              
              - ${{ if or(eq(parameters.EnableSigning, true), eq(parameters.SignDebugBuilds, false))}}:
                - template: param-test.yml
                  parameters:
                    Value: 'Build Solution 2 IF check'
              
              - script: echo 'Build Solution 2 COND check and BuildConfiguration is $(BuildConfiguration)'
                name: 'CMD3_check'
                condition: eq('${{ parameters.EnableSigning }}', true)
    

    Also, if you need to use the variable BuildConfiguration, you can also define it under the different job.