Dies ist Folgeartikel 5 aus meiner Serie zu GitHub-Webhooks.
Und jetzt mal Butter bei die Fische: Mit diesem Skript verarbeite ich
Commits (Event-Typ push
):
- #!/usr/bin/perl -I.
- use warnings;
- use strict;
-
- use WebHookLib;
-
- use constant SENDTO => 'gbsplay-dev@lists.uguu.de';
-
- my $event = read_payload();
-
- if ($event->{deleted}) {
- print " (branch delete ignored)\n";
- exit 0;
- }
-
- my $repository = $event->{repository}->{name};
- my $branch = get_short_branch($event->{ref});
-
- # tweet individual commits
-
- foreach my $commit (@{$event->{commits}}) {
-
- next unless $commit->{distinct};
-
- my $author = $commit->{author}->{username};
- my $message = crlf($commit->{message});
- my $commiturl = $commit->{url};
-
- my $tweet = sprintf "[%s%s] %s - %s\n%s", $repository, $branch, $commiturl, $author, $message;
-
- # tweet
- tweet_it($tweet);
-
- # log the tweet
- $tweet =~ s:commit/[0-9a-f]+ :commit/…:;
- $tweet =~ s/\n/\\n/g;
- printf " tweeted: %s\n", shorten($tweet, 100);
- }
-
- # send mail - only for gbsplay
-
- if ($event->{repository}->{full_name} eq 'mmitch/gbsplay') {
-
- my $title = $event->{head_commit}->{message};
- $title =~ s/\n.*//s;
- my $head = substr($event->{head_commit}->{id}, 0, 7);
-
- my $subject = sprintf "[%s%s] %s: %s", $repository, $branch, $head, $title;
-
- my $body = sprintf "Branch: %s\n", $event->{ref};
- $body .= sprintf "Home: %s\n", $event->{repository}->{url};
- $body .= sprintf "Compare: %s\n", $event->{compare};
-
- foreach my $commit (@{$event->{commits}}) {
- $body .= "\n";
- $body .= sprintf "Commit: %s\n", $commit->{id};
- $body .= sprintf " %s\n", $commit->{url};
- $body .= sprintf "Author: %s\n", $commit->{author}->{username};
- $body .= sprintf "Date: %s\n", $commit->{timestamp};
- $body .= "\n";
- $body .= "Changed Paths:\n";
- $body .= " M $_\n" foreach @{$commit->{modified}};
- $body .= " A $_\n" foreach @{$commit->{added}};
- $body .= " D $_\n" foreach @{$commit->{removed}};
- $body .= "\n";
- $body .= "Log Message:\n";
- $body .= "------------\n";
- $body .= crlf($commit->{message});
- $body .= "\n";
- $body .= "\n";
- $body .= "\n";
- }
-
- send_mail(SENDTO, $subject, $body);
- print " mail sent.\n";
- }
Das Skript twittert die Commits für alle Repositories. Für Commits in
gbsplay wird zusätzlich eine Mail mit Detailinformationen des Commits
an die gbsplay-Mailingliste geschickt.
Setup
Fangen wir oben an:
Erstmal wird die Hilfslibrary geladen, dann die Adresse der
Mailingliste als Konstante definiert.
read_payload()
liest die JSON-Datei mit den Event-Daten ein und
macht Fehlerhandling für „Datei nicht vorhanden“ und sowas.
Wenn das Flag deleted
im Event gesetzt ist, dann habe ich einen
Branch gelöscht – das will ich aber weder twittern noch mailen, also
wird das nach STDOUT geloggt und das Skript vorzeitig beendet.
Bewaffnet mit $repository
und $branch
geht es ansonsten weiter.
Twitter
Ein Push-Event kann mehrere Commits enthalten. Diese werden in einer
Schleife durchlaufen und einzeln abgearbeitet.
Was $commit->distinct
exakt bedeutet, weiß ich nicht, aber wenn das
Flag nicht gesetzt ist, überspringe ich den Commit. Das klingt wie „der war
schonmal woanders mit dabei“ und doppelt twittern will ich nicht.
Ich hole mir den Ersteller des Commits ($author
), die
Commitnachricht ($message
) sowie den Link zum Commit auf GitHub
($commiturl
) und baue mir dann den $tweet
selbst zusammen.
Twitter kann Zeilenumbrüche, die benutze ich auch (einen
selbstgebauten plus die aus $message
, die dafür extra vorher durch
crlf()
von CRLF nach LF konvertiert wurden).
Dank der Hilfslibrary lässt sich der Tweet dann einfach mit der Zeile
tweet_it($tweet);
absetzen.
Anschließend logge ich jeden Tweet noch nach STDOUT, dafür verkürze
ich die Commit-URL und ersetze die Zeilenumbrüche durch den Text \n
.
Mail
Die Mail an die gbsplay-Mailingliste wird nur verschickt, wenn es
ein Push in das Repository mmitch/gbsplay
war.
Hier wird einmal ein allgemeiner Text für das gesamte Push-Event
erstellt (Subject, Einleitung usw.), und danach wird pro einzelnem
Commit noch etwas dazugepackt: Commit-URL, Autor, Datum, veränderte
Dateien (geändert, hinzugefügt, gelöscht) und die Commitnachricht
(wieder mit crlf()
).
Das Layout der Mails kann bestimmt noch verbessert werden. Vielleicht
verstecken sich auch noch weitere interessante Informationen im Event
oder in den Commits. Ich habe hier erstmal nur das nachgebaut, was
GitHub bisher selber in den alten Mails verschickt hat. (Und auf der
Mailingliste hat sich auch noch niemand beschwert.)
Warum ich hier nicht auf das distinct
-Flag achte, weiß ich nicht ;-)
Das Absenden der Mail ist auch wieder ein Einzeiler dank der Library:
send_mail(SENDTO, $subject, $body);
Und dazu noch eine Zeile im Log.
Zukunft
Ich will hier mal eine zentrale Konfiguration bauen, so dass man für
beliebige Repositories auswählen kann, ob getwittert und/oder gemailt
(und wenn ja, wohin) wird. Aktuell passt das mit dem einen if
für
gbsplay, aber das ist – genau wie meine Passwörter u.ä. – lokale
Konfiguration und wenn ich die ganzen Skripte mal auf GitHub packe,
dann muss das vorher raus.
Mit 77 Zeilen ist das Skript ungewöhnlich groß, was daran liegt, dass
es zwei Dinge tut (twittern und mailen – das könnte man
ordentlicherweise auch in zwei Skripte aufteilen) und das dort ganz
viel String-Konkatenation für die Email betrieben wird. Vielleicht
sollte ich die mal durch ein ordentliches Templating ersetzen,
z.B. mit Template. Aber wie bei den anderen Skripte auch war mein
Ansatz ja „schnell, einfach, funktioniert, kann später durch was
besseres ersetzt werden“.
Funktionieren tut das Skript und es erfüllt aktuell auch alle
Anforderungen.
Netz - Rettung - Recht am : Wellenreiten 02/2019