Skip to content

Commit f889a94

Browse files
committed
SHACL validation
1 parent 7ad71f6 commit f889a94

4 files changed

Lines changed: 112 additions & 1 deletion

File tree

.github/workflows/validate.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: validate
2+
on:
3+
push:
4+
branches: [ main ]
5+
pull_request:
6+
branches: [ main ]
7+
8+
jobs:
9+
validate_manifest:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v5
14+
- uses: astral-sh/setup-uv@v7
15+
- run: uv run validate_manifest.py

ns/shapes.ttl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# SHACL shapes for tests
2+
3+
PREFIX mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#>
4+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
5+
PREFIX rdft: <http://www.w3.org/ns/rdftest#>
6+
PREFIX sh: <http://www.w3.org/ns/shacl#>
7+
8+
[
9+
a sh:NodeShape ;
10+
sh:targetClass mf:Manifest ;
11+
sh:property [
12+
sh:path mf:include ;
13+
sh:maxCount 1
14+
] ;
15+
sh:property [
16+
sh:path mf:entries ;
17+
sh:maxCount 1
18+
]
19+
] .
20+
21+
[
22+
a sh:NodeShape ;
23+
sh:targetClass rdft:Test ;
24+
sh:property [
25+
sh:path mf:action ;
26+
sh:nodeKind sh:IRI ;
27+
sh:maxCount 1
28+
] ;
29+
sh:property [
30+
sh:path mf:result ;
31+
sh:or ( [ sh:nodeKind sh:IRI ] [ sh:hasValue false ] ) ;
32+
sh:maxCount 1
33+
] ;
34+
sh:property [
35+
sh:path rdft:approval ;
36+
sh:nodeKind sh:IRI ;
37+
sh:in (rdft:Approved rdft:Proposed rdft:Rejected rdft:NotClassified) ; # TODO: rdft:NotClassified is not in the vocabulary but used
38+
sh:maxCount 1
39+
]
40+
] .

rdf/rdf12/rdf-semantics/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ <h2>
541541
<dt>type</dt>
542542
<dd>mf:PositiveEntailmentTest</dd>
543543
<dt>approval</dt>
544-
<dd property='mf:approval' resource=''>none</dd>
544+
<dd property='mf:approval' resource='rdft:NotClassified'>rdft:NotClassified</dd>
545545
<dt>entailmentRegime</dt>
546546
<dd property='mf:entailmentRegime'></dd>
547547
<dt>recognizedDatatypes</dt>

validate_manifest.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# /// script
2+
# dependencies = [
3+
# "pyshacl~=0.30.0",
4+
# ]
5+
# ///
6+
"""
7+
Small Python script to validate manifest files
8+
"""
9+
10+
import os
11+
from pathlib import Path
12+
from pyshacl import validate
13+
14+
repository_root = Path(__file__).parent
15+
shape_path = repository_root / "ns" / "shapes.ttl"
16+
vocab_path = repository_root / "ns" / "rdftest.ttl"
17+
18+
19+
def log_line(message: str, level: str, file: Path | None = None) -> None:
20+
if "GITHUB_ACTIONS" in os.environ:
21+
print(
22+
f"::{level}{f' file={file.relative_to(repository_root)}' if file else ''}::{message}"
23+
)
24+
else:
25+
print(message)
26+
27+
28+
failure_counter = 0
29+
total_counter = 0
30+
for manifest_path in repository_root.rglob("manifest*.ttl"):
31+
try:
32+
(conforms, _, results_text) = validate(
33+
str(manifest_path),
34+
shacl_graph=str(shape_path),
35+
ont_graph=str(vocab_path),
36+
inference="rdfs",
37+
)
38+
except SyntaxError as e:
39+
conforms = False
40+
results_text = str(e)
41+
if not conforms:
42+
log_line(
43+
f"Error in {manifest_path.relative_to(repository_root)}: results_text",
44+
"error",
45+
manifest_path,
46+
)
47+
failure_counter += 1
48+
total_counter += 1
49+
50+
log_line(
51+
f"Found {failure_counter} files with error"
52+
if failure_counter > 0
53+
else f"Validated {total_counter} files without errors",
54+
"notice",
55+
)
56+
exit(int(failure_counter > 0))

0 commit comments

Comments
 (0)