feat(ci): integrate clang-tidy for changed C/C++ files#116
feat(ci): integrate clang-tidy for changed C/C++ files#116kgeg401 wants to merge 5 commits intoalibaba:mainfrom
Conversation
432cf4a to
25d39a7
Compare
25d39a7 to
f5aedce
Compare
|
Cleaned up PR scope and hardened clang-tidy workflow:\n\n- Removed unrelated FP16 test changes from this PR (keeps overlap out of #116).\n- Kept this PR scoped to .clang-tidy, workflow, and CI plumbing files only.\n- Updated workflow to run clang-tidy only on changed source files that exist in compile_commands.json, with explicit skipped-file reporting and non-failing no-op behavior when no analyzable files are present. |
|
This PR is now strictly scoped to .clang-tidy, the clang-tidy workflow, and related CI plumbing. No further code changes in this pass; it is ready for review when bandwidth allows. |
|
@greptile |
| import os | ||
| from pathlib import Path | ||
|
|
||
| changed = [line.strip() for line in """${{ steps.changed_files.outputs.all_changed_files }}""".splitlines() if line.strip()] |
There was a problem hiding this comment.
Template injection risk in inline Python
The GitHub Actions expression ${{ steps.changed_files.outputs.all_changed_files }} is interpolated directly into a Python triple-quoted string before the shell sees the script. If any changed file path contains """ (three consecutive double-quotes, a valid sequence in Linux filenames), it will close the Python string literal early and allow arbitrary Python code injection from PR authors.
The safe pattern is to pass the value via an environment variable and read it in Python, which avoids any possibility of code injection regardless of the content:
- name: Filter changed files to compile database entries
id: tidy_files
if: steps.changed_files.outputs.any_changed == 'true'
env:
ALL_CHANGED_FILES: ${{ steps.changed_files.outputs.all_changed_files }}
run: |
python3 - <<'PY'
import json
import os
from pathlib import Path
changed = [line.strip() for line in os.environ["ALL_CHANGED_FILES"].splitlines() if line.strip()]
...
PY
By reading the file list from an environment variable inside Python, the content is never evaluated as code.
| modernize-use-nullptr, | ||
| modernize-use-override | ||
| WarningsAsErrors: "*" | ||
| HeaderFilterRegex: "^(src|tests|tools)/" |
There was a problem hiding this comment.
HeaderFilterRegex won't match absolute paths
clang-tidy evaluates HeaderFilterRegex against the full absolute path of each header file. On a typical GitHub-hosted runner the workspace path is something like /home/runner/work/zvec/zvec/, so project headers will have paths like /home/runner/work/zvec/zvec/src/foo.h. The regex ^(src|tests|tools)/ is anchored to the beginning of the string and will therefore never match an absolute path, silently suppressing all diagnostics originating in project headers.
Change the pattern to match the directory name anywhere in the path:
| HeaderFilterRegex: "^(src|tests|tools)/" | |
| HeaderFilterRegex: ".*(src|tests|tools)/.*" |
This will correctly flag headers under src/, tests/, or tools/ regardless of the absolute prefix.
| - "CMakeLists.txt" | ||
| - "**/CMakeLists.txt" |
There was a problem hiding this comment.
Redundant CMakeLists.txt path entry
The pattern "**/CMakeLists.txt" (line 14) already matches all CMakeLists.txt files in the repository, including the root one. The "CMakeLists.txt" entry on line 13 is therefore a no-op. The same redundancy exists in the pull_request trigger block (lines 27–28).
| - "CMakeLists.txt" | |
| - "**/CMakeLists.txt" | |
| - "**/CMakeLists.txt" |
|
|
||
| - name: Collect changed C/C++ files | ||
| id: changed_files | ||
| uses: tj-actions/changed-files@v46 |
There was a problem hiding this comment.
Third-party action not pinned to a commit SHA
tj-actions/changed-files@v46 references a mutable tag. If the tag is force-pushed to a different commit, the workflow silently picks up potentially malicious code without any diff being visible in this repository. GitHub's own security hardening guidelines recommend pinning third-party actions to a full commit SHA.
Example:
| uses: tj-actions/changed-files@v46 | |
| uses: tj-actions/changed-files@4edd678ac3f81e2dc578756871e4d00c19191daf # v46 |
(Replace the SHA with the one that corresponds to the v46 release you intend to use.)
Summary
Notes
Fixes #74
Greptile Summary
This PR adds a clang-tidy CI integration for the
zvecrepository: a new.clang-tidyconfiguration file, a GitHub Actions workflow (.github/workflows/clang_tidy.yml) that runs on C/C++ and CMake changes, and a.gitignoreupdate to allow tracking.clang-tidy. The approach — running CMake to producecompile_commands.json, then runningclang-tidyonly on changed files that appear in that database — is sound and well-structured. However, there are two issues that need to be addressed before this is safe and fully functional:.github/workflows/clang_tidy.yml, line 83): The changed-files list is interpolated directly into the Python triple-quoted string inside the heredoc. A PR author could trigger Python code execution by including a file whose path contains""". The file list should be passed via an environment variable instead.HeaderFilterRegexanchoring (.clang-tidy, line 9): The regex^(src|tests|tools)/is anchored to the start of the string but clang-tidy evaluates it against the full absolute path (e.g./home/runner/work/zvec/zvec/src/foo.h). The anchor prevents any project header from matching, silently disabling all header-level diagnostics. The pattern should be changed to.*(src|tests|tools)/.*."CMakeLists.txt"in bothpushandpull_requestpath blocks is already covered by"**/CMakeLists.txt".tj-actions/changed-files@v46should be pinned to a commit SHA to prevent supply-chain risk.Confidence Score: 3/5
.github/workflows/clang_tidy.yml(template injection, line 83) and.clang-tidy(HeaderFilterRegex, line 9) need the most attention.Important Files Changed
.clang-tidyto be tracked, consistent with the existing pattern for.clang-format; no issues found.Sequence Diagram
sequenceDiagram participant GH as GitHub Event (push/PR) participant WF as clang_tidy.yml participant TJ as tj-actions/changed-files@v46 participant PY as Python Filter Script participant CT as clang-tidy GH->>WF: Trigger on C/C++, CMake, or .clang-tidy change WF->>WF: actions/checkout@v4 (submodules: recursive) WF->>WF: apt-get install clang-tidy cmake ninja-build WF->>WF: cmake -S . -B build (exports compile_commands.json) WF->>TJ: Collect changed *.c / *.cc / *.cpp / *.cxx files TJ-->>WF: all_changed_files (newline-separated) WF->>PY: Interpolate file list into Python heredoc PY->>PY: Cross-reference with build/compile_commands.json PY-->>WF: any_tidy_files, all_tidy_files, skipped_files WF->>WF: Show skipped files (if any) WF->>CT: clang-tidy -p build --warnings-as-errors='*' <file> CT-->>WF: Pass / Fail (fail = CI blocked)Last reviewed commit: 30343b3