DEVX-64: feat: add skip_ref_prefixes config to check_agent_docs
This commit is contained in:
@@ -117,6 +117,7 @@ def _check_file(
|
|||||||
legitimate_indicators: list[str],
|
legitimate_indicators: list[str],
|
||||||
repo_path_prefixes: list[str],
|
repo_path_prefixes: list[str],
|
||||||
min_path_ref_length: int,
|
min_path_ref_length: int,
|
||||||
|
skip_ref_prefixes: list[str],
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
"""Check a single file for stale references."""
|
"""Check a single file for stale references."""
|
||||||
issues: list[str] = []
|
issues: list[str] = []
|
||||||
@@ -147,6 +148,9 @@ def _check_file(
|
|||||||
# Only check references that look like repo paths
|
# Only check references that look like repo paths
|
||||||
if not any(ref.startswith(prefix) for prefix in repo_path_prefixes):
|
if not any(ref.startswith(prefix) for prefix in repo_path_prefixes):
|
||||||
continue
|
continue
|
||||||
|
# Skip references matching configured skip prefixes (e.g. aspirational test files)
|
||||||
|
if any(ref.startswith(prefix) for prefix in skip_ref_prefixes):
|
||||||
|
continue
|
||||||
candidate = repo_root / ref
|
candidate = repo_root / ref
|
||||||
if not candidate.exists():
|
if not candidate.exists():
|
||||||
issues.append(f"{rel_path}:{lineno}: references non-existent file '{ref}'")
|
issues.append(f"{rel_path}:{lineno}: references non-existent file '{ref}'")
|
||||||
@@ -179,6 +183,9 @@ def cli() -> None:
|
|||||||
min_len_raw = cfg.get("min_path_ref_length")
|
min_len_raw = cfg.get("min_path_ref_length")
|
||||||
min_path_ref_length: int = int(min_len_raw) if isinstance(min_len_raw, int) else MIN_PATH_REF_LENGTH_DEFAULT
|
min_path_ref_length: int = int(min_len_raw) if isinstance(min_len_raw, int) else MIN_PATH_REF_LENGTH_DEFAULT
|
||||||
|
|
||||||
|
skip_prefixes_raw = cfg.get("skip_ref_prefixes", [])
|
||||||
|
skip_ref_prefixes: list[str] = [str(d) for d in skip_prefixes_raw] if isinstance(skip_prefixes_raw, list) else []
|
||||||
|
|
||||||
deleted_files: set[str] = set()
|
deleted_files: set[str] = set()
|
||||||
deleted_raw = cfg.get("deleted_files", [])
|
deleted_raw = cfg.get("deleted_files", [])
|
||||||
if isinstance(deleted_raw, list):
|
if isinstance(deleted_raw, list):
|
||||||
@@ -209,6 +216,7 @@ def cli() -> None:
|
|||||||
legitimate_indicators,
|
legitimate_indicators,
|
||||||
repo_path_prefixes,
|
repo_path_prefixes,
|
||||||
min_path_ref_length,
|
min_path_ref_length,
|
||||||
|
skip_ref_prefixes,
|
||||||
)
|
)
|
||||||
all_issues.extend(issues)
|
all_issues.extend(issues)
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class TestCheckFile:
|
|||||||
doc.parent.mkdir(parents=True)
|
doc.parent.mkdir(parents=True)
|
||||||
doc.write_text("See scripts/old.py for details.\n")
|
doc.write_text("See scripts/old.py for details.\n")
|
||||||
issues = _check_file(
|
issues = _check_file(
|
||||||
doc, tmp_path, {"scripts/old.py"}, [], [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT
|
doc, tmp_path, {"scripts/old.py"}, [], [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT, []
|
||||||
)
|
)
|
||||||
assert any("deleted file" in i for i in issues)
|
assert any("deleted file" in i for i in issues)
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ class TestCheckFile:
|
|||||||
doc = tmp_path / "docs" / "guide.md"
|
doc = tmp_path / "docs" / "guide.md"
|
||||||
doc.parent.mkdir(parents=True)
|
doc.parent.mkdir(parents=True)
|
||||||
doc.write_text("See scripts/nonexistent.py for details.\n")
|
doc.write_text("See scripts/nonexistent.py for details.\n")
|
||||||
issues = _check_file(doc, tmp_path, set(), [], [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT)
|
issues = _check_file(doc, tmp_path, set(), [], [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT, [])
|
||||||
assert any("non-existent file" in i for i in issues)
|
assert any("non-existent file" in i for i in issues)
|
||||||
|
|
||||||
def test_does_not_flag_existing_file(self, tmp_path: Path) -> None:
|
def test_does_not_flag_existing_file(self, tmp_path: Path) -> None:
|
||||||
@@ -106,7 +106,7 @@ class TestCheckFile:
|
|||||||
doc = tmp_path / "docs" / "guide.md"
|
doc = tmp_path / "docs" / "guide.md"
|
||||||
doc.parent.mkdir(parents=True)
|
doc.parent.mkdir(parents=True)
|
||||||
doc.write_text("See scripts/exists.py for details.\n")
|
doc.write_text("See scripts/exists.py for details.\n")
|
||||||
issues = _check_file(doc, tmp_path, set(), [], [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT)
|
issues = _check_file(doc, tmp_path, set(), [], [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT, [])
|
||||||
assert issues == []
|
assert issues == []
|
||||||
|
|
||||||
def test_detects_deprecated_pattern(self, tmp_path: Path) -> None:
|
def test_detects_deprecated_pattern(self, tmp_path: Path) -> None:
|
||||||
@@ -115,7 +115,7 @@ class TestCheckFile:
|
|||||||
doc.write_text("Use ansible/envs/prod/secrets.yml for config.\n")
|
doc.write_text("Use ansible/envs/prod/secrets.yml for config.\n")
|
||||||
patterns = [re.compile(r"ansible/envs/[^/]+/secrets\.yml")]
|
patterns = [re.compile(r"ansible/envs/[^/]+/secrets\.yml")]
|
||||||
issues = _check_file(
|
issues = _check_file(
|
||||||
doc, tmp_path, set(), patterns, [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT
|
doc, tmp_path, set(), patterns, [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT, []
|
||||||
)
|
)
|
||||||
assert any("deprecated pattern" in i for i in issues)
|
assert any("deprecated pattern" in i for i in issues)
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ class TestCheckFile:
|
|||||||
doc.write_text("The legacy ansible/envs/prod/secrets.yml is deprecated.\n")
|
doc.write_text("The legacy ansible/envs/prod/secrets.yml is deprecated.\n")
|
||||||
patterns = [re.compile(r"ansible/envs/[^/]+/secrets\.yml")]
|
patterns = [re.compile(r"ansible/envs/[^/]+/secrets\.yml")]
|
||||||
issues = _check_file(
|
issues = _check_file(
|
||||||
doc, tmp_path, set(), patterns, ["deprecated"], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT
|
doc, tmp_path, set(), patterns, ["deprecated"], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT, []
|
||||||
)
|
)
|
||||||
assert issues == []
|
assert issues == []
|
||||||
|
|
||||||
@@ -137,14 +137,14 @@ class TestCheckFile:
|
|||||||
doc = tmp_path / "docs" / "guide.md"
|
doc = tmp_path / "docs" / "guide.md"
|
||||||
doc.parent.mkdir(parents=True)
|
doc.parent.mkdir(parents=True)
|
||||||
doc.write_bytes(b"\xff\xfe\x00\x00")
|
doc.write_bytes(b"\xff\xfe\x00\x00")
|
||||||
issues = _check_file(doc, tmp_path, set(), [], [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT)
|
issues = _check_file(doc, tmp_path, set(), [], [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT, [])
|
||||||
assert issues == []
|
assert issues == []
|
||||||
|
|
||||||
def test_skips_short_ref(self, tmp_path: Path) -> None:
|
def test_skips_short_ref(self, tmp_path: Path) -> None:
|
||||||
doc = tmp_path / "docs" / "guide.md"
|
doc = tmp_path / "docs" / "guide.md"
|
||||||
doc.parent.mkdir(parents=True)
|
doc.parent.mkdir(parents=True)
|
||||||
doc.write_text("See a.py for details.\n")
|
doc.write_text("See a.py for details.\n")
|
||||||
issues = _check_file(doc, tmp_path, set(), [], [], DEFAULT_REPO_PATH_PREFIXES, 5)
|
issues = _check_file(doc, tmp_path, set(), [], [], DEFAULT_REPO_PATH_PREFIXES, 5, [])
|
||||||
# "a.py" is only 4 chars, below min_path_ref_length
|
# "a.py" is only 4 chars, below min_path_ref_length
|
||||||
assert issues == []
|
assert issues == []
|
||||||
|
|
||||||
@@ -152,10 +152,19 @@ class TestCheckFile:
|
|||||||
doc = tmp_path / "docs" / "guide.md"
|
doc = tmp_path / "docs" / "guide.md"
|
||||||
doc.parent.mkdir(parents=True)
|
doc.parent.mkdir(parents=True)
|
||||||
doc.write_text("See vendor/some/long/path.py for details.\n")
|
doc.write_text("See vendor/some/long/path.py for details.\n")
|
||||||
issues = _check_file(doc, tmp_path, set(), [], [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT)
|
issues = _check_file(doc, tmp_path, set(), [], [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT, [])
|
||||||
# "vendor/" is not in repo_path_prefixes
|
# "vendor/" is not in repo_path_prefixes
|
||||||
assert issues == []
|
assert issues == []
|
||||||
|
|
||||||
|
def test_skip_ref_prefixes_skips_nonexistent(self, tmp_path: Path) -> None:
|
||||||
|
doc = tmp_path / "docs" / "guide.md"
|
||||||
|
doc.parent.mkdir(parents=True)
|
||||||
|
doc.write_text("See scripts/test_foo.py for details.\n")
|
||||||
|
issues = _check_file(
|
||||||
|
doc, tmp_path, set(), [], [], DEFAULT_REPO_PATH_PREFIXES, MIN_PATH_REF_LENGTH_DEFAULT, ["scripts/test_"]
|
||||||
|
)
|
||||||
|
assert issues == []
|
||||||
|
|
||||||
|
|
||||||
class TestCli:
|
class TestCli:
|
||||||
def test_passes_when_no_issues(self, tmp_path: Path) -> None:
|
def test_passes_when_no_issues(self, tmp_path: Path) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user