Skip to content

chore: Configure Renovate #63

chore: Configure Renovate

chore: Configure Renovate #63

Workflow file for this run

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,
});