Add Helm chart for Matrix Alertmanager receiver
Validate, publish and promote Matrix Alertmanager receiver chart / validate (push) Successful in 20s
Validate, publish and promote Matrix Alertmanager receiver chart / publish (push) Successful in 31s

This commit is contained in:
2026-10-21 18:54:33 +02:00
commit 3da15981bd
13 changed files with 369 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
name: Validate, publish and promote Matrix Alertmanager receiver chart
on:
push:
pull_request:
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: azure/setup-helm@v4
with:
version: v4.3.0
- name: Install chart-test dependency
run: |
python3 -m venv "${RUNNER_TEMP}/chart-test"
"${RUNNER_TEMP}/chart-test/bin/pip" install PyYAML
echo "${RUNNER_TEMP}/chart-test/bin" >> "${GITHUB_PATH}"
- run: ./test.sh
publish:
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
needs: validate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: azure/setup-helm@v4
with:
version: v4.3.0
- name: Select immutable chart version
run: echo "CHART_VERSION=$(./scripts/chart-version.sh)" >> "${GITHUB_ENV}"
- name: Package and publish chart
env:
PACKAGE_USERNAME: ${{ secrets.PACKAGE_USERNAME }}
PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }}
run: |
set -euo pipefail
test -n "${PACKAGE_USERNAME}" && test -n "${PACKAGE_TOKEN}"
helm package chart/matrix-alertmanager-receiver \
--destination "${RUNNER_TEMP}" \
--version "${CHART_VERSION}"
curl --fail --silent --show-error \
--user "${PACKAGE_USERNAME}:${PACKAGE_TOKEN}" \
--request POST \
--upload-file "${RUNNER_TEMP}/matrix-alertmanager-receiver-${CHART_VERSION}.tgz" \
https://code.brunner.ninja/api/packages/feedc0de/helm/api/charts
- name: Check that this is still current main
id: current-main
env:
COMMIT_SHA: ${{ gitea.sha }}
run: |
git fetch --no-tags origin main
if [ "$(git rev-parse FETCH_HEAD)" = "${COMMIT_SHA}" ]; then
echo "promote=true" >> "${GITHUB_OUTPUT}"
else
echo "promote=false" >> "${GITHUB_OUTPUT}"
fi
- uses: actions/checkout@v4
if: steps.current-main.outputs.promote == 'true'
with:
repository: feedc0de/homelab-gitops
ref: main
token: ${{ secrets.GITOPS_TOKEN }}
path: homelab-gitops
- name: Promote chart version
if: steps.current-main.outputs.promote == 'true'
working-directory: homelab-gitops
run: |
set -euo pipefail
./scripts/update-chart-version.sh matrix-alertmanager-receiver "${CHART_VERSION}"
git config user.name feedc0de
git config user.email feedc0de@users.noreply.code.brunner.ninja
git add bootstrap/matrix-alertmanager-receiver.yaml
if git diff --cached --quiet; then exit 0; fi
git commit -m "Deploy Matrix receiver chart ${CHART_VERSION}"
for attempt in 1 2 3; do
if git push origin HEAD:main; then exit 0; fi
if [ "${attempt}" = 3 ]; then exit 1; fi
git fetch origin main
git rebase origin/main
done
+25
View File
@@ -0,0 +1,25 @@
# Matrix Alertmanager receiver Helm chart
This repository packages the upstream
[`metio/matrix-alertmanager-receiver`](https://github.com/metio/matrix-alertmanager-receiver)
container as a reusable Helm chart. It does not rebuild or mirror the upstream
image. The chart owns the ConfigMap, Deployment, Service, probes, and optional
ServiceMonitor. Set the Matrix homeserver, bot ID, room mapping, and existing
Secret reference in values; never put an access token in Helm values.
The image tag defaults to the chart's `appVersion`. The Git tag follows the
upstream application release; CI derives package versions with
`git describe --tags --long`. A tag release packages without the leading `v`;
later chart-only commits append their distance and commit hash.
For the public chart repository:
```sh
helm repo add brunner.ninja https://brunner.ninja/charts
helm repo update brunner.ninja
helm search repo brunner.ninja/matrix-alertmanager-receiver
```
For a local install from this checkout, create the referenced Kubernetes
Secret first, then run `NAMESPACE=prometheus ./chart/install.sh` with the
appropriate values file. Argo CD is the normal deployment path in this cluster.
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
set -eu
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
repo_dir=$(CDPATH= cd -- "${script_dir}/.." && pwd)
namespace=${NAMESPACE:-default}
version=${CHART_VERSION:-$(${repo_dir}/scripts/chart-version.sh)}
helm repo add brunner.ninja https://brunner.ninja/charts --force-update
helm repo update brunner.ninja
helm upgrade --install matrix-alertmanager-receiver \
brunner.ninja/matrix-alertmanager-receiver \
--version "${version}" \
--namespace "${namespace}" \
--create-namespace \
--wait \
--timeout 10m \
"$@"
@@ -0,0 +1,6 @@
apiVersion: v2
name: matrix-alertmanager-receiver
description: Matrix webhook receiver for Prometheus Alertmanager
type: application
version: 0.0.0
appVersion: "2026.9.23"
@@ -0,0 +1,19 @@
{{- define "matrix-alertmanager-receiver.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "matrix-alertmanager-receiver.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s" (include "matrix-alertmanager-receiver.name" .) | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- define "matrix-alertmanager-receiver.labels" -}}
app.kubernetes.io/name: {{ include "matrix-alertmanager-receiver.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | quote }}
{{- end -}}
@@ -0,0 +1,20 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "matrix-alertmanager-receiver.fullname" . }}
labels:
{{- include "matrix-alertmanager-receiver.labels" . | nindent 4 }}
data:
config.yaml: |
http:
port: {{ .Values.service.port }}
metrics-enabled: true
matrix:
homeserver-url: {{ required "matrix.homeserverUrl is required" .Values.matrix.homeserverUrl | quote }}
user-id: {{ required "matrix.userId is required" .Values.matrix.userId | quote }}
access-token: ${MATRIX_ACCESS_TOKEN}
room-mapping:
{{ toYaml .Values.matrix.roomMapping | indent 8 }}
templating:
firing-template: {{ .Values.templating.firing | quote }}
resolved-template: {{ .Values.templating.resolved | quote }}
@@ -0,0 +1,69 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "matrix-alertmanager-receiver.fullname" . }}
labels:
{{- include "matrix-alertmanager-receiver.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app.kubernetes.io/name: {{ include "matrix-alertmanager-receiver.name" . }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "matrix-alertmanager-receiver.name" . }}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
spec:
automountServiceAccountToken: false
containers:
- name: receiver
image: "{{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
args:
- --config-path
- /etc/matrix-receiver/config.yaml
env:
- name: MATRIX_ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: {{ default (printf "%s-auth" (include "matrix-alertmanager-receiver.fullname" .)) .Values.matrix.accessTokenSecret.name }}
key: {{ .Values.matrix.accessTokenSecret.key }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
volumeMounts:
- name: config
mountPath: /etc/matrix-receiver
readOnly: true
readinessProbe:
httpGet:
path: /metrics
port: http
periodSeconds: 10
livenessProbe:
httpGet:
path: /metrics
port: http
periodSeconds: 30
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumes:
- name: config
configMap:
name: {{ include "matrix-alertmanager-receiver.fullname" . }}
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "matrix-alertmanager-receiver.fullname" . }}
labels:
{{- include "matrix-alertmanager-receiver.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
selector:
app.kubernetes.io/name: {{ include "matrix-alertmanager-receiver.name" . }}
ports:
- name: http
port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
@@ -0,0 +1,17 @@
{{- if .Values.serviceMonitor.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ include "matrix-alertmanager-receiver.fullname" . }}
labels:
{{- include "matrix-alertmanager-receiver.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
app.kubernetes.io/name: {{ include "matrix-alertmanager-receiver.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
endpoints:
- port: http
path: /metrics
interval: 30s
{{- end }}
@@ -0,0 +1,28 @@
replicaCount: 1
image:
repository: docker.io/metio/matrix-alertmanager-receiver
tag: ""
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 12345
matrix:
homeserverUrl: https://matrix.example.org
userId: "@alertmanager:example.org"
roomMapping:
cluster: "!room-id:example.org"
accessTokenSecret:
name: ""
key: token
templating:
firing: '<strong>FIRING</strong> {{ .Alert.Labels.alertname }}{{ if .Alert.Labels.namespace }} in {{ .Alert.Labels.namespace }}{{ end }}: {{ if .Alert.Annotations.description }}{{ .Alert.Annotations.description }}{{ else }}{{ .Alert.Annotations.summary }}{{ end }}'
resolved: '<strong>RESOLVED</strong> {{ .Alert.Labels.alertname }}{{ if .Alert.Labels.namespace }} in {{ .Alert.Labels.namespace }}{{ end }}'
resources: {}
serviceMonitor:
enabled: false
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
set -eu
description=$(git describe --tags --long --match 'v[0-9]*' --dirty)
case "$description" in
*-dirty) echo 'Refusing to package a dirty tree' >&2; exit 1 ;;
esac
version=${description#v}
case "$version" in
*-0-g*) version=${version%%-0-g*} ;;
esac
printf '%s\n' "$version"
Executable
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
set -eu
repo_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
chart_dir=${repo_dir}/chart/matrix-alertmanager-receiver
rendered=$(mktemp)
trap 'rm -f "$rendered"' EXIT
sh -n "${repo_dir}/scripts/chart-version.sh"
sh -n "${repo_dir}/chart/install.sh"
helm lint "${chart_dir}"
helm template matrix-alertmanager-receiver "${chart_dir}" \
--namespace default \
--set matrix.homeserverUrl=https://matrix.example.org \
--set matrix.userId='@alertmanager:example.org' \
--set matrix.roomMapping.cluster='!room-id:example.org' \
--set serviceMonitor.enabled=true > "${rendered}"
python3 "${repo_dir}/tests/assert_chart.py" "${chart_dir}" "${rendered}"
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
import sys
from pathlib import Path
import yaml
chart_dir = Path(sys.argv[1])
rendered_path = Path(sys.argv[2])
chart = yaml.safe_load((chart_dir / "Chart.yaml").read_text())
values = yaml.safe_load((chart_dir / "values.yaml").read_text())
documents = [doc for doc in yaml.safe_load_all(rendered_path.read_text()) if doc]
by_kind = {(doc["kind"], doc["metadata"]["name"]): doc for doc in documents}
name = "matrix-alertmanager-receiver"
deployment = by_kind[("Deployment", name)]
service = by_kind[("Service", name)]
service_monitor = by_kind[("ServiceMonitor", name)]
config = by_kind[("ConfigMap", name)]["data"]["config.yaml"]
container = deployment["spec"]["template"]["spec"]["containers"][0]
service_port = service["spec"]["ports"][0]
app_config = yaml.safe_load(config)
assert container["image"] == f'{values["image"]["repository"]}:{chart["appVersion"]}'
assert deployment["spec"]["replicas"] == 1
assert deployment["spec"]["selector"]["matchLabels"] == service["spec"]["selector"]
assert deployment["spec"]["selector"]["matchLabels"]["app.kubernetes.io/name"] == chart["name"]
assert container["ports"][0]["containerPort"] == service_port["port"]
assert service_port["targetPort"] == container["ports"][0]["name"]
assert container["readinessProbe"]["httpGet"]["path"] == "/metrics"
assert container["livenessProbe"]["httpGet"]["path"] == "/metrics"
assert container["env"][0]["valueFrom"]["secretKeyRef"]["key"] == values["matrix"]["accessTokenSecret"]["key"]
assert "access-token: ${MATRIX_ACCESS_TOKEN}" in config
assert app_config["matrix"]["access-token"] == "${MATRIX_ACCESS_TOKEN}"
assert service_monitor["spec"]["endpoints"][0]["port"] == service_port["name"]
assert service_monitor["spec"]["endpoints"][0]["path"] == "/metrics"