GitHub-Actions: Coverage-Badge ohne 3rd-Party-Service mittels Gist erstellen

Für mein aktuelles GitHub-Projekt secbootctl wollte ich den Code-Coverage-Status mittels Badge von Shields.io in der README.md anzeigen lassen. Leider bietet GitHub-Actions so eine Funktion nicht "out-of-the-box" an. Anscheinend hat es sich etabliert dafür Third-Party-Services, wie z.B. Codecov, zu verwenden, um Coverage-Badges zu erstellen und diese dann im eigenen GitHub-Repository einbinden zu können.

Da ich es bevorzuge auf unnötige bzw. zusätzliche Services zu verzichten, habe ich etwas recherchiert und bin auf eine vielversprechende Lösung gestoßen: Mithilfe der GitHub-Action Dynamic Badges Action können die benötigten Coverage-Daten im Rahmen eines GitHub-Actions-Workflow im JSON-Format in einem GitHub-Gist gespeichert werden. Mit diesen JSON-Daten kann dann wiederum die Shields.io-API angesprochen werden und die so generierte Badge in der eigenen README.md eingebunden werden.

Beispiel

Vorab ein Beispiel, wie das ganze dann letztendlich aussehen könnte:

Beispiel für GitHub-Coverage-Badge ohen Third-Party-Service

Schritt-für-Schritt zur Coverage-Badge

Zum Erstellen und Anzeigen der Coverage-Badge sind folgende Schritte notwendig:

  1. Gist erstellen und Zugriff per Secret einrichten
  2. GitHub-Workflow um Dynamic Badges Action erweitern
  3. Badge in README.md einbinden

Gist erstellen und Zugriff per Secret einrichten

Im ersten Schritt erstellt ein GitHub-Gist unter https://gist.github.com:

Gist erstellen

Merkt euch die URL sowie die ID (der letzte Teil der URL) des erzeugten Gists. Im weiteren Verlauf nehmen wir an, das erzeugte Gist ist unter der folgenden URL verfügbar: https://gist.github.com/username/abc123456789.

Als nächstes muss ein Personal-Access-Token mit Gist-Scope erstellt werden. Dazu klickt auf euer Avatar rechts oben und dann auf Settings - Developer Settings - Personal access tokens - Generate new token.

Personal-Access-Token erstellen

Nach Klick auf Generate token wird euch der Token angezeigt - merkt euch diesen.

Hinweis Personal-Access-Token wie Passwort behandeln

Ein Personal-Access-Token erlaubt den Zugriff auf euren GitHub-Account über die GitHub-API. Die Rechte beschränken sich dabei auf die jeweiligen Scopes, für die der Personal-Access-Token eingerichtet wurde. Der Personal-Access-Token ist somit im Grunde nichts anderes ein Passwort. Ihr sollten diesen also sicher aufbewahren und im konkreten Fall nur den Gist-Scope erlauben.

Wer möchte könnte auch einen neuen GitHub-Account erstellen, der nur zur Erstellung dieses Gists und dem Personal-Access-Token dient. Dieser Account wäre somit losgelöst von eurem eigentlichen GitHub-Account und euren Repositories.

Im nächsten Schritt muss der zuvor erstellte Personal-Access-Token als Secret für das entsprechende Repository eingerichtet werden. Dazu wechselt in euer Repository, für welches ihr eine Coverage-Badge erzeugen wollt. Unter Settings - Secrets - New repository secret richtet nun ein Secret namens GIST_TOKEN ein und hinterlegt den Personal-Access-Token.

Repository-Secret für Gist erstellen

Dieses Secret lässt sich nun in einem GitHub-Action-Workflow verwenden. Mittels dem Secret bzw. dem hinterlegten Personal-Access-Token hat der Workflow Zugriff auf die Gist-Funktionalität mit den Gist-Rechten euren Accounts.

GitHub-Workflow um Dynamic Badges Action erweitern

Jetzt kann der eigentliche Workflow, bei mir z.B. CI.yaml, um einen weiteren "Step" ergänzt werden. Hier kommt nun die Dynamic Badges Action zum Einsatz:

- name: generate coverage badge
  if: ${{ matrix.python-version == '3.10' }}
  uses: schneegans/dynamic-badges-action@5ba090896c84b3521c7a75bd8f4de12656e3f426
  with:
    auth: ${{ secrets.GIST_TOKEN }}
    gistID: abc123456789
    filename: greeter-coverage.json
    label: Coverage
    message: ${{ env.coverage_percent }}%
    color: green
    style: flat-square
YAML - Datei: .github/workflows/CI.yaml

Die Action speichert in unserem Gist eine greeter-coverage.json mit den entsprechenden Daten, wie z.B. Farbe und Stil. Den eigentlichen Coverage-Prozentwert müsst ihr in einem vorherigen Step ermitteln und z.B. als Umgebungsvariable dem Badge-Erstellungs-Step zur Verfügung stellen.

Lasst nun euren Workflow durchlaufen. Ruft ihr nun eure Gist-URL auf, dann solltet ihr die durch den Workflow erzeugte JSON-Datei mit den entsprechenden Daten sehen können:

Gist - JSON-Datei mit Coverage-Daten für Shields.io-Badge

Badge in README.md einbinden

Im letzten Schritt müssen wir nun nur noch unsere Repository-README.md erweitern:

[![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/username/abc123456789/raw/greeter-coverage.json)](https://github.com/username/repo/actions/workflows/CI.yaml)
Markdown - Datei: README.md

Fazit

Eine Coverage-Badge für euer GitHub-Projekt lässt sich auch ohne einen zusätzlichen 3rd-Party-Service umsetzen. Da ihr in einem Gist mehrere Dateien speichern könnt, könnt ihr für mehrere Repositories das gleiche Gist verwenden, indem ihr einfach dem Namen der zu erstellenden Gist-Datei je Repository anpasst.

Feedback

Für Feedback zum Beitrag, seien es Fragen, Korrigierungen und/oder Anregungen, könnt ihr mir gerne eine Nachricht per E-Mail oder Mastodon schreiben (siehe Kontakt).