Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,20 @@ updates:
# images uit compose.yaml (die via pin-consistency.yml ook de ZAD-runtime-pins
# sturen) is deze version-update-ronde het enige signaal dat er een nieuwe versie is.
#
# Hier hangt ook de berichtenbox van de proeftuin aan. Die staat op digest gepind, want
# dat is voor dát image de enige vorm die hij kan bijhouden — de reden staat bij die
# regel in compose.yaml. Zet daar dus geen variabele in terug: dan valt de enige
# automatische bump van de demo weg.
- package-ecosystem: "docker-compose"
directory: "/"
schedule:
interval: "daily"
ignore:
# De berichtenbox van de proeftuin. Dependabot kán deze bump niet meer aanbieden: hij
# houdt de gesloten, niet-gemergde PR #271 vast als bestaande PR voor dit image op
# versie `latest`, en elke volgende bump heet opnieuw `latest` — dus die vastlegging
# matcht altijd. Zijn joblog eindigt dan op `Pull request #271 already exists for
# minbzk/moza-poc with latest version latest`; `@dependabot recreate` en een handmatige
# run vanaf de Dependency graph lopen op hetzelfde vast, en de vastlegging is niet op te
# ruimen zolang die PR bestaat. De workflow Proeftuin-pin doet dit image nu. Expliciet
# negeren, zodat er geen ecosysteem-regel staat die stilzwijgend niets doet.
- dependency-name: "minbzk/moza-poc"
# Dagelijks: alerts komen er alleen voor semver-referenties, en alle `uses:` in dit
# repo zijn op commit-SHA gepind (supply-chain-eis). Ook hier is de version-update
# dus het enige kanaal; de groepering hieronder houdt het bij hooguit twee PR's.
Expand Down
85 changes: 33 additions & 52 deletions .github/scripts/fuzz-basis-pin.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ set -euo pipefail
DOCKERFILE=${DOCKERFILE:-.clusterfuzzlite/Dockerfile}
BRANCH=${BRANCH:-chore/fuzz-basis-pin}

# Het PR-onderhoud zelf is gedeeld met proeftuin-pin-pr.sh: token eisen, de eigen PR vinden, de
# branch publiceren en de PR opruimen. Wat hieronder staat is het deel dat van dít pad is.
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=.github/scripts/pin-pr-lib.sh
source "$HERE/pin-pr-lib.sh"

# De digest komt uit `docker buildx imagetools inspect --format`. Dat commando eindigt ook met 0 als
# het template niets oplevert, en dan draagt DIGEST alleen nog het image-pad. `vervang_pin` zet die
# afgekapte waarde daarna gewoon in de FROM-regel: de sed raakt wél iets, de diff is niet leeg, en de
Expand All @@ -41,9 +47,22 @@ pin_is_actueel() {
# gehouden moet worden. De vervangkant is bewust níét ge-escaped; dat mag alleen omdat
# `digest_is_welgevormd` de tekenset al tot een pad plus hex beperkt.
vervang_pin() {
local digest=$1 pad_regex aantal
local digest=$1 pad_regex aantal status=0
pad_regex=$(printf '%s' "${digest%@*}" | sed 's/[].[^$*\/]/\\&/g')
aantal=$(grep -cE "^FROM +${pad_regex}@sha256:[a-f0-9]{64}$" "$DOCKERFILE" || true)

# `grep -c` kent drie uitkomsten: 0 = gevonden, 1 = niets gevonden, 2 = kon niet zoeken
# (onleesbaar bestand, ongeldige ERE). Met `|| true` erachter zou die derde als een lege telling
# doorgaan, en dan faalt de `-ne 1`-toets hieronder open: hij slaat de tak over die juist bewaakt
# dat er precies één regel geraakt wordt, op het moment dat er niets gemeten is.
aantal=$(grep -cE "^FROM +${pad_regex}@sha256:[a-f0-9]{64}$" "$DOCKERFILE") || status=$?

case $status in
0|1) ;;
*)
echo "::error::kon $DOCKERFILE niet doorzoeken (grep gaf $status)."
return 1
;;
esac

