From a389b5fa2b05e6a4ca6fb16417555dc57a9abe89 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 19 Oct 2026 04:35:54 +0200 Subject: [PATCH] fix: publish each profile document in the envelope --- scripts/generate_site.py | 21 +++++++++++++++++++-- tests/test_site_exports.py | 24 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/scripts/generate_site.py b/scripts/generate_site.py index c1e04dad..6da4ed8f 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -1117,6 +1117,12 @@ def generate_data_page(exports: list[dict]) -> str: "Every JSON document carries `schema_version`. Breaking changes use a new " "URL prefix (`/api/v2/`); fields may only be added compatibly within v1.", "", + "One emulator profile without the whole list: " + "`api/v1/emulators/.json`, the envelope of `emulators.json` holding " + "that one item, its `profile` valid against " + "[`emulator.schema.json`](api/v1/schemas/emulator.schema.json). Each " + "emulator page links its own.", + "", "## Bulk downloads", "", "| Export | Format | Size | SHA256 |", @@ -4495,10 +4501,21 @@ def _generate(args: argparse.Namespace) -> None: for name, profile in public_profiles.items(): # The profile itself, one small document per core: reusing one # meant downloading emulators.json (5.9 MB) and filtering it. + # The envelope and item of emulators.json, one item long: the + # profile validates against the published schema, which an "id" + # beside its fields broke on all 530, and schema_version is there. write_if_changed( str(docs / "api" / "v1" / "emulators" / f"{name}.json"), - json.dumps({"id": name, **profile}, indent=2, sort_keys=True, - ensure_ascii=False, default=str) + "\n", + json.dumps( + { + "schema_version": 1, + "generated_at": str(db.get("generated_at") or _timestamp()), + "kind": "emulators", + "count": 1, + "items": [{"id": name, "profile": profile}], + }, + indent=2, sort_keys=True, ensure_ascii=False, default=str, + ) + "\n", ) page = generate_emulator_page( name, profile, db, consumers, suppl_names, data_registry diff --git a/tests/test_site_exports.py b/tests/test_site_exports.py index de67ce07..cec4d00c 100644 --- a/tests/test_site_exports.py +++ b/tests/test_site_exports.py @@ -627,5 +627,29 @@ class APlatformPageNamesTheFileItTranscribes(unittest.TestCase): self.assertIn("revision read is not recorded", page("https://github.com/RetroDECK/components")) +class APerCoreDocumentValidates(unittest.TestCase): + """api/v1/emulators/.json had no schema_version, and its "id" beside + the profile's fields failed the schema published next to it.""" + + def test_shape(self): + try: + import jsonschema # noqa: PLC0415 + except ImportError: + self.skipTest("jsonschema is not installed") + import yaml # noqa: PLC0415 + + root = Path(__file__).resolve().parents[1] + source = (root / "scripts" / "generate_site.py").read_text(encoding="utf-8") + self.assertNotIn('json.dumps({"id": name, **profile}', source) + envelope = json.loads((root / "schemas" / "site-api-envelope.schema.json").read_text()) + schema = json.loads((root / "schemas" / "emulator.schema.json").read_text()) + profile = yaml.safe_load((root / "emulators" / "handy.yml").read_text()) + document = {"schema_version": 1, "generated_at": "2026-10-19T00:00:00Z", + "kind": "emulators", "count": 1, + "items": [{"id": "handy", "profile": profile}]} + jsonschema.validate(document, envelope) + jsonschema.validate(document["items"][0]["profile"], schema) + + if __name__ == "__main__": unittest.main()