diff --git a/requirements.txt b/requirements.txt index ad79c024bb..e56cd9e740 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,5 @@ markdown-include pymdown-extensions pillow cairosvg +# selectolax 1.0.1 makes mkdocs-glightbox drop every image from the built site (Oct 2026). +selectolax<1.0.1 diff --git a/tools/docs-checks/README.md b/tools/docs-checks/README.md index 44f5a40a2d..73bf12c69d 100644 --- a/tools/docs-checks/README.md +++ b/tools/docs-checks/README.md @@ -9,7 +9,7 @@ changed lines in the PR. Fix them and push again; the checks re-run. | `check_markdown.py` | Missing front matter `title:` or `description:`, heading levels that skip (`##` to `####`), more than one `#` H1, images without alt text, snippet includes that point at a missing file or start inside the included page's front matter | Add the field, use the next heading level, describe the image inside `![...]`, or start the include after the included page's closing `---` | | `check_secrets.py` | ESPHome API encryption keys, generated OTA passwords, passwords typed into YAML, and access tokens | Use `!secret` (or a placeholder like `your-wifi-password`). If the value was real, change it on the device or account too | | `check_images.py` | Photos with GPS location data (phones add it by default) | `exiftool -gps:all= -overwrite_original docs/assets/` | -| `check_links.py` | Broken links, links to a missing `#anchor`, filename case mismatches (GitHub Pages is case-sensitive), and images without alt text, checked on the built site | Fix the link target or anchor; match the file name's case exactly | +| `check_links.py` | Broken links, links to a missing `#anchor`, filename case mismatches (GitHub Pages is case-sensitive), images without alt text, and a built site with far fewer images than the Markdown, checked on the built site | Fix the link target or anchor; match the file name's case exactly. If images went missing, look for a recently updated build package and pin it in `requirements.txt` | Warnings (shown, but they don't fail the PR): em dashes and the filler words the style guide bans, and images or recordings over 1.5 MB that the PR adds diff --git a/tools/docs-checks/check_links.py b/tools/docs-checks/check_links.py index 20198d3412..8d74f254d2 100644 --- a/tools/docs-checks/check_links.py +++ b/tools/docs-checks/check_links.py @@ -6,9 +6,11 @@ links (../page/, https://wiki.apolloautomation.com/...), so this checks every link, #anchor, image, and video in page content against the built site. Filename case must match exactly, because GitHub Pages is case-sensitive. -Fails on any broken link, missing anchor, or image without alt text. +Fails on any broken link, missing anchor, or image without alt text, and +when the built site has far fewer images than the Markdown (a plugin +dropping them, as selectolax 1.0.1 did to mkdocs-glightbox in October 2026). """ -import os, sys, posixpath +import os, re, sys, posixpath from html.parser import HTMLParser from urllib.parse import urlsplit, unquote @@ -136,8 +138,17 @@ def src_md(url): print(f"::error file={a['page']}::Link to a missing #anchor: {a['ref']}") for m in missing_alt: print(f"::error file={m['page']}::Image without alt text: {m['src']}") +md_imgs = 0 +for r, _, fs in os.walk(DOCS): + for f in fs: + if f.endswith(".md"): + md_imgs += len(re.findall(r"!\[[^\]]*\]\(| 0 and built_imgs < md_imgs // 2 +if images_dropped: + print(f"::error::The built site has {built_imgs} images, but the Markdown has about {md_imgs}. A build plugin is probably removing images; check recently updated packages.") for wrong, right in sorted(set(case_mismatch)): print(f"::error::Link case doesn't match the file (GitHub Pages is case-sensitive): {wrong} should be {right}") -total = len(broken) + len(bad_anchor) + len(missing_alt) + len(set(case_mismatch)) -print(f"check_links: {len(pages)} pages, {len(broken)} broken, {len(bad_anchor)} bad anchors, {len(missing_alt)} missing alt, {len(set(case_mismatch))} case mismatches") +total = len(broken) + len(bad_anchor) + len(missing_alt) + len(set(case_mismatch)) + images_dropped +print(f"check_links: {len(pages)} pages, {built_imgs} images, {len(broken)} broken, {len(bad_anchor)} bad anchors, {len(missing_alt)} missing alt, {len(set(case_mismatch))} case mismatches") sys.exit(1 if total else 0)