Table of Contents
- sonar-analysis
- Die Analyse ist eine Beobachtung, kein Torwächter
- fetch-depth: 0 ist nicht optional
- Warum kein Docker-Runner nötig ist
- Was der Scanner zur Laufzeit holt
- Die Adresse ist eine Variable, das Token ein Secret
- Der Wächter, und was er nicht fängt
- Gepinnt auf Commits, nicht auf Zweige
- Was an diesem Baustein nicht gemessen ist
sonar-analysis
.gitea/workflows/sonar-analysis.yml — schickt den Baum des aufrufenden
Repositories durch eine SonarQube-Analyse. Die Eingabe-Tabelle, das
Aufrufbeispiel und die Voraussetzungen stehen im README; diese Seite begründet
den Baustein und benennt, was an ihm nicht gemessen ist.
Der Baustein ist bewusst klein: ein Wächter, ein Checkout, die offizielle
Scan-Action. Alles Projektspezifische — Projektschlüssel, Quellpfade,
Ausschlüsse — steht in der sonar-project.properties des aufrufenden
Repositories. Genau deshalb ist er teilbar: Der Workflow ist für jedes
Repository identisch, und ein zweiter Abnehmer kostet eine Datei mit drei
Zeilen.
Die Analyse ist eine Beobachtung, kein Torwächter
Das ist die wichtigste Eigenschaft des Bausteins, und sie folgt aus dem Auslöser.
Die SonarQube Community Build analysiert ausschließlich den Hauptzweig. Eine
PR-Analyse gibt es dort nicht, der Aufruf hängt deshalb an einem Push auf
main, und damit erscheint ein Befund nach dem Merge. Zu diesem Zeitpunkt
ist nichts mehr zu blockieren.
Daraus folgt das fehlende Quality-Gate, und es fehlt absichtlich. Ein rotes Gate
hinter dem Merge färbte nur den Hauptzweig rot, ohne irgendetwas verhindert zu
haben — und einen Dauerzustand lernt man zu ignorieren. Der Schaden wäre nicht
das Gate selbst, sondern dass es die Glaubwürdigkeit der übrigen Prüfungen
mitnimmt: Wer gewöhnt ist, dass ein rotes main nichts bedeutet, liest auch die
nächste echte Rotfärbung als Rauschen.
Sinnvoll wird ein Gate unter genau einer von zwei Bedingungen, und beide ändern die Lage wirklich:
- PR-Analyse ist verfügbar. Dann blockiert das Gate einen Merge, und das ist eine Entscheidung, die es tragen kann.
- Ein Deployment hängt an diesem Workflow. Dann bewacht es etwas Konkretes.
Bis dahin ist das Hinzufügen eines Gates keine Nachbesserung, sondern eine Verschlechterung. Im Workflow steht das als Kommentar an der Stelle, an der der Schritt stünde — damit niemand ihn als Versehen nachträgt.
fetch-depth: 0 ist nicht optional
Die eine Checkout-Option, ohne die die Analyse ihren Zweck verliert.
Ein flacher Klon — die Vorgabe — liefert dem Scanner keine Historie. Er kann Befunde dann keiner Änderung zuordnen, sondern schreibt sie alle dem letzten Commit zu. Was damit unbrauchbar wird, ist nicht eine Randfunktion, sondern das Meiste, wofür man SonarQube überhaupt aufstellt: die Zuordnung zu Autor und Änderung, der Zeitraum „neuer Code", jedes datumsbasierte Maß. Ein Bericht, in dem alles gleich alt und von derselben Person ist, sagt nichts.
Der Preis ist ein langsamerer Checkout. Das ist der ganze Preis.
Warum kein Docker-Runner nötig ist
Der Nachbarbaustein docker-build-push braucht den Runner
mit dem Label ubuntu-docker, weil er einen Docker-Socket benutzt. Dieser hier
braucht ihn nicht, und das ist nachgesehen und nicht vermutet: Die Scan-Action
deklariert runs: using: node24. Sie ist damit eine JavaScript-Action, läuft
im Node des Runners und nicht in einem Container. Kein Socket, kein Daemon,
ubuntu-latest genügt.
Das hat eine Kehrseite, die man im Blick behalten muss. Eine JavaScript-Action
verlangt ihre Node-Hauptversion im Runner-Image. Die ist dort vorhanden:
node24 ist in Lauf 375 gemessen, siehe
Gemessenes-Verhalten.
Was der Scanner zur Laufzeit holt
Die Action bringt den Sonar-Scanner-CLI nicht mit. Sie lädt ihn bei jedem
Lauf von binaries.sonarsource.com und prüft dessen GPG-Signatur. Das ist
nichts Kleines: Bei der Messung für diese Seite lag das Linux-Archiv bei rund
59 MiB (Content-Length 61 626 644).
Das war die erwartete Stelle für den ersten Fehlschlag auf dieser Infrastruktur, und zwar aus einem konkreten, bereits einmal bezahlten Grund. Im README des ersten Abnehmers steht gemessen: Auf diesem Runner kam einmal eine 17-MB-Datei nicht durch, weil eine Docker-Brücke mit MTU 1500 gegen ein Pod-Netz mit 1450 lief. Kleine Antworten passten in ein Paket, die große nicht — und es sah aus wie ein gesperrter Host, nicht wie ein MTU-Problem. Behoben wurde das auf der Knoten-Seite, nicht in einem Workflow.
Der Download kommt durch. In Lauf 375 waren Archiv und Signatur in etwa drei Sekunden da; die knotenseitige Behebung hält beim Dreifachen der damaligen Größe. Nachzulesen unter Gemessenes-Verhalten. Wenn ein Lauf dieses Bausteins trotzdem einmal beim Download hängt oder abbricht, ist die MTU-Geschichte die erste Spur, die man verfolgt — und man sucht sie auf der Knoten-Seite, nicht im YAML.
Zwei weitere Folgen des Laufzeit-Downloads, nebenbei: Der Baustein hängt an der Erreichbarkeit eines fremden Hosts, und er hängt an einer Signaturprüfung. Beide können unabhängig von allem hier scheitern.
Die Adresse ist eine Variable, das Token ein Secret
SONAR_HOST_URL ist eine Variable des aufrufenden Repositories,
SONAR_TOKEN ein Secret. Die Trennung ist keine Formsache:
Die Adresse des Servers ist kein Zugangsdatum. Als Variable bleibt sie im Protokoll lesbar — und erst das macht eine falsche Adresse überhaupt diagnostizierbar. Als Secret wäre sie maskiert, und ein Fehler gegen eine maskierte Adresse ist nicht zu deuten; genau diese Lage hat bei publish-helm-chart einen eigenen Maskierungsschritt erzwungen.
Beides gehört dem Aufrufer und nicht diesem Repository. Ein geteilter Baustein trägt weder die Infrastruktur-Adresse eines einzelnen Mandanten noch dessen Token — dieselbe Hausregel, aus der bei den anderen Bausteinen der Eigentümer-Zwang folgt.
Der Wächter, und was er nicht fängt
Der Preis der vorigen Entscheidung ist eine Voraussetzung, die man vergessen
kann, und eine nicht gesetzte Variable kommt als leere Zeichenkette an. Eine
leere Adresse scheitert nicht dort, wo sie gesetzt wird, sondern einen Schritt
später in der Scan-Action, gegen eine Adresse, die niemand sieht. Deshalb steht
der Wächter zuerst und benennt die Einstellung, den Ort und die erwartete Form.
Dasselbe Muster wie bei OP_CONNECT_HOST in
publish-helm-chart — es ist inzwischen die Hausform.
Der Wächter prüft beide Werte, bevor er abbricht. Ein Repository, das keinen von beiden eingerichtet hat, erfährt das sonst in zwei Läufen statt in einem, und der zweite kostet genauso viel Aufmerksamkeit wie der erste.
Beim Token fängt er nur zwei der drei Fälle: ein vorhandenes, aber leeres
Secret, und einen Aufrufer, der secrets: inherit vergessen hat. Beide kommen
als leere Zeichenkette an. Ein ganz fehlendes Secret kommt nie so weit — der
Workflow deklariert es als required, und ein fehlendes Pflicht-Secret lässt auf
dieser Instanz die Job-Erzeugung scheitern, nicht den Job; der Lauf steht
dann ohne Schritte da. Nachzulesen unter
Gemessenes-Verhalten.
Dass required: true hier trotzdem richtig ist, folgt der dort aufgeschriebenen
Linie: Jeder Aufrufer braucht das Token, ohne Token gibt es keine Analyse.
Ein Lauf ohne Schritte ist dann der verständliche Fall. required: false wäre
nur richtig, wenn ein Teil des Bausteins ohne das Secret arbeiten könnte — hier
tut keiner das.
Gepinnt auf Commits, nicht auf Zweige
Beide Actions sind auf einen Commit gepinnt, nicht auf einen Zweig und nicht auf eine Hauptversion. Begründung und der Fall, an dem sie hängt, stehen unter Gemessenes-Verhalten, Abschnitt „Wandernde Action-Verweise": Ein beweglicher Verweis holt bei jedem Lauf den neuesten Stand und ändert das Verhalten aller Aufrufer, ohne dass hier etwas angefasst wurde.
Was an diesem Baustein nicht gemessen ist
Diese Liste stand hier mit vier Punkten. Drei davon hat Lauf 375 des ersten
Abnehmers (jlab-cluster/helm-charts, Push auf main) beantwortet, alle drei
positiv — node24 im Runner-Image, der 59-MiB-Scanner-Download, und dass eine
Repository-Variable einen aus einem fremden Repository aufgerufenen Workflow
erreicht. Sie stehen jetzt dort, wo Gemessenes hingehört: auf
Gemessenes-Verhalten, mit je einem Abschnitt. Der dritte
hat dabei dieselbe Frage für OP_CONNECT_HOST in
publish-helm-chart mitbeantwortet.
Lauf 399 des ersten Abnehmers hat den Baustein inzwischen vollständig durchlaufen — 44 Sekunden, ANALYSIS SUCCESSFUL, mit Bericht auf dem Server. Damit ist am Baustein selbst nichts mehr offen; was bleibt, ist eine Eigenschaft der jeweiligen Instanz und des jeweiligen Projekts:
Offen bleibt genau einer, und es ist der inhaltliche:
-
Was eine Analyse überhaupt meldet, insbesondere aus Helm-Templates. Der Analysator trifft dort auf Dateien, die wegen
{{ }}kein gültiges YAML sind. Was er damit macht, bekommt man nur heraus, indem man ihn einmal darauf laufen lässt — deshalb beginnt der erste Abnehmer ohne Ausschlüsse. Das Ergebnis entscheidet, ob Ausschlüsse nachträglich nötig sind, und es gehört in diesonar-project.propertiesdes jeweiligen Repositories, nicht in diesen Baustein.Lauf 375 hat das nicht beantwortet, obwohl der Scan lief: Der Bericht wurde erzeugt — 603 kB — und nie hochgeladen, weil der Upload an einem falschen Projektschlüssel scheiterte. Das Protokoll nennt keine Einzelbefunde. Es braucht also einen erfolgreichen Lauf, nicht nur einen gestarteten.
Lauf 399 war dieser erfolgreiche Lauf — und auch er beantwortet die Frage nicht aus dem Protokoll heraus. Ein Scanner-Protokoll nennt grundsätzlich keine Einzelbefunde, es nennt die Adresse des Berichts. Und die Instanz antwortet auf
api/measures/componentanonym mit 401, während die Oberfläche 200 liefert — die richtige Einstellung, aber sie heißt: Die Zahlen bekommt nur, wer ein Konto dort hat. Diese Frage ist damit keine Messung, die noch aussteht, sondern eine, die aus einer Pipeline grundsätzlich nicht zu beantworten ist. Wer wissen will, ob Helm-Templates Rauschen erzeugen, sieht auf das Dashboard.
Eine Sache, die nicht auf der Liste stand und auf ihr hätte stehen müssen:
welche Analysatoren die Instanz tatsächlich hat. Analysatoren sind
server-seitige Plugins; der Scanner lädt eine CLI, keine Sprachen. Eine
Sprachliste aus der Dokumentation beschreibt die neueste SonarQube-Fassung und
sagt über eine konkrete Instanz nichts. Lauf 375 lud zwei Qualitätsprofile,
json und yaml; gearbeitet haben der IaC-Kubernetes-Sensor und die
Geheimnis-Erkennung. Shell und GitHub Actions waren nicht dabei, obwohl der
erste Abnehmer dafür geplant war. Jeder Aufrufer dieses Bausteins muss also
damit rechnen, dass ein Teil seines Baums ungeprüft bleibt — und was geprüft
wird, steht erst nach dem ersten Lauf fest. Dieser Baustein kann daran nichts
ändern, und sonar-project.properties auch nicht.