diff --git a/.editorconfig b/.editorconfig index 561cd5e..7c42757 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,12 +1,35 @@ +# EditorConfig houdt de opmaak gelijk tussen editors en IDE's. +# VS Code, JetBrains en anderen lezen dit bestand vanzelf. +# Meer info: https://editorconfig.org +# +# Deze versie is organisatiebreed gelijk. Wijk hier niet per repository van af: +# vijf licht verschillende varianten leverden alleen ruis op, geen voordeel. +# +# De configbestanden die zelf geen comments kunnen dragen omdat het JSON is, +# staan hier genoemd zodat er ergens een aanwijzing is wat ze doen: +# +# .htmlhintrc - HTML-linting (HTMLHint). Structuur- en toegankelijkheidsregels. +# renovate.json - Renovate-bot. Automatische dependency-updates via pull requests. + root = true [*] charset = utf-8 end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true indent_style = space indent_size = 2 -trim_trailing_whitespace = true -insert_final_newline = true [*.md] +# Trailing whitespace betekent iets in Markdown (regeleinde) trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[*.py] +indent_size = 4 + +[*.sh] +indent_size = 4 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..45ff4d7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,24 @@ +# .gitattributes zorgt voor consistente regeleindes tussen Windows, Mac en Linux. +# Zonder dit kunnen regeleindes per ontwikkelaar of OS verschillen, wat leidt +# tot onnodige git-diffs en merge-conflicten. +# +# Deze versie is organisatiebreed gelijk. Wijk hier niet per repository van af: +# vier licht verschillende varianten leverden alleen ruis op, geen voordeel. + +# Standaard: forceer LF voor alle tekstbestanden +* text=auto eol=lf + +# Binaire bestanden: geen regeleindeconversie +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.webp binary +*.avif binary +*.pdf binary +*.woff binary +*.woff2 binary +*.ttf binary +*.otf binary +*.zip binary diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..867b553 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +# Beide eigenaren worden om review gevraagd, maar de goedkeuring van één van +# hen is genoeg om de merge vrij te geven. Zo ligt het werk niet stil als de +# ander er even niet is. +* @Stensel8 @AdiH1310 diff --git a/.github/actions/setup-hugo/action.yml b/.github/actions/setup-hugo/action.yml index f52d0bf..55b8a9a 100644 --- a/.github/actions/setup-hugo/action.yml +++ b/.github/actions/setup-hugo/action.yml @@ -39,12 +39,12 @@ inputs: runs: using: composite steps: - # The release comes off the network, so nothing is executed before the - # checksum says it is the archive we pinned. + # De release komt van het netwerk, dus er wordt niets uitgevoerd voordat de + # checksum zegt dat het het archief is dat we hebben vastgezet. # - # --retry: the release CDN hands out an occasional 503, and without this a - # single one fails the whole build. curl retries 5xx and timeouts on its - # own; --retry-all-errors extends that to connection failures. + # --retry: de release-CDN geeft af en toe een 503, en zonder dit laat één + # zo'n antwoord de hele build falen. curl herhaalt 5xx en timeouts uit + # zichzelf; --retry-all-errors trekt dat door naar verbindingsfouten. - name: Download and verify Hugo shell: bash env: diff --git a/.github/htmlhint/package-lock.json b/.github/htmlhint/package-lock.json index 1fded9f..6de4af9 100644 --- a/.github/htmlhint/package-lock.json +++ b/.github/htmlhint/package-lock.json @@ -1,11 +1,11 @@ { - "name": "hugo-template-htmlhint", + "name": "thectic-htmlhint", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "hugo-template-htmlhint", + "name": "thectic-htmlhint", "version": "1.0.0", "license": "ISC", "dependencies": { diff --git a/.github/htmlhint/package.json b/.github/htmlhint/package.json index c5162c3..1569334 100644 --- a/.github/htmlhint/package.json +++ b/.github/htmlhint/package.json @@ -1,5 +1,5 @@ { - "name": "hugo-template-htmlhint", + "name": "thectic-htmlhint", "private": true, "description": "Pinned HTMLHint install for .github/workflows/quality.yml. Not part of the site.", "dependencies": { diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..52adcfc --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,28 @@ +## Samenvatting + + + +## Type wijziging + + + +- [ ] `feat` — nieuwe pagina, sectie of functionaliteit +- [ ] `fix` — bugfix: kapotte link, verkeerde configuratie, renderfout +- [ ] `content` — bestaande inhoud bijwerken of verbeteren +- [ ] `docs` — README, CONTRIBUTING of andere metabestanden +- [ ] `chore` — onderhoud: dependencies, CI/CD, configuratie +- [ ] `refactor` — herstructurering zonder gedragsverandering +- [ ] `style` — opmaak, witruimte, typefouten +- [ ] `revert` — een eerdere commit terugdraaien + +## Checklist + +- [ ] PR-titel volgt de commit-conventie (`type: korte omschrijving`) +- [ ] Geen secrets, tokens of persoonsgegevens in de diff +- [ ] `hugo server` lokaal gedraaid vanuit `src/` en de pagina bekeken +- [ ] Licht én donker thema gecontroleerd, en op een smal scherm +- [ ] Geen gebroken links of verwijzingen naar bestanden die niet bestaan +- [ ] Versiebump van Hugo, actionlint of lychee? De bijbehorende SHA-256 ook bijgewerkt diff --git a/.github/workflows/config-validation.yml b/.github/workflows/config-validation.yml index 9615c5f..cab605e 100644 --- a/.github/workflows/config-validation.yml +++ b/.github/workflows/config-validation.yml @@ -2,9 +2,9 @@ # SPDX-License-Identifier: MIT name: Config validation -# The bot configs are the one part of CI that nothing else exercises: a broken -# renovate.json or dependabot.yml does not fail a build, it just quietly stops -# doing its job. This workflow is the thing that notices. +# De bot-configs zijn het enige deel van CI dat verder nergens door wordt +# geraakt: een kapotte renovate.json of dependabot.yml laat geen build falen, +# die houdt gewoon stilletjes op met zijn werk. Deze workflow merkt dat op. on: push: @@ -46,33 +46,33 @@ jobs: with: node-version: 'lts/*' - # Renovate's own validator. --strict also fails on warnings, such as an - # option that is valid but deprecated. Given no arguments it finds the - # config files itself and validates them as repository config; passing a - # path instead makes it validate them as global config, which is a - # different and weaker set of rules. + # Renovates eigen validator. --strict laat hem ook falen op warnings, + # bijvoorbeeld een optie die geldig maar verouderd is. Zonder argumenten + # zoekt hij de configbestanden zelf op en valideert hij ze als + # repository-config; geef je een pad mee, dan valideert hij ze als + # global config, en dat is een andere en zwakkere set regels. # - # Deliberately unpinned. This is a linter for our config, not something we - # ship, and the newest release is the one that knows about the newest - # deprecations. Its own version is not worth a pull request. + # Bewust niet vastgezet. Dit is een linter op onze eigen config en geen + # onderdeel van wat we uitleveren, en juist de nieuwste release kent de + # nieuwste deprecations. Zijn eigen versie is geen pull request waard. # - # NPM_CONFIG_LOGLEVEL: npm prints "npm warn deprecated ..." for packages - # deep inside Renovate's own dependency tree. Those say nothing about the - # config being validated, and reading them as if they did is the obvious - # mistake to make when they appear directly above the validator's output. + # NPM_CONFIG_LOGLEVEL: npm print "npm warn deprecated ..." voor packages + # diep in Renovates eigen dependency-boom. Die zeggen niets over de config + # die gevalideerd wordt, en ze lezen alsof dat wel zo is, is de fout die + # je vanzelf maakt als ze vlak boven de output van de validator staan. - name: Validate Renovate config env: NPM_CONFIG_LOGLEVEL: error run: npx --yes --package renovate -- renovate-config-validator --strict - # The validator above accepts a well-formed pattern that matches nothing, - # so this covers the gap it leaves. + # De validator hierboven accepteert een correct gevormd patroon dat + # nergens op matcht; dit dekt het gat dat hij daarmee laat. - name: Check Renovate file patterns run: python3 .github/scripts/check-renovate-patterns.py renovate.json .github/renovate.json - # GitHub validates dependabot.yml only after it is on the default branch, - # and reports the result on a tab nobody opens. This brings that forward - # to the pull request. + # GitHub valideert dependabot.yml pas als die op de default branch staat, + # en meldt het resultaat op een tabblad dat niemand opent. Dit haalt dat + # naar voren, naar de pull request. - name: Validate Dependabot config env: # renovate: datasource=pypi depName=check-jsonschema diff --git a/.github/workflows/deploy-bunny.yml b/.github/workflows/deploy-bunny.yml index ddd4f1b..2820f07 100644 --- a/.github/workflows/deploy-bunny.yml +++ b/.github/workflows/deploy-bunny.yml @@ -32,7 +32,7 @@ concurrency: group: deploy cancel-in-progress: true -# No token needed; jobs that do opt in explicitly. +# Geen token nodig; jobs die dat wel zijn, vragen er expliciet om. permissions: {} jobs: diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000..95ffd5f --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,109 @@ +# Copyright (C) 2026 Sten Tijhuis +# SPDX-License-Identifier: MIT +name: PR title + +# Twee controles op de pull request zelf, bewust in één job. +# +# GitHub rekent per job en rondt elke job naar boven af op een hele minuut. +# Beide stappen hieronder zijn in een paar seconden klaar en hebben geen +# checkout nodig, dus als losse jobs kosten ze twee volle minuten voor werk +# van tien seconden. In één job is dat één minuut. +# +# Dat de job `pull-requests: write` heeft terwijl de titelcontrole aan lezen +# genoeg heeft, is de prijs van die samenvoeging. Beide actions staan op een +# vastgezette SHA, dus dat is hier een acceptabele ruil. +# +# De titel en niet de losse commits: de titel is wat er op main terechtkomt +# zodra je squasht. Renovate levert zijn eigen titels al in dit formaat aan; +# dat is de semanticCommits-instelling in renovate.json. + +on: + pull_request: + # edited hoort erbij: zonder dat blijft de check rood staan nadat iemand de + # titel heeft verbeterd, want een titelwijziging is geen nieuwe push. + types: [opened, edited, synchronize, reopened] + +# Snel achter elkaar de omschrijving aanpassen startte evenveel runs. Alleen de +# laatste zegt nog iets, dus de rest mag weg. +concurrency: + group: pr-title-${{ github.event.pull_request.number }} + cancel-in-progress: true + +permissions: {} + +jobs: + pr-title: + name: Conventional commit title + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + content + docs + chore + refactor + style + revert + + # Vinkt de twee punten af die uit de pull request zelf af te leiden zijn, + # en ruimt de niet-gekozen regels onder "Type wijziging" op. De rest van + # de checklist blijft handwerk: of iemand de pagina echt in de browser + # heeft bekeken, valt hier niet te controleren. + # + # Draait op !cancelled(), zodat een afgekeurde titel het opruimen niet + # tegenhoudt -- juist dan wil je de checklist bijgewerkt zien. + - name: Update PR checklist + if: ${{ !cancelled() }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + }); + + let body = pr.body || ''; + if (!body.trim()) return; + + const setCheck = (keyword, passed) => { + body = body.replace( + new RegExp(`- \\[[ xX]\\] (.*${keyword}.*)`, 'i'), + `- [${passed ? 'x' : ' '}] $1` + ); + }; + + // Dezelfde typelijst als hierboven en als CONTRIBUTING.md. Scope en + // een `!` voor een breaking change zijn toegestaan: feat(nav)!: ... + const TITLE_RE = + /^(feat|fix|content|docs|chore|refactor|style|revert)(\([^)]+\))?!?: .+/; + setCheck('PR-titel volgt', TITLE_RE.test(pr.title)); + + // De niet-gekozen types weghalen, maar alleen als er al een gekozen + // is. Zonder die voorwaarde stript de eerste run alle acht regels + // weg voordat de auteur er een heeft aangevinkt, en is de sectie + // weg voordat hij gebruikt kon worden. + const TYPE_LINE = /^- \[([ xX])\] `\w+` —[^\n]*\n?/gm; + const ticked = [...body.matchAll(TYPE_LINE)] + .some(m => m[1].toLowerCase() === 'x'); + if (ticked) { + body = body.replace(/^- \[ \] `\w+` —[^\n]*\n?/gm, ''); + } + + body = body.replace(/\n{3,}/g, '\n\n'); + + if (body !== pr.body) { + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + body, + }); + } diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 290a8f3..0c86b77 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -8,28 +8,28 @@ on: pull_request: branches: [main] -# No token needed; jobs that do opt in explicitly. +# Geen token nodig; jobs die dat wel zijn, vragen er expliciet om. permissions: {} -# Pushing three times in a row to the same PR started three full runs, and the -# first two are already stale by then. Not on main: there the run is the record -# that this commit passed the checks. +# Drie keer achter elkaar naar dezelfde PR pushen startte drie volledige runs, +# en de eerste twee zijn op dat moment al achterhaald. Op main niet annuleren: +# daar is de run de vastlegging dat die commit door de controles kwam. concurrency: group: quality-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: - # Everything that needs the built site, in one job. + # Alles wat de gebouwde site nodig heeft, in één job. # - # This used to be three separate jobs: a build, HTMLHint on a second build, - # and lychee on an artifact of the first. That cost three runners and three - # Hugo setups, and GitHub rounds every job up to a whole minute -- three - # minutes billed for work that finishes in one. + # Dit waren drie losse jobs: een build, HTMLHint op een tweede build, en + # lychee op een artifact van de eerste. Dat kostte drie runners en drie keer + # Hugo opzetten, en GitHub rondt elke job naar boven af op een hele minuut -- + # drie minuten in rekening voor werk dat in één minuut klaar is. # - # In one job it saves more than those two minutes: the second build does not - # have to process the images again, because resources/_gen is already warm - # from the first. The artifact between build and link-check disappears with - # it, and with that the upload, the download and the storage. + # In één job levert het meer op dan die twee minuten: de tweede build hoeft + # de afbeeldingen niet opnieuw te verwerken, omdat resources/_gen er al staat + # van de eerste. Het artifact tussen build en linkcheck verdwijnt ermee, en + # daarmee ook de upload, de download en de opslag. site: name: Build and check the site runs-on: ubuntu-latest @@ -44,18 +44,19 @@ jobs: - name: Set up Hugo uses: ./.github/actions/setup-hugo - # A build that fails here is a broken deploy caught early. --panicOnWarning - # is stricter than the deploy build on purpose: a fresh Hugo deprecation - # should block a merge, not a release that is already on its way out. + # Een build die hier faalt is een kapotte deploy die op tijd gepakt is. + # --panicOnWarning is bewust strenger dan de deploy-build: een nieuwe + # Hugo-deprecation hoort een merge te blokkeren, niet een release die al + # onderweg is. - name: Build site working-directory: src env: TZ: Europe/Amsterdam run: hugo --minify --gc --printPathWarnings --panicOnWarning - # No --minify here: minifying drops the quotes around attribute values - # wherever it can, which is exactly what attr-value-double-quotes flags. - # It goes to its own directory so lychee below keeps the real output. + # Hier geen --minify: minifyen haalt de quotes rond attribuutwaarden weg + # waar dat kan, en dat is precies waar attr-value-double-quotes op slaat. + # Gaat naar een eigen map, zodat lychee hieronder de echte output houdt. - name: Build site unminified for HTMLHint working-directory: src run: hugo --gc --destination public-lint @@ -65,37 +66,38 @@ jobs: with: node-version: 'lts/*' - # `npm ci` against a checked-in package-lock.json (.github/htmlhint/), - # rather than `npm install -g htmlhint@version`: the latter resolves - # through the registry at run time with nothing to verify what comes - # back against, which is what Scorecard's Pinned-Dependencies check and - # zizmor's adhoc-packages audit both flag it for. `npm ci` installs - # exactly what the lockfile's integrity hashes say, and bumping the - # version is a normal Renovate PR against that lockfile. + # `npm ci` tegen een ingecheckte package-lock.json (.github/htmlhint/) in + # plaats van `npm install -g htmlhint@versie`: die tweede haalt op runtime + # iets uit de registry zonder dat er iets is om het resultaat tegen te + # verifiëren, en daar slaan zowel Scorecards Pinned-Dependencies als + # zizmors adhoc-packages op aan. `npm ci` installeert exact wat de + # integrity-hashes in de lockfile zeggen, en een versiebump is gewoon een + # Renovate-PR op die lockfile. - name: Install HTMLHint run: npm ci --prefix .github/htmlhint - # HTMLHint: structural errors, missing attributes, accessibility. Runs on - # the built output, not on the templates: a Hugo layout is not a - # standalone HTML document, so linting one only produces noise. + # HTMLHint: structuurfouten, ontbrekende attributen, toegankelijkheid. + # Draait op de gebouwde output en niet op de templates: een Hugo-layout is + # geen zelfstandig HTML-document, dus daarop linten geeft alleen ruis. - name: Check HTML run: .github/htmlhint/node_modules/.bin/htmlhint "src/public-lint/**/*.html" - # Installed by hand rather than through lycheeverse/lychee-action, which - # fetches its binary with a bare `curl -sfLO`: no retry, and no check on - # what comes back. + # Met de hand geïnstalleerd in plaats van via lycheeverse/lychee-action, + # dat zijn binary met een kale `curl -sfLO` ophaalt: geen retry, en geen + # controle op wat er terugkomt. # - # !cancelled() rather than success(): a broken HTMLHint rule should not - # hide the link check. You want both failures in one run, not the second - # only after you have fixed the first. + # !cancelled() en niet success(): een kapotte HTMLHint-regel hoort de + # linkcheck niet te verbergen. Je wilt beide fouten in één run zien, niet + # de tweede pas nadat je de eerste hebt opgelost. - name: Install lychee if: ${{ !cancelled() }} env: - # extractVersion: lychee tags its releases "lychee-v0.24.2", not - # "v0.24.2", so the default pattern cannot read the version out. + # extractVersion: lychee tagt zijn releases als "lychee-v0.24.2" en + # niet als "v0.24.2", dus het standaardpatroon leest de versie er niet + # uit. # renovate: datasource=github-releases depName=lycheeverse/lychee extractVersion=^lychee-v(?.+)$ LYCHEE_VERSION: "0.24.2" - # From the release's own lychee-x86_64-unknown-linux-gnu.tar.gz.sha256 + # Uit de lychee-x86_64-unknown-linux-gnu.tar.gz.sha256 van de release zelf LYCHEE_SHA256: "1f4e0ef7f6554a6ed33dd7ac144fb2e1bbed98598e7af973042fc5cd43951c9a" run: | curl -sSL --fail-with-body -o lychee.tar.gz \ @@ -105,35 +107,33 @@ jobs: tar -xzf lychee.tar.gz lychee-x86_64-unknown-linux-gnu/lychee sudo install -m 0755 lychee-x86_64-unknown-linux-gnu/lychee /usr/local/bin/lychee - # lychee in offline mode: every internal href and src must resolve to a - # file the build actually produced. + # lychee in offline modus: elke interne href en src moet uitkomen op een + # bestand dat de build daadwerkelijk heeft opgeleverd. # - # --index-files: Hugo serves every page as /index.html, and without - # this lychee stops at the directory. That makes a link to /about/ pass as - # long as the directory exists, even if it is empty, and makes #fragments - # into another page impossible to resolve. + # --index-files: Hugo serveert elke pagina als /index.html, en + # zonder dit stopt lychee bij de map. Daardoor slaagt een link naar + # /about/ zolang die map bestaat, ook als hij leeg is, en zijn #fragments + # naar een andere pagina niet te controleren. # - # The glob is quoted deliberately: unquoted, bash expands it first, and - # without globstar ** collapses to a single directory level. + # De glob staat bewust tussen quotes: zonder quotes vult bash hem eerst + # in, en zonder globstar valt ** terug op één mapniveau. - name: Check internal links if: ${{ !cancelled() }} run: | lychee --offline --include-fragments --index-files index.html \ --root-dir "${GITHUB_WORKSPACE}/src/public" "src/public/**/*.html" - # Everything that has nothing to do with the built site: the Markdown and the - # workflow files. None of it needs Hugo. + # Alles wat niets met de gebouwde site te maken heeft: de Markdown en de + # workflowbestanden. Daar is Hugo nergens voor nodig. # - # Every step runs on !cancelled(), so one red linter does not hide the others. - # The job still fails as soon as anything is wrong. + # Elke stap draait op !cancelled(), zodat één rode linter de andere niet + # verbergt. De job faalt alsnog zodra er iets fout is. repo: name: Check the repository runs-on: ubuntu-latest permissions: contents: read - # For zizmor's SARIF upload below. That step skips itself while the - # repository is private; the permission is here so it starts working by - # itself the day that changes. + # Voor de SARIF-upload van zizmor hieronder. security-events: write steps: - name: Check out source code @@ -141,7 +141,7 @@ jobs: with: persist-credentials: false - # markdownlint: keeps content/ and the repo's own Markdown consistent. + # markdownlint: houdt content/ en de Markdown van de repo zelf consistent. - name: Run markdownlint if: ${{ !cancelled() }} uses: DavidAnson/markdownlint-cli2-action@21c1be1b93ad9ed58fa840aacc3f279cde2a72ff # v24.2.0 @@ -150,8 +150,8 @@ jobs: src/content/**/*.md *.md - # Pinned release plus checksum, rather than piping a script from a branch - # straight into bash. + # Vastgezette release plus checksum, in plaats van een script vanaf een + # branch rechtstreeks door bash halen. - name: Install actionlint if: ${{ !cancelled() }} env: @@ -166,17 +166,17 @@ jobs: tar -xzf actionlint.tar.gz actionlint sudo install -m 0755 actionlint /usr/local/bin/actionlint - # actionlint: syntax errors and incorrect expressions in the workflows. + # actionlint: syntaxfouten en onjuiste expressies in de workflows. - name: Run actionlint if: ${{ !cancelled() }} run: actionlint -color - # zizmor used to be its own job in security.yml. It is a linter on the - # same files as actionlint, only on security rather than syntax, so it - # belongs next to it instead of on a runner of its own. + # zizmor stond als eigen job in security.yml. Het is een linter op + # dezelfde bestanden als actionlint, alleen op beveiliging in plaats van + # syntax, dus hij hoort hiernaast en niet op een eigen runner. # - # pipx and not pip: the runner's system Python is externally managed - # (PEP 668), so a plain pip install aborts. + # pipx en niet pip: de systeem-Python van de runner is externally managed + # (PEP 668), dus een gewone pip install breekt af. - name: Install zizmor if: ${{ !cancelled() }} env: @@ -188,17 +188,12 @@ jobs: if: ${{ !cancelled() }} run: zizmor --format sarif . > zizmor.sarif || true - # Results show up on the repository's Security tab. - # - # Skipped while the repository is private: a code scanning upload needs - # GitHub Advanced Security there. continue-on-error kept the job green but - # not the step quiet: it logged "Resource not accessible by integration" - # as ##[error] on every run -- an error inside a green job, which is worse - # than either on its own. The condition drops the step, and gives it back - # by itself the day this repository goes public or gets Advanced Security. + # De resultaten verschijnen op het Security-tabblad van de repository. + # Advanced Security staat organisatiebreed aan, dus dit werkt ook op een + # privérepository. - name: Upload zizmor results to GitHub Security - uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4 - if: ${{ !cancelled() && !github.event.repository.private }} + uses: github/codeql-action/upload-sarif@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 + if: ${{ !cancelled() }} continue-on-error: true with: sarif_file: zizmor.sarif diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index b16d989..429a127 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -7,33 +7,40 @@ on: branches: [main] pull_request: branches: [main] - # Weekly scan to catch new vulnerabilities in existing code + # Wekelijkse scan, om nieuwe kwetsbaarheden in bestaande code te vinden schedule: - cron: '0 5 * * 1' - # So Scorecard can be run on demand; it no longer runs on pull requests + # Zodat Scorecard op verzoek te draaien is; hij loopt niet meer op PR's workflow_dispatch: +# Geen token nodig; jobs die dat wel zijn, vragen er expliciet om. permissions: {} -# Pushing three times in a row to the same PR started three full scans, and the -# first two are already stale by then. Not on main: there the run is the record -# that this commit was scanned. +# Drie keer achter elkaar naar dezelfde PR pushen startte drie volledige scans, +# en de eerste twee zijn op dat moment al achterhaald. Op main niet annuleren: +# daar is de run de vastlegging dat die commit gescand is. concurrency: group: security-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: - # Semgrep: SAST scanner for XSS and other vulnerabilities in the templates - # and the little JavaScript the site ships + # Semgrep: SAST-scanner voor XSS en andere kwetsbaarheden in de templates en + # het beetje JavaScript dat de site meelevert. + # + # De enige job hier die op een pull request draait. Scorecard hieronder niet: + # die beoordeelt de repository en niet de commit, dus per PR draaien leverde + # een runner op voor een uitkomst die toch hetzelfde was. zizmor is een stap + # in de repo-job van quality.yml geworden, om diezelfde reden: die job stond + # er al, met dezelfde checkout, dus daar kost hij geen minuut extra. semgrep: name: Semgrep SAST scan runs-on: ubuntu-latest permissions: contents: read container: - image: semgrep/semgrep@sha256:bdf7013b2c3634a487671158da77c554f531742326b543a9464d2adf6c433ac8 - # Skip Renovate PRs: no token available in that context + image: semgrep/semgrep@sha256:67319956da3dcb58baf5b322899c15458e3963e7018a86aeeb5cd224e69cb77a + # Renovate-PR's overslaan: daar is geen token beschikbaar if: github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]' steps: - name: Check out source code @@ -41,19 +48,17 @@ jobs: with: persist-credentials: false - # Exclusions live in .semgrepignore + # Uitzonderingen staan in .semgrepignore - name: Run Semgrep scan run: semgrep scan --config auto --error src/layouts/ src/assets/ - # OpenSSF Scorecard: evaluates the security hygiene of the repository - # Checks: branch protection, pinned dependencies, code review requirements + # OpenSSF Scorecard: beoordeelt de beveiligingshygiëne van de repository. + # Kijkt naar branch protection, vastgezette dependencies en code review. scorecard: name: OpenSSF Scorecard runs-on: ubuntu-latest - # Weekly and on main, not on every pull request. Scorecard rates the - # repository's hygiene -- branch protection, pinned dependencies, code - # review -- and none of that changes per commit. Running it on every PR - # cost a runner for an outcome that was the same anyway. + # Wekelijks en op main, niet op elke pull request. Zie de toelichting bij + # semgrep hierboven. if: >- github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || @@ -77,9 +82,11 @@ jobs: repo_token: ${{ secrets.GITHUB_TOKEN }} publish_results: false - # continue-on-error: upload fails for private repos without GitHub Advanced Security + # De resultaten verschijnen op het Security-tabblad van de repository. + # Advanced Security staat organisatiebreed aan, dus dit werkt ook op een + # privérepository. - name: Upload results to GitHub Security - uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4 + uses: github/codeql-action/upload-sarif@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 continue-on-error: true with: sarif_file: scorecard.sarif diff --git a/.github/workflows/update-checksums.yml b/.github/workflows/update-checksums.yml index 4a5feb4..7a2250c 100644 --- a/.github/workflows/update-checksums.yml +++ b/.github/workflows/update-checksums.yml @@ -2,14 +2,14 @@ # SPDX-License-Identifier: MIT name: Update tool SHA256 checksums -# Renovate bumps the pinned tool versions but cannot compute a checksum, so on -# its own every bump lands with the previous release's hash still in place and -# the build stops at "computed checksum did NOT match". This recalculates the -# hashes on Renovate's pull requests and commits them back onto the branch. +# Renovate bumpt de vastgezette toolversies maar kan geen checksum berekenen, +# dus op eigen kracht landt elke bump met de hash van de vorige release er nog +# in, en stopt de build op "computed checksum did NOT match". Dit herberekent +# de hashes op Renovates pull requests en commit ze terug op de branch. # -# Renovate must be told to ignore those commits, or it treats the branch as -# modified by someone else and stops maintaining the pull request. That is the -# gitIgnoredAuthors entry in renovate.json. +# Renovate moet die commits leren negeren, anders ziet hij de branch als door +# iemand anders gewijzigd en onderhoudt hij de pull request niet meer. Dat is +# de gitIgnoredAuthors-regel in renovate.json. on: pull_request: @@ -26,33 +26,34 @@ jobs: update-checksums: name: Recalculate SHA256 checksums runs-on: ubuntu-latest - # Only Renovate's own branches. Running this on a human's pull request - # would mean pushing commits to a branch someone is actively working on. + # Alleen Renovates eigen branches. Dit op een pull request van een mens + # draaien betekent commits pushen naar een branch waar iemand op dat moment + # aan werkt. # - # The author of the pull request, not github.actor. actor is whoever - # triggered the most recent event, which on a synchronize is whoever pushed - # last; comparing that to a bot name is a check zizmor rightly calls - # spoofable. The author is fixed when the pull request is opened and cannot - # be set to another account. + # De auteur van de pull request, niet github.actor. actor is degene die het + # meest recente event veroorzaakte, en dat is bij een synchronize degene die + # als laatste pushte; dat vergelijken met een botnaam is een controle die + # zizmor terecht spoofbaar noemt. De auteur ligt vast zodra de pull request + # geopend wordt en is niet naar een ander account te zetten. if: >- startsWith(github.head_ref, 'renovate/') && github.event.pull_request.user.login == 'renovate[bot]' permissions: contents: write steps: - # persist-credentials: false, even though this job pushes. Otherwise the - # token sits in .git/config for the whole job, including while the script - # below downloads release tarballs off the internet. The push step gets - # the token explicitly instead, for exactly one command. + # persist-credentials: false, ook al pusht deze job. Anders staat het + # token de hele job in .git/config, ook terwijl het script hieronder + # release-tarballs van internet haalt. De push-stap krijgt het token in + # plaats daarvan expliciet mee, voor precies één commando. - name: Check out the pull request branch uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.head_ref }} persist-credentials: false - # The script verifies each download against the checksum the project - # publishes next to the release before writing anything, so a hash only - # lands here if upstream vouches for it too. + # Het script controleert elke download tegen de checksum die het project + # naast de release publiceert voordat er iets wordt weggeschreven, dus een + # hash landt hier alleen als upstream er ook voor instaat. - name: Recalculate and apply checksums run: .github/scripts/update-tool-checksums.sh --apply diff --git a/.markdownlint.yml b/.markdownlint.yml index 81b2b3d..bed9743 100644 --- a/.markdownlint.yml +++ b/.markdownlint.yml @@ -1,19 +1,27 @@ default: true -# ── Disabled: not applicable to this repo ────────────────────────────────── +# ── Uitgezet: niet van toepassing op deze repositories ────────────────────── -# Line length — content is prose-wrapped by hand, not to a column limit +# Regellengte -- de tekst wordt met de hand afgebroken op de zin, niet op een +# kolombreedte. MD013: false -# First line must be a top-level heading — content files start with front matter +# Inline HTML -- bewust gebruikt en niet te vervangen door Markdown:
+# en voor de inklapbare secties in de README, en
in de +# contactgegevens omdat dat het juiste semantische element is. +MD033: false + +# Eerste regel moet een kop op het hoogste niveau zijn -- contentbestanden +# beginnen met front matter. MD041: false -# Bare URLs — used in code blocks and command examples in the README +# Kale URL's -- gebruikt in codeblokken en commandovoorbeelden in de README. MD034: false -# Code block language — the README's blocks are all shell, tagging is optional +# Taal bij een codeblok -- de blokken in de README zijn allemaal shell, dat +# labelen voegt niets toe. MD040: false -# Table column style (pipe spacing/alignment) — overly pedantic, tables render -# correctly regardless of exact pipe spacing +# Kolomstijl in tabellen (spaties rond de pipes) -- overdreven streng, tabellen +# renderen hoe dan ook goed. MD060: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9987e4c..68b3e85 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,6 +24,8 @@ This project uses [Conventional Commits](https://www.conventionalcommits.org/). | `chore` | Maintenance — dependencies, CI/CD, config | | `style` | Formatting, whitespace, typo fixes | | `refactor` | Restructure without changing behavior (e.g. rename files, move sections) | +| `content` | Update or improve existing page content | +| `revert` | Reverting a previous commit | **Examples:** diff --git a/renovate.json b/renovate.json index c44a301..a1d191c 100644 --- a/renovate.json +++ b/renovate.json @@ -7,10 +7,12 @@ "forkProcessing": "enabled", "pinDigests": true, "assignees": [ - "Stensel8" + "Stensel8", + "AdiH1310" ], "reviewers": [ - "Stensel8" + "Stensel8", + "AdiH1310" ], "semanticCommits": "enabled", "gitIgnoredAuthors": [ @@ -66,5 +68,8 @@ ], "automerge": false } - ] + ], + "dependencyDashboard": true, + "osvVulnerabilityAlerts": true, + "configMigration": true }