# Precies één: bij nul wijst de FROM-regel ergens anders heen of is hij van vorm veranderd, bij
# meer dan één (een multi-stage Dockerfile) zou de sed ze allemaal raken behalve die met een
Expand All @@ -56,13 +75,6 @@ vervang_pin() {
sed -i -E "s|^FROM +${pad_regex}@sha256:[a-f0-9]{64}\$|FROM ${digest}|" "$DOCKERFILE"
}

# `isCrossRepository` eruit: `--head` matcht op branchnaam, dus een fork-PR met dezelfde naam zou hier
# als "onze" pin-PR gelden — en dan sluiten we andermans PR.
open_pin_pr() {
gh pr list --head "$BRANCH" --state open --json number,isCrossRepository \
--jq '[.[] | select(.isCrossRepository | not)] | .[0].number // empty'
}

pr_body() {
cat <<EOM
Automatisch aangemaakt na een geslaagde bouw van het fuzz-basis-image.
Expand All @@ -74,47 +86,10 @@ Wijzigen de dependency-declaraties vóór de merge, dan ververst een volgende bo
EOM
}

# Sluiten en de branch opruimen in twee stappen: `gh pr close --delete-branch` wil ook de lokale
# branch weg en die bestaat in dit pad niet.
ruim_pin_pr_op() {
local nummer=$1 status=0

gh pr close "$nummer" \
--comment "De pin in \`$DOCKERFILE\` hoort inmiddels bij het huidige basis-image; deze PR heeft geen wijziging meer te brengen."

git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null || status=$?

# Alleen 2 betekent "die branch is er niet" — een vorige run die na de close afbrak, of iemand die
# hem met de hand verwijderde. Elke andere code (128 bij een auth- of netwerkfout) zegt dat we het
# niet weten, en dan is stil overslaan het slechtste antwoord: een ingetrokken `Contents: write`
# zou zo elke run de opruiming overslaan terwijl de log meldt dat er opgeruimd is.
case $status in
0) git push origin --delete "$BRANCH" ;;
2) echo "Branch $BRANCH bestond al niet meer." ;;
*)
echo "::error::kon niet vaststellen of $BRANCH nog bestaat (git ls-remote gaf $status)."
return 1
;;
esac
}

# Eén commit bovenop main, geen doorgroeiende branch: `switch -C` plus force-push zetten de pin-branch
# elke bouw opnieuw neer. Wat iemand er zelf op zette gaat daarmee weg — bedoeld, want deze PR hoort
# precies één FROM-regel te dragen. De commit is op het Dockerfile begrensd, net als de guard die
# ervoor bepaalt of er iets te committen valt.
publiceer_branch() {
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git switch -C "$BRANCH"
git commit -m "chore(ci): pin het fuzz-basis-image op de huidige pom-set" -- "$DOCKERFILE"
git push -f origin "$BRANCH"
}

