Files
pipelines/.gitea/workflows/sonar-analysis.yml
T
jklein 0f7cd04c70 SonarQube-Analyse als geteilten Baustein aufnehmen
Neuer Baustein sonar-analysis.yml, on: workflow_call. Ein Waechter fuer die
Voraussetzungen des Aufrufers, Checkout mit vollstaendiger Historie, dann die
offizielle Scan-Action. Beide Actions auf einen Commit gepinnt.

Projektspezifisches steht in der sonar-project.properties des aufrufenden
Repositories, nicht hier - damit ist der Workflow fuer jedes Repository
identisch und ein zweiter Abnehmer kostet eine Datei mit drei Zeilen.

Kein Quality-Gate-Schritt, und das absichtlich: Die Community Build analysiert
nur den Hauptzweig, der Ausloeser liegt damit nach dem Merge, und dort ist
nichts mehr zu blockieren. Ein rotes main ohne Wirkung nimmt die
Glaubwuerdigkeit der uebrigen Pruefungen mit. Die zwei Bedingungen, unter denen
ein Gate richtig waere, stehen im README und als Kommentar an der Stelle, an
der der Schritt stuende.

SONAR_HOST_URL ist eine Variable des Aufrufers, SONAR_TOKEN sein Secret. Die
Adresse bleibt als Variable im Protokoll lesbar - erst das macht eine falsche
Adresse diagnostizierbar. Der Waechter prueft beide, bevor er abbricht, damit
ein Repository ohne beide das in einem Lauf erfaehrt.

