diff --git a/scripts/pipeline.py b/scripts/pipeline.py index f4a75922..e813e9bc 100644 --- a/scripts/pipeline.py +++ b/scripts/pipeline.py @@ -2,6 +2,7 @@ """Run the full retrobios pipeline. Steps, numbered as the run prints them: + 0 validate_schemas.py profiles and platform YAMLs, as PR CI checks them 1 generate_db.py --force rebuild database.json from bios/ 1b provenance_report.py dump-catalog coverage, derivable.json 1c romset_recipes.py archive identification, reconstruction targets @@ -39,6 +40,7 @@ from __future__ import annotations import argparse import contextlib +import importlib.util import os import re import subprocess @@ -469,6 +471,20 @@ def main() -> None: total_start = time.monotonic() results = StepResults(total_start) + # Step 0: the schema check PR validation runs. Without it a profile with + # `cores: [81]` unquoted passed the whole pipeline and failed CI on push. + # jsonschema is tooling only, so its absence skips the step. + if importlib.util.find_spec("jsonschema") is None: + print("\n--- 0/8 validate schemas: SKIPPED (jsonschema not installed) ---") + results["validate_schemas"] = SKIPPED + else: + ok, _ = run( + [sys.executable, "scripts/validate_schemas.py", "--source-only"], + "0/8 validate schemas", + ) + results["validate_schemas"] = ok + all_ok = all_ok and ok + # Step 1: Generate database ok, out = run( [ diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index a6394a7b..a0a19423 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -5572,5 +5572,20 @@ class AHashConflictIsReportedOnce(unittest.TestCase): self.assertEqual(err.getvalue().count("WARNING"), 1) +class ThePipelineRunsTheSchemaCheckCIRuns(unittest.TestCase): + """The pipeline the repository asks for after every change never ran + validate_schemas.py, which PR validation does: a profile with an + unquoted numeric core name passed it and failed CI on push.""" + + def test_the_ci_command_is_a_pipeline_step(self): + workflow = (ROOT / ".github" / "workflows" / "validate.yml").read_text(encoding="utf-8") + command = re.search(r"python (scripts/validate_schemas\.py[^\n]*)", workflow).group(1) + script, *flags = command.split() + source = (ROOT / "scripts" / "pipeline.py").read_text(encoding="utf-8") + self.assertIn(f'"{script}"', source) + for flag in flags: + self.assertIn(f'"{flag}"', source) + + if __name__ == "__main__": unittest.main() diff --git a/wiki/adding-a-platform.md b/wiki/adding-a-platform.md index 301bf639..269ac7dc 100644 --- a/wiki/adding-a-platform.md +++ b/wiki/adding-a-platform.md @@ -365,6 +365,7 @@ python scripts/pipeline.py --offline This executes in sequence: +0. `validate_schemas.py --source-only` - the schema check PR validation runs 1. `generate_db.py` - rebuild `database.json` from `bios/` 1b. `provenance_report.py` - dump-catalog coverage from `provenance/` 2. `refresh_data_dirs.py` - update data directories (skipped with `--offline`) diff --git a/wiki/tools.md b/wiki/tools.md index 6adb1ffd..c3ca6b28 100644 --- a/wiki/tools.md +++ b/wiki/tools.md @@ -23,6 +23,7 @@ Pipeline steps: | Step | Description | Skipped by | |------|-------------|------------| +| 0/8 | Validate profiles and platform YAMLs against the schemas | - (no `jsonschema` installed) | | 1/8 | Generate database | - | | 1b | Dump-catalog coverage report | - (offline, reads `provenance/`) | | 1c | Romset recipes: identify archives, list rebuild targets | - (offline, reads `recipes/`) |