Skip to content

Commit 536f755

Browse files
GHA-224 Add notify-failure action for rich CI failure Slack notifications (#131)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 8cd2cfb commit 536f755

File tree

10 files changed

+1284
-2
lines changed

10 files changed

+1284
-2
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Test Notify Failure Action
2+
3+
on:
4+
workflow_call:
5+
pull_request:
6+
paths:
7+
- 'notify-failure/**'
8+
- '.github/workflows/test-notify-failure.yml'
9+
push:
10+
branches:
11+
- branch-*
12+
paths:
13+
- 'notify-failure/**'
14+
- '.github/workflows/test-notify-failure.yml'
15+
workflow_dispatch:
16+
17+
jobs:
18+
integration-test:
19+
name: Send Test Failure Notification
20+
runs-on: ubuntu-latest
21+
continue-on-error: true
22+
permissions:
23+
id-token: write
24+
actions: read
25+
steps:
26+
- name: Checkout code
27+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
28+
- name: Send test failure notification
29+
uses: ./notify-failure
30+
with:
31+
project-name: release-github-actions
32+
slack-channel: alerts-release-github-actions
33+
needs: '{"qa_plugin":{"result":"failure"},"build":{"result":"success"}}'
34+
35+
unit-tests:
36+
name: Run Unit Tests
37+
runs-on: ubuntu-latest
38+
39+
steps:
40+
- name: Checkout code
41+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
42+
43+
- name: Set up Python
44+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
45+
with:
46+
python-version: '3.10'
47+
cache: 'pip'
48+
cache-dependency-path: notify-failure/requirements.txt
49+
50+
- name: Install dependencies
51+
run: |
52+
cd notify-failure
53+
pip install -r requirements.txt
54+
pip install pytest pytest-cov
55+
56+
- name: Run unit tests
57+
run: |
58+
cd notify-failure
59+
python -m pytest test_notify_failure.py -v --cov=notify_failure --cov-report=term-missing

.github/workflows/test-notify-slack.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,24 @@ jobs:
3333
color: good
3434
message: 'Test notification from `notify-slack` action on branch `${{ github.ref_name }}`'
3535

36+
integration-test-failed-jobs:
37+
name: Send Test Notification (deprecated jobs input)
38+
runs-on: ubuntu-latest
39+
continue-on-error: true
40+
permissions:
41+
id-token: write
42+
steps:
43+
- name: Checkout code
44+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
45+
- name: Send test Slack notification with failed-jobs fallback
46+
uses: ./notify-slack
47+
with:
48+
project-name: release-github-actions
49+
slack-channel: alerts-release-github-actions
50+
icon: ':test_tube:'
51+
color: warning
52+
jobs: '{"qa_plugin":{"result":"failure"},"build":{"result":"success"}}'
53+
3654
unit-tests:
3755
name: Run Unit Tests
3856
runs-on: ubuntu-latest

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ A centralized collection of reusable GitHub Actions designed to streamline and a
1616
| [Get Jira Version](get-jira-version/README.md) | Extracts a Jira-compatible version number from a release version by formatting it appropriately for Jira |
1717
| [Get Release Version](get-release-version/README.md) | Extracts the release version from the repox status on a specified branch |
1818
| [Lock Branch](lock-branch/README.md) | Locks or unlocks a branch by modifying the `lock_branch` setting in branch protection rules |
19+
| [Notify Failure](notify-failure/README.md) | Sends a rich Slack failure notification with job details, commit info, root cause analysis, and run links |
1920
| [Notify Slack on Failure](notify-slack/README.md) | Sends a Slack notification when a job fails |
2021
| [Send Slack Message](slack-message/README.md) | Sends a markdown message to a Slack channel |
2122
| [Publish GitHub Release](publish-github-release/README.md) | Publishes a GitHub Release with notes fetched from Jira or provided directly |

notify-failure/README.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Notify Failure Action
2+
3+
This GitHub Action sends a rich Slack failure notification automatically assembled from GitHub context and the GitHub API — no message-building boilerplate required.
4+
5+
## Description
6+
7+
When a CI job fails, this action fetches relevant context (commit info, job logs) from the GitHub API and posts a structured Slack message containing:
8+
9+
- Repository, branch, and workflow name
10+
- Failed job names
11+
- Run attempt number (shows when a job has been retried)
12+
- Last commit author and commit message
13+
- Root cause: first meaningful error line extracted from the failed job logs
14+
- Develocity build scan link (if present in the logs)
15+
- Links to view the run and re-run failed jobs
16+
17+
All sections are included by default. Individual sections can be excluded via inputs.
18+
19+
## Dependencies
20+
21+
- [SonarSource/vault-action-wrapper](https://github.com/SonarSource/vault-action-wrapper) to retrieve the Slack token from Vault
22+
- GitHub token (`github.token`) for reading commit info and job logs via the GitHub API
23+
24+
## Example Slack Output
25+
26+
```
27+
🚨 CI Failure — SonarSource/sonar-php
28+
29+
Workflow: Java CI • Branch: `main` • Attempt: 2
30+
Triggered by: janedoe
31+
PR: #42 Fix authentication bug
32+
Failed Jobs: qa_plugin (step: Run unit tests), build
33+
34+
🚨 Flaky: This workflow has failed 3 consecutive times on this branch
35+
36+
🔬 Root Cause: error: method analyze(UCFG) is not public
37+
🧪 Test Failures: 3 failures, 1 error (across 42 tests)
38+
📊 Build Scan: View Develocity Scan
39+
40+
🔍 View Run & Re-run Failed Jobs
41+
```
42+
43+
## Inputs
44+
45+
| Input | Description | Required | Default |
46+
|-------|-------------|----------|---------|
47+
| `project-name` | The display name of the project; used in the Slack username. | Yes | - |
48+
| `slack-channel` | Slack channel to post to (without `#`). | Yes | - |
49+
| `needs` | The `toJSON(needs)` object from the caller workflow, used to identify which jobs failed. | Yes | - |
50+
| `github-token` | GitHub token for API calls (read commits and job logs). | No | `${{ github.token }}` |
51+
| `include-pr-info` | Include PR title and link when the run is associated with a pull request. | No | `true` |
52+
| `include-failed-step` | Include the name of the failed step within each failed job. | No | `true` |
53+
| `include-test-counts` | Include failed test counts parsed from Maven surefire output in job logs. | No | `true` |
54+
| `include-flakiness` | Include a flakiness indicator when the workflow has failed consecutively on this branch. | No | `true` |
55+
| `include-root-cause` | Include a root cause line extracted from failed job logs. | No | `true` |
56+
| `include-develocity` | Include a Develocity build scan link if found in the logs. | No | `true` |
57+
| `include-run-attempt` | Include the run attempt number. | No | `true` |
58+
59+
## Outputs
60+
61+
No outputs are produced by this action.
62+
63+
## Usage
64+
65+
```yaml
66+
jobs:
67+
build:
68+
runs-on: ubuntu-latest
69+
steps:
70+
- run: ./gradlew build
71+
72+
test:
73+
runs-on: ubuntu-latest
74+
needs: build
75+
steps:
76+
- run: ./gradlew test
77+
78+
notify_on_failure:
79+
needs: [build, test]
80+
runs-on: ubuntu-latest
81+
if: failure()
82+
permissions:
83+
id-token: write
84+
actions: read # needed to read job logs
85+
steps:
86+
- uses: SonarSource/release-github-actions/notify-failure@v1
87+
with:
88+
project-name: 'My Project'
89+
slack-channel: 'squad-alerts'
90+
needs: ${{ toJSON(needs) }}
91+
```
92+
93+
### Excluding sections
94+
95+
```yaml
96+
- uses: SonarSource/release-github-actions/notify-failure@v1
97+
with:
98+
project-name: 'My Project'
99+
slack-channel: 'squad-alerts'
100+
needs: ${{ toJSON(needs) }}
101+
include-develocity: 'false'
102+
include-run-attempt: 'false'
103+
```
104+
105+
## Prerequisites
106+
107+
- Vault policy granting access to `development/kv/data/slack` must be configured for the repository.
108+
- The job must have `id-token: write` permission (for Vault) and `actions: read` permission (for reading job logs via the GitHub API).
109+
110+
## Notes
111+
112+
- Root cause extraction uses heuristics (javac error lines, Maven `BUILD FAILURE`, `Exception in thread`, etc.) and may not always find the most relevant line. It is best-effort.
113+
- Develocity scan links are extracted by regex from job logs. If a project does not use Develocity, the section is simply omitted.
114+
- When `github.token` is used (the default), it has read access to the repository's actions data. No additional token configuration is needed for most public or internal repositories.

notify-failure/action.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: 'Notify Failure'
2+
description: 'Sends a rich Slack failure notification with job details, commit info, root cause analysis, and run links.'
3+
4+
inputs:
5+
project-name:
6+
description: 'The display name of the project; used in the Slack username.'
7+
required: true
8+
slack-channel:
9+
description: 'Slack channel to post to (without #).'
10+
required: true
11+
needs:
12+
description: 'The toJSON(needs) object from the caller workflow, used to identify which jobs failed.'
13+
required: true
14+
github-token:
15+
description: 'GitHub token for API calls (read commits and job logs). Defaults to the built-in GITHUB_TOKEN.'
16+
required: false
17+
default: ${{ github.token }}
18+
include-pr-info:
19+
description: 'Include PR title and link when the run is associated with a pull request.'
20+
required: false
21+
default: 'true'
22+
include-failed-step:
23+
description: 'Include the name of the failed step within each failed job.'
24+
required: false
25+
default: 'true'
26+
include-test-counts:
27+
description: 'Include failed test counts parsed from Maven surefire output in job logs.'
28+
required: false
29+
default: 'true'
30+
include-flakiness:
31+
description: 'Include a flakiness indicator when the same workflow has failed consecutively on this branch.'
32+
required: false
33+
default: 'true'
34+
include-root-cause:
35+
description: 'Include a root cause line extracted from failed job logs.'
36+
required: false
37+
default: 'true'
38+
include-develocity:
39+
description: 'Include a Develocity build scan link if one is found in the job logs.'
40+
required: false
41+
default: 'true'
42+
include-run-attempt:
43+
description: 'Include the run attempt number (useful for showing retries).'
44+
required: false
45+
default: 'true'
46+
47+
runs:
48+
using: "composite"
49+
steps:
50+
- name: Set up Python
51+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
52+
with:
53+
python-version: '3.10'
54+
- name: Install dependencies
55+
shell: bash
56+
run: |
57+
python3 -m venv "$GITHUB_ACTION_PATH/.venv"
58+
"$GITHUB_ACTION_PATH/.venv/bin/pip" install -r "$GITHUB_ACTION_PATH/requirements.txt" --quiet
59+
- name: Build failure message
60+
id: build_message
61+
shell: bash
62+
env:
63+
GITHUB_TOKEN: ${{ inputs.github-token }}
64+
NEEDS_JSON: ${{ inputs.needs }}
65+
GH_REPOSITORY: ${{ github.repository }}
66+
GH_SHA: ${{ github.sha }}
67+
GH_REF_NAME: ${{ github.ref_name }}
68+
GH_WORKFLOW: ${{ github.workflow }}
69+
GH_RUN_ID: ${{ github.run_id }}
70+
GH_RUN_ATTEMPT: ${{ github.run_attempt }}
71+
GH_ACTOR: ${{ github.actor }}
72+
GH_SERVER_URL: ${{ github.server_url }}
73+
GH_HEAD_REF: ${{ github.head_ref }}
74+
75+
INCLUDE_PR_INFO: ${{ inputs.include-pr-info }}
76+
INCLUDE_FAILED_STEP: ${{ inputs.include-failed-step }}
77+
INCLUDE_TEST_COUNTS: ${{ inputs.include-test-counts }}
78+
INCLUDE_FLAKINESS: ${{ inputs.include-flakiness }}
79+
INCLUDE_ROOT_CAUSE: ${{ inputs.include-root-cause }}
80+
INCLUDE_DEVELOCITY: ${{ inputs.include-develocity }}
81+
INCLUDE_RUN_ATTEMPT: ${{ inputs.include-run-attempt }}
82+
run: |
83+
"$GITHUB_ACTION_PATH/.venv/bin/python" "$GITHUB_ACTION_PATH/notify_failure.py"
84+
- name: Vault Secrets
85+
id: secrets
86+
uses: SonarSource/vault-action-wrapper@v3
87+
with:
88+
secrets: |
89+
development/kv/data/slack token | SLACK_TOKEN;
90+
- name: Send Slack Notification
91+
uses: SonarSource/release-github-actions/notify-slack@v1
92+
with:
93+
project-name: ${{ inputs.project-name }}
94+
slack-channel: ${{ inputs.slack-channel }}
95+
message: ${{ steps.build_message.outputs.message }}

0 commit comments

Comments
 (0)