Is it possible to set a condition based on System.PullRequest.TargetBranch for a stage in a pipeline template?

Viewed 3140

I have a solution where a git branch is directly related to an environment (this has to be this way, so please do not discuss whether this is good or bad, I know it is not best practice).

We have the option to run a verification deployment (including automatic tests) towards an environment, without actually deploying the solution to the environment. Because of this, I would like to set up a pipeline that runs this verification for an environment, whenever a pull request is opened towards that environment's branch. Moreover, I am using a template for the majority of the pipeline. The actual pipeline in the main repository is just a tiny solution that points towards the template pipeline in another repository. This template, in turn, has stages for each respective environment.

I have, in the main pipeline, successfully added a solution that identifies the current branch, which for pull requests should be the target branch:

variables:
  - name: currentBranch
    ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
      value: $(System.PullRequest.TargetBranch)
    ${{ if ne(variables['Build.Reason'], 'PullRequest') }}:
      value: $(Build.SourceBranch)

I would like to send this variable currentBranch down to the template through a parameter, as my template pipeline has different stages depending on the branch. My solution was to use the pipeline like this:

extends:
  template: <template-reference>
  parameters:
    branch: $(currentBranch)

...and then for a stage in my pipeline do this:

- stage: TestAndDeployBranchName
    condition: eq('${{ parameters.branch }}', 'refs/heads/branchName')
    jobs:
      - job1... etc.

Basically, the stage should run if the current branch is either "branchName", or (for pull requests) when the target branch is "branchName", which comes from the "branch" parameters that is sent to the template.

However, I see here that System.PullRequest.TargetBranch is not available for templates and further here that the parameters are not available for templates (the variable is empty) when the template is expanded. Thus my pipeline does not work as expected (the condition does not trigger when it should, ie. when there is a match on the branch name).

Is there any way that I can use System.PullRequest.TargetBranch in a condition within a template, or should I look for another solution?

2 Answers

After investigating this further I concluded that what I am trying to do is not possible.

In short, System.PullRequest.TargetBranch (and I assume at least some other variables within System.PullRequest are not available in compile time for template, which is when conditions are evaluated. Thus, using these variables in a condition in a template is not possible.

As my goal was to have certain steps run for pull requests only, based on the target branch of the pull request, I solved this by creating duplicate pipelines. Each pipeline is the same and references the same template, except for that the input parameter for the template is different. I then added each "PR pipelines" to run as part of the branch policy each respective branch this was applicable.

This works great, however it requires me to create a new pipeline if I have the same requirement for another branch. Moreover, I have to maintain each PR pipeline separately (which can be both good and bad).

Not an ideal solution, but it works.

Reference PR pipeline:

trigger: none # no trigger as PR triggers are set by branch policies

#This references the template repository to reuse the basic pipeline
resources:
  repositories:
  - repository: <template repo>
    type: git # "git" means azure devops repository
    name: <template name> # Syntax: <project>/<repo>
    ref: refs/heads/master # Grab latest pipeline template from the master branch

stages:
  - stage: VerifyPullRequest
    condition: |
      and(
        not(failed()), 
        not(canceled()), 
        eq(variables['Build.Reason'], 'PullRequest')
      )
    displayName: 'Verify Pull Request'
    jobs:
      - template: <template reference> # Template reference
        parameters:
          image: <image>
          targetBranch: <targetBranch> # Adjust this to match each respective relevant branch

The targetBranch parameter is the used in relevant places in the template to run PR verification.

Example of branch policy: (Set this up for each relevant branch) Picture of branch policy set up

After checking your script, we find we can not use the

  variables:
  - name: currentBranch
    ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
      value: $(System.PullRequest.TargetBranch)
    ${{ if ne(variables['Build.Reason'], 'PullRequest') }}:
      value: $(Build.SourceBranch)

in the variables. The variables will duplicate the second value to first one. This will cause your issue. So, on my side, I create a work around and hope this will help you. Here is my main yaml:

parameters:
  - name: custom_agent
    displayName: Use Custom Agent
    type: boolean
    default: true
  - name: image
    type: string
    default: default

resources:
  repositories:
    - repository: templates
      type: git
      name: Tech-Talk/template

trigger: none

    
pool:
  vmImage: windows-latest
  # vmImage: ubuntu-20.04

stages:
- stage: A
  jobs:
  - job: A1
    steps:
     - task: PowerShell@2
       name: printvar 
       inputs:
         targetType: 'inline'
         script: |
            If("$(Build.Reason)" -eq "PullRequest"){
                Write-Host "##vso[task.setvariable variable=currentBranch;isOutput=true]$(System.PullRequest.TargetBranch)"
            }
            else{
                Write-Host "##vso[task.setvariable variable=currentBranch;isOutput=true]$(Build.SourceBranch)"
            }    
    
- stage: B
  condition: eq(dependencies.A.outputs['A1.printvar.currentBranch'], 'refs/heads/master')
  dependsOn: A
  jobs:
  - job: B1
    variables:
      varFromA: $[ stageDependencies.A.A1.outputs['printvar.currentBranch'] ]
    steps:
    - task: PowerShell@2
      inputs:
        targetType: 'inline'
        script: |
          # Write your PowerShell commands here.
          
          Write-Host "$(varFromA)"
    - template:  temp.yaml@templates
      parameters:
        branchName: $(varFromA)
        agent_pool_name: ''
        db_resource_path: $(System.DefaultWorkingDirectory)

Please Note: If we use this, we need to modified your temp yaml. We need to move the condition to the main yaml and make the temp yaml only steps is left.

Related