Skip to content

Commit be1fd83

Browse files
github-actions: Add Slack notifications for kernelCI failures
Notify the #linux-kernel Slack channel when the kernelCI pipeline fails on a supported release branch. Successful runs stay silent. Triggers a notification: - build / boot failures - kselftest or LTP execution infrastructure failures - kselftest regressions (>±3 test diff vs baseline) - pre-setup / matrix-setup infra failures Stays silent for: - successful runs - branches whose base is not in VALID_BASES - [skip ci] runs (skip_ci sentinel from pre-setup) - LTP regressions (intentionally not classified — LTP runs informationally per existing pipeline policy: continue-on-error on test-ltp/compare-ltp, no PR-blocking in create-pr) - LTP test failures within tolerance - kselftest pass/fail diffs within ±3 threshold - create-pr failures (avoids noise from branch-name typos, regression-induced skips, transient gh API errors) Implementation: - notify-slack-kernelci.sh follows the create-pr-body-multiarch.sh pattern: named args, lives in .github/scripts/, fetched fresh from main by the calling workflow on each run. - Posts via slackapi/slack-github-action pinned to SHA 45a88b9581bfab2566dc881e2cd66d334e621e2c (v3.0.3) using the org-wide GH_BOT_SLACK_TOKEN secret. - Channel ID stored as repo variable SLACK_CHANNEL_LINUX_KERNEL so the destination can change without code edits. - Message includes mention, failed-stage summary, and branch/commit/PR/run links. Prereqs (already in place): - vars.SLACK_CHANNEL_LINUX_KERNEL set on the repo - GH_BOT_SLACK_TOKEN org secret scoped to kernel-src-tree - Bot user is a member of #linux-kernel
1 parent 1cf6d26 commit be1fd83

2 files changed

Lines changed: 195 additions & 0 deletions

File tree

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/bin/bash
2+
3+
# Builds a Slack chat.postMessage payload for a kernelCI failure and writes
4+
# it to stdout (or to a file via --output). The caller is responsible for
5+
# posting it (e.g. via slackapi/slack-github-action with a bot token).
6+
#
7+
# Usage:
8+
# notify-slack-kernelci.sh \
9+
# --channel-id CHANNEL_ID \
10+
# --base-branch BRANCH --head-ref BRANCH --head-sha SHA \
11+
# --pr-number N --is-pr true|false \
12+
# --repo OWNER/REPO --run-id ID \
13+
# --failed-stages "stage1, stage2, ..." \
14+
# [--mention-id SLACK_USER_ID] \
15+
# [--output PATH]
16+
17+
set -euo pipefail
18+
19+
CHANNEL_ID=""
20+
BASE_BRANCH=""
21+
HEAD_REF=""
22+
HEAD_SHA=""
23+
PR_NUMBER="0"
24+
IS_PR="false"
25+
REPO=""
26+
RUN_ID=""
27+
FAILED_STAGES=""
28+
MENTION_ID=""
29+
OUTPUT=""
30+
31+
while [[ $# -gt 0 ]]; do
32+
case "$1" in
33+
--channel-id) CHANNEL_ID="$2"; shift 2 ;;
34+
--base-branch) BASE_BRANCH="$2"; shift 2 ;;
35+
--head-ref) HEAD_REF="$2"; shift 2 ;;
36+
--head-sha) HEAD_SHA="$2"; shift 2 ;;
37+
--pr-number) PR_NUMBER="$2"; shift 2 ;;
38+
--is-pr) IS_PR="$2"; shift 2 ;;
39+
--repo) REPO="$2"; shift 2 ;;
40+
--run-id) RUN_ID="$2"; shift 2 ;;
41+
--failed-stages) FAILED_STAGES="$2"; shift 2 ;;
42+
--mention-id) MENTION_ID="$2"; shift 2 ;;
43+
--output) OUTPUT="$2"; shift 2 ;;
44+
*) echo "Error: Unknown option: $1" >&2; exit 1 ;;
45+
esac
46+
done
47+
48+
for var in CHANNEL_ID BASE_BRANCH HEAD_REF HEAD_SHA REPO RUN_ID FAILED_STAGES; do
49+
if [ -z "${!var}" ]; then
50+
# Render the var name back into the actual CLI flag (lowercase + _→-)
51+
flag="--${var,,}"
52+
flag="${flag//_/-}"
53+
echo "Error: $flag is required" >&2
54+
exit 1
55+
fi
56+
done
57+
58+
SHORT_SHA="${HEAD_SHA:0:12}"
59+
RUN_URL="https://github.com/$REPO/actions/runs/$RUN_ID"
60+
COMMIT_URL="https://github.com/$REPO/commit/$HEAD_SHA"
61+
62+
MENTION=""
63+
if [ -n "$MENTION_ID" ]; then
64+
MENTION="<@${MENTION_ID}> "
65+
fi
66+
67+
PR_LINE=""
68+
if [ "$IS_PR" = "true" ] && [ "$PR_NUMBER" != "0" ]; then
69+
PR_LINE=$'\n'"*PR:* <https://github.com/$REPO/pull/$PR_NUMBER|#$PR_NUMBER>"
70+
fi
71+
72+
MESSAGE=$(
73+
printf '%s:x: *kernelCI failed on `%s`*\n' "$MENTION" "$BASE_BRANCH"
74+
printf '*Branch:* `%s`\n' "$HEAD_REF"
75+
printf '*Commit:* <%s|%s>' "$COMMIT_URL" "$SHORT_SHA"
76+
printf '%s\n' "$PR_LINE"
77+
printf '*Failed stages:* %s\n' "$FAILED_STAGES"
78+
printf '*Run:* <%s|view logs>' "$RUN_URL"
79+
)
80+
81+
# Build chat.postMessage payload: channel + text. mrkdwn is on by default.
82+
PAYLOAD=$(jq -n \
83+
--arg channel "$CHANNEL_ID" \
84+
--arg text "$MESSAGE" \
85+
'{channel: $channel, text: $text}')
86+
87+
if [ -n "$OUTPUT" ]; then
88+
printf '%s\n' "$PAYLOAD" > "$OUTPUT"
89+
echo "Payload written to $OUTPUT" >&2
90+
else
91+
printf '%s\n' "$PAYLOAD"
92+
fi

