Skip to content

ci: uitrol wacht zonder draaiende bouwmachine op de kwaliteitscontroles - #174

Merged
mreuvekamp merged 7 commits into
mainfrom
feature/deploy-gate-zonder-wachtende-runner
Aug 11, 2026
Merged

mreuvekamp merged 7 commits into
mainfrom
feature/deploy-gate-zonder-wachtende-runner

Conversation

@ericwout-overheid

@ericwout-overheid ericwout-overheid commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Important

Deze PR hernoemt vier required status checks. Branch protection op main moet in dezelfde
beweging mee, en de volgorde waarin dat gebeurt bepaalt of de zeven andere open PR's
tussentijds vastlopen. Lees Handmatige stap vóór de merge
vóórdat je mergt. Er is bewust niets aan branch protection gewijzigd.

De uitrol naar de testomgeving wachtte op een eigen bouwmachine tot de kwaliteitscontroles groen
waren: gemiddeld 7 minuten en 41 seconden per wijziging, waarin die machine niets deed dan elke
15 seconden vragen of de controles al klaar waren. Deze PR laat de uitrol daar met een gewone
afhankelijkheid aan hangen, zodat er niets meer staat te wachten. De poort duurt nu 4 seconden
in plaats van 461
— en blokkeert nog steeds, aantoonbaar, een revisie die een verplichte
controle niet haalt.

Closes #173

Wat er verandert

test.yml, detekt.yml, pin-consistency.yml en cflite_pr.yml krijgen een
workflow_call-trigger en worden vanuit deploy.yml als job aangeroepen. Daardoor kan de deploy
er met needs: aan hangen. De gate-job blijft bestaan, maar verliest zijn poll-lus en wordt een
aggregator van needs.*.result — daar staat de "overgeslagen telt als OK"-regel één keer, in
plaats van als always() && …-formule op elk van de zes deploy-jobs.

Dubbele runs worden voorkomen door de PR-trigger van de aangeroepen workflows te beperken tot
branches-ignore: [main], precies het complement van deploy.yml's branches: [main]. Een
gestapelde PR (base ≠ main) draait ze zelfstandig; een PR naar main draait ze hier. cflite_pr.yml
draait voortaan uitsluitend als aangeroepen workflow — fuzzing gold al alleen voor PR's naar main.

De workflow-brede concurrency verhuist naar job-niveau. Met de tests binnen deze workflow zou
cancel-in-progress: false op workflow-niveau een opvolgende push laten wachten tot de vorige
deploy klaar is én de achterhaalde testrun helemaal uit laten draaien — precies de verspilling die
we wegnemen — terwijl true een lopende ZAD-deploy halverwege zou kunnen afbreken. Per job kan het
allebei: build- en toetsjobs breken achterhaald werk af, deploy- en cleanup-jobs krijgen een eigen
groep per project + doel-deployment die nooit afbreekt.

Twee kleinere posten uit dezelfde analyse zijn meegenomen: timeout-minutes op elke job (GitHub's
default is 360 minuten, dus een hangende job kost zes uur runner voordat iemand ingrijpt), en de
permissions: read-all van test.yml/detekt.yml teruggebracht tot wat de jobs echt nodig
hebben — een aangeroepen workflow mag toch niet meer rechten hebben dan de caller-job toekent.

De afweging van de drie richtingen — inclusief waarom workflow_run afvalt — staat in
docs/plans/2026-08-10-deploy-gate-zonder-wachtende-runner.md. Kort: bij workflow_run hangen de
check-runs aan de default branch in plaats van aan de head-SHA van de PR, en zo'n workflow triggert
pas als hij op de default branch staat. De wijziging met het grootste "blokkeert alle merges"-risico
zou dan de enige zijn die je niet vooraf op een PR kunt uitproberen.

⚠️ Handmatige stap vóór de merge

Branch protection op main pint required contexts op check-run-namen. Door de aanroep krijgen die
het caller-job-voorvoegsel. Zet ze om nadat deze PR groen is en vóór de merge:

oud nieuw
test checks-test / test
detekt-gate checks-detekt / detekt-gate
infra-image-pins checks-pins / infra-image-pins
PR checks-fuzz / PR

Ongewijzigd: Analyze (kotlin), deploy-preview-uitvraag, deploy-preview-externe-stubs,
deploy-preview-magazijnen, strict: true.

Tot die omzetting is deze PR niet merge-baar: de oude contexts verschijnen niet meer. Dat de
nieuwe contexts wél al op deze PR staan, is precies het voordeel van deze richting boven
workflow_run.

De volgorde is de hele afweging

De nieuwe namen bestaan alléén op branches die deze wijziging al bevatten. De zeven andere open
PR's (#172, #170, #168, #166, #163, #150, #147) draaien nog de workflows van main en produceren
dus nog de oude namen. Er is geen moment waarop beide sets tegelijk bestaan, dus wie de knop ook
omzet: één van de twee groepen is even geblokkeerd. Drie routes, met hun prijs:

route gevolg
Contexts omzetten vóór de merge (aanbevolen) Deze PR wordt merge-baar. De zeven andere PR's leveren dan de oude namen die niet meer verplicht zijn, en de nieuwe namen die ze niet kunnen leveren — dus geblokkeerd tot ze rebasen op de gemergede main. Houd het venster kort: zet om, merge direct, meld het in het kanaal.
Mergen met admin-override, daarna omzetten Kortste blokkade voor de andere PR's, maar deze PR gaat in zonder dat de required checks formeel groen stonden — precies de bewaking die we hier repareren, één keer overgeslagen. Alleen doen als de andere PR's echt niet kunnen wachten.
Niets doen Deze PR blijft hangen. Geen optie op termijn: de wijziging veroudert en de 392 min/week blijft lopen.

strict: true staat aan, dus de andere PR's moeten na de merge sowieso rebasen. Het extra
ongemak van route 1 is dus vooral timing, niet extra werk: de rebase moest toch gebeuren.

Er is bewust niets aan branch protection gewijzigd vanuit deze PR — het is een handmatige stap
buiten de repo, en wie hem zet moet weten wat er in het venster ertussen gebeurt.

Meting

Reproduceerbaar met:

# alle deploy-runs sinds <datum>
gh run list --repo MinBZK/moza-poc-fbs-berichtenbox --workflow deploy.yml --limit 200 \
  --json databaseId,createdAt --jq '[.[] | select(.createdAt > "<datum>")] | .[].databaseId'

# per run de looptijd per job
gh api "repos/MinBZK/moza-poc-fbs-berichtenbox/actions/runs/<id>/jobs" \
  --jq '.jobs[] | select(.name=="gate") | "\(.conclusion) \(.started_at) -> \(.completed_at)"'

Vóór (3–10 augustus 2026)

job workflow n totaal gemiddeld max
gate deploy.yml 51 23.522 s (392 min) 461 s 910 s
test test.yml 50 21.001 s 420 s 504 s
PR (fuzz) cflite_pr.yml 17 8.942 s 526 s 663 s
detekt-gate detekt.yml 53 2.840 s 54 s 79 s
infra-image-pins pin-consistency.yml 55 304 s 6 s 31 s
build (matrix ×2) deploy.yml 104 8.572 s 83 s 271 s

Twee dingen die de richtingskeuze bepaalden: de fuzz-check is de langste pool (526 s), niet
test; en build duurt maar 83 s, dus "gate pas na de build starten" (richting 3) zou hooguit
18% schelen.

Ná (deze PR)

vóór
gate runnertijd 461 s gemiddeld (n=51) 3,4 s gemiddeld, spreiding 2–4 s (n=5)
API-verzoeken door de gate ~120 per run, ~6.000 per week 0
Geprojecteerd per week 392 min wachtende runner ~3 min

Ter controle draaide in hetzelfde tijdvak een run van een andere branch
(31389684161) nog
op de oude, gepollde gate uit main: 526 s. Zelfde infrastructuur, zelfde moment.

