name: Shared - Slack Notification
on: workflow_call: inputs: channel: description: 'Slack channel name (without #)' required: true type: string status: description: 'Pipeline status: success, failure, cancelled, info' required: false type: string default: 'info' title: description: 'Notification title (overrides auto-generated)' required: false type: string default: '' message: description: 'Notification message body' required: false type: string default: '' environment: description: 'Deployment environment (develop, staging, prod)' required: false type: string default: '' version: description: 'Release version (e.g., 1.2.3)' required: false type: string default: '' changelog: description: 'Release changelog (commit list)' required: false type: string default: '' mention_on_failure: description: 'Slack mention on failure (e.g., @channel, @here)' required: false type: string default: '' pr_url: description: 'Pull request URL (for PR notifications)' required: false type: string default: ''
jobs: notify: name: Slack Notification runs-on: ubuntu-latest timeout-minutes: 5
steps: - name: Send Slack notification env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} CHANNEL: ${{ inputs.channel }} STATUS: ${{ inputs.status }} TITLE: ${{ inputs.title }} MESSAGE: ${{ inputs.message }} ENVIRONMENT: ${{ inputs.environment }} VERSION: ${{ inputs.version }} CHANGELOG: ${{ inputs.changelog }} MENTION_ON_FAILURE: ${{ inputs.mention_on_failure }} PR_URL: ${{ inputs.pr_url }} REPO: ${{ github.repository }} BRANCH: ${{ github.ref_name }} ACTOR: ${{ github.actor }} COMMIT_AUTHOR: ${{ github.event.head_commit.author.name || github.event.pull_request.user.login || github.actor }} SERVER_URL: ${{ github.server_url }} RUN_ID: ${{ github.run_id }} run: | set -euo pipefail
if [ -z "$SLACK_WEBHOOK_URL" ]; then echo "::warning::SLACK_WEBHOOK_URL secret is not set, skipping notification" exit 0 fi
# Status emoji and color case "$STATUS" in success) EMOJI="✅"; COLOR="#36a64f" ;; failure) EMOJI="❌"; COLOR="#E01E5A" ;; cancelled) EMOJI="⚠️"; COLOR="#ECB22E" ;; *) EMOJI="ℹ️"; COLOR="#1264A3" ;; esac
# Auto-generate title if not provided if [ -z "$TITLE" ]; then if [ -n "$VERSION" ]; then TITLE="Release v${VERSION} — ${REPO##*/}" else TITLE="Pipeline ${STATUS} — ${REPO##*/}" fi fi
HEADER="${EMOJI} ${TITLE}"
# Build blocks array BLOCKS=$(jq -n --arg header "$HEADER" \ '[{"type":"header","text":{"type":"plain_text","text":$header,"emoji":true}}]')
# Info fields BLOCKS=$(echo "$BLOCKS" | jq \ --arg repo "<${SERVER_URL}/${REPO}|${REPO}>" \ --arg branch "\`${BRANCH}\`" \ --arg env "${ENVIRONMENT:-N/A}" \ --arg actor "$ACTOR" \ --arg author "$COMMIT_AUTHOR" \ '. += [{"type":"section","fields":[ {"type":"mrkdwn","text":("*Repository:*\n" + $repo)}, {"type":"mrkdwn","text":("*Branch:*\n" + $branch)}, {"type":"mrkdwn","text":("*Environment:*\n" + $env)}, {"type":"mrkdwn","text":("*Triggered by:*\n" + $actor)}, {"type":"mrkdwn","text":("*Commit author:*\n" + $author)} ]}]')
# Custom message if [ -n "$MESSAGE" ]; then BLOCKS=$(echo "$BLOCKS" | jq --arg msg "$MESSAGE" \ '. += [{"type":"section","text":{"type":"mrkdwn","text":$msg}}]') fi
# Changelog if [ -n "$VERSION" ] && [ -n "$CHANGELOG" ]; then BLOCKS=$(echo "$BLOCKS" | jq --arg cl "*Changelog:*\n${CHANGELOG}" \ '. += [{"type":"section","text":{"type":"mrkdwn","text":$cl}}]') fi
# PR link if [ -n "$PR_URL" ]; then BLOCKS=$(echo "$BLOCKS" | jq --arg pr "📋 *Pull Request:* <${PR_URL}|View PR>" \ '. += [{"type":"section","text":{"type":"mrkdwn","text":$pr}}]') fi
# Mention on failure if [ "$STATUS" = "failure" ] && [ -n "$MENTION_ON_FAILURE" ]; then BLOCKS=$(echo "$BLOCKS" | jq --arg m "<!${MENTION_ON_FAILURE#@}> Pipeline failed — attention needed" \ '. += [{"type":"section","text":{"type":"mrkdwn","text":$m}}]') fi
# View Run button BLOCKS=$(echo "$BLOCKS" | jq --arg url "${SERVER_URL}/${REPO}/actions/runs/${RUN_ID}" \ '. += [{"type":"actions","elements":[{"type":"button","text":{"type":"plain_text","text":"View Run"},"url":$url}]}]')
# Build payload — channel field honored only by legacy incoming-webhook integrations if [ -n "$CHANNEL" ]; then PAYLOAD=$(jq -n \ --arg channel "$CHANNEL" \ --argjson blocks "$BLOCKS" \ --arg color "$COLOR" \ --arg text "$HEADER" \ '{channel: $channel, text: $text, blocks: $blocks, attachments: [{color: $color, blocks: []}]}') else PAYLOAD=$(jq -n \ --argjson blocks "$BLOCKS" \ --arg color "$COLOR" \ --arg text "$HEADER" \ '{text: $text, blocks: $blocks, attachments: [{color: $color, blocks: []}]}') fi
# Send via Slack Incoming Webhook HTTP_CODE=$(curl -s -o /tmp/slack_response.txt -w "%{http_code}" \ -X POST -H "Content-Type: application/json" \ -d "$PAYLOAD" "$SLACK_WEBHOOK_URL")
if [ "$HTTP_CODE" -eq 200 ]; then echo "Slack notification sent (channel hint: #${CHANNEL:-default})" else echo "::warning::Slack notification failed (HTTP $HTTP_CODE)" cat /tmp/slack_response.txt fi Shared (cross-cutting)· Reusable workflow ·on: workflow_call
Shared Slack Notify
Shared - Slack Notification
.github/workflows/shared-slack-notify.yml