diff --git a/.github/workflows/build-docker.yaml b/.github/workflows/build-docker.yaml new file mode 100644 index 0000000..e1684ab --- /dev/null +++ b/.github/workflows/build-docker.yaml @@ -0,0 +1,53 @@ +# from https://docs.github.com/fr/actions/use-cases-and-examples/publishing-packages/publishing-docker-images +name: Create and publish a Docker image + +# Configures this workflow to run every time a change is pushed to the branch called `release`. +on: + push: + branches: ["main"] + tags: ["*"] + +# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds. +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu. +jobs: + build-and-push-image: + runs-on: ubuntu-latest + # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. + permissions: + contents: read + packages: write + attestations: write + id-token: write + # + steps: + - name: Checkout repository + uses: actions/checkout@v4 + # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here. + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels. + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages. + # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see [Usage](https://github.com/docker/build-push-action#usage) in the README of the `docker/build-push-action` repository. + # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. + - name: Build and push Docker image + id: push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + diff --git a/.gitignore b/.gitignore index 1623bf6..1784216 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.mbp *.svg -.idea/ \ No newline at end of file +.idea/ +__pycache__ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..efd0c8f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM dock.mau.dev/maubot/maubot:v0.6.0-standalone AS deps + +WORKDIR /plugin + +RUN apk add --no-cache py3-dateutil-pyc=2.9.0-r1 + +FROM deps AS build + +COPY . . + +RUN mkdir build extract && mbc build -o build && unzip build/* -d extract + +FROM deps + +# no need for the management frontend +# contains libraries triggering our security scanners +RUN rm -rf /opt/maubot/maubot/management/frontend + +COPY --from=build --chmod=444 /plugin/extract/ /plugin + +ENTRYPOINT ["python"] +CMD ["-m", "maubot.standalone"] + diff --git a/alert_examples/grafana_test.json b/alert_examples/grafana_test.json new file mode 100644 index 0000000..92dd4b8 --- /dev/null +++ b/alert_examples/grafana_test.json @@ -0,0 +1,44 @@ +{ + "receiver": "test", + "status": "firing", + "alerts": [ + { + "status": "firing", + "labels": { + "alertname": "TestAlert", + "instance": "Grafana" + }, + "annotations": { + "summary": "Notification test" + }, + "startsAt": "2025-10-22T11:47:38.678838846Z", + "endsAt": "0001-01-01T00:00:00Z", + "generatorURL": "", + "fingerprint": "57c6d9296de2ad39", + "silenceURL": "https://domain.tld/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DTestAlert&matcher=instance%3DGrafana", + "dashboardURL": "", + "panelURL": "", + "values": null, + "valueString": "[ metric='foo' labels={instance=bar} value=10 ]" + } + ], + "groupLabels": { + "alertname": "TestAlert", + "instance": "Grafana" + }, + "commonLabels": { + "alertname": "TestAlert", + "instance": "Grafana" + }, + "commonAnnotations": { + "summary": "Notification test" + }, + "externalURL": "https://domain.tld/", + "version": "1", + "groupKey": "test-57c6d9296de2ad39-1761133658", + "truncatedAlerts": 0, + "orgId": 15, + "title": "[FIRING:1] TestAlert Grafana ", + "state": "alerting", + "message": "**Firing**\n\nValue: [no value]\nLabels:\n - alertname = TestAlert\n - instance = Grafana\nAnnotations:\n - summary = Notification test\nSilence: https://domain.tld/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DTestAlert&matcher=instance%3DGrafana\n" +} diff --git a/alertbot.py b/alertbot.py index 6247439..d8c9a39 100644 --- a/alertbot.py +++ b/alertbot.py @@ -152,6 +152,8 @@ def uptime_kuma_alert_to_markdown(alert_data: dict): def dict_to_markdown(alert_data: dict): md = "" + if alert_data is None: + return md for key_or_dict in alert_data: try: alert_data[key_or_dict] diff --git a/maubot.yaml b/maubot.yaml index 82cc339..ec70188 100644 --- a/maubot.yaml +++ b/maubot.yaml @@ -1,6 +1,6 @@ maubot: 0.1.0 id: de.hyteck.alertbot -version: 1.1.2 +version: 1.1.5 license: AGPL-3.0-or-later modules: - alertbot diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..45a03e8 --- /dev/null +++ b/test.sh @@ -0,0 +1,2 @@ +#!/bin/sh +python -m unittest discover tests diff --git a/tests/test_alertbot.py b/tests/test_alertbot.py index 6947af8..dcf3de1 100644 --- a/tests/test_alertbot.py +++ b/tests/test_alertbot.py @@ -3,23 +3,23 @@ import json examples = [{"name": "Grafana Alert", - "filepath": "../alert_examples/grafana_alert.json", + "filepath": "alert_examples/grafana_alert.json", "expected_response": "", "type": "grafana-alert"}, {"name": "Grafana Resolved", - "filepath": "../alert_examples/grafana_resolved.json", + "filepath": "alert_examples/grafana_resolved.json", "expected_response": "", "type": "grafana-resolved"}, {"name": "Uptime Kuma 503 Alert", - "filepath": "../alert_examples/uptime-kuma-503-alert.json", + "filepath": "alert_examples/uptime-kuma-503-alert.json", "expected_response": "", "type": "uptime-kuma-alert"}, {"name": "Prometheus Alert", - "filepath": "../alert_examples/prometheus_alert.json", + "filepath": "alert_examples/prometheus_alert.json", "expected_response": "", "type": "prometheus-alert"}, {"name": "Slack Alert", - "filepath": "../alert_examples/slack-webhook.json", + "filepath": "alert_examples/slack-webhook.json", "expected_response": "", "type": "slack-webhook"}, ] @@ -31,9 +31,14 @@ def test_classification(self): print(f"Example: {example['name']}") with open(example["filepath"]) as file: alert_data = json.load(file) - found_type = alertbot.find_alert_type(alert_data) + found_type = alertbot.get_alert_type(alert_data) self.assertEqual(found_type, example["type"]) + def test_grafana_test_msg(self): + with open('alert_examples/grafana_test.json') as f: + alert_data = json.load(f) + render = alertbot.get_alert_messages(alert_data) + if __name__ == '__main__': unittest.main()