Skip to content
Merged
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
57 changes: 57 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: "Test"

on:
push:
branches:
- master
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
test:
name: Notification script tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- name: Run tests
run: bash tests/run-tests.sh

- name: Check the action still passes inputs through the environment
run: |
python3 -c "
import yaml
step = yaml.safe_load(open('action.yaml'))['runs']['steps'][0]
required = {
'INPUT_WEBHOOK_URL', 'INPUT_NOTIFICATION_TYPE', 'INPUT_CHANNEL',
'INPUT_WORKFLOW_NAME', 'INPUT_JOB_RESULTS', 'GITHUB_CONTEXT',
}
missing = required - set(step.get('env', {}))
assert not missing, f'inputs no longer reach the script via env: {missing}'
body = step['run']
assert 'inputs.' not in body, f'run body interpolates an input directly: {body}'
"

# Separate job so NOTIFY_DRY_RUN cannot leak into the suite above, whose
# webhook cases must make a real request to the local sink.
smoke:
name: Action wiring smoke test
runs-on: ubuntu-latest
env:
NOTIFY_DRY_RUN: "1"
steps:
- uses: actions/checkout@v5

# Proves action.yaml actually reaches scripts/notify.sh. A packaging
# mistake here would otherwise only surface in the consuming repos.
- name: Invoke the action
uses: ./
with:
webhook-url: "unused-in-dry-run"
notification-type: 1
workflow-name: "Self Test"
job-results: "smoke:${{ job.status }}"
github-context: ${{ toJSON(github) }}
49 changes: 39 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
# CI/CD Notification Action

A reusable GitHub Action for sending workflow status notifications to Mattermost or Slack. Supports all GitHub Actions job statuses: success ✅, failure ❌, cancelled ◻️, and skipped ⏭️.
A reusable GitHub Action for sending workflow status notifications to Mattermost or Slack. Supports all GitHub Actions job statuses: success ✅, failure ❌, cancelled , and skipped ⏭️.

## Status Indicators

| Status | Emoji | Color | Description |
|--------|-------|-------|-------------|
| Success | ✅ | Green (#00FF00) | All jobs completed successfully |
| Failure | ❌ | Red (#FF0000) | One or more jobs failed |
| Cancelled | ◻️ | Gray (#808080) | Workflow was cancelled |
| Cancelled | | Gray (#808080) | Workflow was cancelled |
| Skipped | ⏭️ | Orange (#FFA500) | Jobs were skipped |

`success` is reported only when every job result is exactly `success`. Otherwise the
worst status wins, in the order `failure` > `cancelled` > `skipped`, and any value outside
that set is reported as ❓ UNKNOWN rather than folded into green.

The action **fails closed**: `job-results` that parses to nothing, or an entry without a
non-empty status, exits the step non-zero instead of sending a notification. A silent
fallthrough to green is what let comma-separated `job-results` report success over failing
builds.

## Usage

```yaml
uses: Wire-Network/cicd-notifications/.github/workflows/notification.yaml@v1
uses: Wire-Network/notification-action@v1
with:
webhook-url: ${{ secrets.WEBHOOK_URL }}
# ... other inputs
Expand Down Expand Up @@ -46,10 +55,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: Wire-Network/cicd-notifications/.github/workflows/notification.yaml@v1
uses: Wire-Network/notification-action@v1
with:
webhook-url: ${{ secrets.WEBHOOK_URL }}
notification-type: mattermost
notification-type: 1
channel: cicd-notifications
workflow-name: "Build & Test Workflow"
job-results: "build-and-test:${{ needs.build-and-test.result }}"
Expand Down Expand Up @@ -91,10 +100,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: wire-network/cicd-notifications@v1
uses: Wire-Network/notification-action@v1
with:
webhook-url: ${{ secrets.WEBHOOK_URL }}
notification-type: mattermost
notification-type: 1
channel: cicd-notifications
workflow-name: "Build & Test Workflow"
job-results: |
Expand Down Expand Up @@ -131,15 +140,16 @@ Simply change the `notification-type` to `2` and provide the Slack channel ID:
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `webhook-url` | Yes | - | Webhook URL for Slack or Mattermost |
| `notification-type` | Yes | `mattermost` | Type of notification service (1 or 2) |
| `notification-type` | Yes | `1` | Notification service: `1` for Mattermost, `2` for Slack |
| `channel` | No | `cicd-notifications` | Channel name (Mattermost) or channel ID (Slack) |
| `workflow-name` | Yes | - | Name of the workflow (e.g., `Build & Test Workflow`) |
| `job-results` | Yes | - | Job results in `job:status` format (space or newline-separated) or JSON |
| `job-results` | Yes | - | Job results as `job:status` pairs (comma-, space- or newline-separated) or a JSON object |
| `github-context` | Yes | - | JSON string of GitHub context |

### Job Results Format

You can pass job results in two formats:
You can pass job results in two formats. Entries may be separated by any mix of
commas, spaces and newlines.

**Simple format (recommended):**

Expand All @@ -156,12 +166,31 @@ Or inline for single job:
job-results: "build-and-test:${{ needs.build-and-test.result }}"
```

Or comma-separated on one line:

```yaml
job-results: "tests:${{ needs.tests.result }},build:${{ needs.build.result }}"
```

**JSON format (also supported):**

```yaml
job-results: '{"tests":"success","build":"failure","deploy":"skipped"}'
```

## Development

`scripts/notify.sh` holds the implementation; `action.yaml` only passes the inputs
through the environment. Run the suite with:

```bash
tests/run-tests.sh
```

It exercises every separator form, the status precedence, the fail-closed paths, the
rendered payload and the webhook call itself against a local sink. `NOTIFY_DRY_RUN=1`
prints the payload to stdout and skips the webhook.

## Tips

1. **Always use `if: always()`** on the notification job to ensure it runs even if other jobs fail
Expand Down
Loading