From 628eb360bd84f45cdf4247cd3d1e102fe30bfc45 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Thu, 14 May 2026 00:49:50 +0300 Subject: ci(doc-linter): lint added + renamed files, not only modified MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous workflow: env: FILES_MODIFIED: ${{ steps.file_changes.outputs.files_modified }} run: python scripts/doc-linter.py "$FILES_MODIFIED" `trilom/file-changes-action`'s `files_modified` output is modifications-only. A PR adding a new `.md`/`.rst` doc page passed `files_added`, never `files_modified`, so a brand-new page with long lines or real public IPs slipped past the linter entirely. Workflow: also pass `files_added` and `files_renamed` as separate positional args. Each output is a JSON array (action v1.2.4) and is passed via env to avoid shell-quoting issues. Linter: `main()` now accepts one OR multiple positional argv entries, each a JSON array of paths. Arrays are merged and deduplicated before linting. Single-arg invocations remain backward-compatible. Switched from `ast.literal_eval` to `json.loads` — the action's outputs are JSON, and `json.loads` is the right tool (and dodges literal_eval-via-`eval`-substring linter warnings). Test coverage: - Two JSON arrays merge -> single linter run on union. - Empty-string argv entry skipped (no `files_renamed` in many PRs). - Malformed JSON -> falls back to walking DOCS_ROOT. - No argv -> walks DOCS_ROOT. - Single-arg invocation -> backward-compat preserved. Tracked as item 7 of the rolling-side cleanup backlog from PR #2014 / #2019 / #2020 reviews. 🤖 Generated by [robots](https://vyos.io) (cherry picked from commit 1ef5684729646ca3a24aff83ab8edd0aa57914c7) --- .github/workflows/lint-doc.yml | 11 ++++++++++- scripts/doc-linter.py | 22 +++++++++++++++++----- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/.github/workflows/lint-doc.yml b/.github/workflows/lint-doc.yml index d7f25f38..eb7386d0 100644 --- a/.github/workflows/lint-doc.yml +++ b/.github/workflows/lint-doc.yml @@ -30,5 +30,14 @@ jobs: - name: run doc linter env: + # Pass modified + added + renamed file lists. `files_modified` + # alone misses newly-added pages — a PR adding a new doc with + # long lines or real public IPs would otherwise slip through. FILES_MODIFIED: ${{ steps.file_changes.outputs.files_modified }} - run: python scripts/doc-linter.py "$FILES_MODIFIED" + FILES_ADDED: ${{ steps.file_changes.outputs.files_added }} + FILES_RENAMED: ${{ steps.file_changes.outputs.files_renamed }} + run: | + python scripts/doc-linter.py \ + "$FILES_MODIFIED" \ + "$FILES_ADDED" \ + "$FILES_RENAMED" diff --git a/scripts/doc-linter.py b/scripts/doc-linter.py index e352615c..509b1400 100644 --- a/scripts/doc-linter.py +++ b/scripts/doc-linter.py @@ -2,7 +2,7 @@ import os import re import ipaddress import sys -import ast +import json IPV4SEG = r'(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])' IPV4ADDR = r'\b(?:(?:' + IPV4SEG + r'\.){3,3}' + IPV4SEG + r')\b' @@ -302,11 +302,23 @@ def main(): # Only the argv-parsing step is wrapped in try/except. Errors raised by # handle_file_action() must propagate so CI failures stay visible instead # of silently triggering a full docs/ walk. - try: - files = ast.literal_eval(sys.argv[1]) - except (IndexError, SyntaxError, ValueError): - # No argv or malformed list -> fall back to walking DOCS_ROOT. + # + # Accepts one or more positional argv entries, each a JSON array of + # paths (e.g., `'["foo.md", "bar.rst"]'`). Arrays are merged and + # deduplicated before linting. CI passes `files_modified`, + # `files_added`, etc. as separate args — see lint-doc.yml. + if len(sys.argv) <= 1: files = None + else: + try: + files = [] + for arg in sys.argv[1:]: + if arg.strip(): + files.extend(json.loads(arg)) + files = sorted(set(files)) + except (json.JSONDecodeError, TypeError): + # Malformed input -> fall back to walking DOCS_ROOT. + files = None if files is not None: for file in files: -- cgit v1.2.3