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:
parent
0807adeec5
commit
3e98314d7c
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue