Skip to content
Merged
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
8 changes: 5 additions & 3 deletions pipeline/detect_gaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

import yaml

from util import page_file


REPO_ROOT = Path(__file__).resolve().parent.parent
STANDARDS_PATH = REPO_ROOT / "standards" / "ia.yaml"
Expand Down Expand Up @@ -318,7 +320,7 @@ def detect_gaps(docs_json, endpoints, families, section=None, force=False):
if fm is None:
continue # file missing; separate concern
if not is_openapi_page(fm):
gaps.append(gap("apiref_narrative_page", "medium", None, path=p,
gaps.append(gap("apiref_narrative_page", "medium", None, path=page_file(p) or p,
desc=f"{p} sits in the API Reference tab but is not an endpoint page (narrative belongs in a product tab or Home)"))

# --- Ungrouped OpenAPI tags ------------------------------------------- #
Expand All @@ -334,10 +336,10 @@ def detect_gaps(docs_json, endpoints, families, section=None, force=False):
if fm is None:
continue
if not fm.get("description"):
gaps.append(gap("missing_description", "low", None, path=p,
gaps.append(gap("missing_description", "low", None, path=page_file(p) or p,
desc=f"{p} has no frontmatter description"))
if len(strip_frontmatter(content).split()) < 100:
gaps.append(gap("thin_page", "medium", None, path=p,
gaps.append(gap("thin_page", "medium", None, path=page_file(p) or p,
desc=f"{p} has under 100 words"))

return gaps
Expand Down
59 changes: 59 additions & 0 deletions pipeline/test_page_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
"""Tests for resolving a docs.json page ID to its file.

python pipeline/test_page_file.py

Stdlib only: each test builds a throwaway docs tree in a temp directory.
"""

import sys
import tempfile
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))

from util import page_file # noqa: E402


class PageFileTest(unittest.TestCase):
def setUp(self):
self._tmp = tempfile.TemporaryDirectory()
self.addCleanup(self._tmp.cleanup)
self.root = Path(self._tmp.name)

def touch(self, rel):
path = self.root / rel
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("---\ntitle: t\n---\n", encoding="utf-8")

def test_resolves_an_mdx_page(self):
self.touch("docs/a.mdx")
self.assertEqual(page_file("docs/a", self.root), "docs/a.mdx")

def test_falls_back_to_md(self):
self.touch("docs/a.md")
self.assertEqual(page_file("docs/a", self.root), "docs/a.md")

def test_prefers_mdx_over_md(self):
self.touch("docs/a.mdx")
self.touch("docs/a.md")
self.assertEqual(page_file("docs/a", self.root), "docs/a.mdx")

def test_a_page_and_a_folder_with_the_same_name_resolve_to_the_page(self):
# docs/resources/breaking-changes-change-notices.mdx sits next to a folder of
# the same name; the folder must never be mistaken for the page.
self.touch("docs/a.mdx")
self.touch("docs/a/child.mdx")
self.assertEqual(page_file("docs/a", self.root), "docs/a.mdx")

def test_a_folder_without_a_page_resolves_to_nothing(self):
self.touch("docs/a/child.mdx")
self.assertIsNone(page_file("docs/a", self.root))

def test_a_missing_page_resolves_to_nothing(self):
self.assertIsNone(page_file("docs/missing", self.root))


if __name__ == "__main__":
unittest.main()
15 changes: 15 additions & 0 deletions pipeline/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ def load_text(path):
return ""


def page_file(page_id, repo_root=REPO_ROOT):
"""Repo-relative path of the file behind a docs.json page ID, or None.

docs.json lists pages without an extension (docs/resources/foo). A gap that
carries that ID points at no file, or at a folder that happens to share the
page's name, so resolve it to the real .mdx/.md file before any step reads or
writes it.
"""
for ext in (".mdx", ".md"):
candidate = f"{page_id}{ext}"
if (Path(repo_root) / candidate).is_file():
return candidate
return None


OUTPUT_RULES = """## Output Format

- Output ONLY the .mdx file content. No commentary, no explanation, no markdown fences.
Expand Down
Loading