Doorlooptijd niet verslechterd. Van run-start tot de eerste preview-deploy: 475 s en 655 s ná,
tegenover gemiddeld 735 s (spreiding 434–936 s, n=38) ervóór. Die doorlooptijd wordt bepaald door
de traagste check (fuzz 526 s, test 420 s), niet door de poort — de poort zat er alleen naast te
wachten. Met n=2 is dit "binnen de bestaande spreiding", geen aantoonbare versnelling.

Verificatie

1. De poort werkt nog — wegwerp-PR #175 forceerde detekt-bevindingen. Run
31387899696:

checks-detekt / detekt-gate   failure
gate                          failure (3 s)
  Verplichte check 'test' geslaagd.
  ##[error]Verplichte check 'detekt-gate' eindigde als 'failure' — deploy geblokkeerd.
  Verplichte check 'infra-image-pins' geslaagd.
deploy-preview-uitvraag       skipped
deploy-preview-externe-stubs  skipped
deploy-preview-magazijnen     skipped

2. De merge-route werkt nog — alle vier nieuwe contexts verschijnen op de head-SHA van deze PR,
naast de ongewijzigde deploy-preview-* en Analyze (kotlin). Voor de docs-only route draaide op
#175 een run waarin elke changes-job run=false rapporteerde
(31388993368): alle
vier de contexts verschenen als skipped, alle deploy-jobs sloegen over, run groen — merge-bare
staat.

3. Geen dubbele runs — op de branch van deze PR draaiden alleen Deploy ZAD en CodeQL:

$ gh run list --branch feature/deploy-gate-zonder-wachtende-runner --json workflowName,event
CodeQL      pull_request  completed/success
Deploy ZAD  pull_request  completed/success

Test, detekt, Pin consistency en ClusterFuzzLite PR fuzzing startten niet zelfstandig.

4. Gestapelde PR's blijven getoetst zonder te deployen — wegwerp-PR #177 met base = deze
feature-branch draaide Test (job test, 444 s, geslaagd), detekt en Pin consistency
zelfstandig, met de onveranderde check-namen. Deploy ZAD en ClusterFuzzLite PR fuzzing
startten niet. Het complement-filter dekt dus precies één keer, aan beide kanten.

5. PR-close doet geen toetswerk meer — de close van #175
(31390338566) sloeg
alle vier de checks-*-jobs en de gate over en draaide alleen de drie cleanup-jobs.

6. De rechten kloppen door de aanroep heen — de JaCoCo-coverage-comment
(pull-requests: write via de caller-job), de ZAD-preview-comment en de detekt-SARIF-upload
(security-events: write) werkten alle drie op deze PR.

Kanttekening: bij de eerste run faalde deploy-preview-magazijnen op timed out waiting for application to be created. Dat is de bekende Argo-Application-wait aan ZAD-zijde die in de kop van
deploy.yml staat beschreven; de tweede run deployde hetzelfde project zonder wijziging succesvol.

Samenloop met #172

#172 landde op main terwijl deze PR openstond en raakt dezelfde jobs. Bij het samenvoegen (merge
03f5043) drie conflicten in deploy.yml, alle drie opgelost richting "beide behouden":

De per-commit-tag werkt na de merge: run
31473630425 pushte
fbs-berichtenuitvraag:pr-174-03f5043, gelijk aan de head-sha. gate deed er 3 s over; alle drie
de previews rolden uit.

Eén nieuw randgeval, eerlijk gemeld: een close-run wacht niet meer op een nog lopende build
van een eerdere push (venster ≈ 85 s), dus cleanup-preview-images kan die net mislopen en één
ghcr-versie laten staan. Geen kapotte deploy; herdraaien ruimt hem op. Staat als bekende beperking
in deploy.yml en met denkrichting in MinBZK/MijnOverheidZakelijk#934.

Eén post uit MinBZK/MijnOverheidZakelijk#934 is door #172 al opgelost: build hangt daar nu zelf aan changes.

