2

I'm trying to send a post-build Slack message after the job is done/failed in a Azure DevOps YAML pipeline. But it seems I can't find a proper condition setting.

Basically, I have three stages: test, build, and notification.

I tried the following at last, but dependencies.UnitTest.result returns null, so not giving me Succeeded or Failed.

I also tried a bunch of different conditions but they didn't work. For example, a plain succeeded() and failed() without dependencies, or succeeded('Test') as stage level or succeeded('UnitTest') as job level.

In most cases, they send the success message even if they failed at the Test stage, or a syntax error for job names as argument in succeeded() or failed()

What is the proper condition to send a post-build message like Jenkins?

stages:
- stage: Test
  jobs:
  - job: UnitTest
    steps:
    - script: echo UnitTest
    - script: exit 1

- stage: Build
  jobs:
  - job: Build
    steps:
    - script: echo Build

- stage: Notify
  dependsOn:
  - Test
  - Build

  condition: succeededOrFailed()
  jobs:
  - job: Succeed
    condition: eq(dependencies.UnitTest.result, 'Succeeded')
    steps:
    - script: echo Succeed #(slack)

  - job: Fail
    condition: eq(dependencies.UnitTest.result, 'Failed')
    steps:
    - script: echo Fail #(slack)

--- EDIT ---
MS support confirmed jobs in multi stages can't support as yaml syntax itself.

Not same as the original flow as expected, but you can split succeed and fail into different stages as follow. (It may increase quite number of stages just for the notification if you want different message for each jobs..)

...

- stage: Notify_Succeeded
  condition: succeeded()

  jobs:
  - job: Succeed
    steps:
    - script: echo Succeed #(slack)

- stage: Notify_Fail
  condition: failed()
  jobs:
  - job: Fail
    steps:
    - script: echo Fail #(slack)
kevmando
  • 917
  • 1
  • 10
  • 17
  • You can use WebHooks to accomplish this: https://learn.microsoft.com/en-us/azure/devops/service-hooks/services/webhooks?view=azure-devops – derekbaker783 May 10 '20 at 12:44
  • Thanks @derekbaker783 I can see service hook has slack and general webhook support, but it seems to send predefined message only. Is there any way to send block kit message? – kevmando May 10 '20 at 14:50
  • Also some people might wonder the condition itself in case they want to post job, not just notification. Azure devops doc may miss something with stage to stage condition. I copied this dependencies.result thing from their doc, but the example was in same stage and worked. but for my case, they are in different stages. I wonder if there would be a simple build status variable like jenkins has. – kevmando May 10 '20 at 15:12
  • @CeceDong-MSFT unfortunately, his sample is not working yet for me. I'll confirm once I can make it work on mine. – kevmando May 12 '20 at 16:36

2 Answers2

3

It is possible but you have to use REST API. With below YAML you will get what you described:

variables:
  orgName: 'thecodemanual'

stages:
- stage: Test
  jobs:
  - job: UnitTest
    steps:
    - script: echo UnitTest
    - script: exit 1

- stage: Build
  jobs:
  - job: Build
    steps:
    - script: echo Build

