Skip to content

Commit 2a57f85

Browse files
Try custom clang CI job
1 parent 2336dca commit 2a57f85

2 files changed

Lines changed: 131 additions & 46 deletions

File tree

.github/workflows/builds.yml

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,6 @@ jobs:
553553
continue-on-error: false
554554
permissions:
555555
contents: read
556-
pull-requests: write
557556

558557
steps:
559558
- name: Checkout (with submodules)
@@ -568,8 +567,16 @@ jobs:
568567
sudo apt-get update
569568
sudo apt-get install -y \
570569
build-essential cmake ninja-build pkg-config \
571-
llvm-dev libclang-dev clang clang-tidy \
570+
llvm-dev libclang-dev clang clang-tidy clang-tools \
572571
libssl-dev
572+
# run-clang-tidy ships as run-clang-tidy-<N> on apt; expose an
573+
# unsuffixed name so tidy.sh works unchanged across environments.
574+
if ! command -v run-clang-tidy >/dev/null 2>&1; then
575+
rct_versioned=$(ls /usr/bin/run-clang-tidy-* 2>/dev/null | sort -V | tail -1)
576+
if [ -n "${rct_versioned}" ]; then
577+
sudo ln -sf "${rct_versioned}" /usr/local/bin/run-clang-tidy
578+
fi
579+
fi
573580
574581
- name: Install Rust (stable)
575582
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
@@ -590,33 +597,9 @@ jobs:
590597
run: cmake --build build-release --target livekit_proto
591598

592599
- name: Run clang-tidy
593-
uses: cpp-linter/cpp-linter-action@77c390c5ba9c947ebc185a3e49cc754f1558abb5 # v2.18.0
594-
id: linter
595-
env:
596-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
597-
with:
598-
style: ''
599-
tidy-checks: ''
600-
database: build-release
601-
files-changed-only: false
602-
lines-changed-only: false
603-
extensions: 'c,cpp,cc,cxx'
604-
ignore: 'build-*|cpp-example-collection|client-sdk-rust|vcpkg_installed|src/tests|bridge|examples|docker|data|docs'
605-
file-annotations: true
606-
thread-comments: update
607-
step-summary: true
608-
tidy-review: false
609-
no-lgtm: true
610-
jobs: 0 # 0 == use all available CPU cores
611-
612-
- name: Check warning thresholds
613-
env:
614-
TIDY_FINDINGS: ${{ steps.linter.outputs.clang-tidy-checks-failed }}
615-
MAX_TIDY_FINDINGS: '0'
616-
run: |
617-
echo "clang-tidy findings: ${TIDY_FINDINGS}"
618-
if [ "${TIDY_FINDINGS}" -gt "${MAX_TIDY_FINDINGS}" ]; then
619-
echo "::warning::clang-tidy found ${TIDY_FINDINGS} issue(s), threshold is ${MAX_TIDY_FINDINGS}"
620-
exit 1
621-
fi
622-
echo "clang-tidy findings within threshold"
600+
# tidy.sh auto-detects $GITHUB_ACTIONS and emits ::warning/::error
601+
# workflow commands so findings surface as PR file annotations.
602+
# It exits non-zero only when a finding is promoted to an error via
603+
# WarningsAsErrors in .clang-tidy; pure warnings annotate but don't
604+
# fail the build.
605+
run: ./tidy.sh

tidy.sh

Lines changed: 116 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
#!/usr/bin/env bash
22
#
3-
# tidy.sh -- Run clang-tidy locally using the same file set and config as CI.
4-
#
5-
# Matches the file filter used by the cpp-linter GitHub Action in
6-
# .github/workflows/builds.yml: only src/**/*.{c,cpp,cc,cxx} excluding
7-
# src/tests/. Picks up checks from the repo-root .clang-tidy automatically.
3+
# tidy.sh -- Run clang-tidy locally and in CI using the same file set and
4+
# config. Picks up checks from the repo-root .clang-tidy automatically.
85
#
96
# Usage:
10-
# ./tidy.sh # run on full src/ tree
11-
# ./tidy.sh -j 4 # override parallelism
12-
# ./tidy.sh -fix # auto-apply fixes (forwarded to run-clang-tidy)
7+
# ./tidy.sh # run on full src/ tree
8+
# ./tidy.sh -j 4 # override parallelism
9+
# ./tidy.sh --github-actions # force GitHub Actions annotation mode
10+
# ./tidy.sh -fix # forwarded to run-clang-tidy
11+
#
12+
# In GitHub Actions (auto-detected via $GITHUB_ACTIONS=true, or forced with
13+
# --github-actions), this script additionally:
14+
# - Emits ::warning/::error workflow commands so findings appear as PR file
15+
# annotations (yellow for warnings, red for errors). Severity comes from
16+
# clang-tidy itself -- errors are findings promoted by WarningsAsErrors
17+
# in .clang-tidy.
18+
# - Writes a short markdown summary to $GITHUB_STEP_SUMMARY.
19+
# - Exits non-zero only when run-clang-tidy does (i.e. only on errors);
20+
# warnings annotate but do not fail the build.
1321
#
1422
# Requires CMake to have generated build-release/compile_commands.json.
1523
# Run once: cmake --preset macos-release (or linux-release)
@@ -18,11 +26,29 @@ set -euo pipefail
1826

1927
BUILD_DIR="build-release"
2028
# Positive match for top-level src/*.{c,cpp,cc,cxx}; negative lookahead excludes
21-
# dep paths (_deps/, build-*/, -src/src/) and other top-level dirs that CI's
22-
# cpp-linter `ignore:` list filters out. Python regex (PCRE-ish) supports
23-
# lookahead; this regex is evaluated by run-clang-tidy.
29+
# dep paths (_deps/, build-*/, -src/src/) and every other top-level dir. Python
30+
# regex (PCRE-ish) supports lookahead; this regex is evaluated by run-clang-tidy.
2431
FILE_REGEX='^(?!.*/(_deps|build-[^/]*|bridge|examples|client-sdk-rust|cpp-example-collection|vcpkg_installed|docker|docs|data)/).*/src/(?!tests/).*\.(c|cpp|cc|cxx)$'
2532

