[llvm] [Github][CI] Introduce `doc8` to `code-lint-helper.py` (PR #172123)

via llvm-commits llvm-commits at lists.llvm.org
Sat Dec 13 04:39:08 PST 2025


https://github.com/zeyi2 updated https://github.com/llvm/llvm-project/pull/172123

>From 4b0b1ee150ea820f00564667e734ddd225665d53 Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Fri, 12 Dec 2025 22:39:40 +0800
Subject: [PATCH 1/2] [Github][CI] Introduce `doc8` to `code-lint-helper.py`

---
 .../github-action-ci-tooling/Dockerfile       |  5 +-
 .github/workflows/pr-code-lint.yml            |  6 +-
 llvm/utils/git/code-lint-helper.py            | 61 ++++++++++++++++++-
 3 files changed, 67 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/containers/github-action-ci-tooling/Dockerfile b/.github/workflows/containers/github-action-ci-tooling/Dockerfile
index b78c99efb9be3..47dedba5194e2 100644
--- a/.github/workflows/containers/github-action-ci-tooling/Dockerfile
+++ b/.github/workflows/containers/github-action-ci-tooling/Dockerfile
@@ -94,6 +94,10 @@ COPY --from=llvm-downloader /llvm-extract/LLVM-${LLVM_VERSION}-Linux-X64/bin/cla
 COPY clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py ${LLVM_SYSROOT}/bin/clang-tidy-diff.py
 
 # Install dependencies for 'pr-code-lint.yml' job
+RUN apt-get update && \
+    DEBIAN_FRONTEND=noninteractive apt-get install -y python3-doc8 && \
+    apt-get clean && \
+    rm -rf /var/lib/apt/lists/*
 COPY llvm/utils/git/requirements_linting.txt requirements_linting.txt
 RUN pip install -r requirements_linting.txt --break-system-packages && \
     rm requirements_linting.txt
@@ -119,4 +123,3 @@ RUN git clone https://github.com/universal-ctags/ctags.git && \
     ./configure && \
     sudo make install && \
     rm -Rf ../ctags
-
diff --git a/.github/workflows/pr-code-lint.yml b/.github/workflows/pr-code-lint.yml
index ea4f8217cd003..ed0b371064b80 100644
--- a/.github/workflows/pr-code-lint.yml
+++ b/.github/workflows/pr-code-lint.yml
@@ -20,7 +20,7 @@ jobs:
       run:
         shell: bash
     container:
-      image: 'ghcr.io/llvm/ci-ubuntu-24.04-lint'
+      image: 'ghcr.io/llvm/ci-ubuntu-24.04-lint-doc8'
     timeout-minutes: 60
     concurrency:
       group: ${{ github.workflow }}-${{ github.ref }}
@@ -71,12 +71,12 @@ jobs:
                 -DLLVM_INCLUDE_TESTS=OFF \
                 -DCLANG_INCLUDE_TESTS=OFF \
                 -DCMAKE_BUILD_TYPE=Release
-          
+
           ninja -C build \
                 clang-tablegen-targets \
                 genconfusable               # for "ConfusableIdentifierCheck.h"
 
-      - name: Run code linter
+      - name: Run code linters
         env:
           GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
           CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
diff --git a/llvm/utils/git/code-lint-helper.py b/llvm/utils/git/code-lint-helper.py
index a53549fb69a77..aca88e2be5a8c 100644
--- a/llvm/utils/git/code-lint-helper.py
+++ b/llvm/utils/git/code-lint-helper.py
@@ -36,6 +36,7 @@ class LintArgs:
     issue_number: int = 0
     build_path: str = "build"
     clang_tidy_binary: str = "clang-tidy"
+    doc8_binary: str = "doc8"
 
     def __init__(self, args: argparse.Namespace) -> None:
         if args is not None:
@@ -50,6 +51,7 @@ def __init__(self, args: argparse.Namespace) -> None:
             self.verbose = args.verbose
             self.build_path = args.build_path
             self.clang_tidy_binary = args.clang_tidy_binary
+            self.doc8_binary = args.doc8_binary
 
 
 class LintHelper:
@@ -289,8 +291,59 @@ def _clean_clang_tidy_output(self, output: str) -> str:
         return ""
 
 
+class Doc8LintHelper(LintHelper):
+    name: Final = "doc8"
+    friendly_name: Final = "documentation linter"
 
-ALL_LINTERS = (ClangTidyLintHelper(),)
+    def instructions(self, files_to_lint: Sequence[str], args: LintArgs) -> str:
+        files_str = " ".join(files_to_lint)
+        return f"doc8 -q {files_str}"
+
+    def filter_changed_files(self, changed_files: Sequence[str]) -> Sequence[str]:
+        filtered_files = []
+        for filepath in changed_files:
+            _, ext = os.path.splitext(filepath)
+            if ext != ".rst":
+                continue
+            if not filepath.startswith("clang-tools-extra/docs/clang-tidy/"):
+                continue
+            if os.path.exists(filepath):
+                filtered_files.append(filepath)
+        return filtered_files
+
+    def run_linter_tool(self, files_to_lint: Sequence[str], args: LintArgs) -> str:
+        if not files_to_lint:
+            return ""
+
+        doc8_cmd = [args.doc8_binary, "-q"]
+        doc8_cmd.extend(files_to_lint)
+
+        if args.verbose:
+            print(f"Running doc8: {' '.join(doc8_cmd)}")
+
+        proc = subprocess.run(
+            doc8_cmd,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+        if proc.returncode == 0:
+            return ""
+
+        output = proc.stdout.strip()
+        if output:
+            return output
+
+        error_output = proc.stderr.strip()
+        if error_output:
+            return error_output
+
+        return f"doc8 exited with return code {proc.returncode} but no output."
+
+
+ALL_LINTERS = (ClangTidyLintHelper(), Doc8LintHelper())
 
 
 if __name__ == "__main__":
@@ -331,6 +384,12 @@ def _clean_clang_tidy_output(self, output: str) -> str:
         default="clang-tidy",
         help="Path to clang-tidy binary",
     )
+    parser.add_argument(
+        "--doc8-binary",
+        type=str,
+        default="doc8",
+        help="Path to doc8 binary",
+    )
     parser.add_argument(
         "--verbose", action="store_true", default=True, help="Verbose output"
     )

>From d5f45bb1c2d7deed3eb3ed23f0a18cba6e008706 Mon Sep 17 00:00:00 2001
From: mitchell <mitchell.xu2 at gmail.com>
Date: Sat, 13 Dec 2025 20:39:00 +0800
Subject: [PATCH 2/2] Apply suggestions from code review

Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
 llvm/utils/git/code-lint-helper.py | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/llvm/utils/git/code-lint-helper.py b/llvm/utils/git/code-lint-helper.py
index aca88e2be5a8c..aabc78f2de033 100644
--- a/llvm/utils/git/code-lint-helper.py
+++ b/llvm/utils/git/code-lint-helper.py
@@ -295,11 +295,10 @@ class Doc8LintHelper(LintHelper):
     name: Final = "doc8"
     friendly_name: Final = "documentation linter"
 
-    def instructions(self, files_to_lint: Sequence[str], args: LintArgs) -> str:
-        files_str = " ".join(files_to_lint)
-        return f"doc8 -q {files_str}"
+    def instructions(self, files_to_lint: Iterable[str], args: LintArgs) -> str:
+        return f"doc8 -q {' '.join(files_to_lint)}"
 
-    def filter_changed_files(self, changed_files: Sequence[str]) -> Sequence[str]:
+    def filter_changed_files(self, changed_files: Iterable[str]) -> Sequence[str]:
         filtered_files = []
         for filepath in changed_files:
             _, ext = os.path.splitext(filepath)
@@ -311,7 +310,7 @@ def filter_changed_files(self, changed_files: Sequence[str]) -> Sequence[str]:
                 filtered_files.append(filepath)
         return filtered_files
 
-    def run_linter_tool(self, files_to_lint: Sequence[str], args: LintArgs) -> str:
+    def run_linter_tool(self, files_to_lint: Iterable[str], args: LintArgs) -> str:
         if not files_to_lint:
             return ""
 
@@ -332,12 +331,10 @@ def run_linter_tool(self, files_to_lint: Sequence[str], args: LintArgs) -> str:
         if proc.returncode == 0:
             return ""
 
-        output = proc.stdout.strip()
-        if output:
+        if output := proc.stdout.strip():
             return output
 
-        error_output = proc.stderr.strip()
-        if error_output:
+        if error_output := proc.stderr.strip():
             return error_output
 
         return f"doc8 exited with return code {proc.returncode} but no output."



More information about the llvm-commits mailing list