diff options
| author | Yuriy Andamasov <yuriy@vyos.io> | 2026-05-14 00:48:16 +0300 |
|---|---|---|
| committer | Yuriy Andamasov <yuriy@vyos.io> | 2026-05-14 00:48:16 +0300 |
| commit | 379ed4757b62a7c1df965a59bc4f1fd0cef2d3e8 (patch) | |
| tree | e3c7ef2010281a80d310785e2a4580ee98ec4090 /scripts | |
| parent | cd5759f26a1c18abc3c137234cf1ab503e2b26b7 (diff) | |
| download | vyos-documentation-379ed4757b62a7c1df965a59bc4f1fd0cef2d3e8.tar.gz vyos-documentation-379ed4757b62a7c1df965a59bc4f1fd0cef2d3e8.zip | |
ci(doc-linter): exclude docs/_rst_legacy/ and docs/_build/ from lint scope
`is_docs_path()` returned True for any path under `docs/`, including
the archived RST shadows under `docs/_rst_legacy/` and the build
output under `docs/_build/`. Sphinx excludes both from the build
(per `docs/conf.py`'s exclude_patterns) and AGENTS marks
`_rst_legacy` as reference-only. The linter shouldn't process either.
Add a `DOCS_EXCLUDED_SUBDIRS = ('_build', '_rst_legacy')` constant.
After confirming a path is under `docs/`, walk each excluded subtree
and reject the path if it's contained.
Also unify the auto-discover walk fallback to call `is_docs_path()`
for the filter — previously it had its own hand-rolled `"_build"
not in path` check that didn't handle `_rst_legacy` at all and would
have walked the entire legacy archive. Prune `dirs[:]` in-place at
each walk level so we don't descend into the excluded subtrees in
the first place — optimization on top of correctness. Reverted the
`_dirs` -> `dirs` rename here because we now mutate it.
Test coverage: 10 hand-coded `is_docs_path()` cases — all pass:
- `docs/configuration/foo.md` -> True
- `docs/_rst_legacy/foo.rst` -> False (was True)
- `docs/_rst_legacy/subdir/rst-foo.rst` -> False (was True)
- `docs/_build/html/index.html` -> False (was True)
- `docs/_include/foo.txt` -> True (live snippets stay in scope)
- `docs` -> True
- `AGENTS.md`, `README.md`, `.github/copilot-instructions.md`,
`scripts/doc-linter.py` -> False (already correct)
Tracked as item 4 of the rolling-side cleanup backlog from PR #2014
/ #2019 / #2020 reviews.
🤖 Generated by [robots](https://vyos.io)
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/doc-linter.py | 35 |
1 files changed, 28 insertions, 7 deletions
diff --git a/scripts/doc-linter.py b/scripts/doc-linter.py index 1da215a5..e352615c 100644 --- a/scripts/doc-linter.py +++ b/scripts/doc-linter.py @@ -30,9 +30,15 @@ SUPPORTED_EXTS = ('.md', '.rst', '.txt') # meta, not docs content, and are out of scope. DOCS_ROOT = 'docs' +# Subtrees under docs/ that are excluded from the build (per docs/conf.py) +# and therefore also excluded from lint. +DOCS_EXCLUDED_SUBDIRS = ('_build', '_rst_legacy') + def is_docs_path(path): - """Return True iff `path` resolves under the repo's `docs/` tree. + """Return True iff `path` resolves under the repo's `docs/` tree AND + is not inside one of the build-excluded subtrees (``_build``, + ``_rst_legacy``). Accepts both repo-relative and absolute paths. Both `path` and `DOCS_ROOT` are resolved with `os.path.realpath`, so symlinks are @@ -46,10 +52,19 @@ def is_docs_path(path): abs_path = os.path.realpath(path) abs_docs = os.path.realpath(DOCS_ROOT) try: - return os.path.commonpath([abs_path, abs_docs]) == abs_docs + if os.path.commonpath([abs_path, abs_docs]) != abs_docs: + return False except ValueError: # commonpath raises on mixed drives (Windows) or empty input. return False + for excluded in DOCS_EXCLUDED_SUBDIRS: + abs_excluded = os.path.realpath(os.path.join(DOCS_ROOT, excluded)) + try: + if os.path.commonpath([abs_path, abs_excluded]) == abs_excluded: + return False + except ValueError: + continue + return True # MyST / Markdown fenced code block: leading whitespace + 3+ backticks or 3+ colons. # Same character and length-or-greater closes. @@ -303,12 +318,18 @@ def main(): if handle_file_action(file) is False: bool_error = False else: - for root, _dirs, files in os.walk(DOCS_ROOT): - path = root.split(os.sep) + # In-place dirs prune: skip descending into build-excluded subtrees. + # is_docs_path() also rejects paths under those subtrees, so the + # prune is an optimization (avoids walking thousands of archived + # legacy RST files) and the filter is correctness. + for root, dirs, files in os.walk(DOCS_ROOT): + dirs[:] = [d for d in dirs if d not in DOCS_EXCLUDED_SUBDIRS] for file in files: - if file.endswith(SUPPORTED_EXTS) and "_build" not in path: - fpath = '/'.join(path) - filepath = f"{fpath}/{file}" + filepath = os.path.join(root, file) + if ( + file.endswith(SUPPORTED_EXTS) + and is_docs_path(filepath) + ): if handle_file_action(filepath) is False: bool_error = False |