33+
CI_MODE=0
34+
if [[ "${GITHUB_ACTIONS:-}" == "true" ]]; then
35+
CI_MODE=1
36+
fi
37+
38+
forward_args=()
39+
while (($#)); do
40+
case "$1" in
41+
--github-actions|--gh)
42+
CI_MODE=1
43+
shift
44+
;;
45+
*)
46+
forward_args+=("$1")
47+
shift
48+
;;
49+
esac
50+
done
51+
2652
if [[ ! -f "${BUILD_DIR}/compile_commands.json" ]]; then
2753
echo "ERROR: ${BUILD_DIR}/compile_commands.json not found." >&2
2854
echo "Run: cmake --preset macos-release (or linux-release)" >&2
@@ -32,7 +58,7 @@ fi
3258
if ! command -v run-clang-tidy >/dev/null 2>&1; then
3359
echo "ERROR: run-clang-tidy not found in PATH." >&2
3460
echo "Install LLVM: brew install llvm (macOS)" >&2
35-
echo " apt install clang-tidy (Linux)" >&2
61+
echo " apt install clang-tools-NN (Linux)" >&2
3662
exit 1
3763
fi
3864

@@ -50,10 +76,86 @@ else
5076
jobs=$(sysctl -n hw.ncpu 2>/dev/null || echo 4)
5177
fi
5278

79+
# Emit GitHub Actions workflow commands for each clang-tidy diagnostic line
80+
# in the given log. Notes (`path:L:C: note: ...`) are deliberately skipped --
81+
# they belong to the preceding warning/error and would produce noisy extra
82+
# annotations. Severity (::warning vs ::error) mirrors clang-tidy's prefix.
83+
emit_annotations() {
84+
local log="$1"
85+
local workspace="${GITHUB_WORKSPACE:-${PWD}}"
86+
local line path lineno col severity message check rel_path
87+
88+
while IFS= read -r line; do
89+
[[ "${line}" =~ ^(.+):([0-9]+):([0-9]+):[[:space:]]+(warning|error):[[:space:]]+(.+)[[:space:]]\[([^]]+)\][[:space:]]*$ ]] || continue
90+
path="${BASH_REMATCH[1]}"
91+
lineno="${BASH_REMATCH[2]}"
92+
col="${BASH_REMATCH[3]}"
93+
severity="${BASH_REMATCH[4]}"
94+
message="${BASH_REMATCH[5]}"
95+
check="${BASH_REMATCH[6]}"
96+
97+
rel_path="${path#${workspace}/}"
98+
99+
message="${message//$'%'/%25}"
100+
message="${message//$'\r'/%0D}"
101+
message="${message//$'\n'/%0A}"
102+
103+
printf '::%s file=%s,line=%s,col=%s,title=clang-tidy (%s)::%s\n' \
104+
"${severity}" "${rel_path}" "${lineno}" "${col}" "${check}" "${message}"
105+
done < "${log}"
106+
}
107+
108+
# Append a small markdown summary (counts + top checks) to $GITHUB_STEP_SUMMARY
109+
# so the GitHub job page surfaces totals without needing to scan the log.
110+
write_step_summary() {
111+
local log="$1"
112+
local summary_file="${GITHUB_STEP_SUMMARY:-}"
113+
[[ -n "${summary_file}" ]] || return 0
114+
115+
local warnings errors
116+
warnings=$(grep -Ec '^.+:[0-9]+:[0-9]+:[[:space:]]+warning:[[:space:]]' "${log}" || true)
117+
errors=$(grep -Ec '^.+:[0-9]+:[0-9]+:[[:space:]]+error:[[:space:]]' "${log}" || true)
118+
119+
{
120+
echo "## clang-tidy results"
121+
echo
122+
echo "| Severity | Count |"
123+
echo "|----------|-------|"
124+
echo "| Errors | ${errors} |"
125+
echo "| Warnings | ${warnings} |"
126+
echo
127+
128+
if (( warnings + errors > 0 )); then
129+
echo "### Top checks"
130+
echo
131+
echo '| Check | Count |'
132+
echo '|-------|-------|'
133+
grep -Eo '\[[a-zA-Z0-9._,-]+\]$' "${log}" \
134+
| sort | uniq -c | sort -rn | head -5 \
135+
| awk '{ n = $1; $1 = ""; sub(/^ /, ""); gsub(/[\[\]]/, "", $0); printf("| `%s` | %d |\n", $0, n) }'
136+
echo
137+
fi
138+
} >> "${summary_file}"
139+
}
140+
141+
log="$(mktemp -t tidy-log.XXXXXX)"
142+
trap 'rm -f "${log}"' EXIT
143+
144+
set +e
53145
run-clang-tidy \
54146
-p "${BUILD_DIR}" \
55147
-quiet \
56148
-j "${jobs}" \
57149
"${extra_args[@]}" \
58-
"$@" \
59-
"${FILE_REGEX}"
150+
"${forward_args[@]}" \
151+
"${FILE_REGEX}" \
152+
2>&1 | tee "${log}"
153+
rc="${PIPESTATUS[0]}"
154+
set -e
155+
156+
if [[ "${CI_MODE}" == "1" ]]; then
157+
emit_annotations "${log}"
158+
write_step_summary "${log}"
159+
fi
160+
161+
exit "${rc}"

0 commit comments

Comments
 (0)