Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/scripts/check_spec_coverage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env python3
"""Map a spec-diff to affected docs.pinecone.io pages + impacted SDKs via the manifest.

Reads spec-diff.json (from extract_spec_diff.py) and spec-manifest.json, joins on
operation id / schema name, and writes spec-gaps.json. Changed surface absent from
the manifest is logged to spec-gaps-unmapped.json so the manifest can be kept current.
Sets the `has_gaps` GitHub Actions output.
"""
import argparse
import json
import os
from pathlib import Path


def main():
ap = argparse.ArgumentParser()
ap.add_argument("--spec-diff", required=True)
ap.add_argument("--manifest", required=True)
ap.add_argument("--output", default="spec-gaps.json")
a = ap.parse_args()

diff = json.loads(Path(a.spec_diff).read_text())
man = json.loads(Path(a.manifest).read_text())
ops = man.get("operations", {})
schs = man.get("schemas", {})

gaps, unmapped = [], []
for c in diff:
if c["kind"] == "operation":
entry = ops.get(c["id"])
else:
# schema ids may be "Schema" or "Schema.property" — map by schema name
entry = schs.get(c["id"].split(".")[0])
if not entry:
unmapped.append(c)
continue
for page in entry.get("docs", []):
gaps.append({
"symbol": c["id"], "change": c["change"], "breaking": c.get("breaking", False),
"doc_page": page, "sdks": entry.get("sdks", []), "detail": c.get("detail", ""),
})

Path(a.output).write_text(json.dumps(gaps, indent=2))
if unmapped:
Path("spec-gaps-unmapped.json").write_text(json.dumps(unmapped, indent=2))

has_gaps = bool(gaps)
gh = os.environ.get("GITHUB_OUTPUT")
if gh:
with open(gh, "a") as f:
f.write(f"has_gaps={'true' if has_gaps else 'false'}\n")
pages = len({g["doc_page"] for g in gaps})
print(f"{len(gaps)} doc-gaps across {pages} page(s); {len(unmapped)} unmapped")


if __name__ == "__main__":
main()
31 changes: 31 additions & 0 deletions .github/scripts/tests/test_check_spec_coverage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Unit test for check_spec_coverage. Run: pytest .github/scripts/tests -v"""
import json
import os
import subprocess
import sys


def test_maps_to_docs_and_sdks_and_unmapped(tmp_path):
diff = [
{"kind": "operation", "id": "create_index", "change": "modified", "breaking": True, "detail": "x"},
{"kind": "schema", "id": "Index.host", "change": "added", "breaking": False, "detail": "y"},
{"kind": "operation", "id": "unknown_op", "change": "added", "breaking": False, "detail": "z"},
]
man = {"operations": {"create_index": {"docs": ["guides/index-data/create-an-index"], "sdks": ["python", "ts"]}},
"schemas": {"Index": {"docs": ["guides/index-data/indexes"], "sdks": ["python"]}}}
dp = tmp_path / "diff.json"
mp = tmp_path / "man.json"
op = tmp_path / "gaps.json"
dp.write_text(json.dumps(diff))
mp.write_text(json.dumps(man))
script = os.path.join(os.path.dirname(__file__), "..", "check_spec_coverage.py")
r = subprocess.run([sys.executable, script, "--spec-diff", str(dp),
"--manifest", str(mp), "--output", str(op)],
capture_output=True, text=True, cwd=str(tmp_path))
assert r.returncode == 0, r.stderr
gaps = json.loads(op.read_text())
pages = {g["doc_page"] for g in gaps}
assert "guides/index-data/create-an-index" in pages
assert "guides/index-data/indexes" in pages # schema property mapped via "Index"
assert any(g["breaking"] for g in gaps)
assert all(g["symbol"] != "unknown_op" for g in gaps) # unmapped, not in gaps