GitHub Webhooks per API einrichten
Dies ist Folgeartikel 8 aus meiner Serie zu GitHub-Webhooks.
Jetzt kann man Webhooks verarbeiten, aber irgendwer muss die Events ja auch schicken. Ganz klar und einfach, dafür muss man in der GitHub-Administration eines Repositories folgendes eintragen:
- Ziel-URL
- Passwort
- zu sendende Event-Typen
- Format der Payload (JSON)
Das kann man für ein oder zwei Repositories manuell eintragen, aber ich laufe mit über 90 Repositories durch die Gegend und da macht die Klickorgie keinen Spaß. Ich habe mir deshalb dieses total hässliche Oneshot-Perlskript gebastelt (use at your own risk!), das für alle erreichbaren eigenen Repositories die oben genannten Änderungen vornimmt:
- #!/usr/bin/perl
- use strict;
- use warnings;
- use Pithub;
- use Data::Dumper::Concise;
- # .------ USER CONFIG ------
- use constant TOKEN => '** das Personal access token **';
- use constant HOOKURL => '** Ziel-URL des Webhooks **';
- use constant HOOKPWD => '** Password des Webhooks **';
- use constant USER => '** euer GitHub-Username **';
- # `------ USER CONFIG ------
- use constant CONTENT_TYPE => 'json';
- use constant ANY_EVENT => '*';
- use constant TRUE => 'true';
- use constant FALSE => 'false';
- use constant HOOK_DATA => {
- # active => TRUE,
- events => [ ANY_EVENT ],
- config => {
- url => HOOKURL,
- secret => HOOKPWD,
- content_type => CONTENT_TYPE,
- # insecure_ssl => FALSE,
- },
- };
- sub ensure
- {
- my $result = shift;
- return if $result->success;
- my ($package, $filename, $line) = caller;
- die sprintf "error `%s' in %s, %s, line %d:\n%s\n",
- $result->response->status_line,
- $package, $filename, $line,
- $result->response->content;
- }
- sub is_archived
- {
- my $result = shift;
- return 0 unless exists $result->{archived};
- return $result->{archived};
- }
- sub hook_is_ok
- {
- my $hook = shift;
- my @events = @{$hook->{events}};
- my $active = $hook->{active};
- my $url = $hook->{config}->{url};
- my $type = $hook->{config}->{content_type};
- my $ssl = $hook->{config}->{insecure_ssl};
- die sprintf "HOOK INACTIVE, I can't change that!\n%s\n", Dumper($hook) unless $active == 1;
- die sprintf "INSECURE SSL, I can't change that!\n%s\n", Dumper($hook) unless $ssl == 0;
- return 0 unless $url eq HOOKURL;
- return 0 unless $type eq CONTENT_TYPE;
- return 0 unless @events == 1;
- return 0 unless $events[0] eq ANY_EVENT;
- return 1;
- }
- sub check_hook
- {
- my ($repo, $hook) = @_;
- my $id = $hook->{id};
- my @events = @{$hook->{events}};
- my $active = $hook->{active};
- my $url = $hook->{config}->{url};
- my $type = $hook->{config}->{content_type};
- printf " checking existing hook %d\n", $id;
- if (hook_is_ok($hook)) {
- printf " OK\n";
- return
- };
- printf " events = %s\n", join(' ,', @events);
- printf " active = %s\n", $active;
- printf " url = %s\n", $url;
- printf " type = %s\n", $type;
- my $hooks = Pithub::Repos::Hooks->new( token => TOKEN );
- my $result = $hooks->update(
- user => USER,
- repo => $repo,
- hook_id => $id,
- data => HOOK_DATA,
- );
- ensure $result;
- print " UPDATED\n";
- }
- sub create_hook
- {
- my $repo = shift;
- print " adding new hook\n";
- my $hooks = Pithub::Repos::Hooks->new( token => TOKEN );
- my $result = $hooks->create(
- user => USER,
- repo => $repo,
- data => HOOK_DATA,
- );
- ensure $result;
- print " CREATED\n";
- }
- sub check_repo
- {
- my $repo = shift;
- my $archived = is_archived($repo);
- $repo = $repo->{name};
- print "processing $repo\n";
- if ($archived) {
- print " archived -> SKIP\n";
- return
- }
- my $hooks = Pithub::Repos::Hooks->new( token => TOKEN );
- my $result = $hooks->list( user => USER, repo => $repo );
- ensure $result;
- while (my $hook = $result->next) {
- next unless exists $hook->{config}->{url};
- next unless $hook->{config}->{url} eq HOOKURL;
- check_hook $repo, $hook;
- return;
- }
- create_hook $repo;
- }
- my $repos = Pithub::Repos->new( token => TOKEN );
- my $result = $repos->list( user => USER );
- ensure $result;
- while (my $repo = $result->next) {
- check_repo $repo;
- }
Um das Skript laufen zu lassen, braucht man in seinem GitHub-Profil
ein Personal access token mit dem Recht admin:repo_hook
.
Leider klappt irgendwas mit der Übermittlung von Booleans nicht, so dass beim Update bestehender Hooks die Einstellungen insecure SSL und deactivated nicht geändert werden können – es gibt dann eine dicke Fehlermeldung und man muss manuell eingreifen (also auf GitHub in der Repository-Administration herumklicken). Bei der Neuanlage von Hooks passen die Defaults der beiden Einstellungen, man kann sie deshalb einfach weglassen.
Das Skript hangelt sich per HTTP-Request von einem Ding zum nächsten, das ist nicht besonders toll. Möchte das mal jemand auf Graph-QL umstellen? Meine Versuche damit waren nicht sehr zielführend, irgendwie habe ich kein passendes Beispiel gefunden… Soweit ich das verstanden habe, wäre Graph-QL genau richtig, um das gesamte Skript durch eine serverseitige Transaktion zu ersetzen (sinngemäß: „setze Webhook-Einstellungen für alle Repositories, die große Filterbedigung entsprechen“).
Netz - Rettung - Recht am : Wellenreiten 03/2019