Kürzlich habe ich festgestellt, dass ich eine CODEOWNERS-Datei in mehrere Repositories innerhalb unserer Github-Organisation einbinden musste. Das Ziel bestand darin, automatisch Reviewer für Pull Requests festzulegen. Codeowner sind dafür das perfekte Tool. Auf diese Weise kann jeder Mitarbeitende im Unternehmen unserer GitHub-Organisation beitreten und Schreibzugriff auf die Repositories erhalten (damit sie Branches erstellen können) und schließlich Pull Requests erstellen, ohne sie in ihre persönlichen Konten kopieren zu müssen.

Ich wollte jedoch unbedingt vermeiden, die Codeowners-Datei auf folgende Weise zu erstellen:

  • Alle Repositories klonen, Dateien hinzufügen, committen und pushen (am besten über einen Pull Request)
  • Die Dateien über die GitHub-Benutzeroberfläche hinzufügen (d.h. auf “Datei hinzufügen” klicken, “Neue Datei erstellen”, Inhalt einfügen und committen).

Glücklicherweise kann man dies auch über die REST-API von GitHub automatisieren. Es war jedoch schwieriger als erwartet!

Ich wollte keine einzelnen Personen zur Codeowners-Datei hinzufügen, da ich die Datei ändern müsste, wenn eine Person das Unternehmen verlässt oder nicht mehr als Codeowner fungieren möchte. Teams sind in diesem Zusammenhang äußerst nützlich. GitHub ermöglicht die Erstellung von Teams in einer Organisation, denen dann Personen hinzugefügt werden können. Diese Teams können dann in der Codeowners-Datei verwendet werden. Auf diese Weise werden Personen, die aus einem Team entfernt werden, automatisch als Codeowner für alle Repositories entfernt, in denen das Team verwendet wird.

Nachdem ich die Teams erstellt und Personen hinzugefügt hatte (ich habe dies manuell gemacht), musste ich den Teams Zugriff auf die Repositories gewähren. Dies habe ich nicht manuell gemacht, sondern über die API.

Der Befehl zum Hinzufügen eines Teams zu einem Repository sieht folgendermaßen aus:

Damit hat das Team “terraform-maintainers” Zugriff auf das Repository “examplerepo” und erhält Schreibzugriff (mit -f permission=push).

Wenn ich dies nun für alle terraform-Repositories in unserer Organisation tun möchte, verwende ich eine einfache Schleife auf der Befehlszeile, um mit dem Befehl gh repo list nach Repositories zu suchen und dann nur die Repositories mit “terraform” im Namen herauszufiltern:

Sobald die Teams hinzugefügt wurden, kann ich sie zur Codeowners-Datei hinzufügen. Um dies über die API zu tun, muss ich im Grunde genommen den Inhalt der Datei in das Repository “PUT”en (weitere Informationen gibt es in der Dokumentation). Da es sich hierbei um Git handelt, muss ich dies in einem Commit durchführen.

Ein Commit erfordert:

  • eine Commit-Nachricht – dies wird erreicht, indem das Formularfeld “message” mit der Nachricht als Wert hinzugefügt wird.
  • einen “Committer” – dies ist etwas komplizierter zu erreichen. Es muss ein JSON-String mit einem Namen und einer E-Mail-Adresse hinzugefügt werden.
  • den Inhalt der Datei – dies ist der Inhalt der Datei als Base64-codierter String:

Der endgültige API-Aufruf sieht folgendermaßen aus:

Dies kann in einer Schleife durchgeführt werden, und schon ist man fertig.

Aber natürlich macht man dabei einen Fehler und man muss dann die Datei nochmals aktualisieren. Um eine Datei mithilfe eines Commits zu aktualisieren, kann man den oben genannten Befehl nicht direkt verwenden. Man muss den “blob-SHA” der Datei angeben, die ersetzt werden soll.

Wie bekommt man diesen? Indem man die API erneut abfragt (es gibt einen Weg, dies ohne Verwendung der API zu tun, aber das habe ich nicht gemacht, da der API-Weg einfacher war):

Fügt man diesen “SHA” jetzt in den API-Aufruf ein, kann man die Datei aktualisieren:

Mit den richtigen Codeownern an ihrem Platz war der letzte Schritt, die Branch Protection Rule festzulegen, die besagt, dass Codeowner Reviews durchführen müssen. Dies hat am längsten gedauert, da es aus der Dokumentation allein nicht möglich war, eine funktionierende Anfrage zu erstellen.

Also habe ich versucht, den existierenden Branch Protection eines Repositories zu verwenden (den man leicht mit dem Befehl gh api repos/telekom-mms/examplerepo/branches/main/protection erhalten kann), aber es war unmöglich, den folgenden Code in eine funktionierende Curl-Anfrage zu integrieren und dann über diese Curl-Anfrage zu iterieren.

Daher habe ich diesen Code verwendet:

Und dann versucht, ihn auf eine minimale funktionierende Anfrage zu reduzieren. Das hat eine Weile gedauert, aber hier ist das endgültige Ergebnis:

Wenn man über diesen Code iteriert, funktioniert es. Ich kann nun unsere Repositories im großen Maßstab verwalten (Und jetzt: kann mir jetzt jemand bitte die bereits bestehende Lösung sagen, um dies zu tun?)


Weiterführende Informationen

Software Engineering Community – vernetzt, innovativ, geschäftsrelevant

>Zum Blogbeitrag & zur Podcast-Folge