Skip to content

fix(demo): zet de FSC-beheeromgeving achter de inlogmuur #548

fix(demo): zet de FSC-beheeromgeving achter de inlogmuur

fix(demo): zet de FSC-beheeromgeving achter de inlogmuur #548

Workflow file for this run

# De scripts onder .github/scripts/ beslissen wát de rest van de CI doet: wijzigingsfilter.sh
# bepaalt welke jobs draaien, uitrol-poort.sh bepaalt of een run een merge mag dragen,
# merge-guard.sh bewaakt de compose-overlays en demo-grens.sh bewaakt dat het stelsel niet van
# demonstratiecode afhangt, en jdk-handtekening.py bewaakt dat elke setup-java-stap de JDK op
# handtekening controleert. Een fout daarin is stil — een overgeslagen job rapporteert 'skipped'
# en dat telt als succes voor branch protection. Vandaar een eigen, lichte workflow: shellcheck,
# actionlint en de bash-unittests, zonder de docker build van de FSC-harness.
name: CI-scripts
# Op een PR bewust ZONDER pad-filter. Twee redenen. Ten eerste kruiscontroleren de unittests de
# workflow-namen in wijzigingsfilter.sh tegen .github/workflows/ op schijf; met een pad-filter
# vuurde die controle op de verkeerde PR — niet op de PR die een workflow toevoegt, maar op de
# volgende die toevallig een script aanraakt. Ten tweede hoort deze check een required context te
# zijn, en een required check achter een workflow-niveau `paths`-filter wordt bij een
# niet-matchende PR nooit gerapporteerd: die blijft eeuwig 'expected' en blokkeert de merge
# voorgoed. De job kost enkele seconden (linting plus bash-unittests, geen build).
#
# Op een push naar main gelden beide redenen niet: required contexts worden alleen op PR's
# beoordeeld, en de kruiscontrole heeft daar al gevuurd. De push-run blijft als vangnet voor een
# commit die buiten de PR-flow om op main landt; het pad-filter beperkt hem tot de bestanden waar
# dat vangnet over gaat — inclusief de paden die de kruiscontroles van schijf lezen.
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [main]
paths: ['.github/scripts/**', '.claude/hooks/**', '.github/workflows/**', 'docs/architecture/**', '**/pom.xml', '.clusterfuzzlite/base/Dockerfile', 'demo/*.sh', 'demo/*.py', 'demo/environment/**']
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
scripts:
# Expliciete naam, want deze wordt de status-check-context zodra de check als required wordt
# gezet — en de kale job-id `scripts` is daarvoor te generiek. Een standalone workflow krijgt
# geen `<aanroeper-job> / <job>`-voorvoegsel dat hem onderscheidt.
name: ci-scripts
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: actionlint
env:
# Vaste versie plus checksum in plaats van een action van derden: dezelfde
# pin-discipline als de SHA-gepinde actions elders, met één partij minder in de keten.
ACTIONLINT_VERSIE: 1.7.12
ACTIONLINT_SHA256: 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8
run: |
set -euo pipefail
curl -fsSL --max-time 60 --retry 3 --retry-connrefused -o /tmp/actionlint.tar.gz \
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSIE}/actionlint_${ACTIONLINT_VERSIE}_linux_amd64.tar.gz"
echo "${ACTIONLINT_SHA256} /tmp/actionlint.tar.gz" | sha256sum -c -
tar -xzf /tmp/actionlint.tar.gz -C /tmp actionlint
# actionlint draait shellcheck over elk `run:`-blok, maar schakelt die regel stil uit
# wanneer shellcheck ontbreekt: alleen een `verbose:`-regel, exitcode 0. Een
# runner-image zonder shellcheck zou de stap dus halveren zonder signaal.
command -v shellcheck >/dev/null \
|| { echo "::error::shellcheck ontbreekt op de runner — actionlint zou zijn run-blok-analyse stil uitschakelen."; exit 1; }
# Vangt onder meer een `needs.<job>.result` die verwijst naar een job die niet in
# `needs:` staat, en een `cancelled()` op een plek waar die niet mag staan.
/tmp/actionlint
- name: Elke setup-java verifieert de handtekening van de JDK
# De suite hierboven toetst de guard tegen fixtures; deze stap laat hem los op de
# werkelijke workflows. Los, zodat een falende guard als eigen regel in de log staat en
# niet als één van de twintig meldingen van de unittest-stap.
if: ${{ !cancelled() }}
run: python3 .github/scripts/jdk-handtekening.py .github/workflows
- name: De COPY-lijst van het fuzz-basis-image volgt de reactor
# De suite hieronder toetst deze guard tegen fixtures; deze stap laat hem los op de
# werkelijke root-pom en het werkelijke Dockerfile. Los, zodat een achterlopende COPY-lijst
# als eigen regel in de log staat en niet als één van de twintig meldingen van de
# unittest-stap. Het Dockerfile draagt dezelfde controle, maar die gaat pas af tijdens de
# bouw van het image — en die draait alleen op een push naar main, dus ná de merge.
if: ${{ !cancelled() }}
run: .github/scripts/fuzz-basis-modules.sh
- name: shellcheck + bash-unittests
# Ook draaien als actionlint viel: twee losse signalen zijn in één run meer waard dan één.
if: ${{ !cancelled() }}
run: |
set -euo pipefail
fail=0
echo "== shellcheck =="
# `find` in plaats van een glob: een script in een toekomstige submap zou anders
# stilzwijgend ongecontroleerd blijven. -x volgt `source`-directives, zodat een test
# zijn script meegecontroleerd krijgt.
#
# .claude/hooks/ hoort erbij: die scripts draaien op elke machine van het team, en een
# fout erin blokkeert of verstomt een guard zonder dat iemand het merkt.
scripts=$(find .github/scripts .claude/hooks -name '*.sh' | wc -l)
# `xargs -r` draait shellcheck niet bij een lege lijst; zonder deze guard zou die stilte
# dan als geslaagd tellen in plaats van als "niets gemeten".
if [ "$scripts" -eq 0 ]; then
echo "FOUT: geen enkel script onder .github/scripts/ of .claude/hooks/ — deze stap meet niets."
fail=1
elif ! find .github/scripts .claude/hooks -name '*.sh' -print0 | xargs -0 -r shellcheck -x -S warning; then
fail=1
fi
echo "== bash-unittests =="
gedraaid=0
# Per suite het aantal asserties dat er minimaal uit moet komen. De suite rapporteert dat
# zelf als afsluitende ASSERTIES=-regel; een kale `echo "OK: ..."` telt daardoor niet mee,
# en het verlagen van een verwachting is een zichtbare diff in plaats van stille erosie.
declare -A VERWACHTE_ASSERTIES=(
[.github/scripts/test-wijzigingsfilter.sh]=107
[.github/scripts/test-uitrol-poort.sh]=86
[.github/scripts/test-demo-grens.sh]=62
[.github/scripts/test-cross-domain-preview.sh]=51
[.github/scripts/test-preview-klaarzetten.sh]=54
[.github/scripts/test-zad-deployment-bestaat.sh]=29
[.github/scripts/test-preview-comment.sh]=121
[.github/scripts/test-jdk-handtekening.sh]=22
[.github/scripts/test-fuzz-basis-modules.sh]=16
[.github/scripts/test-fuzz-basis-pin.sh]=74
[.github/scripts/test-proeftuin-image.sh]=20
[.github/scripts/test-proeftuin-pin-pr.sh]=128
[.github/scripts/test-pin-pr-teststubs.sh]=40
[.github/scripts/test-proeftuin-pin.sh]=139
)
# De andere richting: een suite die verdwijnt of buiten het `test-*.sh`-patroon wordt
# hernoemd, laat alleen een verweesde sleutel achter — `find` levert hem niet meer, de
# `gedraaid`-guard hieronder telt de overige suites en de stap wordt groen. Voor
# demo-grens.sh is dat extra duur: die controle heeft geen andere aanroep in de keten,
# dus één hernoeming haalt de architectuurgrens er geruisloos uit.
for t in "${!VERWACHTE_ASSERTIES[@]}"; do
if [ ! -f "$t" ]; then
echo "FOUT: $t staat in VERWACHTE_ASSERTIES maar bestaat niet meer."
fail=1
fi
done
while IFS= read -r t; do
gedraaid=$((gedraaid + 1))
echo "-- $t"
if ! uitvoer=$(bash "$t" 2>&1); then
printf '%s\n' "$uitvoer"
fail=1
continue
fi
printf '%s\n' "$uitvoer"
gemeten=$(sed -n 's/^ASSERTIES=//p' <<<"$uitvoer" | tail -1)
verwacht=${VERWACHTE_ASSERTIES[$t]:-}
if [ -z "$verwacht" ]; then
echo "FOUT: $t staat niet in VERWACHTE_ASSERTIES — voeg het verwachte aantal toe."
fail=1
elif [ -z "$gemeten" ]; then
echo "FOUT: $t rapporteert geen ASSERTIES=-regel; het aantal is niet te bewaken."
fail=1
elif ! [[ "$gemeten" =~ ^[0-9]+$ ]]; then
# Zonder deze tak valt de vergelijking hieronder met 'integer expression expected'
# om, grijpt `set -e` niet in op een elif-conditie, en wordt de stap groen.
echo "FOUT: $t rapporteert ASSERTIES=$gemeten; dat is geen aantal."
fail=1
elif [ "$gemeten" -lt "$verwacht" ]; then
echo "FOUT: $t leverde $gemeten asserties, verwacht minstens $verwacht."
fail=1
else
# Het aantal is zelf-gerapporteerd, dus een uitgeholde suite die alleen nog de
# ASSERTIES-regel print zou de drempel halen zonder iets te toetsen. Tel daarom de
# geslaagde asserties in de uitvoer mee: die moeten er minstens evenveel zijn.
geteld=$(grep -c '^OK: ' <<<"$uitvoer" || true)
if [ "$geteld" -lt "$gemeten" ]; then
echo "FOUT: $t meldt ASSERTIES=$gemeten maar print $geteld geslaagde asserties; de suite toetst minder dan hij rapporteert."
fail=1
fi
fi
done < <(find .github/scripts -name 'test-*.sh' | sort)
# Zonder deze guard meldt de stap groen zodra er geen `test-*.sh` meer onder
# .github/scripts/ staat — verwijderd, of buiten dat pad of die naamvorm gebracht.
#
if [ "$gedraaid" -eq 0 ]; then
echo "FOUT: geen enkele test-*.sh gevonden — deze stap meet niets."
fail=1
fi
# En een benoemde set, geen aantal: een suite die mét zijn VERWACHTE_ASSERTIES-sleutel
# verdwijnt laat de guards hierboven tevreden, en een vervangende stub die één `OK:`
# print haalt elke telling. Deze drie dragen elk een controle die nergens anders in de
# keten wordt aangeroepen — demo-grens.sh is de architectuurgrens zelf.
for verplicht in .github/scripts/test-wijzigingsfilter.sh \
.github/scripts/test-uitrol-poort.sh \
.github/scripts/test-demo-grens.sh \
.github/scripts/test-jdk-handtekening.sh; do
if [ ! -f "$verplicht" ]; then
echo "FOUT: $verplicht ontbreekt; die controle draait nergens meer in de keten."
fail=1
fi
done
if [ "$fail" -ne 0 ]; then
echo "::error::shellcheck of een bash-unittest faalde. Zie de log hierboven."
fi
exit "$fail"