main() {
if [ -z "${GH_TOKEN:-}" ]; then
echo "::error::FUZZ_PIN_TOKEN ontbreekt — de pin-PR kan niet aangemaakt worden. Zet de repo-secret (fine-grained PAT met Contents: write en Pull requests: write)."
return 1
fi
# Kaal, zonder `|| return 1`: dat zou `errexit` in de hele functie uitzetten, en dan zou een fout
# in een zwaardere controle die hier ooit bij komt stilzwijgend doorlopen.
pin_pr_vereis_token FUZZ_PIN_TOKEN

if ! digest_is_welgevormd "${DIGEST:-}"; then
echo "::error::de bouw leverde geen bruikbare digest ('${DIGEST:-}') — de pin is niet te bepalen."
Expand All @@ -130,16 +105,21 @@ main() {
fi

local open_pr body
open_pr=$(open_pin_pr)
open_pr=$(pin_pr_open_pr "$BRANCH")

# De pin kan ook buiten deze PR om goed komen: iemand werkt hem met de hand bij, of de bouw levert
# bij uitzondering dezelfde digest. Blijft de PR dan openstaan, dan draagt hij een diff die niets
# meer verandert en kan een reviewer niet zien of hij nog actueel is.
if pin_is_actueel "$DIGEST"; then
echo "De pin hoort al bij dit image."

# Ook zónder open PR opruimen: een vorige run die tussen het sluiten en het verwijderen afbrak
# laat een branch achter die anders nooit meer wordt aangeraakt, en die de eerstvolgende bump
# dan met een vreemde historie zou dragen.
pin_pr_ruim_op "$open_pr" "$BRANCH" \
"De pin in \`$DOCKERFILE\` hoort inmiddels bij het huidige basis-image; deze PR heeft geen wijziging meer te brengen."

if [ -n "$open_pr" ]; then
ruim_pin_pr_op "$open_pr"
echo "Openstaande pin-PR #$open_pr gesloten."
fi

Expand All @@ -155,7 +135,8 @@ main() {
return 1
fi

publiceer_branch
pin_pr_publiceer_branch "$BRANCH" "$DOCKERFILE" \
"chore(ci): pin het fuzz-basis-image op de huidige pom-set"
body=$(pr_body "${POMS:-onbekend}")

# Ook de body verversen: hij draagt de pom-hash van déze bouw.
Expand Down
77 changes: 77 additions & 0 deletions .github/scripts/pin-pr-lib.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# shellcheck shell=bash
#
# Gedeeld PR-onderhoud voor de twee pin-scripts: fuzz-basis-pin.sh (het fuzz-basis-image) en
# proeftuin-pin-pr.sh (de berichtenbox van de demo).
#
# Beide bieden één regel in één bestand aan op een vaste branch, en beide moeten daarvoor dezelfde
# vier dingen goed doen: het token eisen, de eigen PR vinden zonder een fork-PR te raken, de branch
# als één commit bovenop main neerzetten, en de PR opruimen zodra hij niets meer verandert. Dat deel
# staat hier. Wat "verouderd" betekent en welke regel vervangen wordt, verschilt per pad en blijft in
# het aanroepende script — daar zit het oordeel, en dat hoort niet gedeeld te worden.
#
# Dit bestand wordt gesourcet en definieert alleen functies; het doet uit zichzelf niets.

# De secretnaam als argument, want de melding moet de naam noemen die in de workflow staat: een
# generieke "het token ontbreekt" laat degene die hem leest zoeken naar wélke secret.
pin_pr_vereis_token() {
local secret=$1

if [ -z "${GH_TOKEN:-}" ]; then
echo "::error::$secret ontbreekt — de pin-PR kan niet aangemaakt worden. Zet de repo-secret (fine-grained PAT met Contents: write en Pull requests: write)."
return 1
fi
}

# `isCrossRepository` eruit: `--head` matcht op branchnaam, dus een fork-PR met dezelfde naam zou
# hier als onze pin-PR gelden — en dan sluiten of overschrijven we andermans PR.
pin_pr_open_pr() {
local branch=$1

gh pr list --head "$branch" --state open --json number,isCrossRepository \
--jq '[.[] | select(.isCrossRepository | not)] | .[0].number // empty'
}

# Eén commit bovenop main, geen doorgroeiende branch: `switch -C` plus force-push zetten de pin-branch
# elke run opnieuw neer. Wat iemand er zelf op zette gaat daarmee weg — bedoeld, want deze PR hoort
# precies één gewijzigde regel te dragen. De commit is op dat ene bestand begrensd, net als de guard
# die ervoor bepaalt of er iets te committen valt.
pin_pr_publiceer_branch() {
local branch=$1 bestand=$2 bericht=$3

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git switch -C "$branch"
git commit -m "$bericht" -- "$bestand"
git push -f origin "$branch"
}

# Sluiten en de branch opruimen in twee stappen: `gh pr close --delete-branch` wil ook de lokale
# branch weg en die bestaat in dit pad niet.
#
# Een leeg PR-nummer is geen fout maar een normale toestand: de PR kan al gesloten zijn doordat een
# vorige run tussen het sluiten en het verwijderen afbrak. Die run was rood, maar de volgende zou de
# branch nooit meer aanraken als het opruimen aan een ópen PR hing — en dan draagt de eerstvolgende
# bump een branch met de historie van een vorige cyclus.
pin_pr_ruim_op() {
local nummer=$1 branch=$2 reden=$3 status=0

if [ -n "$nummer" ]; then
gh pr close "$nummer" --comment "$reden"
fi

git ls-remote --exit-code --heads origin "$branch" >/dev/null || status=$?

# Alleen 2 betekent "die branch is er niet" — een vorige run die na de close afbrak, of iemand die
# hem met de hand verwijderde. Elke andere code (128 bij een auth- of netwerkfout) zegt dat we het
# niet weten, en dan is stil overslaan het slechtste antwoord: een ingetrokken `Contents: write`
# zou zo elke run de opruiming overslaan terwijl de log meldt dat er opgeruimd is.
case $status in
0) git push origin --delete "$branch" ;;
2) echo "Branch $branch bestond al niet meer." ;;
*)
echo "::error::kon niet vaststellen of $branch nog bestaat (git ls-remote gaf $status)."
return 1
;;
esac
}
156 changes: 156 additions & 0 deletions .github/scripts/pin-pr-teststubs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#!/usr/bin/env bash
# shellcheck shell=bash
#
# Gedeelde harness voor de twee pin-suites: test-fuzz-basis-pin.sh en test-proeftuin-pin-pr.sh.
#
# Beide scripts muteren dezelfde soort gedeelde toestand — een branch, een PR en één regel in één
# bestand — en hebben daardoor dezelfde stubs nodig: een `gh` en een `git` die elke aanroep
# vastleggen, de PR-lijst teruggeven zoals het script hem uitvraagt, en op commando kunnen falen.
# Zonder die faal-schakelaars overleeft elke `|| true` achter een gh-aanroep de suite, en juist die
# maakt een mislukte PR-actie stil.
#
# Bewust NIET `test-*.sh` genoemd: ci-scripts.yml draait elk `test-*.sh` onder deze map als suite en
# eist er een `ASSERTIES=`-regel van. Dit bestand is er geen; het wordt gesourcet.
#
# Contract met de suite die dit sourcet:
# WERKMAP — bestaat al, de suite ruimt hem zelf op
# SCRIPT — het te draaien script
# TESTBRANCH — de branchnaam die aan dat script wordt meegegeven
# DOELBESTAND — het bestand dat het script wijzigt; `nieuw_geval` zet hem per geval
# DOELVAR — de naam waaronder het gemeten script dát bestand leest (DOCKERFILE of COMPOSE);
# `nieuw_geval` exporteert hem mee, zodat de suites hun eigen gevallen
# onveranderd kunnen schrijven

