Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .github/scripts/notify-slack-kernelci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/bin/bash

# Builds a Slack chat.postMessage payload for a kernelCI failure and writes
# it to stdout (or to a file via --output). The caller is responsible for
# posting it (e.g. via slackapi/slack-github-action with a bot token).
#
# Usage:
# notify-slack-kernelci.sh \
# --channel-id CHANNEL_ID \
# --base-branch BRANCH --head-ref BRANCH --head-sha SHA \
# --pr-number N --is-pr true|false \
# --repo OWNER/REPO --run-id ID \
# --failed-stages "stage1, stage2, ..." \
# [--mention-id SLACK_USER_ID] \
# [--output PATH]

set -euo pipefail

CHANNEL_ID=""
BASE_BRANCH=""
HEAD_REF=""
HEAD_SHA=""
PR_NUMBER="0"
IS_PR="false"
REPO=""
RUN_ID=""
FAILED_STAGES=""
MENTION_ID=""
OUTPUT=""

while [[ $# -gt 0 ]]; do
case "$1" in
--channel-id) CHANNEL_ID="$2"; shift 2 ;;
--base-branch) BASE_BRANCH="$2"; shift 2 ;;
--head-ref) HEAD_REF="$2"; shift 2 ;;
--head-sha) HEAD_SHA="$2"; shift 2 ;;
--pr-number) PR_NUMBER="$2"; shift 2 ;;
--is-pr) IS_PR="$2"; shift 2 ;;
--repo) REPO="$2"; shift 2 ;;
--run-id) RUN_ID="$2"; shift 2 ;;
--failed-stages) FAILED_STAGES="$2"; shift 2 ;;
--mention-id) MENTION_ID="$2"; shift 2 ;;
--output) OUTPUT="$2"; shift 2 ;;
*) echo "Error: Unknown option: $1" >&2; exit 1 ;;
esac
done

for var in CHANNEL_ID BASE_BRANCH HEAD_REF HEAD_SHA REPO RUN_ID FAILED_STAGES; do
if [ -z "${!var}" ]; then
echo "Error: --${var,,} is required" >&2
Comment on lines +48 to +50
exit 1
fi
done

SHORT_SHA="${HEAD_SHA:0:12}"
RUN_URL="https://github.com/$REPO/actions/runs/$RUN_ID"
COMMIT_URL="https://github.com/$REPO/commit/$HEAD_SHA"

MENTION=""
if [ -n "$MENTION_ID" ]; then
MENTION="<@${MENTION_ID}> "
fi

PR_LINE=""
if [ "$IS_PR" = "true" ] && [ "$PR_NUMBER" != "0" ]; then
PR_LINE=$'\n'"*PR:* <https://github.com/$REPO/pull/$PR_NUMBER|#$PR_NUMBER>"
fi

MESSAGE=$(
printf '%s:x: *kernelCI failed on `%s`*\n' "$MENTION" "$BASE_BRANCH"
printf '*Branch:* `%s`\n' "$HEAD_REF"
printf '*Commit:* <%s|%s>' "$COMMIT_URL" "$SHORT_SHA"
printf '%s\n' "$PR_LINE"
printf '*Failed stages:* %s\n' "$FAILED_STAGES"
printf '*Run:* <%s|view logs>' "$RUN_URL"
)

# Build chat.postMessage payload: channel + text. mrkdwn is on by default.
PAYLOAD=$(jq -n \
--arg channel "$CHANNEL_ID" \
--arg text "$MESSAGE" \
'{channel: $channel, text: $text}')

if [ -n "$OUTPUT" ]; then
printf '%s\n' "$PAYLOAD" > "$OUTPUT"
echo "Payload written to $OUTPUT" >&2
else
printf '%s\n' "$PAYLOAD"
fi
95 changes: 95 additions & 0 deletions .github/workflows/kernel-build-and-test-multiarch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1605,3 +1605,98 @@ jobs:
--body-file pr_body.md \
--label "created-by-kernelci"
fi

notify-slack:
name: Notify Slack on failure
runs-on: ubuntu-latest
needs: [pre-setup, setup, build, boot, test-kselftest, test-ltp, compare-kselftest, compare-ltp, create-pr]
if: always() && needs.pre-setup.outputs.skip_ci != 'true'

