-
-
Notifications
You must be signed in to change notification settings - Fork 209
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #746 from json-schema-org/annotations
Annotate Specification Links in GH Actions
- Loading branch information
Showing
3 changed files
with
170 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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,21 @@ | ||
name: Show Specification Annotations | ||
|
||
on: | ||
pull_request: | ||
paths: | ||
- 'tests/**' | ||
|
||
jobs: | ||
annotate: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Set up Python | ||
uses: actions/setup-python@v5 | ||
with: | ||
python-version: '3.x' | ||
|
||
- name: Generate Annotations | ||
run: pip install uritemplate && bin/annotate-specification-links |
This file contains 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,115 @@ | ||
#!/usr/bin/env python3 | ||
""" | ||
Annotate pull requests to the GitHub repository with links to specifications. | ||
""" | ||
|
||
from __future__ import annotations | ||
|
||
from pathlib import Path | ||
from typing import Any | ||
import json | ||
import re | ||
import sys | ||
|
||
from uritemplate import URITemplate | ||
|
||
|
||
BIN_DIR = Path(__file__).parent | ||
TESTS = BIN_DIR.parent / "tests" | ||
URLS = json.loads(BIN_DIR.joinpath("specification_urls.json").read_text()) | ||
|
||
|
||
def urls(version: str) -> dict[str, URITemplate]: | ||
""" | ||
Retrieve the version-specific URLs for specifications. | ||
""" | ||
for_version = {**URLS["json-schema"][version], **URLS["external"]} | ||
return {k: URITemplate(v) for k, v in for_version.items()} | ||
|
||
|
||
def annotation( | ||
path: Path, | ||
message: str, | ||
line: int = 1, | ||
level: str = "notice", | ||
**kwargs: Any, | ||
) -> str: | ||
""" | ||
Format a GitHub annotation. | ||
See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions | ||
for full syntax. | ||
""" | ||
|
||
if kwargs: | ||
additional = "," + ",".join(f"{k}={v}" for k, v in kwargs.items()) | ||
else: | ||
additional = "" | ||
|
||
relative = path.relative_to(TESTS.parent) | ||
return f"::{level} file={relative},line={line}{additional}::{message}\n" | ||
|
||
|
||
def line_number_of(path: Path, case: dict[str, Any]) -> int: | ||
""" | ||
Crudely find the line number of a test case. | ||
""" | ||
with path.open() as file: | ||
description = case["description"] | ||
return next( | ||
(i + 1 for i, line in enumerate(file, 1) if description in line), | ||
1, | ||
) | ||
|
||
|
||
def main(): | ||
# Clear annotations which may have been emitted by a previous run. | ||
sys.stdout.write("::remove-matcher owner=me::\n") | ||
|
||
for version in TESTS.iterdir(): | ||
if version.name in {"draft-next", "latest"}: | ||
continue | ||
|
||
version_urls = urls(version.name) | ||
|
||
for path in version.rglob("*.json"): | ||
try: | ||
contents = json.loads(path.read_text()) | ||
except json.JSONDecodeError as error: | ||
error = annotation( | ||
level="error", | ||
path=path, | ||
line=error.lineno, | ||
col=error.pos + 1, | ||
title=str(error), | ||
) | ||
sys.stdout.write(error) | ||
|
||
for test_case in contents: | ||
specifications = test_case.get("specification") | ||
if specifications is not None: | ||
for each in specifications: | ||
quote = each.pop("quote", "") | ||
(kind, section), = each.items() | ||
|
||
number = re.search(r"\d+", kind) | ||
spec = "" if number is None else number.group(0) | ||
|
||
url = version_urls[kind].expand( | ||
spec=spec, | ||
section=section, | ||
) | ||
|
||
message = f"{url}\n\n{quote}" if quote else url | ||
sys.stdout.write( | ||
annotation( | ||
path=path, | ||
line=line_number_of(path, test_case), | ||
title="Specification Link", | ||
message=message, | ||
), | ||
) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains 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,34 @@ | ||
{ | ||
"json-schema": { | ||
"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}" | ||
}, | ||
"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}" | ||
}, | ||
"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}" | ||
}, | ||
"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}" | ||
}, | ||
"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}" | ||
}, | ||
"draft3": { | ||
"core": "https://json-schema.org/draft-03/draft-zyp-json-schema-03.pdf" | ||
} | ||
}, | ||
|
||
"external": { | ||
"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" | ||
} | ||
} |