feat(ci): speed up clang-tidy CI workflow (#438)

- Add `clang_tidy_deps` CMake target that only builds generated headers
  (protobuf, Arrow, glog, gflags, lz4) instead of full `zvec_db`
- Run clang-tidy in parallel via `xargs -P $(nproc)` with per-file logs
- Split actions/cache into restore/save to ensure cache is saved even
  when clang-tidy fails, without caching incomplete build artifacts
- Shallow-clone submodules with `--depth 1`
- Bump ccache size from 100M to 500M
- Add `--quiet` to suppress noisy clang-tidy summary lines
- Remove redundant concurrency block from reusable workflow

Performance comparison (3 runs, same repo):

| Step            | Before  | After (cold) | After (warm) |
|-----------------|---------|--------------|--------------|
| Submodule clone | 66s     | 49s          | 28s          |
| Build deps      | 70s     | 21s          | 0s (skipped) |
| Cache restore   | 1s      | 1s           | 12s          |
| Total           | 3m03s   | 2m34s        | 1m46s        |

Dominant win: reduced build target (~49s). Headers cache adds ~9s net.
`--depth 1` contributes ~15s (with network variance).
This commit is contained in:
egolearner 2026-06-02 10:28:05 +08:00 committed by GitHub
parent 0807adeec5
commit 3e98314d7c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 55 additions and 22 deletions

View File

@ -4,10 +4,6 @@ on:
workflow_call:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
@ -41,7 +37,7 @@ jobs:
- name: Checkout submodules
if: steps.changed_files.outputs.any_changed == 'true'
run: git submodule update --init --recursive
run: git submodule update --init --recursive --depth 1
- name: Install dependencies
if: steps.changed_files.outputs.any_changed == 'true'
@ -54,7 +50,7 @@ jobs:
uses: hendrikmuhs/ccache-action@v1.2
with:
key: clang-tidy
max-size: 100M
max-size: 500M
- name: Configure CMake and export compile commands
if: steps.changed_files.outputs.any_changed == 'true'
@ -126,36 +122,71 @@ jobs:
hash=$(git submodule status --recursive | awk '{print $1}' | sort | sha256sum | awk '{print $1}')
echo "value=${hash}" >> "$GITHUB_OUTPUT"
- name: Cache third-party build artifacts
id: cache_thirdparty
- name: Restore generated headers cache
id: cache_headers
if: steps.changed_files.outputs.any_changed == 'true'
uses: actions/cache@v5
uses: actions/cache/restore@v5
with:
path: build/external
key: thirdparty-${{ runner.os }}-${{ hashFiles('thirdparty/**/*.cmake', 'thirdparty/**/CMakeLists.txt', 'thirdparty/**/*.patch') }}-${{ steps.submodule_hash.outputs.value }}
path: |
build/external
build/thirdparty
build/src/db/proto
key: clang-tidy-headers-${{ runner.os }}-${{ hashFiles('thirdparty/**/*.cmake', 'thirdparty/**/CMakeLists.txt', 'thirdparty/**/*.patch', 'src/db/proto/*.proto') }}-${{ steps.submodule_hash.outputs.value }}
- name: Build
if: steps.tidy_files.outputs.any_tidy_files == 'true'
- name: Build generated headers only
if: steps.tidy_files.outputs.any_tidy_files == 'true' && steps.cache_headers.outputs.cache-hit != 'true'
run: |
ninja -C build zvec_db
ninja -C build clang_tidy_deps
- name: Run clang-tidy on changed files
- name: Save generated headers cache
if: steps.tidy_files.outputs.any_tidy_files == 'true' && steps.cache_headers.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
with:
path: |
build/external
build/thirdparty
build/src/db/proto
key: clang-tidy-headers-${{ runner.os }}-${{ hashFiles('thirdparty/**/*.cmake', 'thirdparty/**/CMakeLists.txt', 'thirdparty/**/*.patch', 'src/db/proto/*.proto') }}-${{ steps.submodule_hash.outputs.value }}
- name: Run clang-tidy on changed files (parallel)
if: steps.tidy_files.outputs.any_tidy_files == 'true'
run: |
mapfile -t files_to_check <<'TIDY_EOF'
${{ steps.tidy_files.outputs.all_tidy_files }}
TIDY_EOF
log_dir=$(mktemp -d)
printf '%s\n' "${files_to_check[@]}" \
| grep -v '^\s*$' \
| xargs -I{} -P "$(nproc)" sh -c '
file="{}"
if [ -f "$file" ]; then
log="'"$log_dir"'/$$.log"
echo "$file" > "$log"
if clang-tidy -p build --quiet --warnings-as-errors="*" "$file" >> "$log" 2>&1; then
echo "PASS: $file"
rm -f "$log"
else
echo "FAIL: $file"
fi
fi'
failed=0
for file in "${files_to_check[@]}"; do
[[ -z "${file// }" ]] && continue
if [ -f "$file" ]; then
echo "=== clang-tidy: $file ==="
clang-tidy -p build --warnings-as-errors='*' "$file" || failed=1
fi
for f in "$log_dir"/*.log; do
[ -e "$f" ] || break
failed=1
src=$(head -1 "$f")
echo ""
echo "::group::clang-tidy errors: $src"
tail -n +2 "$f"
echo "::endgroup::"
done
exit $failed
rm -rf "$log_dir"
if [ "$failed" -eq 1 ]; then
echo "::error::clang-tidy found issues in one or more files"
exit 1
fi
- name: No files to analyse
if: steps.changed_files.outputs.any_changed == 'true' && steps.tidy_files.outputs.any_tidy_files != 'true'

View File

@ -119,6 +119,8 @@ cc_directory(thirdparty)
cc_directories(src)
cc_directories(tests)
add_custom_target(clang_tidy_deps DEPENDS zvec_proto ARROW.BUILD glog gflags Lz4.BUILD)
if(BUILD_TOOLS)
cc_directories(tools)
endif()