diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a59fcf6..9f2f24b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,6 +15,20 @@ on: branches: [ main ] jobs: + site: + name: Published pages + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + # The suites below cover the code. This covers the nine HTML files the + # repository actually serves, and the GitHub Pages traps CLAUDE.md + # records: a folder becomes a page only if it holds a README.md, and a + # theme in _config.yml makes every rendered README open in stock styling. + - run: python3 scripts/check_site.py + python: name: Python runs-on: ubuntu-latest diff --git a/scripts/check_site.py b/scripts/check_site.py new file mode 100644 index 0000000..386458e --- /dev/null +++ b/scripts/check_site.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +"""Checks for the published pages, as distinct from the code. + + python3 scripts/check_site.py + +`tests.yml` runs the Python and R suites and says nothing about the nine HTML +files this repository serves at https://varnasr.github.io/InsightStack/. The +traps below are all written down in CLAUDE.md and none of them was checked by +anything. +""" + +import os +import re +import subprocess +import sys + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +errors = [] +checks = 0 + + +def check(name, ok, detail=''): + global checks + checks += 1 + if not ok: + errors.append('%s: %s' % (name, detail)) + + +def read(p): + with open(os.path.join(ROOT, p), encoding='utf-8', errors='replace') as fh: + return fh.read() + + +def tracked(pattern): + out = subprocess.run(['git', 'ls-files', pattern], cwd=ROOT, + capture_output=True, text=True).stdout + return sorted(f for f in out.split('\n') if f) + + +def main(): + pages = [p for p in tracked('*.html') if not p.startswith('_layouts/')] + check('there are pages to check', pages, 'none tracked') + + for page in pages: + body = read(page) + # Without a viewport a phone lays the page out at desktop width and + # zooms out, so the text arrives too small to read. It looks correct + # on the machine it was built on and only a visitor on a phone sees it. + check('%s sets a viewport' % page, + re.search(r']*name=["\']?viewport', body, re.I) is not None, + 'a phone lays this out at desktop width') + check('%s declares a charset' % page, + re.search(r']*charset', body, re.I) is not None, + 'the curly quotes and en dashes in the exports depend on it') + check('%s declares a language' % page, + re.search(r']*\blang=', body, re.I) is not None, + 'a screen reader guesses the pronunciation') + check('%s has a title' % page, + re.search(r'