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
177 changes: 177 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# GitHub Actions Notifications

The `testsPython.yml` workflow can notify Slack, Discord, Microsoft Teams, Mailgun, and Twilio when the Python unit test job fails. It can also send manual test notifications or print dry-run payloads from the GitHub Actions UI.

The workflow calls `scripts/github_actions_notify.py`. The script uses only the Python standard library, so the GitHub Actions runner does not need to install vendor SDKs.

## Behavior

- Notifications are sent when `python-unit-tests` fails.
- The notification job uses `if: ${{ always() }}` so it still runs after a test failure.
- Each provider is optional. A provider is skipped when its required configuration or addressees are not configured.
- Each provider supports JSON arrays where multiple addressees make sense.
- Slack and Discord support target objects so mentions can be set per webhook destination.
- Notification failures are allowed to continue so a broken notification provider does not block CI.
- Dry-run output masks webhook URLs, email addresses, and phone numbers in logs.

## GitHub Secrets and Variables

Add secrets and variables from the repository settings:

1. Open the repository in GitHub.
2. Go to `Settings` -> `Secrets and variables` -> `Actions`.
3. Add sensitive values under `Secrets`.
4. Add non-sensitive values under `Variables`.

Treat webhook URLs, API keys, email addresses, phone numbers, Account SIDs, and Messaging Service SIDs as secrets. Dry-run logs mask these values, but GitHub Actions can still print workflow environment values that are stored as variables.

Use JSON arrays for multiple addressees:

```json
["first@example.com", "second@example.com"]
```

Provider addressee fields use JSON arrays so one workflow configuration can send to multiple destinations.

Slack and Discord use target objects so each webhook destination can have its own optional mentions:

```json
[
{
"url": "https://example.invalid/webhook",
"mentions": ["<@U0123456789>", "<!channel>"]
},
{
"url": "https://example.invalid/another-webhook"
}
]
```

## Manual Testing

Open the `Actions` tab, select `Python Unit Tests`, choose `Run workflow`, then set `notification-mode`.

### `notify-on-failure`

Use `notify-on-failure` for normal workflow behavior. Notifications are sent only when the Python unit test job fails.

### `dry-run`

Use `dry-run` to print parsed addressee lists and payloads without sending notifications. This is the safest first test after adding configuration.

### `test-notification`

Use `test-notification` to send configured notifications even if the Python tests pass. Use this after dry-run output looks correct.

## Slack

Get credentials from Slack by creating or selecting a Slack app and enabling Incoming Webhooks:

- Slack Incoming Webhooks: https://api.slack.com/messaging/webhooks
- Slack GitHub Action docs: https://docs.slack.dev/tools/slack-github-action/

After you have webhook URLs, configure this secret:

| Type | Name | Example |
| --- | --- | --- |
| Secret | `SLACK_WEBHOOK_TARGETS_JSON` | `[{"url": "https://hooks.slack.com/services/T000/B000/XXX", "mentions": ["<@U0123456789>", "<!channel>"]}, {"url": "https://hooks.slack.com/services/T111/B111/YYY"}]` |

Each target object requires `url` and may include an optional `mentions` array.

Each Slack webhook is tied to the channel selected when the webhook is created, so a list of webhook URLs is the Slack addressee list.

## Discord

Get credentials by creating webhooks in the Discord channels that should receive alerts:

- Discord webhook resources: https://discord.com/developers/docs/resources/webhook
- Discord webhook guide: https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks

After you have webhook URLs, configure this secret:

| Type | Name | Example |
| --- | --- | --- |
| Secret | `DISCORD_WEBHOOK_TARGETS_JSON` | `[{"url": "https://discord.com/api/webhooks/111/aaa", "mentions": ["<@everyone>"]}, {"url": "https://discord.com/api/webhooks/222/bbb"}]` |

Each target object requires `url` and may include an optional `mentions` array.

Each Discord webhook is tied to one channel.

## Microsoft Teams

Get a Teams webhook URL from the Microsoft Teams Workflows app or from the incoming webhook option supported by your tenant:

- Teams incoming webhook setup: https://support.microsoft.com/en-US/Workflows/send-messages-in-teams-using-incoming-webhooks

After you have webhook URLs, configure this secret:

| Type | Name | Example |
| --- | --- | --- |
| Secret | `TEAMS_WEBHOOK_URLS_JSON` | `["https://example.webhook.office.com/webhookb2/...", "https://prod-00.westus.logic.azure.com/..."]` |

Each Teams webhook maps to the team, channel, or workflow destination selected during setup.

## Mailgun

Get credentials from Mailgun:

- Mailgun dashboard: https://app.mailgun.com/
- Mailgun API keys: https://app.mailgun.com/app/account/security/api_keys
- Mailgun sending domains: https://app.mailgun.com/app/sending/domains
- Mailgun Messages API: https://documentation.mailgun.com/docs/mailgun/api-reference/send/mailgun/messages

After you have an API key and a sending domain, configure:

| Type | Name | Example |
| --- | --- | --- |
| Secret | `MAILGUN_API_KEY` | `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` |
| Variable | `MAILGUN_DOMAIN` | `mg.example.com` |
| Secret | `MAILGUN_FROM_EMAIL` | `GitHub Actions <postmaster@mg.example.com>` |
| Secret | `MAILGUN_TO_EMAILS_JSON` | `["first@example.com", "second@example.com"]` |
| Variable <sup>[1]</sup> | `MAILGUN_API_BASE_URL` | `https://api.mailgun.net/v3` |

<sup>[1]</sup> `MAILGUN_API_BASE_URL` is optional. The default is `https://api.mailgun.net/v3`. Use `https://api.eu.mailgun.net/v3` for Mailgun EU domains.

