Azure DevOps has the ability to add multiple stage approval styles like human intervention, Azure Monitoring and schedules. However, there is not the ability to value if a source has changed before triggering a Stage. This can be done on the Pipeline level, but not the Stage level where it can be helpful to have sometimes. I have a PowerShell solution on how this can be done.
The problem to solve came when I had a Pipeline that build multiple Nuget Packages. Without having a human intervention approval button on each of the stages to publish the Nuget Package, they would Publish each time even if there was not changes. This would then cause the version number in the Artifacts to keep increasing for no reason.
Therefore, we wanted a method to automatically check if there has been a change in a particular source location, before allowing it to publish the Package. This has been done using PowerShell, to get the requested branch changes through Git. It can get all the names of the changed files, which included the full path, then we can check what it contains.
The code below gets the Diff files, looping through each of them while checking if it matches the provided path. If it does then it can set the global variable to say there has been changes. I did add an option to break on the first found item, but you could leave it to keep going and leave a log of all the files changed. In my case, I just wanted to know if there has or has not been a change.
$files = $(git diff HEAD HEAD~ --name-only)
$changedFiles = $files -split ' '
$changedCount = $changedFiles.Length
Write-Host("Total changed $changedCount")
$hasBeenChanged = $false
For ($i = 0; $i -lt $count; $i++) {
$changedFile = $changedFiles[$i]
if ($changedFile -like "${{parameters.projectPath}}") {
Write-Host("CHANGED: $changedFile")
$hasBeenChanged = $true
if (${{parameters.breakOnChange}} -eq 'true')
break
}
}
You can then set the outcome as an output variable of the task for usage later as per this Template below:
- name: 'projectPath'
type: string
- name: 'name'
type: string
- name: 'breakOnChange'
type: string
default: 'true'
steps:
- task: PowerShell@2
name: ${{parameters.name}}
displayName: 'Code change check for ${{parameters.name}}'
inputs:
targetType: 'inline'
script: |
$files = $(git diff HEAD HEAD~ --name-only)
$changedFiles = $files -split ' '
$changedCount = $changedFiles.Length
Write-Host("Total changed $changedCount")
$hasBeenChanged = $false
For ($i = 0; $i -lt $count; $i++) {
$changedFile = $changedFiles[$i]
if ($changedFile -like "${{parameters.projectPath}}") {
Write-Host("CHANGED: $changedFile")
$hasBeenChanged = $true
if (${{parameters.breakOnChange}} -eq 'true')
break
}
}
if ($hasBeenChanged -eq $true) {
Write-Host "##vso[task.setvariable variable=IsContainFile;isOutput=true]True"
break
}
else {
Write-Host "##vso[task.setvariable variable=IsContainFile;isOutput=true]False"
}
Once you have that output variable, it can be used throughout your pipeline as a condition on Stages, Jobs and Task, plus more. Here I use it on a Stage to check before it is run:
- stage: '${{parameters.projectExtension}}Build'
displayName: ' Nuget Stage'
condition: eq(variables['${{parameters.name}}.IsContainFile'], 'true')