Files
libretro/website/tools/search_gate.py
T
Abdessamad Derraz 123d698e43 feat: build the site with Astro Starlight
generate_site.py writes the Starlight project in website/ and nothing
else: mkdocs.yml, the MkDocs hooks, theme overrides and stylesheet go,
and the wiki is written in Starlight's syntax. Both workflows set up
Node with the official actions/setup-node pinned by commit, run npm ci
from the committed lockfile, build with the strict link validator and
check the result with validate_site.py.

The search weighs a page by what it answers: Start here pages first,
a system page's cards and file names before a guide citing them, deep
guides, launchers, aliases and frozen copies after. Status badges,
hash blocks and the export change lists leave the index. The ranking
sits at the centre of a range where the 13 queries of search_gate.py
all answer, in the search box itself.

839 pages, every URL and 45,611 ids match the MkDocs build. The tests
read the Starlight content and syntax.
2026-10-22 18:47:22 +02:00

66 lines
2.7 KiB
Python

#!/usr/bin/env python3
"""The search gate: queries a visitor types, and the pages that answer them.
Runs measure.mjs against a served build, through the site's own search box,
and checks each query's expected pages against the rank it must reach: the
first result, or the first three.
python website/tools/search_gate.py http://127.0.0.1:4321/retrobios/
"""
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
TOOLS = Path(__file__).resolve().parent
# Query: (pages that answer it, as path prefixes under the site, and the rank one
# of them must reach). An emulator name must find the emulator itself first,
# not its launcher or a frozen copy.
QUERIES: dict[str, tuple[tuple[str, ...], int]] = {
"batocera folder": (("platforms/batocera/",), 1),
"missing bios": (("wiki/troubleshooting/", "wiki/what-are-bios-files/", "wiki/faq/"), 1),
"neogeo.zip": (("emulators/fbneo", "emulators/mame", "systems/snk/", "systems/arcade"), 1),
"retroarch system folder": (("platforms/retroarch/",), 1),
"installer": (("wiki/installer/", "wiki/first-install/"), 1),
# The file's card on its system page, where it is downloaded.
"scph5501": (("systems/sony/",), 3),
"md5": (("wiki/verification-modes/",), 1),
"where do bios files go": (("which-pack/", "wiki/first-install/"), 1),
"dolphin": (("emulators/dolphin/",), 1),
"duckstation": (("emulators/duckstation/",), 1),
"playstation bios": (("systems/sony/",), 3),
"steam deck": (("platforms/emudeck/", "platforms/retrodeck/", "which-pack/"), 1),
"knulli": (("platforms/knulli/",), 1),
}
def main() -> int:
base = sys.argv[1] if len(sys.argv) > 1 else "http://127.0.0.1:4321/retrobios/"
root = "/" + base.split("/", 3)[3]
run = subprocess.run(
["node", str(TOOLS / "measure.mjs"), "search", base, "starlight-ui", *QUERIES],
capture_output=True, text=True, check=True,
)
answered = 0
for line in run.stdout.splitlines():
result = json.loads(line)
if "query" not in result:
print(f"first search: {result['first_search_kb']} KB in {result['first_search_requests']} requests")
continue
pages, rank = QUERIES[result["query"]]
top = [entry.split(" | ") for entry in result["top"][:3]]
hit = any(href[len(root):].startswith(pages) for _title, href in top[:rank])
answered += hit
titles = "; ".join(title for title, _href in top)
print(f"{'ok ' if hit else 'MISS'} {result['query']!r} (top {rank}): {titles}")
print(f"{answered}/{len(QUERIES)} queries answered")
return 0 if answered == len(QUERIES) else 1
if __name__ == "__main__":
sys.exit(main())