## Twilio

Get credentials from Twilio:

- Twilio Console: https://console.twilio.com/
- Twilio API credentials: https://www.twilio.com/docs/iam/api
- Twilio Messaging API: https://www.twilio.com/docs/messaging/api/message-resource
- Twilio phone numbers: https://console.twilio.com/us1/develop/phone-numbers/manage/incoming

After you have an Account SID, Auth Token, and a sending phone number or Messaging Service SID, configure:

| Type | Name | Example |
| --- | --- | --- |
| Secret | `TWILIO_ACCOUNT_SID` | `ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` |
| Secret | `TWILIO_AUTH_TOKEN` | `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` |
| Secret | `TWILIO_TO_PHONES_JSON` | `["+16045550123", "+12505550123"]` |
| Secret <sup>[1]</sup> | `TWILIO_FROM_PHONE` | `+16045550999` |
| Secret <sup>[1]</sup> | `TWILIO_MESSAGING_SERVICE_SID` | `MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` |

<sup>[1]</sup> Set either `TWILIO_FROM_PHONE` or `TWILIO_MESSAGING_SERVICE_SID`. SMS providers may charge for sent messages, phone numbers, carrier fees, and message segments.

## Example Dry-Run Configuration

For a safe dry run, configure personal addresses and phone numbers as secrets and non-sensitive routing values as variables.

Secrets:

```text
MAILGUN_FROM_EMAIL=GitHub Actions <postmaster@mg.example.com>
MAILGUN_TO_EMAILS_JSON=["first@example.com", "second@example.com"]
TWILIO_TO_PHONES_JSON=["+16045550123", "+12505550123"]
TWILIO_FROM_PHONE=+16045550999
```

Variables:

```text
MAILGUN_DOMAIN=mg.example.com
```

Then manually run `Python Unit Tests` with `notification-mode=dry-run`.

For webhook providers, the webhook URL is both the addressee and the credential, so store webhook lists as GitHub secrets before testing.
49 changes: 43 additions & 6 deletions .github/workflows/testsPython.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ name: Python Unit Tests

on:
workflow_dispatch: # Allows the workflow to be manually triggered from the GitHub Actions tab
inputs:
notification-mode:
description: "Notification behavior for this manual run"
required: false
type: choice
default: notify-on-failure
options:
- notify-on-failure
- dry-run
- test-notification
pull_request: #
paths: # Trigger workflow on pull requests, but
- "**.py" # only if Python files are changed
Expand Down Expand Up @@ -67,13 +77,40 @@ jobs:
# is scaffolded to facilitate sending notifications based
# on the test results.
notifications:
if: ${{ always() }}
needs: python-unit-tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
id: checkout
uses: actions/checkout@v6

- name: Notify on test results
run: |
if [ "${{ needs.python-unit-tests.result }}" == "success" ]; then
echo "success notifications go here"
else
echo "failure notifications go here"
fi
continue-on-error: true
env:
PYTHON_UNIT_TEST_RESULT: ${{ needs.python-unit-tests.result }}
NOTIFICATION_TEST: ${{ github.event.inputs['notification-mode'] == 'test-notification' && 'true' || 'false' }}
NOTIFICATION_DRY_RUN: ${{ github.event.inputs['notification-mode'] == 'dry-run' && 'true' || 'false' }}
GITHUB_REPOSITORY_NAME: ${{ github.repository }}
GITHUB_WORKFLOW_NAME: ${{ github.workflow }}
GITHUB_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
GITHUB_REF_NAME: ${{ github.ref_name }}
GITHUB_SHA: ${{ github.sha }}
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_EVENT_NAME: ${{ github.event_name }}
GITHUB_RUN_ID: ${{ github.run_id }}
GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }}
SLACK_WEBHOOK_TARGETS_JSON: ${{ secrets.SLACK_WEBHOOK_TARGETS_JSON }}
DISCORD_WEBHOOK_TARGETS_JSON: ${{ secrets.DISCORD_WEBHOOK_TARGETS_JSON }}
TEAMS_WEBHOOK_URLS_JSON: ${{ secrets.TEAMS_WEBHOOK_URLS_JSON }}
MAILGUN_API_KEY: ${{ secrets.MAILGUN_API_KEY }}
MAILGUN_API_BASE_URL: ${{ vars.MAILGUN_API_BASE_URL }}
MAILGUN_DOMAIN: ${{ vars.MAILGUN_DOMAIN }}
MAILGUN_FROM_EMAIL: ${{ secrets.MAILGUN_FROM_EMAIL }}
MAILGUN_TO_EMAILS_JSON: ${{ secrets.MAILGUN_TO_EMAILS_JSON }}
TWILIO_ACCOUNT_SID: ${{ secrets.TWILIO_ACCOUNT_SID }}
TWILIO_AUTH_TOKEN: ${{ secrets.TWILIO_AUTH_TOKEN }}
TWILIO_FROM_PHONE: ${{ secrets.TWILIO_FROM_PHONE }}
TWILIO_MESSAGING_SERVICE_SID: ${{ secrets.TWILIO_MESSAGING_SERVICE_SID }}
TWILIO_TO_PHONES_JSON: ${{ secrets.TWILIO_TO_PHONES_JSON }}
run: python3 scripts/github_actions_notify.py
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ make docker-prune # prune (permanently delete) all existing data in Docker: co

Documentation is available here: [Documentation](./doc/)

GitHub Actions notification setup is available here: [Workflow Notifications](./.github/workflows/README.md)

## Support

To get community support, go to the official [Issues Page](https://github.com/FullStackWithLawrence/agentic-ai-workflow/issues) for this project.
Expand Down
Loading
Loading