-
Notifications
You must be signed in to change notification settings - Fork 0
docs: plug all documentation gaps in the v2 spec #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
33ac7c1
docs: plug all documentation gaps identified in audit
Copilot 0a4318f
ci: add permissions: contents: read to validate workflow
Copilot 0c2ef1e
chore: document merge conflict reconciliation
mdheller 07ac5c1
Merge pull request #10 from SourceOS-Linux/codex/resolve-merge-confli…
mdheller 369f8c0
Merge remote-tracking branch 'origin/main' into copilot/audit-documen…
Copilot 41ca6d1
docs: fix all post-merge duplication and consistency issues
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,217 @@ | ||
| name: Validate Spec | ||
|
|
||
| on: | ||
| push: | ||
| branches: ["main"] | ||
| pull_request: | ||
| branches: ["main"] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| validate-schemas: | ||
| name: Validate JSON Schemas | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: "20" | ||
|
|
||
| - name: Install AJV CLI | ||
| run: npm install -g ajv-cli@5 | ||
|
|
||
| - name: Compile all schemas (syntax check) | ||
| run: | | ||
| failed=0 | ||
| for schema in schemas/*.json; do | ||
| echo -n "Compiling $schema ... " | ||
| if ajv compile -s "$schema" --strict=false 2>&1; then | ||
| echo "ok" | ||
| else | ||
| echo "FAILED" | ||
| failed=1 | ||
| fi | ||
| done | ||
| exit $failed | ||
|
|
||
| - name: Validate examples against schemas | ||
| run: | | ||
| python3 - << 'EOF' | ||
| import json, glob, os, sys | ||
|
|
||
| # Pre-load all schemas into a registry keyed by both $id and local path | ||
| schema_dir = 'schemas' | ||
| registry = {} | ||
| for schema_file in glob.glob(f'{schema_dir}/*.json'): | ||
| with open(schema_file) as f: | ||
| schema = json.load(f) | ||
| # Register by $id | ||
| if '$id' in schema: | ||
| registry[schema['$id']] = schema | ||
| # Register by local relative path (used in $ref) | ||
| base = os.path.basename(schema_file) | ||
| registry[f'./{base}'] = schema | ||
| registry[base] = schema | ||
|
|
||
| from jsonschema import RefResolver, validate, ValidationError | ||
|
|
||
| failed = 0 | ||
| skipped = 0 | ||
| passed = 0 | ||
|
|
||
| for example_path in sorted(glob.glob('examples/*.json')): | ||
| with open(example_path) as f: | ||
| example = json.load(f) | ||
|
|
||
| etype = example.get('type') | ||
| if not etype: | ||
| print(f"SKIP {example_path} (no top-level type field — sub-schema example)") | ||
| skipped += 1 | ||
| continue | ||
|
|
||
| schema_path = f'{schema_dir}/{etype}.json' | ||
| if not os.path.exists(schema_path): | ||
| print(f"WARN {example_path}: schema '{schema_path}' not found, skipping") | ||
| skipped += 1 | ||
| continue | ||
|
|
||
| with open(schema_path) as f: | ||
| schema = json.load(f) | ||
|
|
||
| # Build a resolver that serves refs from the pre-loaded registry | ||
| base_uri = f'file://{os.path.abspath(schema_dir)}/' | ||
|
|
||
| class LocalRegistry(RefResolver): | ||
| def resolve_remote(self, uri): | ||
| # Strip fragment | ||
| clean = uri.split('#')[0] | ||
| # Try registry first | ||
| if clean in registry: | ||
| return registry[clean] | ||
| # Try by basename | ||
| name = os.path.basename(clean) | ||
| if name in registry: | ||
| return registry[name] | ||
| # Fall back to file:// resolution | ||
| return super().resolve_remote(uri) | ||
|
|
||
| resolver = LocalRegistry(base_uri=base_uri, referrer=schema, store=registry) | ||
|
|
||
| try: | ||
| validate(example, schema, resolver=resolver) | ||
| print(f"ok {example_path}") | ||
| passed += 1 | ||
| except ValidationError as e: | ||
| print(f"FAIL {example_path}: {e.message}", file=sys.stderr) | ||
| failed += 1 | ||
| except Exception as e: | ||
| print(f"FAIL {example_path}: {type(e).__name__}: {e}", file=sys.stderr) | ||
| failed += 1 | ||
|
|
||
| print(f"\nResults: {passed} passed, {skipped} skipped, {failed} failed") | ||
| sys.exit(failed) | ||
| EOF | ||
|
|
||
| validate-openapi: | ||
| name: Validate OpenAPI | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: "20" | ||
|
|
||
| - name: Install Spectral | ||
| run: npm install -g @stoplight/spectral-cli | ||
|
|
||
| - name: Lint openapi.yaml | ||
| run: spectral lint openapi.yaml --ruleset https://unpkg.com/@stoplight/spectral-openapi/dist/ruleset.js || true | ||
|
|
||
| - name: Validate openapi.yaml parses as valid OpenAPI | ||
| run: | | ||
| python3 - << 'EOF' | ||
| import yaml, sys | ||
| try: | ||
| with open('openapi.yaml') as f: | ||
| doc = yaml.safe_load(f) | ||
| assert doc.get('openapi', '').startswith('3.'), "Not an OpenAPI 3.x document" | ||
| assert 'info' in doc | ||
| assert 'paths' in doc | ||
| print("openapi.yaml: valid structure") | ||
| except Exception as e: | ||
| print(f"openapi.yaml: FAILED - {e}", file=sys.stderr) | ||
| sys.exit(1) | ||
| EOF | ||
|
|
||
| validate-asyncapi: | ||
| name: Validate AsyncAPI | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Validate asyncapi.yaml parses correctly | ||
| run: | | ||
| python3 - << 'EOF' | ||
| import yaml, sys | ||
| try: | ||
| with open('asyncapi.yaml') as f: | ||
| doc = yaml.safe_load(f) | ||
| assert doc.get('asyncapi', '').startswith('2.'), "Not an AsyncAPI 2.x document" | ||
| assert 'info' in doc | ||
| assert 'channels' in doc | ||
| channels = list(doc['channels'].keys()) | ||
| print(f"asyncapi.yaml: valid structure, {len(channels)} channels: {channels}") | ||
| except Exception as e: | ||
| print(f"asyncapi.yaml: FAILED - {e}", file=sys.stderr) | ||
| sys.exit(1) | ||
| EOF | ||
|
|
||
| validate-jsonld: | ||
| name: Validate JSON-LD | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Check JSON-LD files are valid JSON | ||
| run: | | ||
| python3 - << 'EOF' | ||
| import json, sys, glob | ||
|
|
||
| failed = 0 | ||
| for path in glob.glob('semantic/*.jsonld'): | ||
| try: | ||
| with open(path) as f: | ||
| doc = json.load(f) | ||
| ctx = doc.get('@context', {}) | ||
| mapped = [k for k, v in ctx.items() if isinstance(v, str) and (v.startswith('srcos:') or v.startswith('prov:'))] | ||
| print(f"{path}: valid JSON, {len(mapped)} type mappings") | ||
| except Exception as e: | ||
| print(f"{path}: FAILED - {e}", file=sys.stderr) | ||
| failed = 1 | ||
|
|
||
| sys.exit(failed) | ||
| EOF | ||
|
|
||
| - name: Check all schemas are mapped in context.jsonld | ||
| run: | | ||
| python3 - << 'EOF' | ||
| import json, glob, os, sys | ||
|
|
||
| with open('semantic/context.jsonld') as f: | ||
| ctx = json.load(f)['@context'] | ||
|
|
||
| mapped = set(k for k, v in ctx.items() if isinstance(v, str) and (v.startswith('srcos:') or v.startswith('prov:'))) | ||
| schemas = set(os.path.basename(f).replace('.json', '') for f in glob.glob('schemas/*.json')) | ||
|
|
||
| missing = schemas - mapped | ||
| if missing: | ||
| print(f"WARNING: {len(missing)} schema(s) not mapped in context.jsonld: {sorted(missing)}") | ||
| else: | ||
|
mdheller marked this conversation as resolved.
|
||
| print(f"All {len(schemas)} schemas are mapped in context.jsonld") | ||
| EOF | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.