validate_configs: use referencing library

The script was raising the following deprecation warning:
```
DeprecationWarning: jsonschema.RefResolver is deprecated as of v4.18.0,
in favor of the https://github.com/python-jsonschema/referencing library,
which provides more compliant referencing behavior as well as more
flexible APIs for customization. A future release will remove
RefResolver. Please file a feature request (on referencing) if you are
missing an API for the kind of customization you need.
```

Update the script to use the referencing library.  Unfortunately, this
moves the URI crawling into our code rather than as handled by the
RefResolver.

Change-Id: I742e4dff9470849947f24866bd0cfcbd0173170c
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
diff --git a/scripts/validate_configs.py b/scripts/validate_configs.py
index 99af69c..9ff1785 100755
--- a/scripts/validate_configs.py
+++ b/scripts/validate_configs.py
@@ -3,6 +3,7 @@
 """
 A tool for validating entity manager configurations.
 """
+
 import argparse
 import json
 import os
@@ -10,7 +11,10 @@
 import sys
 from concurrent.futures import ProcessPoolExecutor
 
+import jsonschema.exceptions
 import jsonschema.validators
+import referencing
+from referencing.jsonschema import DRAFT202012
 
 DEFAULT_SCHEMA_FILENAME = "global.json"
 
@@ -203,17 +207,46 @@
 
 
 def validator_from_file(schema_file):
-    schema = {}
-    with open(schema_file) as fd:
-        schema = json.load(fd)
+    # Get root directory of schema file, so we can walk all the directories
+    # for referenced schemas.
+    schema_path = os.path.dirname(schema_file)
 
-    spec = jsonschema.Draft202012Validator
-    spec.check_schema(schema)
-    base_uri = "file://{}/".format(
-        os.path.split(os.path.realpath(schema_file))[0]
+    root_schema = None
+    registry = referencing.Registry()
+
+    # Pre-load all .json files from the schemas directory and its subdirectories
+    # into the registry. This allows $refs to resolve to any schema.
+    for dirpath, _, directory in os.walk(schema_path):
+        for filename in directory:
+            if filename.endswith(".json"):
+                full_file_path = os.path.join(dirpath, filename)
+
+                # The URI  is their path relative to schema_path.
+                relative_uri = os.path.relpath(full_file_path, schema_path)
+
+                with open(full_file_path, "r") as fd:
+                    schema_contents = json.loads(remove_c_comments(fd.read()))
+                    jsonschema.validators.Draft202012Validator.check_schema(
+                        schema_contents
+                    )
+
+                    # Add to the registry.
+                    registry = registry.with_resource(
+                        uri=relative_uri,
+                        resource=referencing.Resource.from_contents(
+                            schema_contents, default_specification=DRAFT202012
+                        ),
+                    )
+
+                    # If this was the schema_file we need to save the contents
+                    # as the root schema.
+                    if schema_file == full_file_path:
+                        root_schema = schema_contents
+
+    # Create the validator instance with the schema content and the configured registry.
+    validator = jsonschema.validators.Draft202012Validator(
+        root_schema, registry=registry
     )
-    resolver = jsonschema.RefResolver(base_uri, schema)
-    validator = spec(schema, resolver=resolver)
 
     return validator
 
@@ -235,11 +268,7 @@
         if not expect_fail:
             is_invalid = True
             if args.verbose:
-                print(e)
-    except FileNotFoundError:
-        is_invalid = True
-        if args.verbose:
-            print(f"Could not read schema file: {schema_file}")
+                print(f"Validation Error for {filename}: {e}")
 
     return (is_invalid, is_unexpected_pass)