diff --git a/.github/workflows/show_specification_annotations.yml b/.github/workflows/show_specification_annotations.yml new file mode 100644 index 00000000..634fa42b --- /dev/null +++ b/.github/workflows/show_specification_annotations.yml @@ -0,0 +1,21 @@ +name: Show Specification Annotations + +on: + pull_request: + paths: + - 'tests/**' + +jobs: + annotate: + runs-on: ubuntu-latest + + steps: + uses: actions/checkout@v4 + + - name: Generate Annotations + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Run Python script + run: python bin/annotation_workflow.py diff --git a/bin/annotation_workflow.py b/bin/annotation_workflow.py new file mode 100644 index 00000000..cee3d654 --- /dev/null +++ b/bin/annotation_workflow.py @@ -0,0 +1,67 @@ +import json +from pathlib import Path + +def github_action_notice(file, url, line): + """ + Print GitHub action notice with file path, URL, and line number. + + Parameters: + file (str): File path. + url (str): URL. + line (int): Line number. + """ + return f"::warning file={file},line={line}::Annotation: {url}" + +def find_test_line_number(test_content, test_name): + """ + Find the line number of a test in the JSON content. + + Parameters: + test_content (str): JSON content. + test_name (str): Test name. + + Returns: + int: Line number of the test. + """ + lines = test_content.split("\n") # Split content into lines + for i, line in enumerate(lines, start=1): # Iterate over lines + if test_name in line: # Check if test name is found in the line + return i # Return the line number if found + return 1 # Return None if test name is not found + +# Specify the path to the JSON file using pathlib.Path +json_file_path = Path("bin/specification_urls.json") + +# Read specification URLs from JSON file using pathlib.Path +with json_file_path.open("r", encoding="utf-8") as f: + urls = json.load(f) + +# Iterate through JSON files in tests folder and subdirectories + +for file_path in Path("tests").rglob("*.json"): + # Read the file content using pathlib.Path + with file_path.open('r', encoding='utf-8') as f: + changed_file_content = f.read() + + # Parse JSON content + try: + json_content = json.loads(changed_file_content) + for test in json_content: + if "specification" in test: + line_number = find_test_line_number(changed_file_content, test.get("description") ) + + for specification_object in test["specification"]: + for spec, section in specification_object.items(): + draft = file_path.parent.name + if spec in ["quote"]: + continue + elif spec in ["core", "validation", "hyper-schema"]: + url = urls[draft][spec].format(spec=spec, section=section) + else: + url = urls[spec].format(spec=spec, section=section) + annotation = github_action_notice(file_path, url, line_number) + print(annotation) + + except json.JSONDecodeError: + print(f"::error file={file_path}::Failed to parse JSON content") + diff --git a/bin/specification_urls.json b/bin/specification_urls.json new file mode 100644 index 00000000..faa60ea9 --- /dev/null +++ b/bin/specification_urls.json @@ -0,0 +1,34 @@ +{ + "draft3": { + "core": "https://json-schema.org/draft-03/draft-zyp-json-schema-03.pdf" + }, + "draft4": { + "core": "https://json-schema.org/draft-04/draft-zyp-json-schema-04#rfc.section.{section}", + "validation": "https://json-schema.org/draft-04/draft-fge-json-schema-validation-00#rfc.section.{section}", + "hyper-schema": "https://json-schema.org/draft-04/draft-luff-json-hyper-schema-00#rfc.section.{section}" + }, + "draft6": { + "core": "https://json-schema.org/draft-06/draft-wright-json-schema-01#rfc.section.{section}", + "validation": "https://json-schema.org/draft-06/draft-wright-json-schema-validation-01#rfc.section.{section}", + "hyper-schema": "https://json-schema.org/draft-06/draft-wright-json-schema-hyperschema-01#rfc.section.{section}" + }, + "draft7": { + "core": "https://json-schema.org/draft-07/draft-handrews-json-schema-01#rfc.section.{section}", + "validation": "https://json-schema.org/draft-07/draft-handrews-json-schema-validation-01#rfc.section.{section}", + "hyper-schema": "https://json-schema.org/draft-07/draft-handrews-json-schema-hyperschema-01#rfc.section.{section}" + }, + "draft2019-09": { + "core": "https://json-schema.org/draft/2019-09/draft-handrews-json-schema-02#rfc.section.{section}", + "validation": "https://json-schema.org/draft/2019-09/draft-handrews-json-schema-validation-02#rfc.section.{section}", + "hyper-schema": "https://json-schema.org/draft/2019-09/draft-handrews-json-schema-hyperschema-02#rfc.section.{section}" + }, + "draft2020-12": { + "core": "https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01#section-{section}", + "validation": "https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-{section}", + "hyper-schema": "https://json-schema.org/draft/2019-09/draft-handrews-json-schema-hyperschema-02#rfc.section.{section}" + }, + "ecma262": "https://262.ecma-international.org/{section}", + "perl5": "https://perldoc.perl.org/perlre#{section}", + "rfc": "https://www.rfc-editor.org/rfc/{spec}.txt#{section}", + "iso": "https://www.iso.org/obp/ui" +} diff --git a/tests/draft2020-12/allOf.json b/tests/draft2020-12/allOf.json index 9e87903f..e4c0cb32 100644 --- a/tests/draft2020-12/allOf.json +++ b/tests/draft2020-12/allOf.json @@ -1,6 +1,7 @@ [ { "description": "allOf", + "specification":[ { "core": "10.2" } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "allOf": [ @@ -43,6 +44,7 @@ }, { "description": "allOf with base schema", + "specification":[ { "core": "10.2", "validation": "1.2.3", "rfc1234": "2.3", "perl5": "Hello", "iso123": "Anysection" } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "properties": {"bar": {"type": "integer"}}, @@ -92,6 +94,7 @@ }, { "description": "allOf simple types", + "specification":[ { "core": "10.2", "validation": "1.2.3" } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "allOf": [ @@ -114,6 +117,7 @@ }, { "description": "allOf with boolean schemas, all true", + "specification":[ { "core": "10.2", "validation": "1.2.3" } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "allOf": [true, true] @@ -211,6 +215,7 @@ }, { "description": "allOf with the last empty schema", + "specification":[ { "core": "10.2", "validation": "1.2.3" } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "allOf": [ @@ -233,6 +238,7 @@ }, { "description": "nested allOf, to check validation semantics", + "specification":[ { "core": "10.2", "validation": "1.2.3" } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "allOf": [ @@ -260,6 +266,7 @@ }, { "description": "allOf combined with anyOf, oneOf", + "specification": [ { "core":"10.3.2.3", "quote": "The value of \"additionalProperties\" MUST be a valid JSON Schema." } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "allOf": [ { "multipleOf": 2 } ], diff --git a/tests/draft2020-12/anchor.json b/tests/draft2020-12/anchor.json index 99143fa1..546e3a88 100644 --- a/tests/draft2020-12/anchor.json +++ b/tests/draft2020-12/anchor.json @@ -52,6 +52,7 @@ }, { "description": "Location-independent identifier with base URI change in subschema", + "specification": [ { "core":"10.3.2.3", "quote": "The value of \"additionalProperties\" MUST be a valid JSON Schema." } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "http://localhost:1234/draft2020-12/root",