Reputation: 23
I'm trying to send a Slack message to a specified channel post-build, all branch policies are successful, and all status checks pass in a Azure DevOps YAML pipeline. But it seems I am having a few individual issues. This is fairly new to me so any help is appreciated.
Concept:
What I Tried:
Problems:
I have included some reference images below as well.
trigger:
- none # Disable automatic trigger for commits. Trigger only for PRs.
resources:
repositories:
- repository: templates
type: git
name: Hagerty/azure-pipelines-templates
pr:
branches:
include:
- main # Only trigger for PRs targeting 'main'
pool:
vmImage: 'windows-latest'
variables:
- group: billing_pr_slack_notification # ADO variable group
stages:
- stage: BuildAndNotify
displayName: 'Billing PR Slack Notifications'
# Ensure this stage runs only after all PR policies succeed
condition: and(succeeded(), eq(coalesce(variables['System.PullRequest.IsDraft'], false), false), startsWith(variables['Build.SourceBranch'], 'refs/pull/'))
jobs:
- job: LogVariables
displayName: 'Log Pipeline Variables'
steps:
- script: |
echo '=== Debugging Variables ==='
echo 'AZURE_DEVOPS_ORG: $(AZURE_DEVOPS_ORG)'
echo 'AZURE_DEVOPS_PROJECT: $(AZURE_DEVOPS_PROJECT)'
echo 'SLACK_API_URL: $(SLACK_API_URL)'
echo 'SLACK_BOT_TOKEN: $(SLACK_BOT_TOKEN)'
echo 'SLACK_CHANNEL: $(SLACK_CHANNEL)'
echo 'PRODUCT_BILLING_DEV_WEBHOOK_URL: $(PRODUCT_BILLING_DEV_WEBHOOK_URL)'
echo 'System.PullRequest.IsDraft: $(System.PullRequest.IsDraft)'
echo 'System.PullRequest.SourceBranch: $(System.PullRequest.SourceBranch)'
echo 'System.PullRequest.TargetBranch: $(System.PullRequest.TargetBranch)'
displayName: 'Output Debugging Variables'
- job: FetchSlackUserID
displayName: 'Fetch Slack User ID'
dependsOn: LogVariables
steps:
- powershell: |
$creatorEmail = $env:BUILD_REQUESTEDFOR
if (-not $creatorEmail) {
Write-Host "##vso[task.logissue type=error]The Build.RequestedFor email is empty. Cannot proceed to fetch Slack User ID."
exit 1
}
$slackToken = "$env:SLACK_BOT_TOKEN"
$response = Invoke-RestMethod -Uri "https://slack.com/api/users.lookupByEmail?email=$creatorEmail" -Headers @{Authorization="Bearer $slackToken"}
if ($response.ok -eq $true -and $response.user.id) {
$userId = $response.user.id
Write-Host "##vso[task.setvariable variable=userId]$userId"
Write-Host "Successfully retrieved Slack User ID: $userId"
Write-Host "Debug: Slack User ID is $userId"
} else {
Write-Host "Defaulting to Slack channel $env:SLACK_CHANNEL."
Write-Host "##vso[task.setvariable variable=userId]$env:SLACK_CHANNEL"
}
displayName: 'Fetch Slack User ID'
- job: SlackNotification
displayName: 'Send Slack Notifications'
dependsOn: FetchSlackUserID
steps:
# Send a channel notification only on success
- powershell: |
$webhookUrl = "$env:PRODUCT_BILLING_DEV_WEBHOOK_URL"
$createdBy = $env:SYSTEM_PULLREQUEST_CREATEDBY
$prTitle = $env:SYSTEM_PULLREQUEST_TITLE
$prDescription = $env:SYSTEM_PULLREQUEST_DESCRIPTION
$prUrl = $env:SYSTEM_PULLREQUEST_URL
# Handle unset variables by providing default values
if (-not $createdBy) { $createdBy = "Unknown User" }
if (-not $prTitle) { $prTitle = "Untitled PR" }
if (-not $prDescription) { $prDescription = "No description provided." }
if (-not $prUrl) { $prUrl = "No link available." }
$message = @{
text = "<@here> [$createdBy] has created a new Pull Request & is ready for Code Review:\n**Title**: $prTitle\n**Description**: $prDescription\n[View PR]($prUrl)"
} | ConvertTo-Json -Depth 10
Invoke-RestMethod -Uri $webhookUrl -Method Post -Body $message -ContentType 'application/json'
Write-Host "Slack channel notification sent successfully."
displayName: 'Send Slack Notification'
condition: and(succeeded(), endsWith(variables['System.PullRequest.TargetBranch'], '/main'))
# Send a direct message only on failure
- powershell: |
$gifUrls = @(
"https://media.giphy.com/media/Ju7l5y9osyymQ/giphy.gif",
"https://media.giphy.com/media/26AHONQ79FdWZhAI0/giphy.gif",
"https://media.giphy.com/media/3o6Zt481isNVuQI1l6/giphy.gif"
)
$randomGif = $gifUrls | Get-Random
$slackToken = "$env:SLACK_BOT_TOKEN"
$userId = "$env:userId"
$prTitle = $env:SYSTEM_PULLREQUEST_TITLE
$prUrl = $env:SYSTEM_PULLREQUEST_URL
# Handle unset variables by providing default values
if (-not $prTitle) { $prTitle = "Untitled PR" }
if (-not $prUrl) { $prUrl = "No link available." }
$message = if ($env:BUILD_STATUS -eq "Failed") {
"Your Pull Request **$prTitle** failed to build. Please review your branch.\n[View PR]($prUrl)\n\n![Failure GIF]($randomGif)"
} else {
"Your Pull Request **$prTitle** failed the following policy check: $env:BUILD_DEFINITIONNAME. Please review your branch.\n[View PR]($prUrl)\n\n![Failure GIF]($randomGif)"
}
$body = @{
channel = $userId
text = $message
} | ConvertTo-Json -Depth 10
Invoke-RestMethod -Uri "https://slack.com/api/chat.postMessage" -Method Post -Headers @{Authorization="Bearer $slackToken"} -Body $body
Write-Host "Slack DM sent successfully to user $userId."
displayName: 'Send Slack DM if Build or Policy Fails'
condition: failed()
Upvotes: 0
Views: 28
Reputation: 17953
There's a few concerns with your pipeline.
The first is a common misunderstanding about jobs and variable scope. Each job in the Microsoft cloud-hosted agents (windows-latest) runs on a different agent, so any environment variables that you create within that job are not available to subsequent jobs. Jobs should be large units of work where you group related things together. In your scenario you're actually running three similar activities in different jobs, which in addition to the scope issues will slow down the pipeline considerably as the system will spend a fair bit of time to allocate new agents. You'd be better off with a single job with multiple steps as the variables you create in early steps will always be available in subsequent steps (plus a minor reduction in execution time).
The second concern is related to your conditions. The function succeeded()
and failed
refers to the current job's execution. As you've written it, you have conditions on the tasks within the SlackNotification job. The succeeded()
condition on the first task has no effect because it's the first task in the job, and the failed()
condition on the second task only executes if the 'Send Slack Notification' powershell task fails, which presumably is not what you're after.
I'm assuming that you want the succeeded or failed conditions applied based on the outcome of the build activity. You haven't shown the build activity in your pipeline, but assuming it's been omitted for clarity you can achieve the desired effect with two jobs:
trigger: none
jobs:
- job: build
steps:
... steps to build are here
- job: slacknotify
displayName: 'Send Slack Notification'
dependsOn:
- build
# specify a condition that looks at the "build" job's final result
condition: |
and(
in(dependencies.build.result, 'Succeeded', 'Failed'),
eq(variables['Build.Reason'],'PullRequest'),
eq(variables['System.PullRequest.TargetBranch'], 'ref/heads/main')
)
variables:
# create a job scoped variable that holds the outcome of the build result
buildResult: $[ dependencies.build.result ]
steps:
- powershell: |
...
displayName: Resolve Slack User ID
- powershell: |
...
displayName: Send Success Message
condition: eq(variables['buildResult'], 'Success')
- powershell: |
...
displayName: Send Failure Message
condition: eq(variables['buildResult'], 'Failed')
Related to your variables:
$(Build.RequestedBy)
(System.PullRequest.SourceRepositoryURI)
and $(System.PullRequest.PullRequestId)
to construct the URL.As for determine branch policy completion criteria, you'd have to use the REST API to fetch the Pull Request to evaluate which conditions have been met.
Lastly, the pr
trigger you've specified has no effect. Because you're using Azure Repository (as evidenced by the Branch Policy), pr triggers can only be used with GitHub or BitBucket Cloud. This pipeline will only be triggered by a pull request if it's configured in the Build Validation of the Branch Policy.
Upvotes: 0