From 2d2addd6d2a80985394d7b7e6eac147daac91eef Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:11:07 +0200 Subject: [PATCH] fix: align the suite with the site layout The redesign moved the technical questions to technical-faq.md, made the Get your files rows generated from each platform key and reduced getting-started.md to links. Tests now read those places: the pack-row test reads the rendered page. The new site scripts go through run_cli and refuse abbreviated options. The three maintenance image tools are exempt by name from the build dependency rule: their output is committed and the build only reads it. siteguides.py now triggers the site deploy, and the recipe counts match the store. --- .github/workflows/deploy-site.yml | 1 + scripts/fetch_site_images.py | 6 ++- scripts/generate_site.py | 5 +- scripts/make_og_cards.py | 4 +- scripts/make_site_icons.py | 4 +- scripts/sitediagrams.py | 8 ++- tests/test_audit_regressions.py | 4 +- tests/test_contributing_text.py | 3 ++ tests/test_optional_dependencies.py | 12 +++++ tests/test_split_pack.py | 3 +- tests/test_which_pack.py | 77 ++++++++++------------------- wiki/tools.md | 2 +- 12 files changed, 66 insertions(+), 63 deletions(-) diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index 7a7a5de7..d2927d53 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -44,6 +44,7 @@ on: - "scripts/safeparse.py" - "scripts/sect233r1.py" - "scripts/sitediagrams.py" + - "scripts/siteguides.py" - "scripts/siteexports.py" - "scripts/siterender.py" - "scripts/sitesources.py" diff --git a/scripts/fetch_site_images.py b/scripts/fetch_site_images.py index ddaeaaba..2cb4a8b9 100644 --- a/scripts/fetch_site_images.py +++ b/scripts/fetch_site_images.py @@ -172,7 +172,7 @@ def fetch_icons() -> None: def main() -> None: - parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0]) + parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0], allow_abbrev=False) parser.add_argument("--only", choices=("logos", "icons"), help="fetch one kind only") args = parser.parse_args() if args.only in (None, "logos"): @@ -184,4 +184,6 @@ def main() -> None: if __name__ == "__main__": - main() + from common import run_cli + + run_cli(main) diff --git a/scripts/generate_site.py b/scripts/generate_site.py index 3a70aac1..6266e04c 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -74,6 +74,7 @@ from generate_readme import ( from provenance_report import build_report, distinct_missing from siteexports import generate_exports_page, load_export_reports from siteguides import ( + Guide, ALSO_RUNS, DEVICE_GROUPS, GROUP_ANCHORS, @@ -5535,7 +5536,7 @@ def _mkdocs_config(docs: Path, text: str) -> tuple[Path, str]: _OS_NAMES = {"windows": "Windows", "darwin": "macOS", "linux": "Linux"} -def _runs_on(entry: dict, guide) -> str: +def _runs_on(entry: dict, guide: Guide) -> str: """Where a setup runs, read from the systems the installer detects it on.""" if guide.on_card: return "SD card, filled from a computer" @@ -5552,7 +5553,7 @@ def _runs_on(entry: dict, guide) -> str: return ", ".join(names) -def _folder_cell(name: str, guide, prefix: str = "") -> str: +def _folder_cell(name: str, guide: Guide, prefix: str = "") -> str: """The BIOS folder in a table cell, or a link where it depends on the system.""" if len(guide.folders) > 3: return f"Depends on the system: [listed on its page]({prefix}platforms/{name}.md#where-the-files-go)" diff --git a/scripts/make_og_cards.py b/scripts/make_og_cards.py index ec472fb5..e25f5731 100644 --- a/scripts/make_og_cards.py +++ b/scripts/make_og_cards.py @@ -45,4 +45,6 @@ def main() -> None: if __name__ == "__main__": - main() + from common import run_cli + + run_cli(main) diff --git a/scripts/make_site_icons.py b/scripts/make_site_icons.py index 551791bd..a4f1cdca 100644 --- a/scripts/make_site_icons.py +++ b/scripts/make_site_icons.py @@ -131,4 +131,6 @@ def main() -> None: if __name__ == "__main__": - main() + from common import run_cli + + run_cli(main) diff --git a/scripts/sitediagrams.py b/scripts/sitediagrams.py index d06cacb0..99f28365 100644 --- a/scripts/sitediagrams.py +++ b/scripts/sitediagrams.py @@ -744,7 +744,9 @@ def write_svgs(directory: Path) -> list[Path]: def main() -> None: import argparse - parser = argparse.ArgumentParser(description="Write the site diagrams as SVG files.") + parser = argparse.ArgumentParser( + description="Write the site diagrams as SVG files.", allow_abbrev=False + ) parser.add_argument("--output-dir", default="tmp/diagrams", help="default: %(default)s") args = parser.parse_args() for path in write_svgs(Path(args.output_dir)): @@ -752,4 +754,6 @@ def main() -> None: if __name__ == "__main__": - main() + from common import run_cli + + run_cli(main) diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index 3d872ef4..f189f963 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -779,7 +779,9 @@ class FaqRegressions(unittest.TestCase): @staticmethod def _faq() -> str: - return (ROOT / "wiki" / "faq.md").read_text(encoding="utf-8") + # The questions about how files are identified and checked moved to + # the technical FAQ when the site was laid out by level. + return (ROOT / "wiki" / "technical-faq.md").read_text(encoding="utf-8") @staticmethod def _profile(key: str) -> dict: diff --git a/tests/test_contributing_text.py b/tests/test_contributing_text.py index 3803722a..f23fe0aa 100644 --- a/tests/test_contributing_text.py +++ b/tests/test_contributing_text.py @@ -20,6 +20,9 @@ import generate_site # noqa: E402 def _normalized(text: str) -> str: + # The site ends each page with its next step in the reading order. That + # is navigation, not contributing text, and a repository file has none. + text = text.split('\n\n!!! tip "Next"\n', 1)[0].rstrip("\n") text = re.sub(r"\]\([^)]*\)", "](link)", text) return "\n".join(text.splitlines()[1:]).replace("on this site", "on the documentation site") diff --git a/tests/test_optional_dependencies.py b/tests/test_optional_dependencies.py index 9eb0ff6f..6dfac486 100644 --- a/tests/test_optional_dependencies.py +++ b/tests/test_optional_dependencies.py @@ -52,6 +52,16 @@ class SuiteWithoutJsonschema(unittest.TestCase): self.assertEqual(completed.returncode, 0, completed.stdout + completed.stderr[-2000:]) +# Tools a maintainer runs by hand when a logo or a card changes. Their output +# is committed under docs_assets/ and the build only reads it, so the build +# keeps to the standard library and pyyaml. +MAINTENANCE_TOOLS = { + "fetch_site_images.py": "PIL", + "make_og_cards.py": "playwright", + "make_site_icons.py": "playwright", +} + + class ScriptsNeedOnlyPyyaml(unittest.TestCase): """The build scripts need the standard library and pyyaml. jsonschema is validate_schemas.py's alone. CI installs jsonschema with its whole tree @@ -80,6 +90,8 @@ class ScriptsNeedOnlyPyyaml(unittest.TestCase): continue if name == "jsonschema" and path.name == "validate_schemas.py": continue + if MAINTENANCE_TOOLS.get(path.name) == name: + continue found.append(f"{path.relative_to(scripts)}: {name}") self.assertEqual(found, []) diff --git a/tests/test_split_pack.py b/tests/test_split_pack.py index 42f3d4fb..3d0f0aa0 100644 --- a/tests/test_split_pack.py +++ b/tests/test_split_pack.py @@ -202,10 +202,11 @@ class ReleaseProcessPublishesArchives(unittest.TestCase): class PartsAreExplainedWhereTheyAreDownloaded(unittest.TestCase): + # getting-started.md now only links the pages that took its content: the + # downloads are explained on Get your files, which generate_site.py writes. PAGES = ( "scripts/generate_readme.py", "scripts/generate_site.py", - "wiki/getting-started.md", "wiki/troubleshooting.md", ) diff --git a/tests/test_which_pack.py b/tests/test_which_pack.py index bce7a34d..7d214096 100644 --- a/tests/test_which_pack.py +++ b/tests/test_which_pack.py @@ -1,4 +1,4 @@ -"""The "which pack" table sends each platform to the pack built for it. +"""The Get your files page sends each platform to the pack built for it. RetroPie's row linked the RetroArch pack, although a RetroPie pack is published and carries 3916 files the RetroArch one does not. @@ -15,58 +15,31 @@ REPO_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(REPO_ROOT / "scripts")) -def _packs_of() -> dict[str, set[str]]: - """Each platform's display name, mapped to the platforms its pack serves.""" - from common import ( - group_identical_platforms, - list_registered_platforms, - load_platform_config, - ) - - platforms_dir = str(REPO_ROOT / "platforms") - registered = list_registered_platforms(platforms_dir, include_archived=True) - display = { - p: load_platform_config(p, platforms_dir).get("platform", p) - for p in registered - } - packs_of: dict[str, set[str]] = {} - for group, _rep in group_identical_platforms(registered, platforms_dir): - names = {display[p] for p in group} - for p in group: - packs_of[display[p]] = names - return packs_of - - class WhichPackRows(unittest.TestCase): + """Every row of the Get your files page links only its own pack. + + The rows were written by hand and the RetroPie one sent readers to the + RetroArch pack. They are now built from each platform's registry key, and + this reads the rendered page to keep it so. The per-platform folder notes + that getting-started.md carried moved to each platform page, built by the + same key. + """ + def test_platform_rows_name_their_own_pack(self): - packs_of = _packs_of() - source = (REPO_ROOT / "scripts" / "generate_site.py").read_text(encoding="utf-8") - row = re.compile(r"^\| \[([^\]]+)\]\([^)]*\) \|[^|]*\| \[([^\]]+)\]\(\{rel\}\)", re.M) + import generate_site # noqa: PLC0415 + import release_record # noqa: PLC0415 + + record = release_record.load_record() + page = generate_site.generate_which_pack() + row = re.compile(r"^\| \[[^\]]+\]\(platforms/([a-z0-9]+)\.md\) \|(.*)$", re.M) + asset = re.compile(r"/releases/download/[^/]+/([^)\s]+)\)") checked = 0 - for setup, pack in row.findall(source): - if setup not in packs_of: - continue - checked += 1 - with self.subTest(setup=setup): - self.assertIn(pack, packs_of[setup]) + for key, rest in row.findall(page): + linked = set(asset.findall(rest)) + pack = release_record.pack_for(key, record) or {} + with self.subTest(platform=key): + self.assertLessEqual(linked, set(pack.get("assets") or [])) + if pack: + checked += 1 + self.assertTrue(linked) self.assertGreater(checked, 5) - - def test_getting_started_sends_each_platform_to_its_pack(self): - """Its RetroPie section said the RetroArch pack applies as well.""" - packs_of = _packs_of() - text = (REPO_ROOT / "wiki" / "getting-started.md").read_text(encoding="utf-8") - section = text.split("## BIOS directory by platform", 1)[1].split("\n## ", 1)[0] - checked = 0 - for block in section.split("\n### ")[1:]: - heading, _, body = block.partition("\n") - if heading not in packs_of: - continue - checked += 1 - for named in [name for name in packs_of if f"{name} pack" in body]: - with self.subTest(section=heading, named=named): - self.assertIn(named, packs_of[heading]) - self.assertGreater(checked, 5) - - -if __name__ == "__main__": - unittest.main() diff --git a/wiki/tools.md b/wiki/tools.md index 7b9fab1d..b31e73f6 100644 --- a/wiki/tools.md +++ b/wiki/tools.md @@ -821,7 +821,7 @@ set that ships it: MAME's `fm7740sx` carries the FM77AV40SX `DICROM.rom`, members are merged into the child, and members with no CRC32 are dropped: those are undumped and a real romset does not carry them. -The store is compacted: 22 MAME versions produce 24,101 entries but only 2,148 +The store is compacted: 22 MAME versions produce 31,141 entries but only 2,399 distinct recipes, since most sets do not change between releases. Each is kept once, `dats` listing every version that agrees and `dat` naming the earliest, so an identification reads as "unchanged since that version".