Wat blijft staan

Vier kleinere posten uit dezelfde analyse staan met meetgegevens in MinBZK/MijnOverheidZakelijk#934 — waaronder de vier
changes-jobs die dezelfde vraag stellen, en de preview-uitrol voor PR's die de applicatie niet
raken (de enige post met echt clusterverbruik). Elk is bewust doorgeschoven; de afwegingen staan
in het plan.

De `gate`-job hield per deploy-run gemiddeld 461 s een runner bezet met
`sleep 15`, omdat de verplichte checks in aparte workflows draaien en er
dus geen `needs:` aan te hangen viel. Over 3-10 augustus 2026: 51 runs,
23.522 s (6 u 32 min) wachtende runner en ~6.000 API-verzoeken.

test.yml, detekt.yml, pin-consistency.yml en cflite_pr.yml krijgen een
`workflow_call`-trigger en worden vanuit deploy.yml aangeroepen. De gate
blijft bestaan als aggregator van `needs.*.result` — daar staat de
"overgeslagen telt als OK"-regel één keer, in plaats van als
`always() && ...`-formule op elk van de zes deploy-jobs.

Dubbele runs worden voorkomen door de PR-trigger van de aangeroepen
workflows te beperken tot `branches-ignore: [main]`, het complement van
deploy.yml's `branches: [main]`: een gestapelde PR draait ze zelfstandig,
een PR naar main draait ze hier. cflite_pr.yml draait voortaan alleen als
aangeroepen workflow — fuzzing gold al uitsluitend voor PR's naar main.

De workflow-brede concurrency verhuist naar job-niveau. Met de tests in
deze workflow zou `cancel-in-progress: false` op workflow-niveau een
opvolgende push laten wachten op de vorige deploy en de achterhaalde
testrun helemaal uit laten draaien; `true` zou een lopende ZAD-deploy
halverwege kunnen afbreken. Per job: build/toetsing breken af, deploy en
cleanup krijgen een eigen groep per project en doel-deployment.

Branch protection op main moet mee: `test` -> `checks-test / test`,
`detekt-gate` -> `checks-detekt / detekt-gate`, `infra-image-pins` ->
`checks-pins / infra-image-pins`, `PR` -> `checks-fuzz / PR`.

Refs #173

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

JaCoCo coverage

Overall Project 91.59% 🍏

There is no coverage information present for the Files changed

…kost

GitHub's default job-timeout is 360 minuten. Een job die hangt houdt zo
lang een runner bezet voordat iemand ingrijpt. De waarden staan op ~3x de
waargenomen maximumduur.

Refs #173

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Refs #173, #176

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ericwout-overheid and others added 3 commits August 10, 2026 13:05
…voegd

Refs #173

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Refs #173

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Refs #173

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ericwout-overheid
ericwout-overheid marked this pull request as ready for review August 10, 2026 14:16
mreuvekamp
mreuvekamp previously approved these changes Aug 11, 2026
…onder-wachtende-runner

# Conflicts:
#	.github/workflows/deploy.yml
@mreuvekamp
mreuvekamp merged commit 36c246f into main Aug 11, 2026
26 checks passed
@mreuvekamp
mreuvekamp deleted the feature/deploy-gate-zonder-wachtende-runner branch August 11, 2026 08:54
ericwout-overheid pushed a commit that referenced this pull request Aug 12, 2026
De impliciete success() op deploy-test-uitvraag/-externe-stubs/-magazijnen
kijkt niet alleen naar hun vier directe needs, maar transitief door tot
checks-fuzz — die bij elke push naar main 'skipped' is (PR-only check).
Sinds #174 werden de drie test-deploys daardoor bij elke push stilzwijgend
overgeslagen, ook al waren meta/build/build-externe-stubs/gate stuk voor
stuk 'success'. gate's eigen !cancelled()-fix (#174) beschermt alleen gate
zelf, niet de jobs die ervan afhangen.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Uitrol wacht met een draaiende bouwmachine op de kwaliteitscontroles

2 participants