You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sourcelume.apache.org by jg...@apache.org on 2026/09/05 18:36:53 UTC
(sourcelume-spec) 01/02: Initial content for context, schema, and specification. Initial tooling for validation.
This is an automated email from the ASF dual-hosted git repository.
jgoodyear pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sourcelume-spec.git
commit 24a31fd13bb7bbae0215ff12eb5fd4560ee5ffb3
Author: jgoodyear <jg...@apache.org>
AuthorDate: Sat Sep 5 16:05:54 2026 -0230
Initial content for context, schema, and specification. Initial tooling for validation.
Apache-ai: Yes
Generated-by: PyCharm AI Assistant (Claude 4.5 Sonnet)
Reviewed-by: jgoodyear
---
.gitignore | 3 +
SETUP.md | 23 +++++
VERSIONING.md | 17 ++++
context/0.0.1/sourcelume.jsonld | 16 ++++
pyproject.toml | 17 ++++
schema/0.0.1/sourcelume.schema.json | 36 ++++++++
schema/0.0.1/sourcelume.shacl.ttl | 52 +++++++++++
spec/0.0.1/index.md | 74 +++++++++++++++
tests/test_validate.py | 104 +++++++++++++++++++++
tools/render-docs.py | 115 ++++++++++++++++++++++++
tools/validate.py | 175 ++++++++++++++++++++++++++++++++++++
11 files changed, 632 insertions(+)
diff --git a/.gitignore b/.gitignore
index c8e7871..eeac8aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,9 @@ __pycache__/
# Maven / Java build output
target/
+# Generated docs (see tools/render-docs.py)
+spec/*/generated-field-reference.md
+
# Editor/OS noise
.vscode/
*.swp
\ No newline at end of file
diff --git a/SETUP.md b/SETUP.md
index ed08b0c..b12eb1d 100644
--- a/SETUP.md
+++ b/SETUP.md
@@ -1,3 +1,20 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
# Apache Sourcelume Spec — Development Setup
This document describes how to set up a local development environment for the
@@ -93,6 +110,12 @@ Example:
uv run python tools/validate.py
```
+To test the validation tooling:
+```bash
+uv run python -m unittest discover -s tests
+```
+
+
The validation tooling is expected to check things such as:
- JSON-LD example syntax
diff --git a/VERSIONING.md b/VERSIONING.md
index 823d3fe..9d3cf12 100644
--- a/VERSIONING.md
+++ b/VERSIONING.md
@@ -1,3 +1,20 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
# Versioning
This project follows [Semantic Versioning](https://semver.org)
diff --git a/context/0.0.1/sourcelume.jsonld b/context/0.0.1/sourcelume.jsonld
new file mode 100644
index 0000000..35abea4
--- /dev/null
+++ b/context/0.0.1/sourcelume.jsonld
@@ -0,0 +1,16 @@
+{
+ "@context": {
+ "@version": 1.1,
+ "sl": "https://sourcelume.apache.org/ns/0.0.1#",
+ "id": "@id",
+ "type": "@type",
+
+ "name": "sl:name",
+ "description": "sl:description",
+ "version": "sl:version",
+ "createdAt": {
+ "@id": "sl:createdAt",
+ "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
+ }
+ }
+}
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index a7d7892..69f6a23 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
[project]
name = "sourcelume-spec"
version = "0.0.1"
diff --git a/schema/0.0.1/sourcelume.schema.json b/schema/0.0.1/sourcelume.schema.json
new file mode 100644
index 0000000..5a682f7
--- /dev/null
+++ b/schema/0.0.1/sourcelume.schema.json
@@ -0,0 +1,36 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://sourcelume.apache.org/schema/0.0.1/sourcelume.schema.json",
+ "title": "Apache Sourcelume Record",
+ "description": "Minimal skeleton schema for an Apache Sourcelume record. Fields and constraints will be filled in as the specification develops.",
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique identifier (IRI) for this record."
+ },
+ "type": {
+ "type": "string",
+ "description": "The record type."
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name for the record."
+ },
+ "description": {
+ "type": "string",
+ "description": "Free-text description of the record."
+ },
+ "version": {
+ "type": "string",
+ "description": "Version of the record content, e.g. following semver."
+ },
+ "createdAt": {
+ "type": "string",
+ "format": "date-time",
+ "description": "Creation timestamp, ISO 8601."
+ }
+ },
+ "required": ["id", "type"],
+ "additionalProperties": true
+}
\ No newline at end of file
diff --git a/schema/0.0.1/sourcelume.shacl.ttl b/schema/0.0.1/sourcelume.shacl.ttl
new file mode 100644
index 0000000..ac577f0
--- /dev/null
+++ b/schema/0.0.1/sourcelume.shacl.ttl
@@ -0,0 +1,52 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+@prefix sh: <http://www.w3.org/ns/shacl#> .
+@prefix sl: <https://sourcelume.apache.org/ns/0.0.1#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+# Minimal skeleton SHACL shape for a Sourcelume record.
+# Constraints will be expanded as the specification develops, and should
+# stay aligned with schema/0.0.1/sourcelume.schema.json.
+
+sl:RecordShape
+ a sh:NodeShape ;
+ sh:targetClass sl:Record ;
+
+ sh:property [
+ sh:path sl:name ;
+ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ ] ;
+
+ sh:property [
+ sh:path sl:description ;
+ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ ] ;
+
+ sh:property [
+ sh:path sl:version ;
+ sh:datatype xsd:string ;
+ sh:maxCount 1 ;
+ ] ;
+
+ sh:property [
+ sh:path sl:createdAt ;
+ sh:datatype xsd:dateTime ;
+ sh:maxCount 1 ;
+ ] .
\ No newline at end of file
diff --git a/spec/0.0.1/index.md b/spec/0.0.1/index.md
new file mode 100644
index 0000000..a2dd094
--- /dev/null
+++ b/spec/0.0.1/index.md
@@ -0,0 +1,74 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+# Apache Sourcelume Specification — v0.0.1
+
+> **Status:** Draft / skeleton. This version is a starting point for
+> development and has not been released. Content will be filled in as the
+> specification develops. See `VERSIONING.md` for how this version number
+> relates to changes in `context/` and `schema/`.
+
+## 1. Introduction
+
+Sourcelume defines a vocabulary and data model for describing
+[fill in: what Sourcelume records represent, and the problem the spec
+solves].
+
+## 2. Terminology
+
+This section defines terms used throughout the specification.
+
+- **Record** — [definition]
+
+## 3. Data model
+
+This section describes the structure of a Sourcelume record.
+
+Normative field definitions live in the corresponding JSON Schema
+(`schema/0.0.1/sourcelume.schema.json`) and SHACL shapes
+(`schema/0.0.1/sourcelume.shacl.ttl`). This document should describe the
+*meaning* and intended usage of each field; the schema/shapes describe the
+*constraints*.
+
+| Field | Type | Required | Description |
+|---------------|----------|----------|---------------------------------------|
+| `id` | IRI | Yes | Unique identifier for the record. |
+| `type` | string | Yes | The record type. |
+| `name` | string | No | Human-readable name. |
+| `description` | string | No | Free-text description. |
+| `version` | string | No | Version of the record content. |
+| `createdAt` | datetime | No | Creation timestamp (ISO 8601). |
+
+## 4. JSON-LD context
+
+Sourcelume records are expressed as JSON-LD using the context published at
+`context/0.0.1/sourcelume.jsonld`.
+
+## 5. Examples
+
+See `examples/` for sample records conforming to this version of the
+specification.
+
+## 6. Conformance
+
+[fill in: what it means for a document/tool to conform to this version of
+the spec]
+
+## 7. Changes from previous versions
+
+This is the first version of the specification; there is no prior version
+to compare against.
\ No newline at end of file
diff --git a/tests/test_validate.py b/tests/test_validate.py
new file mode 100644
index 0000000..58fe3db
--- /dev/null
+++ b/tests/test_validate.py
@@ -0,0 +1,104 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from __future__ import annotations
+
+import importlib.util
+import json
+import tempfile
+import unittest
+from pathlib import Path
+
+
+REPO_ROOT = Path(__file__).resolve().parent.parent
+VALIDATE_PATH = REPO_ROOT / "tools" / "validate.py"
+
+
+def load_validate_module():
+ spec = importlib.util.spec_from_file_location("validate", VALIDATE_PATH)
+ if spec is None or spec.loader is None:
+ raise RuntimeError(f"Could not load {VALIDATE_PATH}")
+
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ return module
+
+
+validate = load_validate_module()
+
+
+class ValidateToolTests(unittest.TestCase):
+ def test_find_version_dirs_returns_only_semver_dirs(self) -> None:
+ with tempfile.TemporaryDirectory() as tmp:
+ base = Path(tmp)
+
+ (base / "0.0.1").mkdir()
+ (base / "1.2.3").mkdir()
+ (base / "latest").mkdir()
+ (base / "1-draft").mkdir()
+ (base / "2026-old").mkdir()
+ (base / "not-a-version").mkdir()
+
+ version_dirs = validate.find_version_dirs(base)
+
+ self.assertEqual(
+ [path.name for path in version_dirs],
+ ["0.0.1", "1.2.3"],
+ )
+
+ def test_find_version_dirs_returns_empty_for_missing_base(self) -> None:
+ with tempfile.TemporaryDirectory() as tmp:
+ missing = Path(tmp) / "missing"
+
+ self.assertEqual(validate.find_version_dirs(missing), [])
+
+ def test_check_json_file_accepts_valid_json(self) -> None:
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "valid.jsonld"
+ path.write_text(json.dumps({"@context": {}, "id": "example"}), encoding="utf-8")
+
+ self.assertEqual(validate.check_json_file(path), [])
+
+ def test_check_json_file_reports_invalid_json(self) -> None:
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "invalid.jsonld"
+ path.write_text("{ invalid json", encoding="utf-8")
+
+ errors = validate.check_json_file(path)
+
+ self.assertEqual(len(errors), 1)
+ self.assertIn("invalid JSON", errors[0])
+
+ def test_require_file_accepts_existing_file(self) -> None:
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "asset.json"
+ path.write_text("{}", encoding="utf-8")
+
+ self.assertEqual(validate.require_file(path), [])
+
+ def test_require_file_reports_missing_file(self) -> None:
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "missing.json"
+
+ errors = validate.require_file(path)
+
+ self.assertEqual(len(errors), 1)
+ self.assertIn("missing required file", errors[0])
+
+
+if __name__ == "__main__":
+ unittest.main()
\ No newline at end of file
diff --git a/tools/render-docs.py b/tools/render-docs.py
new file mode 100644
index 0000000..df4b35e
--- /dev/null
+++ b/tools/render-docs.py
@@ -0,0 +1,115 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Render human-readable docs from the JSON Schema.
+
+This is a starting skeleton. It currently generates a single Markdown
+field-reference table per published schema version, from
+`schema/<version>/sourcelume.schema.json`. It does not yet render SHACL
+shapes, cross-link to the prose spec, or produce HTML output — see
+SETUP.md for the intended scope.
+
+Usage:
+ uv run python tools/render-docs.py
+
+Output is written to `spec/<version>/generated-field-reference.md` for
+each published schema version. Generated files are not intended to be
+committed until the project decides how generated output should be
+handled (see the "Notes for contributors" section of SETUP.md).
+Note: gitIgnores spec/*/generated-field-reference.md entry exists.
+"""
+
+from __future__ import annotations
+
+import json
+import sys
+from pathlib import Path
+
+REPO_ROOT = Path(__file__).resolve().parent.parent
+SCHEMA_DIR = REPO_ROOT / "schema"
+SPEC_DIR = REPO_ROOT / "spec"
+
+# Find published schema version directories that should have docs generated.
+def find_version_dirs(base: Path) -> list[Path]:
+ """Return published version directories under `base` (e.g. schema/0.0.1)."""
+ if not base.is_dir():
+ return []
+ return sorted(
+ p for p in base.iterdir()
+ if p.is_dir() and not p.is_symlink() and p.name[0].isdigit()
+ )
+
+
+# Build a Markdown field-reference table from a JSON Schema dictionary.
+def render_field_table(schema: dict) -> str:
+ properties = schema.get("properties", {})
+ required = set(schema.get("required", []))
+
+ lines = [
+ f"# {schema.get('title', 'Schema Reference')}",
+ "",
+ "> Generated from the JSON Schema. Do not edit by hand — edit the",
+ "> schema and re-run `tools/render-docs.py` instead.",
+ "",
+ "| Field | Type | Required | Description |",
+ "|-------|------|----------|-------------|",
+ ]
+
+ for field_name, field_def in properties.items():
+ field_type = field_def.get("type", field_def.get("format", "—"))
+ is_required = "Yes" if field_name in required else "No"
+ description = field_def.get("description", "").replace("\n", " ")
+ lines.append(f"| `{field_name}` | {field_type} | {is_required} | {description} |")
+
+ return "\n".join(lines) + "\n"
+
+
+# Render generated documentation for one schema version and return the output path.
+def render_version(version_dir: Path) -> Path | None:
+ schema_files = list(version_dir.glob("*.schema.json"))
+ if not schema_files:
+ return None
+
+ schema = json.loads(schema_files[0].read_text())
+ doc = render_field_table(schema)
+
+ output_dir = SPEC_DIR / version_dir.name
+ output_dir.mkdir(parents=True, exist_ok=True)
+ output_path = output_dir / "generated-field-reference.md"
+ output_path.write_text(doc)
+ return output_path
+
+
+# Generate documentation for all available schema versions and return an exit code.
+def main() -> int:
+ version_dirs = find_version_dirs(SCHEMA_DIR)
+
+ if not version_dirs:
+ print("No schema versions found under schema/.")
+ return 0
+
+ for version_dir in version_dirs:
+ output_path = render_version(version_dir)
+ if output_path:
+ print(f"Wrote {output_path}")
+ else:
+ print(f"Skipped {version_dir}: no *.schema.json file found.")
+
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/tools/validate.py b/tools/validate.py
new file mode 100644
index 0000000..9528b65
--- /dev/null
+++ b/tools/validate.py
@@ -0,0 +1,175 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Validate Sourcelume specification assets.
+
+This is a starting skeleton. It currently checks that the versioned
+schema, context, and SHACL files for each published version are present
+and syntactically valid (valid JSON / valid Turtle). It does not yet
+validate examples against the schema/SHACL, or check cross-version
+consistency — see SETUP.md for the full intended scope.
+
+Usage:
+ uv run python tools/validate.py
+"""
+
+from __future__ import annotations
+
+import json
+import re
+import sys
+from pathlib import Path
+
+VERSION_DIR_PATTERN = re.compile(r"^\d+\.\d+\.\d+$")
+
+REPO_ROOT = Path(__file__).resolve().parent.parent
+CONTEXT_DIR = REPO_ROOT / "context"
+SCHEMA_DIR = REPO_ROOT / "schema"
+EXAMPLES_DIR = REPO_ROOT / "examples"
+
+# Convert a filesystem path to a repository-relative string when possible.
+def display_path(path: Path) -> str:
+ """Return `path` relative to the repository root when possible."""
+ try:
+ return str(path.relative_to(REPO_ROOT))
+ except ValueError:
+ return str(path)
+
+
+# Check that a required path exists and is a regular file.
+def require_file(path: Path) -> list[str]:
+ """Return an error if `path` is not a regular file."""
+ if not path.is_file():
+ return [f"{display_path(path)}: missing required file"]
+ return []
+
+
+# Find all published version directories whose names match the expected SemVer format.
+def find_version_dirs(base: Path) -> list[Path]:
+ """Return published SemVer-like version directories under `base`."""
+ if not base.is_dir():
+ return []
+ return sorted(
+ p for p in base.iterdir()
+ if p.is_dir() and not p.is_symlink() and VERSION_DIR_PATTERN.fullmatch(p.name)
+ )
+
+
+# Validate that a file can be read and parsed as JSON.
+def check_json_file(path: Path) -> list[str]:
+ errors = []
+ try:
+ json.loads(path.read_text(encoding="utf-8"))
+ except json.JSONDecodeError as exc:
+ errors.append(f"{display_path(path)}: invalid JSON ({exc})")
+ except OSError as exc:
+ errors.append(f"{display_path(path)}: could not read file ({exc})")
+ return errors
+
+
+# Validate that a Turtle file can be read and parsed with rdflib.
+def check_turtle_file(path: Path) -> list[str]:
+ errors = []
+ try:
+ import rdflib
+ except ImportError as exc:
+ errors.append(f"{display_path(path)}: cannot validate Turtle because rdflib is not installed ({exc})")
+ return errors
+
+ try:
+ rdflib.Graph().parse(str(path), format="turtle")
+ except OSError as exc:
+ errors.append(f"{display_path(path)}: could not read file ({exc})")
+ except Exception as exc: # noqa: BLE001 - report any parse failure
+ errors.append(f"{display_path(path)}: invalid Turtle ({exc})")
+ return errors
+
+
+# Validate required schema, context, and SHACL assets for each published version.
+def validate_schema_and_context() -> list[str]:
+ errors: list[str] = []
+
+ context_version_dirs = find_version_dirs(CONTEXT_DIR)
+ schema_version_dirs = find_version_dirs(SCHEMA_DIR)
+
+ context_versions = {version_dir.name for version_dir in context_version_dirs}
+ schema_versions = {version_dir.name for version_dir in schema_version_dirs}
+
+ for version in sorted(context_versions - schema_versions):
+ errors.append(
+ f"{display_path(SCHEMA_DIR / version)}: missing schema directory for context version {version}"
+ )
+
+ for version in sorted(schema_versions - context_versions):
+ errors.append(
+ f"{display_path(CONTEXT_DIR / version)}: missing context directory for schema version {version}"
+ )
+
+ for version_dir in context_version_dirs:
+ context_file = version_dir / "sourcelume.jsonld"
+
+ errors.extend(require_file(context_file))
+ if context_file.is_file():
+ errors.extend(check_json_file(context_file))
+
+ for version_dir in schema_version_dirs:
+ schema_file = version_dir / "sourcelume.schema.json"
+ shacl_file = version_dir / "sourcelume.shacl.ttl"
+
+ errors.extend(require_file(schema_file))
+ errors.extend(require_file(shacl_file))
+
+ if schema_file.is_file():
+ errors.extend(check_json_file(schema_file))
+ if shacl_file.is_file():
+ errors.extend(check_turtle_file(shacl_file))
+
+ return errors
+
+
+# Validate example JSON-LD files when an examples directory is present.
+def validate_examples() -> list[str]:
+ errors: list[str] = []
+ if not EXAMPLES_DIR.is_dir():
+ return errors
+
+ for example_file in EXAMPLES_DIR.rglob("*.jsonld"):
+ if example_file.is_file() and not example_file.is_symlink():
+ errors.extend(check_json_file(example_file))
+
+ # TODO: validate each example against the corresponding version's
+ # schema (jsonschema) and SHACL shapes (pyshacl), once example files
+ # and a real data model exist.
+
+ return errors
+
+
+# Run all validation checks and return a process exit code.
+def main() -> int:
+ errors = validate_schema_and_context() + validate_examples()
+
+ if errors:
+ print("Validation failed:")
+ for error in errors:
+ print(f" - {error}")
+ return 1
+
+ print("Validation passed (skeleton checks only; see TODOs in this file).")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@sourcelume.apache.org
For additional commands, e-mail: commits-help@sourcelume.apache.org