diff --git a/README.md b/README.md index 354d21c5..2f59b4ef 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ A centralized collection of reusable GitHub Actions designed to streamline and a * [**Get Jira Release Notes**](get-jira-release-notes/README.md): Fetches Jira release notes and generates the release notes URL for a given project and version. * [**Get Jira Version**](get-jira-version/README.md): Extracts a Jira-compatible version number from a release version by formatting it appropriately for Jira. * [**Get Release Version**](get-release-version/README.md): Extracts the release version from the repox status on a specified branch. +* [**Notify Slack on Failure**](notify-slack/README.md): Sends a Slack notification when a job fails. * [**Publish GitHub Release**](publish-github-release/README.md): Publishes a GitHub Release with notes fetched from Jira or provided directly. * [**Release Jira Version**](release-jira-version/README.md): Releases a Jira version and creates the next one. * [**Update Analyzer**](update-analyzer/README.md): Updates an analyzer version in SonarQube or SonarCloud and creates a pull request. diff --git a/notify-slack/README.md b/notify-slack/README.md new file mode 100644 index 00000000..198aef74 --- /dev/null +++ b/notify-slack/README.md @@ -0,0 +1,101 @@ +# Notify Slack on Failure Action + +This GitHub Action sends a Slack notification summarizing failed jobs in a workflow run when used with an `if: failure()` condition. + +## Description + +The action posts a concise message to a Slack channel containing: +1. A link to the failed GitHub Actions run +2. A list of failed jobs provided by the workflow +3. A custom project-branded username and icon + +It is intended to be used as the last step (or in a dedicated job) guarded by `if: failure()` so that it only triggers on failures. + +## Dependencies + +This action depends on: +- [SonarSource/vault-action-wrapper](https://github.com/SonarSource/vault-action-wrapper) to retrieve the Slack token from Vault +- [rtCamp/action-slack-notify](https://github.com/rtCamp/action-slack-notify) to send the Slack message + +## Inputs + +| Input | Description | Required | Default | +|-----------------|------------------------------------------------------------------------|----------|-----------| +| `project-name` | The display name of the project; used in the Slack username. | Yes | - | +| `icon` | Emoji icon for the Slack message (Slack emoji code). | No | `:alert:` | +| `slack-channel` | Slack channel (without `#`) to post the notification into. | Yes | - | +| `jobs` | Comma-separated list of job names to report as failed (provided by you). | Yes | - | + +Note: The list of failed jobs must be provided via the `jobs` input by your workflow logic. + +## Outputs + +No outputs are produced by this action. + +## Usage + +### Basic usage (in a dedicated failure notification job) + +```yaml +jobs: + notify_on_failure: + needs: [ build, test, deploy ] # Example job dependencies + runs-on: ubuntu-latest + if: failure() + permissions: + statuses: read + id-token: write + steps: + - uses: SonarSource/release-github-actions/notify-slack@v1 + with: + project-name: 'My Project' + slack-channel: 'engineering-alerts' + jobs: ${{ toJSON(needs) }} +``` + +### Minimal usage (only required inputs) + +```yaml +- uses: SonarSource/release-github-actions/notify-slack@v1 + if: failure() + with: + project-name: 'My Project' + slack-channel: 'engineering-alerts' + jobs: 'build, test' +``` + +### Custom icon + +```yaml +- uses: SonarSource/release-github-actions/notify-slack@v1 + if: failure() + with: + project-name: 'My Project' + slack-channel: 'engineering-alerts' + icon: ':rocket:' + jobs: 'build, test' +``` + +## Implementation Details + +The action is a composite action that: +- Fetches `SLACK_TOKEN` from Vault path `development/kv/data/slack` using `vault-action-wrapper` +- Uses a workflow-provided `jobs` input (comma-separated string) to populate the "Failed Jobs" section +- Constructs a Slack message with run URL and the provided failed jobs list +- Uses `rtCamp/action-slack-notify` to send a minimal styled message (no title/footer) with danger color +- Sets Slack username to: ` CI Notifier` +- If the `jobs` list is empty or not provided, the "Failed Jobs" line will be blank + +## Prerequisites + +- Vault policy granting access to `development/kv/data/slack` must be configured for the repository. +- The secret at that path must contain a key named `token`. +- The Slack channel provided must exist and the token must have permission to post there. + +## Notes + +- Use `if: failure()` on the step or job; otherwise it will also fire on success. +- `project-name`, `slack-channel`, and `jobs` are required inputs (no defaults). +- Do not prefix the channel with `#`. +- Message formatting is intentionally minimal for quick triage in alert-focused channels. +- How you compute the failed jobs list is up to your workflow; you can use prior steps to gather job results and pass them to this action via `jobs`. diff --git a/notify-slack/action.yml b/notify-slack/action.yml new file mode 100644 index 00000000..a62354a4 --- /dev/null +++ b/notify-slack/action.yml @@ -0,0 +1,59 @@ +name: 'Notify Slack on Failure' +description: 'Sends a Slack notification when a job fails.' + +inputs: + project-name: + description: 'The name of the project.' + required: true + icon: + description: 'The icon for the Slack notification.' + required: false + default: ':alert:' + slack-channel: + description: 'The Slack channel to send the notification to.' + required: true + jobs: + description: 'A GitHub needs-like object string of jobs and their results (e.g., from toJSON(needs)).' + required: true + +runs: + using: "composite" + steps: + - name: Collect Failed Jobs + shell: bash + id: collect_failed_jobs + env: + JOBS: ${{ inputs.jobs }} + run: | + echo "Checking Jobs: $JOBS" + # Parse the provided jobs mapping string without relying on strict JSON. Extract keys where result == failure. + # This handles inputs like: { build: { result: failure, outputs: {} }, qa_tests: { result: skipped, outputs: {} } } + FAILED_JOBS=$(echo "$JOBS" | awk ' + /[[:alnum:]_.-]+:[[:space:]]*\{/{ key=$1; sub(/:$/, "", key) } + /result[[:space:]]*:[[:space:]]*failure/{ fails=(fails?fails", ":"") key } + END{ print fails } + ') + echo "Failed Jobs: $FAILED_JOBS" + echo "failed-jobs=$FAILED_JOBS" >> $GITHUB_OUTPUT + - name: Vault Secrets + id: secrets + uses: SonarSource/vault-action-wrapper@v3 + with: + secrets: | + development/kv/data/slack token | SLACK_TOKEN; + - name: Send Slack Notification + uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 + env: + SLACK_CHANNEL: ${{ inputs.slack-channel }} + SLACK_MSG_AUTHOR: " " + SLACK_TITLE: " " + SLACK_FOOTER: " " + SLACK_COLOR: danger + MSG_MINIMAL: true + SLACK_MESSAGE: | + *Run:* ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + *Failed Jobs:* ${{ steps.collect_failed_jobs.outputs.failed-jobs }} + SLACK_TOKEN: "${{ fromJSON(steps.secrets.outputs.vault).SLACK_TOKEN }}" + SLACK_ICON_EMOJI: "${{ inputs.icon }}" + SLACK_USERNAME: "${{ inputs.project-name }} CI Notifier" +