From e9653069a2bf219249ebdd97afad3540960f3525 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Tue, 6 Oct 2026 09:45:25 +0200 Subject: [PATCH] fix: require the jsonschema format checkers --- .github/workflows/deploy-site.yml | 2 +- .github/workflows/validate.yml | 4 ++-- pyproject.toml | 2 +- scripts/check_freshness.py | 4 +++- scripts/validate_schemas.py | 24 ++++++++++++++++++++++++ tests/test_audit_regressions.py | 20 ++++++++++++++++++++ tests/test_check_freshness.py | 8 ++++++++ 7 files changed, 59 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index d0e758d4..90f1cb25 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -84,7 +84,7 @@ jobs: # mkdocs-material >= 9.7.5 caps mkdocs < 2; the upper bound keeps a major # theme release from changing the site without a deliberate bump here. - - run: pip install pyyaml jsonschema==4.26.0 "mkdocs-material>=9.7.5,<10" "pymdown-extensions>=10.14" + - run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0" "mkdocs-material>=9.7.5,<10" "pymdown-extensions>=10.14" - name: Validate data contracts run: python scripts/validate_schemas.py diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index a560d16c..398b1932 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -112,7 +112,7 @@ jobs: python-version: "3.12" - name: Install dependencies - run: pip install pyyaml jsonschema==4.26.0 + run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0" - name: Validate platform configs and emulator profiles run: python scripts/validate_schemas.py --source-only @@ -127,7 +127,7 @@ jobs: python-version: "3.12" - name: Install dependencies - run: pip install pyyaml jsonschema==4.26.0 + run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0" # The exporter fidelity cases compare what we hand back against the # file each platform publishes, so they need those files. They skip diff --git a/pyproject.toml b/pyproject.toml index be180190..0e73c027 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ dependencies = [ # and the CI contract check are the only callers, and no user path imports it. [project.optional-dependencies] dev = [ - "jsonschema>=4.23", + "jsonschema[format-nongpl]>=4.23", ] # The site build the repo generates and documents. Both were hard # requirements of deploy-site.yml and of wiki/release-process.md while being diff --git a/scripts/check_freshness.py b/scripts/check_freshness.py index 5e35d0d0..ff6b5823 100644 --- a/scripts/check_freshness.py +++ b/scripts/check_freshness.py @@ -804,7 +804,9 @@ def _check_fbneo(cache_dir: str, offline: bool) -> Finding: # --- CI toolchain ----------------------------------------------------------- -_PIN_RE = re.compile(r'"?([A-Za-z0-9_.\-]+)((?:[<>=!~]=?[^,"\s]+)(?:,[<>=!~]=?[^,"\s]+)*)?"?') +_PIN_RE = re.compile( + r'"?([A-Za-z0-9_.\-]+)(?:\[[^\]]*\])?((?:[<>=!~]=?[^,"\s]+)(?:,[<>=!~]=?[^,"\s]+)*)?"?' +) _USES_RE = re.compile(r"uses:\s*([\w.\-]+/[\w.\-]+)@([0-9a-f]{40})\s*(?:#\s*(\S+))?") diff --git a/scripts/validate_schemas.py b/scripts/validate_schemas.py index 8f7c30ed..46f429a2 100644 --- a/scripts/validate_schemas.py +++ b/scripts/validate_schemas.py @@ -254,6 +254,20 @@ def _semantic_install_checks(path: Path, manifest: dict) -> list[str]: return out +def unchecked_formats() -> list[str]: + """Formats the schemas declare that this jsonschema install cannot check. + + date-time and uri need the format extras; without them the ten format + constraints of the published schemas were accepted unread. + """ + used = { + match + for path in SCHEMAS.glob("*.json") + for match in re.findall(r'"format"\s*:\s*"([^"]+)"', path.read_text(encoding="utf-8")) + } + return sorted(used - set(FormatChecker().checkers)) + + def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( @@ -263,6 +277,16 @@ def main() -> int: ) args = parser.parse_args() + unchecked = unchecked_formats() + if unchecked: + # jsonschema skips a format it has no checker for, silently. + print( + f"ERROR no checker for format(s) {', '.join(unchecked)}: " + 'install "jsonschema[format-nongpl]"', + file=sys.stderr, + ) + return 1 + errors: list[str] = [] errors.extend( _validate_yaml_directory(ROOT / "emulators", "emulator.schema.json") diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index b5a6c6db..7d08c39d 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -570,6 +570,26 @@ class PipelineRegressions(unittest.TestCase): self.assertTrue(full_models, truth_runs) +class SchemaFormatsAreChecked(unittest.TestCase): + """date-time and uri need jsonschema's format extras; without them the + published format constraints were accepted unread.""" + + @unittest.skipUnless(HAS_JSONSCHEMA, "validating a schema needs jsonschema") + def test_a_missing_checker_is_named(self): + import validate_schemas + + empty = mock.Mock(checkers={}) + with mock.patch.object(validate_schemas, "FormatChecker", return_value=empty): + self.assertEqual(validate_schemas.unchecked_formats(), ["date-time", "uri"]) + + def test_every_install_asks_for_the_extras(self): + for path in (".github/workflows/validate.yml", ".github/workflows/deploy-site.yml", + "pyproject.toml"): + text = (ROOT / path).read_text(encoding="utf-8") + with self.subTest(path=path): + self.assertNotRegex(text, r"jsonschema(?!\[format)[=>]") + + class ResolverRegressions(unittest.TestCase): def _database(self, entries: dict[str, Path], suffix: str | None = None) -> dict: files = {} diff --git a/tests/test_check_freshness.py b/tests/test_check_freshness.py index 809f8b56..eedf04a1 100644 --- a/tests/test_check_freshness.py +++ b/tests/test_check_freshness.py @@ -318,5 +318,13 @@ class RepeatedDestinations(unittest.TestCase): self.assertEqual(diff.files_changed, ["pce/syscard3.pce"]) +class PinsWithExtras(unittest.TestCase): + def test_an_extras_bracket_keeps_its_pin(self): + from check_freshness import parse_pip_pins # noqa: PLC0415 + + pins = parse_pip_pins(' run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0"') + self.assertEqual(pins["jsonschema"], "==4.26.0") + + if __name__ == "__main__": unittest.main()