DEVX-99: fix: exclude .devin/.terraform dirs from lint_docs, add duplicate heading excludes
Post-merge / detect-type (push) Successful in 9s
Post-merge / validate-commit-msg (push) Successful in 8s
Post-merge / vikunja (push) Successful in 12s
Post-merge / configure-repo (push) Successful in 10s
Post-merge / sync-wiki (push) Successful in 19s
Build Images / detect-type (push) Successful in 35s
Post-merge / release (push) Successful in 25s
Post-merge / badges (push) Successful in 28s
Post-merge / publish (push) Successful in 15s
Build Images / build-and-push (push) Successful in 2m51s
Build Images / cleanup (push) Successful in 2m18s

This commit was merged in pull request #155.
This commit is contained in:
2026-06-28 17:01:37 +00:00
parent 1a27983750
commit e6f022ae96
+27 -39
View File
@@ -49,13 +49,31 @@ REQUIRED_DOC_FILES = ["index.md"]
# Maximum age for docs before they're considered stale (days)
STALE_THRESHOLD_DAYS = 180
# Files excluded from duplicate heading checks (auto-generated or structured)
DUPLICATE_HEADING_EXCLUDES = {"CHANGELOG.md"}
# Files excluded from duplicate heading checks (auto-generated or structured
# with repeated subsections under different parent sections)
DUPLICATE_HEADING_EXCLUDES = {
"CHANGELOG.md",
"incident-response-sso.md",
"role-sync-design.md",
}
# TODO/FIXME pattern — matches "TODO:" or "FIXME:" at start of line/after whitespace
# Does NOT match references to the word "TODO" in rules/documentation
_TODO_RE = re.compile(r"(?m)^\s*(?:>>>?\s*)?(TODO|FIXME|HACK|XXX)\s*:", re.IGNORECASE)
# Directories excluded from markdown file scanning
_EXCLUDE_DIRS = {
".venv",
".git",
"node_modules",
"__pycache__",
".pytest_cache",
".devin",
".terraform",
"site-packages",
"dist-info",
}
def slugify(text: str) -> str:
"""Convert heading text to a GitHub-style slug."""
@@ -155,11 +173,7 @@ def check_internal_links(root: Path, docs_dir: Path) -> list[str]:
issues: list[str] = []
md_files = list(root.rglob("*.md"))
# Exclude .venv, .git, node_modules
md_files = [
f
for f in md_files
if not any(part in {".venv", ".git", "node_modules", "__pycache__", ".pytest_cache"} for part in f.parts)
]
md_files = [f for f in md_files if not any(part in _EXCLUDE_DIRS for part in f.parts)]
# Load wiki page names from mapping.json — these are valid link targets
wiki_pages: set[str] = set()
@@ -216,11 +230,7 @@ def check_internal_links(root: Path, docs_dir: Path) -> list[str]:
def check_heading_hierarchy(root: Path) -> list[str]:
"""Check that headings don't skip levels."""
issues: list[str] = []
md_files = [
f
for f in root.rglob("*.md")
if not any(part in {".venv", ".git", "node_modules", "__pycache__", ".pytest_cache"} for part in f.parts)
]
md_files = [f for f in root.rglob("*.md") if not any(part in _EXCLUDE_DIRS for part in f.parts)]
for md_file in md_files:
rel_path = md_file.relative_to(root)
@@ -242,11 +252,7 @@ def check_todo_fixme(root: Path) -> list[str]:
references to the word "TODO" in rules or documentation about TODOs.
"""
issues: list[str] = []
md_files = [
f
for f in root.rglob("*.md")
if not any(part in {".venv", ".git", "node_modules", "__pycache__", ".pytest_cache"} for part in f.parts)
]
md_files = [f for f in root.rglob("*.md") if not any(part in _EXCLUDE_DIRS for part in f.parts)]
for md_file in md_files:
rel_path = md_file.relative_to(root)
@@ -262,11 +268,7 @@ def check_todo_fixme(root: Path) -> list[str]:
def check_trailing_whitespace(root: Path) -> list[str]:
"""Check for trailing whitespace in markdown files."""
issues: list[str] = []
md_files = [
f
for f in root.rglob("*.md")
if not any(part in {".venv", ".git", "node_modules", "__pycache__", ".pytest_cache"} for part in f.parts)
]
md_files = [f for f in root.rglob("*.md") if not any(part in _EXCLUDE_DIRS for part in f.parts)]
for md_file in md_files:
rel_path = md_file.relative_to(root)
@@ -282,11 +284,7 @@ def check_stale_docs(root: Path) -> list[str]:
"""Check for stale documentation (not modified in >180 days)."""
issues: list[str] = []
threshold = datetime.now() - timedelta(days=STALE_THRESHOLD_DAYS)
md_files = [
f
for f in root.rglob("*.md")
if not any(part in {".venv", ".git", "node_modules", "__pycache__", ".pytest_cache"} for part in f.parts)
]
md_files = [f for f in root.rglob("*.md") if not any(part in _EXCLUDE_DIRS for part in f.parts)]
for md_file in md_files:
rel_path = md_file.relative_to(root)
@@ -301,11 +299,7 @@ def check_stale_docs(root: Path) -> list[str]:
def check_duplicate_headings(root: Path) -> list[str]:
"""Check for duplicate headings within the same file."""
issues: list[str] = []
md_files = [
f
for f in root.rglob("*.md")
if not any(part in {".venv", ".git", "node_modules", "__pycache__", ".pytest_cache"} for part in f.parts)
]
md_files = [f for f in root.rglob("*.md") if not any(part in _EXCLUDE_DIRS for part in f.parts)]
for md_file in md_files:
rel_path = md_file.relative_to(root)
@@ -386,13 +380,7 @@ def main(
ws_issues = check_trailing_whitespace(root_path)
if fix and ws_issues:
fixed = 0
md_files = [
f
for f in root_path.rglob("*.md")
if not any(
part in {".venv", ".git", "node_modules", "__pycache__", ".pytest_cache"} for part in f.parts
)
]
md_files = [f for f in root_path.rglob("*.md") if not any(part in _EXCLUDE_DIRS for part in f.parts)]
for md_file in md_files:
content = md_file.read_text(encoding="utf-8")
fixed_content = _TRAILING_WS_RE.sub("", content)