Beide Eingaben sind type: string, auch project-base-dir: Boolesche
workflow_call-Eingaben kommen auf dieser Gitea-Version nicht an (Issue #4), und
die Regel gilt fuer jede Eingabe hier, damit sie nicht je Datei neu entschieden
wird.

Die Langform - warum fetch-depth: 0 nicht optional ist, warum kein
Docker-Runner noetig ist, was der Scanner zur Laufzeit holt und was an diesem
Baustein noch nicht gemessen ist - steht im Wiki unter sonar-analysis.

Der Baustein ist ab v4 verfuegbar; die Tag-Nummer bezeichnet wie bisher den
Zustand des Repositories und nicht die Version einer Datei.
2026-10-01 23:33:05 +00:00

146 lines
8.0 KiB
YAML

name: SonarQube Analysis
# Called through "uses:" only, never triggered here - there is nothing in this
# repository worth analysing, and a dispatch here would run against these files.
#
# The caller decides WHEN the analysis runs, through its own "on:". On the
# SonarQube Community Build only the main branch is analysed, so that is in
# practice a push to main, and the consequence belongs said out loud: a finding
# appears AFTER the merge. This block observes a state, it does not guard a
# gate - see the note on the missing quality-gate step at the bottom.
on:
workflow_call:
inputs:
# Both inputs are type: string, and that is not an oversight waiting to be
# cleaned up. On this Gitea version (measured on 1.27.0, issue #4) a
# boolean workflow_call input does not reach the called workflow at all -
# neither its default nor an explicitly passed value - while the run stays
# green. Every input of every block here is therefore a string, even one
# that is a switch in meaning. Whoever "fixes" that later brings the fault
# back silently.
args:
required: false
type: string
default: ""
description: >-
Additional arguments for the Sonar Scanner CLI, passed through to the
scan action's "args" input. Everything project-specific belongs in the
sonar-project.properties of the calling repository, not here; this is
for the rest, e.g. -Dsonar.verbose=true for one diagnostic run.
# The scan action's own default for projectBaseDir is ".", and this input
# repeats it instead of defaulting to the empty string: an empty value
# would be passed on and would override the action's default rather than
# leave it alone.
project-base-dir:
required: false
type: string
default: "."
description: >-
Directory the analysis starts from, i.e. sonar.projectBaseDir. The
sonar-project.properties is looked up relative to it.
secrets:
# required: true, and that is a real decision on this forge, not a
# formality: a missing secret that the called workflow declares as
# required fails JOB CREATION, not the job - the run then stands there
# with no steps at all and nothing in it names the cause. Measured in
# jlab-cluster/helm-charts, written up on the wiki page
# "Gemessenes-Verhalten".
#
# It is the right setting here all the same, because every caller needs
# the token: without it there is no analysis to run, so a run without
# steps is the understandable case - exactly this prerequisite is missing.
# (required: false would be right only if a part of the block worked
# without it.)
SONAR_TOKEN:
required: true
jobs:
sonar-analysis:
name: SonarQube Analysis
# ubuntu-latest is enough, and that is worth a line because the sibling
# block docker-build-push needs the ubuntu-docker runner: the scan action
# declares "runs: using: node24", so it is a JavaScript action and runs in
# the runner's Node, not in a container. No Docker socket is involved, and
# ubuntu-docker is the only runner here that has one.
#
# What it does need is node24 in the runner image. That is NOT verified; if
# it is absent the action fails, and it fails with a message about the
# action rather than about the image.
runs-on: ubuntu-latest
steps:
# Both settings belong to the CALLING repository, not to this file: a
# shared block does not carry one tenant's infrastructure address, and it
# certainly does not carry anyone's token. The price of that is two
# prerequisites that can be forgotten, and an unset variable arrives as an
# empty string.
#
# An empty SONAR_HOST_URL does not fail here - it fails one step later,
# inside the scan action, against an address nobody can see. So the guard
# comes first and names the setting, the place to set it and the shape of
# the value. The publish block needed the same pattern for
# OP_CONNECT_HOST.
#
# The token is checked in the same step even though a MISSING one never
# gets this far (see the secrets block above): what this catches is a
# secret that exists and is empty, and a caller that forgot
# "secrets: inherit". Both arrive as an empty string, both would otherwise
# surface as an unauthenticated request against the server.
#
# Both are checked before either exits, so a repository that has set up
# neither learns both in one run instead of in two.
- name: "Check the calling repository's prerequisites"
env:
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |-
missing=0
if [ -z "$SONAR_HOST_URL" ]; then
echo "::error::the variable SONAR_HOST_URL is not set in the calling repository (Settings -> Actions -> Variables, or on the organisation). It must hold the address of the SonarQube server, e.g. https://sonar.example.com - no trailing path. This workflow deliberately does not hardcode a host."
missing=1
fi
if [ -z "$SONAR_TOKEN" ]; then
echo "::error::the secret SONAR_TOKEN is empty in the calling repository (Settings -> Actions -> Secrets, or on the organisation), or the caller did not pass it on with 'secrets: inherit'. It must hold a SonarQube token permitted to run analyses - and to create the project, if the first run is supposed to create it."
missing=1
fi
[ "$missing" -eq 0 ] || exit 1
# fetch-depth: 0, and this is the one checkout option the analysis cannot
# do without. With the default shallow clone the scanner has no history to
# attribute lines to, so every finding is reported against the last commit
# instead of the change that introduced it: blame, the "new code" period
# and every date-based measure become unusable. The clone is slower, which
# is the whole price.
#
# Pinned to a commit, not to a tag or a branch: a moving reference pulls
# the newest state into every run of every caller, and the resulting
# failure shows up at a caller that changed nothing (issue #5).
- name: "Checkout Repository"
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 0
# The action downloads the Sonar Scanner CLI from binaries.sonarsource.com
# at run time and verifies its GPG signature. That download is around
# 59 MiB, and it is the most likely place for a first failure on this
# infrastructure - the wiki page for this block says why.
- name: "SonarQube Scan"
uses: SonarSource/sonarqube-scan-action@7006c4492b2e0ee0f816d36501671557c97f5995 # v8.1.0
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# A variable of the caller, not a secret: the address of the server is
# not a credential, and as a variable it stays readable in the log,
# which is what makes a wrong one diagnosable at all.
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
with:
args: ${{ inputs.args }}
projectBaseDir: ${{ inputs.project-base-dir }}
# There is deliberately NO quality-gate step here, and it is missing on
# purpose rather than by oversight.
#
# With an after-the-merge trigger there is nothing left to block. A red
# gate would only colour the main branch red, and a permanent red is a
# thing people learn to ignore - at which point the gate has cost the
# credibility of every other check without having stopped anything.
#
# It becomes the right thing under one of two conditions: pull-request
# analysis is available (then the gate blocks a merge, which is a decision
# it can actually carry), or a deployment hangs off this workflow (then it
# guards something concrete). Until then, adding it is a regression.