Skip to content

chore: standardization across the organization #91

chore: standardization across the organization

chore: standardization across the organization #91

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.1.1
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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: DavidAnson/markdownlint-cli2-action@ded1f9488f68a970bc66ea5619e13e9b52e601cd # v23.2.0
with:
globs: "src/content/**/*.md"
# ── 3. Python security scan ─────────────────────────────────────────────────
python-security:
name: Python security (bandit)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.x"
- run: pip install bandit
- name: Run bandit
run: bandit -r src/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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Find non-AVIF images
id: check
run: |
{
echo "files<<EOF"
find src/static/images -type f \( -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" \) | sort
echo "EOF"
} >> "$GITHUB_OUTPUT"
count=$(find src/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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
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 `src/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 src/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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Check every .md has a matching .nl.md
run: |
missing=""
for en in src/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.163.1
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version-file: src/go.mod
- 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: cd src && hugo --gc --minify --baseURL "http://localhost/"
- name: Upload built site
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: hugo-public
path: src/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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: hugo-public
path: public/
- name: Check internal links
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0
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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
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,
});