Reputation: 350
I have following job structure:
Main goal is to run Publish artifact if any task succeed, if all of them fail, Publish artifact should not be executed and the job should fail.
Things I try and consider:
Solutions I have tried:
I try to use a custom condition on Publish artifact asking the result of all Tasks before, but azure pipeline doesn't hold result stauts for tasks.
I also try to use continueOnError on every task except the last task (Task N) but if this taks fail the whole process fails even if one of them succeeded.
I tried this solution but the script in this solution doesn't know if the task I'm setting the variable for actually passed or not, it will just consider that any task failed and it will not be sure which one is the one that fail.
Any recommendation on how I can make this pass?
Upvotes: 0
Views: 7587
Reputation: 350
There are 2 possible answers each with some problems on their own. But they do resolve the issue.
This will require that every task is treated as a individual job, so the condition and dependency is on jobs and should be pass between them.
# Sample pipeline
# Avoiding setup and rest of variables
- job: task_1
steps:
- task: Bash@3
name: task1
displayName: Task 1
continueOnError: true
inputs:
targetType: 'inline'
script: |
echo 'Task 1'
- task: Bash@1
displayName: 'Publish Artifact'
inputs:
targetType: 'inline'
script: |
echo "Publish artifact"
- job: task_2
dependsOn:
- task_1
condition: eq(dependencies.task_1.result,'SucceededWithIssues')
steps:
- task: Bash@3
name: task2
displayName: Task 2
continueOnError: true
inputs:
targetType: 'inline'
script: |
echo 'Task 2'
- task: Bash@1
displayName: 'Publish Artifact'
inputs:
targetType: 'inline'
script: |
echo "Publish artifact"
#Keep on doing tasks...
- job: task_N
dependsOn:
- task_N-1
condition: eq(dependencies.task_N-1.result,'SucceededWithIssues')
steps:
- task: Bash@3
name: task_N
displayName: Task N
inputs:
targetType: 'inline'
script: |
echo 'Task N'
- task: Bash@1
displayName: 'Publish Artifact'
inputs:
targetType: 'inline'
script: |
echo "Publish artifact"
Note:
Pros:
Cons:
I did not manage to test this example on the code that I needed, but looks logical and I don't see why it should failed. This was based on @Vito Liu-MSFT answer
# Sample pipeline
- job: task_1
steps:
- task: Bash@3
name: task1
displayName: Task 1
inputs:
targetType: 'inline'
script: |
echo 'Task 1'
failOnStderr: true
- task: Bash@3
name: task2
displayName: Task 2
condition: failed()
inputs:
targetType: 'inline'
script: |
echo 'Task 1'
failOnStderr: true
# Keep doing tasks
- task: Bash@3
name: taskN
displayName: Task N
inputs:
targetType: 'inline'
script: |
echo 'Task 1'
failOnStderr: true
condition: failed()
- task: PowerShell@2
inputs:
targetType: 'inline'
script: |
$PAT="{pat}"
$base64AuthInfo= System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($PAT)"))
#List all build timeline via build ID
$ListAllBuildTimeLineURL="https://dev.azure.com/{Org name}/{Project name}/_apis/build/builds/$(Build.BuildId)/timeline?api-version=6.1-preview.2"
$ListAllBuildTimeLineResult = Invoke-RestMethod -Uri $ListAllBuildTimeLineURL -Headers @{Authorization = "Basic {0}" -f $base64AuthInfo} -Method get
$ListAllBuildTimeLineResult.records.Count
#Check task result and set variable
foreach($Task in $ListAllBuildTimeLineResult.records){
if($Task.name -eq "task1"){
if($Task.result -eq "succeeded"){
Write-Host "##vso[task.setvariable variable=task1.status]Success"
}
}
if($Task.name -eq "task2"){
if($Task.result -eq "succeeded"){
Write-Host "##vso[task.setvariable variable=task2.status]Success"
}
}
# Keep printing each task if succeed
if($Task.name -eq "taskN"){
if($Task.result -eq "succeeded"){
Write-Host "##vso[task.setvariable variable=taskN.status]Success"
}
}
}
condition: SucceededOrFailed()
- task: Bash@1
displayName: 'Publish Artifact'
inputs:
targetType: 'inline'
script: |
echo "Publish artifact"
condition: |
or(
SucceededOrFailed(),
or(
eq(variables['task1.status'], 'success'),
or(
eq(variables['task2.status'], 'success'),
or(
# Keep writing ors
eq(variables['taskN-1.status'], 'success')
eq(variables['taskN.status'], 'success')
)
)
)
)
Notes:
Pros:
Cons:
I did not test this and I am unsure, but as the task failed I believe the Status of the Job will be failed, future dependencies will have to handle this case, which will be uncertain if the job actually failed or not. Pipeline will probably return as error. A better result could be achieved when changing failed with ContinueOnError, and the job will return SucceededWithIssues.
Upvotes: 4
Reputation: 8278
Check this YAML, we could add a power shell task and call the REST API to check the task result.
# 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
trigger: none
pool:
vmImage: ubuntu-latest
steps:
- task: PowerShell@2
displayName: task1
inputs:
targetType: 'inline'
script: 'Write-Host "Hello World"'
- task: PowerShell@2
displayName: task2
inputs:
targetType: 'inline'
script: 'Write-Host "Hello World" $(xasda)'
- task: PowerShell@2
displayName: taskn
inputs:
targetType: 'inline'
script: 'Write-Host "Hello World"'
condition: always()
- task: PowerShell@2
inputs:
targetType: 'inline'
script: |
$PAT="{pat}"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($PAT)"))
#List all build timeline via build ID
$ListAllBuildTimeLineURL="https://dev.azure.com/{Org name}/{Project name}/_apis/build/builds/$(Build.BuildId)/timeline?api-version=6.1-preview.2"
$ListAllBuildTimeLineResult = Invoke-RestMethod -Uri $ListAllBuildTimeLineURL -Headers @{Authorization = "Basic {0}" -f $base64AuthInfo} -Method get
$ListAllBuildTimeLineResult.records.Count
#Check task result and set variable
foreach($Task in $ListAllBuildTimeLineResult.records){
if($Task.name -eq "taskn"){
#write-host $Task.state
#write-host $Task.id
if($Task.result -eq "succeeded"){
Write-Host "##vso[task.setvariable variable=taskn.status]Success"
}
}
if($Task.name -eq "task1"){
if($Task.result -eq "succeeded"){
Write-Host "##vso[task.setvariable variable=task.status]Success"
}
}
if($Task.name -eq "task2"){
if($Task.result -eq "succeeded"){
Write-Host "##vso[task.setvariable variable=task.status]Success"
}
}
}
condition: always()
#
- task: Bash@3
displayName: publishArtifact
inputs:
targetType: 'inline'
script: 'printenv'
condition: and(eq(variables['taskn.status'], 'success'),eq(variables['task.status'], 'success'))
Result:
Note: The Task Bash
should be the task Publish artifact
Upvotes: 1
Reputation: 424
I'm not sure if I follow you exactly but I think this might do what you need.
Assuming I am correct that:
# Sample pipeline
# Inline bash scripts to simulate failing tasks.
# Comment out the 'exit 1' line to allow a task to succeed
# The Publish Artifact task below is just a fake for the purposes of testing
steps:
- task: Bash@3
name: task1
displayName: Task 1
continueOnError: true
inputs:
targetType: 'inline'
script: |
echo 'Task 1'
exit 1
echo "##vso[task.setvariable variable=task1_succeeded]true"
failOnStderr: true
- task: Bash@3
name: task2
displayName: Task 2
condition: ne(variables.task1_succeeded, true)
continueOnError: true
inputs:
targetType: 'inline'
script: |
echo 'Task 2'
exit 1
echo "##vso[task.setvariable variable=task2_succeeded]true"
failOnStderr: true
- task: Bash@3
name: task3
displayName: Task 3
condition: |
and(
ne(variables.task1_succeeded, true),
ne(variables.task2_succeeded, true)
)
continueOnError: true
inputs:
targetType: 'inline'
script: |
echo 'Task 3'
exit 1
echo "##vso[task.setvariable variable=task3_succeeded]true"
failOnStderr: true
- task: Bash@3
name: taskN
displayName: Task N
condition: |
and(
ne(variables.task1_succeeded, true),
ne(variables.task2_succeeded, true),
ne(variables.task3_succeeded, true)
)
continueOnError: false
inputs:
targetType: 'inline'
script: |
echo 'Task N'
exit 1
echo "##vso[task.setvariable variable=taskN_succeeded]true"
failOnStderr: true
- task: Bash@3
name: publishArtifact
condition: |
or(
eq(variables.task1_succeeded, true),
eq(variables.task2_succeeded, true),
eq(variables.task3_succeeded, true),
eq(variables.taskN_succeeded, true)
)
displayName: 'Publish Artifact'
inputs:
targetType: 'inline'
script: |
echo 'Pseudo Publish Artifact'
failOnStderr: true
Upvotes: 1