Skip to content

greenc-FNAL checking C++ code with clang-tidy #2320

greenc-FNAL checking C++ code with clang-tidy

greenc-FNAL checking C++ code with clang-tidy #2320

name: Clang-Tidy Check
"run-name": "${{ github.actor }} checking C++ code with clang-tidy"
permissions:
contents: read
pull-requests: read
"on":
pull_request:
issue_comment:
types: [created]
workflow_dispatch:
inputs:
ref:
description: "The branch, ref, or SHA to checkout. Defaults to the repository's default branch."
required: false
type: string
jobs:
setup:
runs-on: ubuntu-latest
if: >
github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || (
github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
contains(fromJSON('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) &&
startsWith(github.event.comment.body, '@phlexbot tidy-check')
)
outputs:
is_act: ${{ steps.setup.outputs.is_act }}
ref: ${{ steps.setup.outputs.ref }}
repo: ${{ steps.setup.outputs.repo }}
base_sha: ${{ steps.setup.outputs.base_sha }}
pr_number: ${{ steps.setup.outputs.pr_number }}
checkout_path: ${{ steps.setup.outputs.checkout_path }}
build_path: ${{ steps.setup.outputs.build_path }}
has_changes: ${{ steps.setup.outputs.has_changes }}
steps:
- name: Workflow setup
id: setup
uses: Framework-R-D/phlex/.github/actions/workflow-setup@main
with:
file-type: |
cpp
cmake
clang-tidy-check:
needs: setup
if: >
always() && (github.event_name == 'workflow_dispatch' || needs.setup.outputs.has_changes == 'true')
runs-on: ubuntu-24.04
container:
image: ghcr.io/framework-r-d/phlex-ci:latest
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ needs.setup.outputs.ref }}
path: ${{ needs.setup.outputs.checkout_path }}
repository: ${{ needs.setup.outputs.repo }}
persist-credentials: false
- name: Setup build environment
uses: Framework-R-D/phlex/.github/actions/setup-build-env@main
with:
build-path: ${{ needs.setup.outputs.build_path }}
- name: Configure CMake (Debug)
uses: Framework-R-D/phlex/.github/actions/configure-cmake@main
with:
build-type: Debug
source-path: ${{ needs.setup.outputs.checkout_path }}
build-path: ${{ needs.setup.outputs.build_path }}
extra-options: "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_SCAN_FOR_MODULES=OFF"
- name: Run clang-tidy
id: tidy
shell: bash
working-directory: ${{ needs.setup.outputs.build_path }}
env:
REPO: ${{ needs.setup.outputs.repo }}
SOURCE_PATH: ${{ needs.setup.outputs.checkout_path }}
BUILD_PATH: ${{ needs.setup.outputs.build_path }}
run: |
. /entrypoint.sh
REPO_NAME="${REPO##*/}"
SOURCE_DIR="$GITHUB_WORKSPACE/$SOURCE_PATH"
BUILD_DIR="$GITHUB_WORKSPACE/$BUILD_PATH"
# run-clang-tidy runs clang-tidy on every translation unit in
# compile_commands.json in parallel and merges the per-TU fix YAML files
# into a single comprehensive clang-tidy-fixes.yaml. The alternative
# (setting CMAKE_CXX_CLANG_TIDY=clang-tidy;--export-fixes=... and
# building with -j) suffers from a race condition: every parallel
# clang-tidy invocation independently overwrites the same output file, so
# only the fixes from whichever translation unit finishes last are
# retained.
#
# Path arguments are substring-matched against file paths in
# compile_commands.json; restricting to the source directory automatically
# excludes generated files (e.g. version.cpp, ROOT dictionaries) that live
# in the build directory and lack access to the .clang-tidy config.
#
# run-clang-tidy validates the check list by calling clang-tidy from its
# working directory; run from the source root so that clang-tidy discovers
# the .clang-tidy config file there, preventing a spurious "No checks
# enabled." error.
if ! command -v run-clang-tidy >/dev/null 2>&1; then
echo "::error::run-clang-tidy not found in PATH; cannot run clang-tidy checks"
exit 1
fi
echo "➡️ Running clang-tidy checks..."
cd "$SOURCE_DIR"
set +e
run-clang-tidy \
-p "$BUILD_DIR" \
-export-fixes "$BUILD_DIR/clang-tidy-fixes.yaml" \
-j "$(nproc)" \
"$SOURCE_DIR/phlex" \
"$SOURCE_DIR/form" \
"$SOURCE_DIR/plugins" \
"$SOURCE_DIR/test" \
> "$BUILD_DIR/clang-tidy.log" 2>&1
TIDY_EXIT=$?
set -e
if grep -qE '^/.+\.(cpp|hpp|c|h):[0-9]+:[0-9]+: (warning|error):' "$BUILD_DIR/clang-tidy.log"; then
echo "::warning::Clang-tidy found issues in the code"
echo "Comment '@${REPO_NAME}bot tidy-fix [<check>...]' on the PR to attempt auto-fix"
elif [ "$TIDY_EXIT" -ne 0 ] || grep -q "No checks enabled\.\|Traceback\|RuntimeError\|ModuleNotFoundError\|ImportError" "$BUILD_DIR/clang-tidy.log"; then
echo "::error::run-clang-tidy failed (see log below)"
echo "::group::clang-tidy.log (error)"
cat "$BUILD_DIR/clang-tidy.log"
echo "::endgroup::"
exit 1
else
echo "✅ clang-tidy check passed"
fi
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: clang-tidy-check
path: |
${{ needs.setup.outputs.build_path }}/clang-tidy-fixes.yaml
${{ needs.setup.outputs.build_path }}/clang-tidy.log
retention-days: 7
if-no-files-found: ignore
report:
needs: [setup, clang-tidy-check]
if: always() && needs.setup.result == 'success'
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: write
steps:
- name: Post Clang-Tidy results
uses: Framework-R-D/phlex/.github/actions/post-clang-tidy-results@main
with:
build-path: ${{ needs.setup.outputs.build_path }}
pr-number: ${{ needs.setup.outputs.pr_number }}
post-summary: "true"