chore: Configure Renovate #63
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR Checks | |
| on: | |
| pull_request: | |
| branches: [main, development] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| # ── 1. PR title follows conventional commits ──────────────────────────────── | |
| pr-title: | |
| name: Conventional commit title | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| types: | | |
| feat | |
| fix | |
| content | |
| docs | |
| chore | |
| refactor | |
| style | |
| revert | |
| # ── 2. Markdown formatting ────────────────────────────────────────────────── | |
| markdown: | |
| name: Markdown lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: DavidAnson/markdownlint-cli2-action@ce4853d43830c74c1753b39f3cf40f71c2031eb9 # v23.0.0 | |
| with: | |
| globs: "content/**/*.md" | |
| # ── 3. Python security scan ───────────────────────────────────────────────── | |
| python-security: | |
| name: Python security (bandit) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.x" | |
| - run: pip install bandit | |
| - name: Run bandit | |
| run: bandit -r static/scripts/ -ll | |
| # ── 4. Images must be AVIF ────────────────────────────────────────────────── | |
| image-format: | |
| name: No PNG/JPG in static/images | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Find non-AVIF images | |
| id: check | |
| run: | | |
| { | |
| echo "files<<EOF" | |
| find static/images -type f \( -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" \) | sort | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| count=$(find static/images -type f \( -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" \) | wc -l) | |
| if [ "$count" -gt 0 ]; then | |
| echo "found=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "found=false" >> "$GITHUB_OUTPUT" | |
| echo "All images are AVIF." | |
| fi | |
| - name: Post PR comment | |
| if: steps.check.outputs.found == 'true' | |
| uses: actions/github-script@v8 | |
| env: | |
| FILES: ${{ steps.check.outputs.files }} | |
| ACTOR: ${{ github.event.pull_request.user.login }} | |
| with: | |
| script: | | |
| const files = process.env.FILES.trim().split('\n').map(f => `- \`${f}\``).join('\n'); | |
| const actor = process.env.ACTOR; | |
| const body = [ | |
| `Hey @${actor}, looks like you forgot something!`, | |
| '', | |
| 'The following images in `static/images/` are not in AVIF format:', | |
| files, | |
| '', | |
| 'Please convert them before merging. Install `avifenc` first:', | |
| '```bash', | |
| 'sudo pacman -S libavif', | |
| '```', | |
| '', | |
| 'Then batch-convert all images in `static/images/`:', | |
| '```bash', | |
| 'cd static/images', | |
| 'for f in *.png *.jpg *.jpeg; do', | |
| ' [ -f "$f" ] && avifenc -q 80 -s 6 "$f" "${f%.*}.avif" && rm "$f"', | |
| 'done', | |
| '```', | |
| ].join('\n'); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.payload.pull_request.number, | |
| body, | |
| }); | |
| - name: Annotate and fail | |
| if: steps.check.outputs.found == 'true' | |
| env: | |
| FILES: ${{ steps.check.outputs.files }} | |
| run: | | |
| while IFS= read -r f; do | |
| echo "::error file=$f::Convert to AVIF before merging (see README → Image assets)" | |
| done <<< "$FILES" | |
| exit 1 | |
| # ── 5. Both EN and NL files present ───────────────────────────────────────── | |
| bilingual: | |
| name: EN/NL file parity | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Check every .md has a matching .nl.md | |
| run: | | |
| missing="" | |
| for en in content/docs/*.md; do | |
| base="${en%.md}" | |
| nl="${base}.nl.md" | |
| # Skip files that are already .nl.md | |
| [[ "$en" == *.nl.md ]] && continue | |
| if [ ! -f "$nl" ]; then | |
| missing="$missing\n $en → $nl missing" | |
| fi | |
| done | |
| if [ -n "$missing" ]; then | |
| echo -e "::error::Missing Dutch translation(s):$missing" | |
| exit 1 | |
| fi | |
| echo "All docs have EN + NL versions." | |
| # ── 6. Hugo builds without errors ─────────────────────────────────────────── | |
| hugo-build: | |
| name: Hugo build | |
| runs-on: ubuntu-latest | |
| env: | |
| HUGO_VERSION: 0.152.2 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| submodules: recursive | |
| fetch-depth: 0 | |
| - name: Install Hugo | |
| run: | | |
| wget -O ${{ runner.temp }}/hugo.deb \ | |
| https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ | |
| && sudo dpkg -i ${{ runner.temp }}/hugo.deb | |
| - name: Build | |
| env: | |
| HUGO_CACHEDIR: ${{ runner.temp }}/hugo_cache | |
| HUGO_ENVIRONMENT: production | |
| TZ: Europe/Amsterdam | |
| run: hugo --gc --minify --baseURL "http://localhost/" | |
| - name: Upload built site | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: hugo-public | |
| path: public/ | |
| retention-days: 1 | |
| # ── 7. Broken internal links ───────────────────────────────────────────────── | |
| link-check: | |
| name: Broken link check | |
| runs-on: ubuntu-latest | |
| needs: hugo-build | |
| steps: | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: hugo-public | |
| path: public/ | |
| - name: Check internal links | |
| uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2 | |
| with: | |
| args: >- | |
| --offline | |
| --include-fragments | |
| --root-dir ./public | |
| public/**/*.html | |
| fail: true | |
| # ── 8. Auto-tick PR checklist ──────────────────────────────────────────────── | |
| update-checklist: | |
| name: Update PR checklist | |
| runs-on: ubuntu-latest | |
| if: always() | |
| needs: [pr-title, bilingual, image-format, hugo-build, link-check] | |
| steps: | |
| - uses: actions/github-script@v8 | |
| env: | |
| RESULT_PR_TITLE: ${{ needs.pr-title.result }} | |
| RESULT_BILINGUAL: ${{ needs.bilingual.result }} | |
| RESULT_IMAGE_FORMAT: ${{ needs.image-format.result }} | |
| RESULT_HUGO_BUILD: ${{ needs.hugo-build.result }} | |
| RESULT_LINK_CHECK: ${{ needs.link-check.result }} | |
| 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 || ''; | |
| // ── Tick checklist items based on CI results ────────────────────── | |
| const setCheck = (keyword, passed) => { | |
| body = body.replace( | |
| new RegExp(`- \\[[ xX]\\] (.*${keyword}.*)`, 'i'), | |
| `- [${passed ? 'x' : ' '}] $1` | |
| ); | |
| }; | |
| setCheck('PR title follows', process.env.RESULT_PR_TITLE === 'success'); | |
| setCheck('Both EN and NL', process.env.RESULT_BILINGUAL === 'success'); | |
| setCheck('Media is in AVIF', process.env.RESULT_IMAGE_FORMAT === 'success'); | |
| setCheck('No broken image', process.env.RESULT_LINK_CHECK === 'success'); | |
| setCheck('Tested locally', process.env.RESULT_HUGO_BUILD === 'success'); | |
| // ── Remove unchecked "Type of change" options ───────────────────── | |
| body = body.replace(/^- \[ \] `\w+` —[^\n]*\n?/gm, ''); | |
| // ── Collapse leftover blank lines ───────────────────────────────── | |
| body = body.replace(/\n{3,}/g, '\n\n'); | |
| await github.rest.pulls.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.payload.pull_request.number, | |
| body, | |
| }); |