Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/explainers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Explainers

# Explainers PDFs must be valid, single-page, and correctly sized.
# This check validates the published contract only; rendering lives
# in transitrix/coordinator.

on:
push:
branches: [main]
paths:
- 'explainers/**'
- 'scripts/check_explainers.py'
- '.github/workflows/explainers.yml'
pull_request:
branches: [main]
paths:
- 'explainers/**'
- 'scripts/check_explainers.py'
- '.github/workflows/explainers.yml'

permissions:
contents: read

jobs:
check-explainers:
runs-on: ubuntu-latest
name: explainers/ published PDF contract holds
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install PDF validator
run: python3 -m pip install 'pypdf==6.16.2'

- name: Check explainers/ contract
run: python3 scripts/check_explainers.py
Binary file not shown.
2 changes: 1 addition & 1 deletion explainers/how-a-document-prints/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
</header>
<main id="main" class="wrap">
<div class="hero">
<p class="pub-date"><a href="../">&larr; Explainers</a></p>
<p class="pub-date"><a href="../">&larr; Explainers</a> · <a href="how-a-document-prints.pdf">one-pager</a></p>
<h1>How a model-backed document prints</h1>
</div>

Expand Down
71 changes: 71 additions & 0 deletions scripts/check_explainers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env python3
"""Check explainers PDF contract.

Published explainer PDFs must:
- Be valid PDFs
- Have exactly one page
- Have geometry 842 × 474 pt (A4 short form, 297 × 167 mm)
"""

import glob
import sys
from pathlib import Path

try:
from pypdf import PdfReader
except ImportError:
print("❌ pypdf not installed; skipping checks", file=sys.stderr)
sys.exit(0)


def check_explainer_pdfs():
"""Validate all explainer PDFs."""
errors = []
pdf_files = glob.glob("explainers/*/[!_]*.pdf")

if not pdf_files:
# No explainer PDFs yet; check passes
return errors

for pdf_path in sorted(pdf_files):
try:
reader = PdfReader(pdf_path)

# Check page count
page_count = len(reader.pages)
if page_count != 1:
errors.append(
f"❌ {pdf_path}: expected 1 page, got {page_count}"
)
continue

# Check geometry on the one page
page = reader.pages[0]
mediabox = page.mediabox
width = float(mediabox.width)
height = float(mediabox.height)

# Expected: 842 × 474 pt (A4 short form)
# Allow ±1pt tolerance for rounding
expected_w, expected_h = 842, 474
if abs(width - expected_w) > 1 or abs(height - expected_h) > 1:
errors.append(
f"❌ {pdf_path}: geometry {width:.0f}×{height:.0f} pt, "
f"expected {expected_w}×{expected_h} pt"
)
else:
print(f"✓ {pdf_path}: {width:.0f}×{height:.0f} pt, 1 page")

except Exception as e:
errors.append(f"❌ {pdf_path}: {e}")

return errors


if __name__ == "__main__":
errors = check_explainer_pdfs()
if errors:
for error in errors:
print(error, file=sys.stderr)
sys.exit(1)
sys.exit(0)
Loading