darren25
darren25

Reputation: 317

VSTS - Is there a way to only run a task if a specific task has failed?

I need to run a publish task in my build definition but only if a certain task before it has failed. If the other task passes I want this to be ignored and not run.

Is there a way of doing this?

I was hoping that I could set an output variable based on the task success and then use that variable in a custom condition to run the task if it's failed.

I can't see how to set an output variable if the task fails. Is this possible?

Upvotes: 7

Views: 7253

Answers (4)

hmz
hmz

Reputation: 1017

Based on @Merivale's answer this was the code that worked for me;

$targetTaskName = "SomeTaskName"

$pat = ":$(System.AccessToken)"
$b  = [System.Text.Encoding]::ASCII.GetBytes($pat)
$token = [System.Convert]::ToBase64String($b)

$releaseURL="$(System.TeamFoundationServerUri)"
$releaseId= $(Release.ReleaseId)
$environmentId = $(Release.EnvironmentId)
$releaseAttemptNumber = $(Release.AttemptNumber)

$releaseREST_p1="$releaseURL"
$releaseREST=$releaseREST_p1+"$(System.TeamProject)/_apis/release/releases/$releaseId/environments/$environmentId/?api-version=5.1-preview.6"
Write-Host "GET request to Azure Pipelines with URL: $releaseREST"

$result= Invoke-RestMethod -Method GET -Uri $releaseREST -ContentType "application/json" -Headers @{'Authorization' = "Basic $token"}

$targetReleaseAttempt = $result.deploySteps | where { $_.attempt -eq $releaseAttemptNumber}
$targetTask = $targetReleaseAttempt.releaseDeployPhases.deploymentJobs.tasks | where { $_.name -eq $targetTaskName }
$targetTask

$targetTaskStatus = $targetTask.status
Write-Host "Setting variable releaseStatus to $targetTaskStatus"
Write-Host "##vso[task.setvariable variable=releaseStatus;]$targetTaskStatus"

Upvotes: 0

Merivale
Merivale

Reputation: 11

Further to starian chen-MSFT's code, which will work for build pipelines, below is what I pulled together for release pipelines (in case it is useful for anyone). This will output a pipeline variable "deploymentStatus" that equals the status of the task (as defined by Azure DevOps: https://learn.microsoft.com/en-us/rest/api/azure/devops/release/releases/get%20release?view=azure-devops-rest-5.1#taskstatus):

$targetTaskName = "Some Task Name"
$token = $env:System_AccessToken
$releaseURL = $env:System_TeamFoundationServerUri
$projectName = $env:System_TeamProject
$releaseId= $env:Release_ReleaseId
$environmentId = $env:Release_EnvironmentId
$releaseAttemptNumber = $env:Release_AttemptNumber

$releaseREST="$releaseURL$projectName/_apis/release/releases/$releaseId/environments/$environmentId/?api-version=5.1-preview.6"
Write-Host "GET request to Azure Pipelines with URL: $releaseREST"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "test",$token)))
$result= Invoke-RestMethod -Method GET -Uri $releaseREST -ContentType "application/json" -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)}

$targetReleaseAttempt = $result.deploySteps | where { $_.attempt -eq $releaseAttemptNumber}
$targetTask = $targetReleaseAttempt.releaseDeployPhases.deploymentJobs.tasks | where { $_.name -eq $targetTaskName }
$targetTask

$targetTaskStatus = $targetTask.status
Write-Host "Setting variable deploymentStatus to $targetTaskStatus"
Write-Host "##vso[task.setvariable variable=deploymentStatus;]$targetTaskStatus"

Upvotes: 1

Shayki Abramczyk
Shayki Abramczyk

Reputation: 41765

  • In the Task settings expand the "Control Options"
  • Choose in "Run this task" - Only when a previous task has failed.

enter image description here

If you want to run the task only if last/specific task failed:

Suppose your specific task (the one you examine in regards to its status) is called A. The goal is to call another build task (let's say B) only in case A fails.

  • Define a custom build variable, call it task.A.status and set to success
  • Create another build task, e.g. C and schedule it right after A; condition it to only run if A fails - there's a standard condition for that
  • The task C should only do one thing - set task.A.status build variable to 'failure' (like this, if we are talking PowerShell: Write-Host "##vso[task.setvariable variable=task.A.status]failure")
  • Finally, the task B is scheduled sometime after C and is conditioned to run in case task.A.status equals failure, like this: eq(variables['task.A.status'], 'failure')

enter image description here

Upvotes: 5

starian chen-MSFT
starian chen-MSFT

Reputation: 33738

You can check previous tasks through PowerShell with build REST API, for example to check taskA (display name) and run taskB if taskA failed:

  1. Check Allow scripts to access the OAuth token option in the Phase
  2. Add PowerShell task (Run this task: Only when a previous task has failed; Arguments: -targetTaskName "taskA" -collectionURL $(Build.Repository.Uri) -projectName $(System.TeamProject) -buildId $(Build.BuildId) -token $(System.AccessToken))

Code:

param(
    [string]$token,
        [string]$targetTaskName,
        [string]$collectionURL,
        [string]$projectName,
        [string]$buildId
    )
    $buildTimelineREST="$collectionURL$projectName/_apis/build/builds/$buildId/Timeline?api-version=4.1"
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "test",$token)))
    $result= Invoke-RestMethod -Method GET -Uri $buildTimelineREST -ContentType "application/json" -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)}
    $targetTask=$result.records | where { $_.Name -eq $targetTaskName }
    Write-Host $targetTask.result
    if($targetTask.result -eq "failed"){
        Write-Host "##vso[task.setvariable variable=isTaskAFailed;]true"
    }
  1. The taskB (Run this task: Custom conditions; Custom condition: eq(variables['isTaskAFailed'],'true')

Upvotes: 4

Related Questions