- stage: Notify
  dependsOn:
  - Test
  - Build

  condition: succeededOrFailed()
  jobs:
  - job: InitialJob
    condition: always()
    steps:
    - pwsh: |
        $url = "https://dev.azure.com/$(orgName)/$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/timeline?api-version=5.1"
        $timeline = Invoke-RestMethod -Uri $url -Headers @{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"}
        Write-Host "Pipeline = $($timeline | ConvertTo-Json -Depth 100)"

        $test = $timeline.records | where { $_.identifier -eq "Test.UnitTest" }

        $result = $test.result

        Write-Host "##vso[task.setvariable variable=testResult;isOutput=true]$result"
      name: initial
      env:
        SYSTEM_ACCESSTOKEN: $(system.accesstoken)

  - job: Succeed
    dependsOn: InitialJob
    condition: eq(dependencies.InitialJob.outputs['initial.testResult'], 'succeeded')
    steps:
    - script: echo Succeed #(slack)

  - job: Fail
    dependsOn: InitialJob
    condition: eq(dependencies.InitialJob.outputs['initial.testResult'], 'failed')
    steps:
    - script: echo Fail #(slack)

Let me explain what I did above:

  • I made a call to REST API to get result of step from previous stage (as the the moment is not possible to get task result)
  • I assign status of that step to output variable
  • I used this variable as condition to run specific job Succeed or Fail

Remark: before you run code, please change orgName to your.

EDIT

Below url return you details for your build. But you will not fine there information about specific tasks or stages. But you will get there url for a timeline.

https://dev.azure.com/$(orgName)/$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)?api-version=5.1

This REST endpoint will return you a timeline which includes details for tasks and stages.

https://dev.azure.com/$(orgName)/$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/timeline?api-version=5.1
Krzysztof Madej
  • 32,704
  • 10
  • 78
  • 107
  • Hi, thanks. seems complecated, but I'll try and update. – kevmando May 11 '20 at 13:57
  • I have line 7 error saying Uri is null or empty. But if I click $url value, seems it returns json response. Maybe some convert error? timeline = Invoke-RestMethod -Uri $build._links.timeline.href -Header … Cannot validate argument on parameter 'Uri'. The argument is null or empty. – kevmando May 11 '20 at 14:10
  • I simplified this a bit. Now you have one call - directly to timeline. Can you give a try? BTW it is strange that you have an issue with original version. It works on my Azure DevOps :) – Krzysztof Madej May 11 '20 at 14:59
  • hmm not sure what's the diff between yours and mine :), but this time it doesn't give an error, but seems no result returned from initial.testResult - Expanded: eq('', 'failed') Result: False – kevmando May 11 '20 at 15:23
  • In first version you made first a call to build and it took timeline href from that response. Here you call directly timeline. Can you try make a call from postman and check what you get? – Krzysztof Madej May 11 '20 at 15:25
  • I'm just looking at the previous json (https://dev.azure.com/myorg/myproject/_apis/build/builds/buildid?api-version=5.1) and it seems there is no 'Test.UnitTest'. I think your script is looking for that key/value, right? – kevmando May 11 '20 at 15:35
  • In build you have a url for a timeline. Yes there is no `Test.UnitTest` in build, but it is in timeline. – Krzysztof Madej May 11 '20 at 15:59
  • I can see 'Test.UnitTest' in timeline result and can see "result": "failed". I'm not good at powershell, so not sure why dependencies.InitialJob.outputs['initial.testResult'] this ends up with 'null' thou. – kevmando May 11 '20 at 18:07
  • No worries. I am here to help you. You cant see it becayse at the moment jobs are limited to stage they belong. It may change in the future. But with rest api we can solve your issue today :) – Krzysztof Madej May 11 '20 at 18:20
  • BTW how do you know all these rest apis? :) It seems most of azure devops questions ends up with some rest apis calls and/or powershell combo. Yaml itself seems limited.. – kevmando May 11 '20 at 18:55
  • No I don't. IMHO documentation is really good. But sometime the problem is to know what to look for to achieve a goal. YAML configuration and REST API they quite different faces of the same product. – Krzysztof Madej May 11 '20 at 21:52
  • Now I can see why. It seems it fails to call azure rest api because of some security setting. (Not sure what it would be. It says internet Explorer enhanced security is enabled?) So the result was not the one I can see from browser session. (thought the response was weird since it's look like xml, and just assumed convert-to-json would convert it to json, but it wasn't.) – kevmando May 13 '20 at 03:37
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/213774/discussion-between-krzysztof-madej-and-kevmando). – Krzysztof Madej May 13 '20 at 11:18
  • Have to add this line in initial job to pass token properly in my devops, but the logic is working. Thank you! (Probably working with bash/jq as well) env: SYSTEM_ACCESSTOKEN: $(system.accesstoken) – kevmando May 13 '20 at 14:57
2

You can specify the conditions under which each stage runs. By default, a stage runs if it does not depend on any other stage, or if all of the stages that it depends on have completed and succeeded.

Example to run a stage based upon the status of running a previous stage:

stages:
- stage: A

# stage B runs if A fails
- stage: B
  condition: failed()

# stage C runs if B succeeds
- stage: C
  dependsOn:
  - A
  - B
  condition: succeeded('B')

Example of using a custom condition:

stages:
- stage: A

- stage: B
  condition: and(succeeded(), eq(variables['build.sourceBranch'], 'refs/heads/master'))

Note: You cannot currently specify that a stage run based on the value of an output variable set in a previous stage.

Documentation:

https://learn.microsoft.com/en-us/azure/devops/pipelines/process/stages?view=azure-devops&tabs=yaml#conditions

Cece Dong - MSFT
  • 29,631
  • 1
  • 24
  • 39
  • Hi, thanks. This is what I heard from MS after reporting issue. It seems conditions works in jobs in same stage or stage to stage, but not jobs in multiple stages. Also they suggested to request a feature if it is required. – kevmando May 11 '20 at 13:56
  • We are keeping improve the product, any idea you can submit a suggestion here: https://developercommunity.visualstudio.com/content/idea/post.html?space=21. Currently, you may try the workaround using api as @ Krzysztof Madej suggested. – Cece Dong - MSFT May 12 '20 at 07:23