fails=0
geslaagd=0
ok() { geslaagd=$((geslaagd + 1)); echo "OK: $1"; }
fout() { echo "FAIL: $1" >&2; fails=$((fails + 1)); }

pin_pr_stubs_opzetten() {
mkdir -p "$WERKMAP/bin"

# De body gaat óók naar een eigen bestand: hij loopt over meerdere regels en maakt het
# aanroepenlogboek onleesbaar, terwijl de inhoud wél te toetsen moet zijn.
cat > "$WERKMAP/bin/gh" <<'STUB'
#!/usr/bin/env bash
printf 'gh %s\n' "$*" >> "$AANROEPEN"

if [ -n "${BODYS:-}" ]; then
vorige=""

for arg in "$@"; do
[ "$vorige" = "--body" ] && printf '%s' "$arg" >> "$BODYS"
vorige=$arg
done
fi

if [ "${GH_FAALT:-}" = "${1:-} ${2:-}" ]; then
echo "error connecting to api.github.com" >&2
exit 1
fi

if [ "${1:-}" = "pr" ] && [ "${2:-}" = "list" ]; then
# De echte `gh pr list` levert zónder deze twee vlaggen een heel andere verzameling: elke open PR
# in het repo in plaats van die op onze branch, en bij een weggevallen `--state open` ook gesloten
# PR's — waarna het script een gesloten PR zou proberen bij te werken of te sluiten. Een stub die
# de vlaggen negeert, laat dat stilzwijgend slagen.
case " $* " in
*" --head "*) ;;
*) echo "stub: gh pr list zonder --head" >&2; exit 64 ;;
esac

case " $* " in
*" --state open "*) ;;
*) echo "stub: gh pr list zonder --state open" >&2; exit 64 ;;
esac

filter=""
vorige=""

for arg in "$@"; do
[ "$vorige" = "--jq" ] && filter=$arg
vorige=$arg
done

printf '%s' "${PR_LIJST:-[]}" | jq -r "$filter"
fi

exit 0
STUB

cat > "$WERKMAP/bin/git" <<'STUB'
#!/usr/bin/env bash
printf 'git %s\n' "$*" >> "$AANROEPEN"

