From f2158cd2d9a3c50bda318ddb6d128ccc115aba51 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 5 May 2026 15:51:25 +0100 Subject: [PATCH] ci: Make clang-tidy work on individual files in PRs (#3067) --- .clang-tidy | 8 +- .github/workflows/clang-tidy.yml | 132 ++++++++++++++++++++++++------- 2 files changed, 108 insertions(+), 32 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index c5e2b370c..069b62dc6 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -156,6 +156,11 @@ Checks: "-*, " CheckOptions: + bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true + bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc + + misc-include-cleaner.IgnoreHeaders: '.*/(detail|impl)/.*;.*(expected|unexpected).*;.*ranges_lower_bound\.h;time.h;stdlib.h;__chrono/.*;fmt/chrono.h;boost/uuid/uuid_hash.hpp' + readability-braces-around-statements.ShortStatementLines: 2 readability-identifier-naming.MacroDefinitionCase: UPPER_CASE readability-identifier-naming.ClassCase: CamelCase @@ -190,9 +195,6 @@ CheckOptions: readability-identifier-naming.ProtectedMemberSuffix: _ readability-identifier-naming.PublicMemberSuffix: "" readability-identifier-naming.FunctionIgnoredRegexp: ".*tag_invoke.*" - bugprone-unsafe-functions.ReportMoreUnsafeFunctions: true - bugprone-unused-return-value.CheckedReturnTypes: ::std::error_code;::std::error_condition;::std::errc - misc-include-cleaner.IgnoreHeaders: '.*/(detail|impl)/.*;.*(expected|unexpected).*;.*ranges_lower_bound\.h;time.h;stdlib.h;__chrono/.*;fmt/chrono.h;boost/uuid/uuid_hash.hpp' HeaderFilterRegex: '^.*/(src|tests)/.*\.(h|hpp)$' WarningsAsErrors: "*" diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 8944176b0..7b1eed2df 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -1,16 +1,22 @@ -name: Clang-tidy check +name: Run clang-tidy on files on: push: branches: [develop] - schedule: - - cron: "0 9 * * 1-5" workflow_dispatch: pull_request: branches: [develop] paths: - .github/workflows/clang-tidy.yml + - CMakeLists.txt + - conanfile.py + - conan.lock + - "cmake/**" + - "src/**" + - "tests/**" + - "benchmarks/**" + - .clang_tidy concurrency: @@ -19,6 +25,13 @@ concurrency: cancel-in-progress: true env: + BUILD_DIR: build + BUILD_TYPE: Debug # Debug so that ASSERTS and such participate in clang-tidy check + + OUTPUT_FILE: clang-tidy-output.txt + DIFF_FILE: clang-tidy-git-diff.txt + ISSUE_FILE: clang-tidy-issue.md + CONAN_PROFILE: clang LLVM_TOOLS_VERSION: 21 @@ -27,8 +40,16 @@ defaults: shell: bash jobs: - clang_tidy: - if: github.event_name != 'push' || contains(github.event.head_commit.message, 'clang-tidy auto fixes') + determine-files: + if: ${{ github.event_name == 'pull_request' }} + permissions: + contents: read + uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@12f5dbc98a2260259a66970e57fa4d26fb7f285c + + run-clang-tidy: + name: Run clang tidy + needs: [determine-files] + if: ${{ always() && !cancelled() && (github.event_name != 'pull_request' || needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }} runs-on: heavy container: image: ghcr.io/xrplf/clio-ci:f174b47f4909ae41b80406d836ab52adc39eacc6 @@ -40,8 +61,6 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - name: Prepare runner uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab @@ -54,26 +73,37 @@ jobs: - name: Run conan uses: ./.github/actions/conan with: + build_dir: ${{ env.BUILD_DIR }} conan_profile: ${{ env.CONAN_PROFILE }} + build_type: ${{ env.BUILD_TYPE }} - name: Run CMake uses: ./.github/actions/cmake with: + build_dir: ${{ env.BUILD_DIR }} conan_profile: ${{ env.CONAN_PROFILE }} + build_type: ${{ env.BUILD_TYPE }} - name: Get number of processors uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf id: nproc - - name: Run clang-tidy (several times) + - name: Run clang tidy continue-on-error: true - id: clang_tidy + id: run_clang_tidy + env: + TARGETS: ${{ (needs.determine-files.outputs.clang_tidy_config_changed != 'true' && github.event_name == 'pull_request') && needs.determine-files.outputs.cpp_changed_files || 'benchmarks src tests' }} run: | - # We run clang-tidy several times, because some fixes may enable new fixes in subsequent runs. - CLANG_TIDY_COMMAND="run-clang-tidy-${{ env.LLVM_TOOLS_VERSION }} -p build -j ${{ steps.nproc.outputs.nproc }} -fix -quiet" - ${CLANG_TIDY_COMMAND} || - ${CLANG_TIDY_COMMAND} || - ${CLANG_TIDY_COMMAND} + set -o pipefail + run-clang-tidy-${{ env.LLVM_TOOLS_VERSION }} -j ${{ steps.nproc.outputs.nproc }} -p "${BUILD_DIR}" -quiet -fix -allow-no-checks ${TARGETS} 2>&1 | tee "${OUTPUT_FILE}" + + - name: Upload clang-tidy output + if: ${{ github.event.repository.visibility == 'public' && steps.run_clang_tidy.outcome != 'success' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + path: ${{ env.OUTPUT_FILE }} + archive: false + retention-days: 30 - name: Check for changes id: files_changed @@ -81,34 +111,76 @@ jobs: run: | git diff --exit-code - - name: Fix local includes and clang-format style + - name: Fix style if: ${{ steps.files_changed.outcome != 'success' }} run: | - pre-commit run --all-files fix-local-includes || true - pre-commit run --all-files clang-format || true + pre-commit run --all-files || true - name: Generate git diff if: ${{ steps.files_changed.outcome != 'success' }} run: | - git diff | tee clang-tidy-git-diff.txt + git diff | tee "${DIFF_FILE}" - name: Upload clang-tidy diff output - if: ${{ steps.files_changed.outcome != 'success' }} + if: ${{ github.event.repository.visibility == 'public' && steps.files_changed.outcome != 'success' }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: clang-tidy-git-diff - path: clang-tidy-git-diff.txt + path: ${{ env.DIFF_FILE }} + archive: false retention-days: 30 - - name: Create an issue - if: ${{ (steps.clang_tidy.outcome != 'success' || steps.files_changed.outcome != 'success') && github.event_name != 'pull_request' }} - id: create_issue + - name: Write issue header + if: ${{ steps.run_clang_tidy.outcome != 'success' }} + run: | + cat > "${ISSUE_FILE}" < filtered-output.txt || true + + # If filtered output is empty, use original (might be a different error format) + if [ ! -s filtered-output.txt ]; then + cp "${OUTPUT_FILE}" filtered-output.txt + fi + + # Truncate if too large + head -c 60000 filtered-output.txt >> "${ISSUE_FILE}" + if [ "$(wc -c < filtered-output.txt)" -gt 60000 ]; then + echo "" >> "${ISSUE_FILE}" + echo "... (output truncated, see artifacts for full output)" >> "${ISSUE_FILE}" + fi + + rm filtered-output.txt + else + echo "No output file found" >> "${ISSUE_FILE}" + fi + + - name: Append issue footer + if: ${{ steps.run_clang_tidy.outcome != 'success' }} + run: | + cat >> "${ISSUE_FILE}" <