Introduction

For simplicity I want to have one Azure DevOps CI pipeline to build both PR and main branch of my Java app.

Problem

PR build should focus on building code and running tests and not perform deploy step (publish artefact to a repo) - in other words those PR and main build steps have to be different and not interfer.

Solution

Use condition feature of pipelines and check the reason of the build:

  • if reason is PR (eq(variables['Build.Reason'], 'PullRequest')) - PR task should be executed and main should be skipped
  • if reason is not PR (ne(variables['Build.Reason'], 'PullRequest')) - main task should be executed and PR should be skipped

Example

azure-pipelines.yml


---
jobs:
- job: Windows2019_with_Java11
  pool:
    vmImage: 'windows-2019'
 
  steps:
  - task: Maven@3
    displayName: 'Build/Verify PR'
    condition: eq(variables['Build.Reason'], 'PullRequest')
    inputs:
      mavenPomFile: 'pom.xml'
      goals: 'verify'
      jdkVersionOption: '1.11'
  - task: Maven@3
    displayName: 'Build/Deploy'
    condition: ne(variables['Build.Reason'], 'PullRequest')
    inputs:
      mavenPomFile: 'pom.xml'
      goals: 'verify'
      jdkVersionOption: '1.11'
  - task: PublishTestResults@2
    displayName: 'Publish Test Results **\TEST-*.xml'
    inputs:
      mergeTestResults: true

Great. It works!

Is there a way to eliminate dublication?

Yes - using templates feature of pipelines.

  • Create steps.yml template file
  • Extract build taks into template
  • Parametrize template
  • Replace tasks in azure-pipelines.yml with template

Example

steps.yml


---
parameters:
  mavenTaskName: ''
  mavenTaskGoals: ''
  mavenTaskCondition: ''
steps:
    - task: Maven@3
      displayName: $
      condition: $
      inputs:
        mavenPomFile: 'pom.xml'
        goals: $
        jdkVersionOption: '1.11' 

azure-pipelines.yml


---
jobs:
- job: Windows2019_with_Java11
  pool:
    vmImage: 'windows-2019'
 
  steps:
  - template: steps.yml
    parameters:
      mavenTaskName: 'Build/Verify PR'
      mavenTaskGoals: 'verify'
      mavenTaskCondition: eq(variables['Build.Reason'], 'PullRequest')
  - template: steps.yml
    parameters:
      mavenTaskName: 'Build/Deploy'
      mavenTaskGoals: 'deploy'
      mavenTaskCondition: ne(variables['Build.Reason'], 'PullRequest')
  - task: PublishTestResults@2
    displayName: 'Publish Test Results **\TEST-*.xml'
    inputs:
      mergeTestResults: true

That’s it.

Conclusion

  • Keeping all steps in one pipeline config file (vs having multiple yml configs for different needs) should simplify maintenance.
  • In case the steps for PR and main builds are significantly different the template can become too complex so having two separate pipeleines might be a better option.
  • The approach descibed above is not the only or the best option - it is just an option.