steps:
- name: Resolve base branch and collect failed stages
id: decide
env:
HEAD_REF: ${{ needs.pre-setup.outputs.head_ref }}
BASE_REF: ${{ needs.pre-setup.outputs.base_ref }}
KSELFTEST_BASE: ${{ needs.compare-kselftest.outputs.base_branch }}
LTP_BASE: ${{ needs.compare-ltp.outputs.base_branch }}
run: |
# Whitelist must stay in sync with compare-kselftest / compare-ltp jobs
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"

BASE_BRANCH="$KSELFTEST_BASE"
[ -z "$BASE_BRANCH" ] && BASE_BRANCH="$LTP_BASE"
[ -z "$BASE_BRANCH" ] && BASE_BRANCH="$BASE_REF"
if [ -z "$BASE_BRANCH" ] && [[ "$HEAD_REF" =~ \{[^}]+\}[_-](.+) ]]; then
BASE_BRANCH="${BASH_REMATCH[1]}"
fi

if ! echo "$VALID_BASES" | grep -wq "$BASE_BRANCH"; then
echo "Base '$BASE_BRANCH' not in whitelist — skipping Slack notification"
echo "should_notify=false" >> $GITHUB_OUTPUT
exit 0
Comment on lines +1618 to +1637
fi

FAILED_STAGES=()
[ "${{ needs.pre-setup.result }}" = "failure" ] && FAILED_STAGES+=("infra: pre-setup")
[ "${{ needs.setup.result }}" = "failure" ] && FAILED_STAGES+=("infra: matrix setup")
[ "${{ needs.build.result }}" = "failure" ] && FAILED_STAGES+=("build")
[ "${{ needs.boot.result }}" = "failure" ] && FAILED_STAGES+=("boot")
[ "${{ needs.test-kselftest.result }}" = "failure" ] && FAILED_STAGES+=("kselftest execution")
[ "${{ needs.test-ltp.result }}" = "failure" ] && FAILED_STAGES+=("LTP execution (infrastructure)")

[ "${{ needs.compare-kselftest.outputs.comparison_status_x86_64 }}" = "failed" ] && FAILED_STAGES+=("kselftest regression (x86_64)")
[ "${{ needs.compare-kselftest.outputs.comparison_status_aarch64 }}" = "failed" ] && FAILED_STAGES+=("kselftest regression (aarch64)")

# LTP regressions are intentionally NOT classified as failures: LTP
# runs informationally (continue-on-error in test-ltp, no PR-blocking
# in create-pr). Only LTP infra failures (test-ltp.result == failure
# above) are notified, since a crashed VM is a real CI problem.

if [ ${#FAILED_STAGES[@]} -eq 0 ]; then
echo "No failures detected — skipping Slack notification"
echo "should_notify=false" >> $GITHUB_OUTPUT
exit 0
fi

SUMMARY=$(IFS=", "; echo "${FAILED_STAGES[*]}")
echo "should_notify=true" >> $GITHUB_OUTPUT
echo "base_branch=$BASE_BRANCH" >> $GITHUB_OUTPUT
echo "failed_summary=$SUMMARY" >> $GITHUB_OUTPUT

- name: Checkout kernel source
if: steps.decide.outputs.should_notify == 'true'
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Fetch notification script from main
if: steps.decide.outputs.should_notify == 'true'
run: |
git fetch origin main:main
git checkout origin/main -- .github/scripts/notify-slack-kernelci.sh
chmod +x .github/scripts/notify-slack-kernelci.sh

- name: Build Slack payload
if: steps.decide.outputs.should_notify == 'true'
run: |
.github/scripts/notify-slack-kernelci.sh \
--channel-id "${{ vars.SLACK_CHANNEL_LINUX_KERNEL }}" \
--base-branch "${{ steps.decide.outputs.base_branch }}" \
--head-ref "${{ needs.pre-setup.outputs.head_ref }}" \
--head-sha "${{ needs.pre-setup.outputs.head_sha }}" \
--pr-number "${{ needs.pre-setup.outputs.pr_number }}" \
--is-pr "${{ needs.pre-setup.outputs.is_pr }}" \
--repo "${{ github.repository }}" \
--run-id "${{ github.run_id }}" \
--failed-stages "${{ steps.decide.outputs.failed_summary }}" \
--mention-id "U092R1ERDJQ" \
--output slack_payload.json

- name: Post to Slack
if: steps.decide.outputs.should_notify == 'true'
uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3
with:
method: chat.postMessage
token: ${{ secrets.GH_BOT_SLACK_TOKEN }}
payload-file-path: slack_payload.json
Comment on lines +1698 to +1702