case "${1:-}" in
# `git diff --quiet -- <bestand>`: 0 als er niets gewijzigd is. De momentopname is de staat van
# vóór de aanroep, dus dit is een getrouwe simulatie en geen aanname.
diff) cmp -s "$MOMENTOPNAME" "$DOELBESTAND" ;;
ls-remote) exit "${LS_REMOTE_CODE:-0}" ;;
push) [ "${GIT_PUSH_FAALT:-0}" = 0 ] ;;
# `git commit` eindigt niet-nul als er niets te committen valt — een reële uitkomst zodra de pin
# tussen twee stappen door al goed blijkt te staan.
commit) [ "${GIT_COMMIT_FAALT:-0}" = 0 ] ;;
*) true ;;
esac
STUB

chmod +x "$WERKMAP/bin/gh" "$WERKMAP/bin/git"
export PATH="$WERKMAP/bin:$PATH"
}

# Zet een verse werkmap klaar voor één geval: eigen doelbestand, momentopname, aanroepenlogboek en
# bodybestand. Het vullen van dat doelbestand en het draaien doet de suite.
nieuw_geval() {
local map="$WERKMAP/$1"

mkdir -p "$map"
export DOELBESTAND="$map/doel"
export MOMENTOPNAME="$map/doel.voor"
export AANROEPEN="$map/aanroepen"
export BODYS="$map/bodys"
: > "$AANROEPEN"
: > "$BODYS"

# Het gemeten script kent dit bestand onder zijn eigen naam; die moet dus mee bewegen met elk
# nieuw geval, anders wijst hij nog naar de map van het vorige.
export "${DOELVAR:?DOELVAR moet gezet zijn vóór het eerste geval}=$DOELBESTAND"
}

vastleggen() { cp "$DOELBESTAND" "$MOMENTOPNAME"; }

# `set +e` omdat een deel van de gevallen juist een niet-nul exitcode verwacht en de suites zelf
# onder `set -e` draaien. De branch expliciet meegeven, zodat de aanroep-asserties niet meeschuiven
# als de default in het script wijzigt.
#
# shellcheck disable=SC2034 # UITVOER en CODE worden gelezen door de suite die dit bestand sourcet
uitvoeren() {
set +e
UITVOER=$(BRANCH="$TESTBRANCH" bash "$SCRIPT" 2>&1)
CODE=$?
set -e
}

bevat() { grep -qF "$2" "$AANROEPEN" && ok "$1" || fout "$1 (aanroepen: $(tr '\n' '|' < "$AANROEPEN"))"; }
bevat_niet() { grep -qF "$2" "$AANROEPEN" && fout "$1 (aanroepen: $(tr '\n' '|' < "$AANROEPEN"))" || ok "$1"; }
body() { grep -qF "$2" "$BODYS" && ok "$1" || fout "$1 (body: $(tr '\n' '|' < "$BODYS"))"; }
gelijk() { [ "$2" = "$3" ] && ok "$1" || fout "$1 (verwacht '$3', kreeg '$2')"; }
niet_nul() { [ "$2" -ne 0 ] && ok "$1" || fout "$1 (exitcode 0, uitvoer: $UITVOER)"; }
meldt() { grep -qF "$2" <<<"$UITVOER" && ok "$1" || fout "$1 (uitvoer: $UITVOER)"; }
regel() { grep -qxF "$2" "$DOELBESTAND" && ok "$1" || fout "$1 (bestand: $(tr '\n' '|' < "$DOELBESTAND"))"; }
regel_niet() { grep -qxF "$2" "$DOELBESTAND" && fout "$1 (bestand: $(tr '\n' '|' < "$DOELBESTAND"))" || ok "$1"; }

# De afsluiting is voor beide suites gelijk: de telling is zelf-gerapporteerd en ci-scripts.yml
# bewaakt hem tegen een ondergrens, dus hij hoort er ook bij een gefaalde run te staan.
pin_pr_uitkomst() {
echo

if [ "$fails" -gt 0 ]; then
echo "$fails test(s) gefaald." >&2
echo "ASSERTIES=$geslaagd"
exit 1
fi

echo "Alle tests geslaagd."
echo "ASSERTIES=$geslaagd"
}
Loading
Loading