.github/workflows/kernel-build-and-test-multiarch.yml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,3 +1605,106 @@ jobs:
16051605
--body-file pr_body.md \
16061606
--label "created-by-kernelci"
16071607
fi
1608+
1609+
notify-slack:
1610+
name: Notify Slack on failure
1611+
runs-on: ubuntu-latest
1612+
needs: [pre-setup, setup, build, boot, test-kselftest, test-ltp, compare-kselftest, compare-ltp, create-pr]
1613+
if: always() && needs.pre-setup.outputs.skip_ci != 'true'
1614+
1615+
steps:
1616+
- name: Resolve base branch and collect failed stages
1617+
id: decide
1618+
env:
1619+
# pre-setup outputs may be empty if pre-setup itself failed before
1620+
# writing them (artifact download / checksum failure). Fall back to
1621+
# the workflow_run event payload, which is always populated.
1622+
HEAD_REF: ${{ needs.pre-setup.outputs.head_ref || github.event.workflow_run.head_branch }}
1623+
HEAD_SHA: ${{ needs.pre-setup.outputs.head_sha || github.event.workflow_run.head_sha }}
1624+
BASE_REF: ${{ needs.pre-setup.outputs.base_ref }}
1625+
KSELFTEST_BASE: ${{ needs.compare-kselftest.outputs.base_branch }}
1626+
LTP_BASE: ${{ needs.compare-ltp.outputs.base_branch }}
1627+
run: |
1628+
# Whitelist must stay in sync with compare-kselftest / compare-ltp jobs
1629+
VALID_BASES="ciqlts9_2 ciqlts9_4 ciqlts8_6 ciqlts9_6 ciq-6.12.y ciq-6.12.y-next ciq-6.18.y ciq-6.18.y-next ciqcbr7_9"
1630+
1631+
BASE_BRANCH="$KSELFTEST_BASE"
1632+
[ -z "$BASE_BRANCH" ] && BASE_BRANCH="$LTP_BASE"
1633+
[ -z "$BASE_BRANCH" ] && BASE_BRANCH="$BASE_REF"
1634+
if [ -z "$BASE_BRANCH" ] && [[ "$HEAD_REF" =~ \{[^}]+\}[_-](.+) ]]; then
1635+
BASE_BRANCH="${BASH_REMATCH[1]}"
1636+
fi
1637+
1638+
if ! echo "$VALID_BASES" | grep -wq "$BASE_BRANCH"; then
1639+
echo "Base '$BASE_BRANCH' not in whitelist — skipping Slack notification"
1640+
echo "should_notify=false" >> $GITHUB_OUTPUT
1641+
exit 0
1642+
fi
1643+
1644+
FAILED_STAGES=()
1645+
[ "${{ needs.pre-setup.result }}" = "failure" ] && FAILED_STAGES+=("infra: pre-setup")
1646+
[ "${{ needs.setup.result }}" = "failure" ] && FAILED_STAGES+=("infra: matrix setup")
1647+
[ "${{ needs.build.result }}" = "failure" ] && FAILED_STAGES+=("build")
1648+
[ "${{ needs.boot.result }}" = "failure" ] && FAILED_STAGES+=("boot")
1649+
[ "${{ needs.test-kselftest.result }}" = "failure" ] && FAILED_STAGES+=("kselftest execution")
1650+
[ "${{ needs.test-ltp.result }}" = "failure" ] && FAILED_STAGES+=("LTP execution (infrastructure)")
1651+
1652+
[ "${{ needs.compare-kselftest.outputs.comparison_status_x86_64 }}" = "failed" ] && FAILED_STAGES+=("kselftest regression (x86_64)")
1653+
[ "${{ needs.compare-kselftest.outputs.comparison_status_aarch64 }}" = "failed" ] && FAILED_STAGES+=("kselftest regression (aarch64)")
1654+
1655+
# LTP regressions are intentionally NOT classified as failures: LTP
1656+
# runs informationally (continue-on-error in test-ltp, no PR-blocking
1657+
# in create-pr). Only LTP infra failures (test-ltp.result == failure
1658+
# above) are notified, since a crashed VM is a real CI problem.
1659+
1660+
if [ ${#FAILED_STAGES[@]} -eq 0 ]; then
1661+
echo "No failures detected — skipping Slack notification"
1662+
echo "should_notify=false" >> $GITHUB_OUTPUT
1663+
exit 0
1664+
fi
1665+
1666+
# Join with ", " — bash ${array[*]} only uses the first char of IFS,
1667+
# so set IFS to ',' and post-process the comma into ", ".
1668+
SUMMARY=$(IFS=','; echo "${FAILED_STAGES[*]}")
1669+
SUMMARY="${SUMMARY//,/, }"
1670+
echo "should_notify=true" >> $GITHUB_OUTPUT
1671+
echo "base_branch=$BASE_BRANCH" >> $GITHUB_OUTPUT
1672+
echo "head_ref=$HEAD_REF" >> $GITHUB_OUTPUT
1673+
echo "head_sha=$HEAD_SHA" >> $GITHUB_OUTPUT
1674+
echo "failed_summary=$SUMMARY" >> $GITHUB_OUTPUT
1675+
1676+
- name: Checkout kernel source
1677+
if: steps.decide.outputs.should_notify == 'true'
1678+
uses: actions/checkout@v4
1679+
with:
1680+
fetch-depth: 1
1681+
1682+
- name: Fetch notification script from main
1683+
if: steps.decide.outputs.should_notify == 'true'
1684+
run: |
1685+
git fetch origin main:main
1686+
git checkout origin/main -- .github/scripts/notify-slack-kernelci.sh
1687+
chmod +x .github/scripts/notify-slack-kernelci.sh
1688+
1689+
- name: Build Slack payload
1690+
if: steps.decide.outputs.should_notify == 'true'
1691+
run: |
1692+
.github/scripts/notify-slack-kernelci.sh \
1693+
--channel-id "${{ vars.SLACK_CHANNEL_LINUX_KERNEL }}" \
1694+
--base-branch "${{ steps.decide.outputs.base_branch }}" \
1695+
--head-ref "${{ steps.decide.outputs.head_ref }}" \
1696+
--head-sha "${{ steps.decide.outputs.head_sha }}" \
1697+
--pr-number "${{ needs.pre-setup.outputs.pr_number || '0' }}" \
1698+
--is-pr "${{ needs.pre-setup.outputs.is_pr || 'false' }}" \
1699+
--repo "${{ github.repository }}" \
1700+
--run-id "${{ github.run_id }}" \
1701+
--failed-stages "${{ steps.decide.outputs.failed_summary }}" \
1702+
--output slack_payload.json
1703+
1704+
- name: Post to Slack
1705+
if: steps.decide.outputs.should_notify == 'true'
1706+
uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3
1707+
with:
1708+
method: chat.postMessage
1709+
token: ${{ secrets.GH_BOT_SLACK_TOKEN }}
1710+
payload-file-path: slack_payload.json

0 commit comments

Comments
 (0)