summaryrefslogtreecommitdiff
path: root/scripts/doc-linter.py
AgeCommit message (Collapse)Author
2026-05-13doc-linter: narrow argv parsing exception scope to (IndexError, SyntaxError, ↵Claude
ValueError) CodeRabbit / Ruff BLE001: the previous 'except Exception as e:' on the explicit-file-list path caught any error, masking runtime failures from handle_file_action() as silent fallback behavior. Only input validation errors from ast.literal_eval(sys.argv[1]) should trigger the fallback walk. Refactor: - Wrap only the parse step in try/except, catching just IndexError (missing argv[1]), SyntaxError (malformed literal), and ValueError (non-literal input). - On parse failure, set files = None and dispatch to the DOCS_ROOT walk via an explicit 'else' branch. - On parse success, run the file loop outside the try so any errors from handle_file_action() propagate normally and CI fails loudly. Also drops the unused 'as e' (Ruff BLE001 noise) and the implicit catch of TypeError (e.g. ast.literal_eval('42') returns an int and 'for file in 42:' would have been silently swallowed -> fallback walk; now it raises clearly). Verified scenarios: - explicit file list (CI normal path) -> exit 0. - no argv -> IndexError caught -> walks DOCS_ROOT. - malformed argv ('not-a-list') -> SyntaxError caught -> walks DOCS_ROOT. - explicit list with a non-existent file -> FileNotFoundError propagates (previously silently triggered a fallback walk). - explicit list with a non-list literal ('42') -> TypeError propagates (programming error stays visible).
2026-05-13doc-linter: rename unused walker var to _dirsClaude
CodeRabbit nit (Ruff B007): the dirs variable from os.walk(DOCS_ROOT) in the auto-discover fallback is unused. Renaming to _dirs makes the intent explicit and silences the warning.
2026-05-13doc-linter: realpath() resolution, DOCS_ROOT in walker, indent fixClaude
Three Copilot findings on ab497bf: 1. is_docs_path() docstring claimed paths 'resolve' under docs/, but the implementation only normalized via abspath() — a symlink under docs/ that points outside the tree would be treated as in-scope. Switch both inputs to os.path.realpath() so symlinks are followed to their real targets. The reverse case is also handled: if docs/ is itself a symlink (some CI checkouts), realpath() resolves it consistently for both sides of the commonpath comparison. Verified with a synthetic case: docs/poison.md -> /etc/hosts now returns False (with abspath() it returned True). 2. The auto-discover fallback in main() still hardcoded os.walk('docs') instead of using the new DOCS_ROOT constant. Use DOCS_ROOT in both paths so the docs root is configured in exactly one place. 3. Indentation inside 'for file in files:' was double-indented (8 spaces under the for, instead of 4) — pre-existing oddity from before 65a8e9f, preserved through the is_docs_path() addition. Normalize to a single indent level under the loop. CI behavior unchanged: tj-actions/changed-files passes repo-relative paths with no symlinks under docs/, which were already handled. The realpath() switch only changes behavior in the symlink-escape case, which was a bug.
2026-05-13docs(linter): add one-line docstrings to clear coverage warningYuriy Andamasov
CodeRabbit Pre-merge Docstring Coverage check reported 50% on scripts/doc-linter.py (threshold 80%). Add minimal one-line docstrings to each public function; no behavior change. 🤖 Generated by [robots](https://vyos.io)
2026-05-13doc-linter: handle absolute paths in is_docs_path()Claude
CodeRabbit review on 28224f3 flagged that is_docs_path() introduced in 65a8e9f only matched repo-relative path strings. An absolute path to docs/... (e.g., from a local invocation that pre-resolves paths, or from tooling that uses git ls-files --full-path) would silently fail the docs/ check and the file would be skipped. Rewrite the helper to use os.path.commonpath against an absolute docs/ root computed on each call. Both inputs are normalized to absolute form, so repo-relative and absolute callers produce the same result. ValueError from commonpath (mixed Windows drives or empty input) is caught and treated as 'not a docs path'. abs_docs is recomputed per call rather than captured at import time so the helper picks up the actual cwd at invocation, matching the existing assumption that CI / local runs invoke the linter from the repo root. Verified against 12 edge cases: - repo-relative docs paths (docs, docs/foo.md, docs/sub/dir/foo.md, ./docs/foo.md) -> True. - repo-relative meta paths (AGENTS.md, README.md, .github/copilot-instructions.md, docs_other/foo.md) -> False. - absolute paths inside docs/ -> True; inside repo root but outside docs/ -> False. - traversal attempts (../other/foo.md, docs/../AGENTS.md) -> False. CI behavior unchanged: tj-actions/changed-files passes repo-relative paths, which were already handled by the previous logic.
2026-05-13ci(doc-linter): scope to docs/ only — skip repo-root meta filesYuriy Andamasov
The linter targets published documentation sources; the auto-discover fallback already walks `docs/` only. CI was passing root-level meta files (README.md, AGENTS.md, .github/copilot-instructions.md — the last is a symlink to AGENTS.md) which forced docs-publication conventions (80-char wrap, RFC IP rules, suppression markers) onto project meta that has no business obeying them. Add an `is_docs_path()` guard in `main()` so the explicit-file-list path matches the auto-discover behavior — only files under `docs/` are linted. AGENTS.md and the Copilot-instruction symlink are now out of scope. Verified: - `python3 scripts/doc-linter.py "['AGENTS.md', 'README.md', '.github/copilot-instructions.md']"` → exit 0 (all skipped). - `python3 scripts/doc-linter.py "['docs/_test_lint.md']"` with a real public IP → still errors as expected. 🤖 Generated by [robots](https://vyos.io)
2026-05-10ci: stack-based fence tracking + file-ext-aware suppression markersYuriy Andamasov
Two issues from PR review: 1. MD/MyST fence tracking treated any longer same-char fence as a closer, which would close `:::{note}` (3 cols) when seeing a nested `::::{code-block}` (4 cols) opener inside it. Real bug in `docs/configuration/interfaces/wireless.md:198–209` (currently unobservable because inner code lines are <80 chars). The "opener has info string / closer has none" heuristic is not sufficient on its own: there are 2,826 bare-fence opens in the tree, so info-string presence cannot distinguish opener from closer. Fix: stack-based tracking. A fence is treated as a closer only when (a) the stack is non-empty, (b) char and length match the top, AND (c) no info string follows. Anything else opens a new (possibly nested) fence. The outermost fence's info string still determines the `md_fence_is_eval_rst` flag. 2. `is_suppression_marker()` accepted `% stop_vyoslinter` in any file outside an MD fence. Per AGENTS.md and the doc-linter instructions, MyST `% ...` markers are only valid in `.md` files; a stray `% stop_vyoslinter` in `.rst`/`.txt` should not silently disable linting. Pass `file_ext` and gate the marker forms accordingly: `% ...` only in `.md` outside fences; `.. ...` in `.rst`/`.txt` outside RST code-blocks, or in `.md` inside an `{eval-rst}` fence. 3. Drop the `not in_rst_codeblock` guard on `.. code-block::` detection. Each occurrence resets the tracked indent (matches `origin/rolling` baseline). Without this, code-block-inside- code-block kept the outer indent and broke dedent detection (verified regression: `_rst_legacy/configuration/system/ rst-syslog.rst:216` long-line warning was lost; restored). Verified: - All 7 original synthetic fixtures pass. - New fixture `nested.md` (3-col outer wraps 4-col inner with long line in between fences) produces exactly one warning at the line outside both fences. - New fixture `wrongmarker.rst` (`%` in `.rst`) — IP error fires (marker correctly ignored). - Full-tree run vs origin/rolling baseline: zero regressions on pre-existing `.rst`/`.txt` warnings; all new output is `.md`. 🤖 Generated by [robots](https://vyos.io)
2026-05-10fix: scope vyoslinter markers to real parser contextscopilot-swe-agent[bot]
Agent-Logs-Url: https://github.com/vyos/vyos-documentation/sessions/5d679560-8a77-4735-b585-74c09293eea5 Co-authored-by: andamasov <12631358+andamasov@users.noreply.github.com>
2026-05-10ci: extend doc-linter to MyST MarkdownYuriy Andamasov
Active docs are now MyST `.md`; the linter previously only inspected `.rst` and `.txt`, so ~250 active pages were unchecked for IP usage and line length on every PR. scripts/doc-linter.py: - Add `.md` to the extension filter (use `endswith` for correctness; the prior 4-char slice silently skipped `.md` files). - Track MyST/Markdown fenced code blocks (```` ``` ```` and `:::`) for line-length exemption — same semantics as `.. code-block::` for RST. - Recognize both suppression marker forms: `.. stop_vyoslinter` / `.. start_vyoslinter` (RST and `.txt` includes) and `% stop_vyoslinter` / `% start_vyoslinter` (MyST). Both work in either context; pick the form that matches the surrounding parser. - Replace the brittle `try/finally: fp.close()` with a `with` block — the previous form raised `UnboundLocalError` if `open()` itself failed. - Fix typo `forgett` → `forget`. .github/instructions/rst-linter.instructions.md → doc-linter.instructions.md: - Broaden `applyTo` from `**/*.rst` to `**/*.md,**/*.rst,**/*.txt`. - Document MyST suppression syntax and fenced-code line-length exemption. - Note the parser-form rule for `{eval-rst}` blocks. No regression on `.txt` includes: identical lint output verified against the origin/rolling baseline on a sample of files. Pre-existing IP violations exist in 14 `.md` files (e.g. `configexamples/lac-lns.md` line 95 — a `8.8.8.8` already wrapped in `% stop_vyoslinter`/`% start_vyoslinter`, correctly suppressed). PRs touching unsuppressed violations will start failing CI; this is the intent of enabling the check. 🤖 Generated by [robots](https://vyos.io)
2026-05-10ci: inline doc lint workflow, drop vyos/.github cross-repo dependencyYuriy Andamasov
The reusable lint-doc workflow at vyos/.github checks out vyos/.github on the consumer's PR base.ref to source doc-linter.py — designed for per-release-train linter rules. With this repo's default renamed current → rolling and vyos/.github still on current, the checkout errors with "fetch +refs/heads/rolling*: exit code 1". Rather than chase branch parity across repos, move the linter where it belongs: doc-linter.py is doc-specific and only consumed here. Inlining removes the cross-repo coupling permanently and unblocks any future branch renames in this repo without touching vyos/.github. - scripts/doc-linter.py: copied byte-for-byte from vyos/.github@current:.github/doc-linter.py (sha 3dc7c2fc16242e62b0ea7107f767577e999ca417 — identical across all four release-train branches in vyos/.github, so no behavioral change). - .github/workflows/lint-doc.yml: replaces `uses: vyos/.github/.github/workflows/lint-doc.yml@current` with the inlined steps. Same actions (bullfrogsec/bullfrog, trilom/file-changes-action, setup-python) and the same final invocation, just sourcing the script from this repo. Adds explicit minimal permissions (contents/pull-requests read) and passes the file list via env var to follow the workflow- injection guidance. Follow-up: vyos/.github still hosts the now-orphaned doc-linter.py and its reusable workflow — separate cleanup PR can delete them once any other consumers migrate (none observed today; this repo was the only caller). 🤖 Generated by [robots](https://vyos.io)