# Azure DevOps Pipelines: differentiate your builds

> It is very common to use the same build script for builds triggered by a Pull Request (PR) and those triggered by continuous integration (CI). Although these…

- Author: Jérôme Giacomini
- Language: en
- Canonical URL: [Azure DevOps Pipelines: differentiate your builds](https://jeromegiacomini.net/articles/2020/07/26/azure-devops-pipelines-differentiate-your-builds)
- Published: 2020-07-26
- Last modified: 2026-09-05
- Topics: .NET, C#, Azure, DevOps

It is very common to use the same build script for builds triggered by a **Pull Request** (PR) and those triggered by **continuous integration** (CI).

Although these builds are similar, their needs are not exactly the same.

In this article we will look at:
- the differences between PR and CI builds;
- how to use the same [YAML script](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&amp;tabs=schema%2Cparameter-schema) for both while disabling unnecessary steps according to the build type.


## The Problem

### Steps in a PR build

A build triggered for each Pull Request usually contains these steps:
- choose the .NET Core SDK: specifying the SDK version through [global.json](https://docs.microsoft.com/fr-fr/dotnet/core/tools/global-json?tabs=netcore3x) ensures the build behaves like it does on your machine;
- `dotnet restore`: restore the NuGet packages;
- `dotnet build`: compile the application;
- `dotnet test`: run unit and integration tests. Validating every PR helps detect regressions **as early as possible**.


### Steps in a CI build
- choose the .NET Core SDK;
- `dotnet restore`;
- `dotnet build`;
- `dotnet publish`: create the binaries in a dedicated folder;
- publish the artifacts so a release pipeline can use them.


The PR build contains the test step. Running it again during CI is unnecessary when the Pull Request has already validated the same revision.

### Why different stages?

The CI build instead contains two additional steps related to publishing artifacts. A PR build does not need to prepare or publish binaries for a release.

### Why keep the build time low?

Keeping build and release times low matters for several reasons:
- it directly affects how quickly you can publish;
- fast feedback tells the team whether a PR can be merged without slowing delivery;
- build-agent time is limited, so it should only be spent on useful work 🙂


## My solution

A simple solution is to enable or disable build steps according to the trigger by using [conditions](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/conditions?view=azure-devops&amp;tabs=yaml).

To enable a step for PR builds, add this condition to the step:

```yaml

condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest'))
```


To activate a step during CI builds I will add the following code to each step:

```yaml

 condition: and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'))
```


Example of full script:

```yaml

#Lance la build CI lors d'un merge sur develop staging et master
trigger:
- develop
- staging
- master
# Lance la build pr sur les branches suivantes
pr:
  branches:
    include:
    - develop
    - staging
    - master

# Image de la VM utilisée
pool:
  vmImage: 'ubuntu-latest'

variables:
  solution: '**/*.sln'
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'

steps:

- task: UseDotNet@2
  displayName: use dotnet SDK
  inputs:
    packageType: 'sdk'
    useGlobalJson: true

- task: DotNetCoreCLI@2
  displayName: dotnet restore
  inputs:
    command: 'restore'

- task: DotNetCoreCLI@2
  displayName: dotnet build
  inputs:
    command: 'build'
    projects: '**/*.csproj'
    arguments: '--configuration $(BuildConfiguration)'

- task: DotNetCoreCLI@2
  displayName: dotnet test
  condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest'))
  inputs:
    command: 'test'
    projects: '**/*Test.csproj'

- task: DotNetCoreCLI@2
  displayName: dotnet publish
  condition: and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'))
  inputs:
    command: 'publish'
    publishWebProjects: true
    arguments: '--output $(build.artifactstagingdirectory) --no-build --configuration $(BuildConfiguration)'

- task: PublishBuildArtifacts@1
  displayName: publish artefacts
  condition: and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'))
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)'
    ArtifactName: 'drop'
    publishLocation: 'Container'
```


Happy building 🙂

## To go further:
- [The conditions in YAML](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/conditions?view=azure-devops&amp;tabs=yaml)
- [YAML scheme](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&amp;tabs=schema%2Cparameter-schema)
