GitHub Webhooks: Hilfsskripte und Betrieb
Dies ist Folgeartikel 7 aus meiner Serie zu GitHub-Webhooks.
Eigentlich habe ich inzwischen alles erklärt, was man braucht, um die Webhooks zu verarbeiten, aber ich habe mir noch ein paar Kleinigkeiten für den täglichen Betrieb gebaut, die ich euch nicht vorenthalten möchte.
Um schnellen Zugriff auf die Funktionen sowie Tab-Completion zu haben, habe ich diese Dinge in ein Makefile gepackt: Die Ein- und Zweizeiler direkt, den Rest jeweils über den Aufruf eines kurzen Bash-Skriptes. Das könnt ihr euch auch so bauen, wenn ihr wollt, ist aber eigentlich auch egal.
Queue-Überblick
Da alle Events einzelne Dateien sind, ist es denkbar einfach, einen Überblick über alle Queue-Verzeichnisse mit der Anzahl der enthaltenen Dateien zu zeigen:
- find json/ -type f | cut -d/ -f1-2 | sort | uniq -c
Queue-Details
Die Ausgabe ist etwas detaillierter als der Queue-Überblick und zeigt für jede Datei folgendes an:
- Anlage-Zeitstempel in lesbarer Form
- Event-Typ
- Repository
- Dateiname
Die Ausgabe lässt sich leicht mit grep durchsuchen, um z.B. Beispieldateien für noch nicht unterstützte Eventtypen zu finden.
- #!/bin/bash
- echo
- for DIR in json/tmp json/queue json/done json/error; do
- set -- "$DIR"/*
- [ "$1" = "$DIR/*" ] && continue
- echo " :: $DIR"
- echo
- jq -r .event,.payload.repository.full_name,input_filename "$@" \
- | \
- while read -r EVENT; do
- read -r REPO
- read -r FILENAME
- TIMESTAMP=${FILENAME##*/}
- TIMESTAMP=${TIMESTAMP%%.*}
- printf "%(%Y-%m-%d %H:%M:%S)T %-15s %-32s %s\n" "$TIMESTAMP" "$EVENT" "$REPO" "$FILENAME"
- done
- echo
- done
Hier sieht man mal wieder jq in Aktion: Dieses Mal werden aus jeder JSON-Datei drei Zeilen erzeugt (die zugehörigen Suchmuster sind durch Komma getrennt):
- Event-Typ
- Repository-Name
- Dateiname des JSONs (der steht nicht im JSON, aber jq kennt ihn und kann ihn ausgeben)
Das Konstrukt über set -- "$DIR"/*
geht bestimmt kaputt, wenn man zu
viele Dateien hat, also beachtet den nächsten Abschnitt :-)
Aufräumjob
Damit das Verzeichnis json/done/
nicht mit der Zeit überläuft, habe
ich einen minimalen Aufräumjob geschrieben, den ich täglich per cron
starten lasse:
- #!/bin/sh
- CUTOFF="$(date -d "1 week ago" +%s)"
- find json/done -type f \
- | while read -r FILENAME; do
- TIMESTAMP="${FILENAME%%.*}"
- if [ "$TIMESTAMP" -lt "$CUTOFF" ]; then
- FILENAME="json/done/$FILENAME"
- rm "$FILENAME" && echo "removed $FILENAME"
- fi
- done
Der Job löscht alle Dateien, die älter als eine Woche sind.
Errorhandling
Wichtig für den laufenden Betrieb: Wenn mal was kaputtgeht, muss man
im Nachhinein irgendwas machen können. Deshalb schiebt watcher.sh
JSON-Dateien, bei denen die Verarbeitung fehlgeschlagen ist, in das
Verzeichnis json/error
.
Von dort aus kann man dann die einzelne Datei nochmal durchlaufen
lassen. Damit man dabei zugucken kann und nichts mit „Außenwirkung“
passiert, unterstützt die Hilfslibrary die Umgebungsvariable DEBUG=1
und schaltet den Versand von E-Mails, Tweets usw. ab. Der Aufruf
erfolgt beispielsweise so:
- DEBUG=1 ./handler.sh json/error/1551440808.082092.1061
Jetzt kann man sehen, was schiefgeht, das entsprechende Skript anpassen und es dann nochmal probieren.
Wenn der Debug-Lauf schließlich ohne Fehler klappt, kann man die
Echtverarbeitung anstoßen, indem man die Datei einfach in die echte
Queue verschiebt, wo sie watcher.sh
dann findet und regulär
verarbeitet:
- mv json/error/1551440808.082092.1061 json/queue
Massenware
Sowohl das Fehler-Überprüfen als auch das Fehlerdateien-nochmal-richtig-Verarbeiten lassen sich auf die gesamte Fehler-Queue am Stück anwenden. Das bietet sich an, wenn sich durch einen Skriptfehler ein Haufen an Fehlerdateien angesammelt hat:
- for JSON in json/error/*; do
- DEBUG=1 ./handler.sh "$JSON"
- done
bzw.
- mv json/error/* json/queue
Fehler-Nomitoring
Um eine Info zu bekommen, dass was schiefgegangen ist, lasse ich
mein Nomitoring sowohl die Eingangs-Queues als auch die Fehler-Queue
überwachen. Wenn dort Dateien liegenbleiben, gibt es eine E-Mail.
Meine check.local
hat dafür diese Einträge:
- # check Github Webhooks
- check_file_no_older_in /home/webhook/json/tmp/ 1 h
- check_file_no_older_in /home/webhook/json/queue/ 1 h
- check_file_no_older_in /home/webhook/json/error/ 1 m
Syntax-Checks
Das hier ist hauptsächlich für die Entwicklung relevant (und könnte, so ich denn endlich ein GitHub-Repository für das Projekt aufmache, als minimaler CI-Test herhalten):
- #!/bin/sh
- grep_shebang()
- {
- grep -nm1 "$@" | grep sh:1: | cut -d: -f 1
- }
- STATUS=0
- for SCRIPT in $(grep_shebang /bin/sh *.sh); do
- if ! sh -n "$SCRIPT"; then
- STATUS=1
- echo "sh: error in $SCRIPT"
- else
- echo "$SCRIPT syntax OK"
- fi
- done
- for SCRIPT in $(grep_shebang /bin/bash *.sh); do
- if ! bash -n "$SCRIPT"; then
- STATUS=1
- echo "bash: error in $SCRIPT"
- else
- echo "$SCRIPT syntax OK"
- fi
- done
- for SCRIPT in *.pl; do
- if ! perl -c "$SCRIPT"; then
- STATUS=1
- echo "perl: error in $SCRIPT"
- fi
- done
- exit $STATUS
Das ist ein minimaler Check auf korrekte Syntax von Bash-, Shell- und Perlskripten.
Eigentlich müsste ich auch noch ShellCheck mitlaufen lassen, aber damit die Prüfung nicht ständig fehlschlägt, müsste ich dauerhaft warnungsfrei programmieren… Das war bei den Schnellschüssen aktuell nicht im Fokus. Ich merke aber, dass ich bei jedem Editieren an den Skripten die eine oder andere Warnung im Vorbeigehen fixe.
Werbung: Nutzt flycheck im Emacs, um solche Warnungen direkt im Quellcode anzuzeigen – für Shellskripte mit ShellCheck, für Perl mittels Perl::Critic.
Netz - Rettung - Recht am : Wellenreiten 03/2019