Skip to content

Commit c0e9584

Browse files
committed
fix(ci): reuse built coverage tree + fix clang-tidy error gate false-positive
The coverage job's 'Generate coverage report' step (now in-container) still failed: generate_coverage.sh did 'rm -rf $BUILD_DIR' then reconfigured CMake from scratch, which re-ran Conan find_package(GTest) in a fresh cache and aborted with "Library 'gmock_main' not found in package" (GTest-Target-debug.cmake) — an edge case the incremental in-container build does not hit. Make the script reuse $BUILD_DIR when it is already a configured+built coverage tree (the CI path: make compile.debug.coverage + make test.debug.coverage already ran cmake/ ninja/ctest in-container against this exact dir). Local 'make coverage' (BUILD_DIR=build/coverage, not pre-built) still configures+builds fresh. The lint job's 'Fail on clang-tidy errors' gate matched /error:/, which also matches source-context echo lines like 'using std::runtime_error::runtime_error;' ('runtime_error::' contains the substring 'error:'). This produced false-positive failures once clang-tidy began analysing the in-container _deps/ tree (cista_exception.h). Anchor the gate on the canonical ': error: ' diagnostic form, restrict to /src/ and /include/ paths, and exclude /_deps/ so only genuine first-party clang-tidy errors gate the job. Signed-off-by: Micah Villmow <4211002+mvillmow@users.noreply.github.com>
1 parent 93725e8 commit c0e9584

3 files changed

Lines changed: 86 additions & 39 deletions

File tree

.github/workflows/_required.yml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,10 +296,22 @@ jobs:
296296
- name: Fail on clang-tidy errors
297297
run: |
298298
set -euo pipefail
299-
# Only fail on errors that reference actual source files (src/ or include/).
300-
# awk always exits 0; emits only error: lines that aren't third-party noise.
299+
# Only fail on errors that reference actual first-party source files
300+
# (src/ or include/). clang-tidy diagnostics have the canonical form
301+
# <path>:<line>:<col>: error: <message> [<check>]
302+
# so we anchor on ": error: " (with surrounding spaces) — a bare
303+
# /error:/ also matches source-context echo lines such as
304+
# 8 | using std::runtime_error::runtime_error;
305+
# because "runtime_error::" contains the substring "error:", which
306+
# caused false-positive failures once clang-tidy began analysing the
307+
# in-container _deps/ tree (e.g. cista_exception.h). We also restrict
308+
# to /src/ and /include/ paths and exclude /_deps/ so third-party
309+
# header warnings never gate the job. awk always exits 0; it emits
310+
# only genuine first-party error: diagnostics.
301311
REAL_ERRORS=$(awk '
302-
/error:/ &&
312+
/: error: / &&
313+
/\/(src|include)\// &&
314+
!/\/_deps\// &&
303315
!/no input files/ &&
304316
!/no such file or directory/ &&
305317
!/unable to handle compilation/ { print }

.github/workflows/extras.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,19 @@ jobs:
202202
run: make test.debug.coverage
203203

204204
- name: Generate coverage report
205+
# Must run inside the dev container: the make compile/test.debug.coverage
206+
# steps above ran cmake/ninja/ctest in-container (via the Makefile's
207+
# CONTAINER_PREFIX), so the build tree's CMakeCache, CTestTestfile and
208+
# .gcda paths are all stamped with the in-container /workspace/... root.
209+
# Running generate_coverage.sh on the host re-runs ctest against those
210+
# /workspace paths (which do not exist on the host) and finds no .gcda
211+
# files. Executing in-container keeps lcov/genhtml/ctest consistent with
212+
# the build+test steps; the report lands in the bind-mounted build/ dir.
205213
run: |
206214
chmod +x scripts/generate_coverage.sh
207-
BUILD_DIR=build/x86.coverage.debug ./scripts/generate_coverage.sh
215+
DOCKER_HOST="${DOCKER_HOST:-}" podman-compose exec -T dev bash -c '
216+
BUILD_DIR=build/x86.coverage.debug ./scripts/generate_coverage.sh
217+
'
208218
209219
- name: Upload coverage report
210220
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1

scripts/generate_coverage.sh

Lines changed: 60 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -83,42 +83,67 @@ fi
8383
mkdir -p "$COVERAGE_DIR"
8484

8585
if [[ "$HTML_ONLY" == "false" ]]; then
86-
# Clean previous build
87-
echo -e "${YELLOW}Cleaning previous build...${NC}"
88-
rm -rf "$BUILD_DIR"
89-
mkdir -p "$BUILD_DIR"
90-
cd "$BUILD_DIR"
91-
92-
# Configure with coverage enabled
93-
echo -e "${YELLOW}Configuring CMake with coverage enabled...${NC}"
94-
# Pass Conan toolchain if it exists
95-
TOOLCHAIN_ARG=""
96-
if [ -f "$PROJECT_ROOT/build/conan-deps/conan_toolchain.cmake" ]; then
97-
TOOLCHAIN_ARG="-DCMAKE_TOOLCHAIN_FILE=$PROJECT_ROOT/build/conan-deps/conan_toolchain.cmake"
86+
# If BUILD_DIR is already a configured + built coverage tree (the CI path:
87+
# `make compile.debug.coverage` + `make test.debug.coverage` already ran
88+
# cmake/ninja/ctest inside the dev container against this exact directory),
89+
# reuse it. Wiping it and reconfiguring from scratch re-runs Conan's
90+
# find_package(GTest) resolution in a fresh cache and fails with
91+
# "Library 'gmock_main' not found in package" (GTest-Target-debug.cmake) —
92+
# an edge case the incremental in-container build does not hit. Reusing the
93+
# already-built tree also keeps every CMakeCache absolute path consistent
94+
# with the build/test steps (all under the same in-container /workspace/...
95+
# root). For the local `make coverage` path BUILD_DIR (build/coverage) does
96+
# not yet exist, so we fall through to a clean configure + build as before.
97+
if [ -f "$BUILD_DIR/CMakeCache.txt" ]; then
98+
# Reuse path: the upstream `make compile.debug.coverage` +
99+
# `make test.debug.coverage` steps already configured, built AND ran
100+
# ctest against this directory, so the .gcda coverage data is already
101+
# present. We must NOT zero the counters or re-run ctest here — doing so
102+
# would wipe the existing .gcda and then fail to regenerate it if the
103+
# build tree's CTestTestfile references paths from a different mount
104+
# (e.g. the in-container /workspace/... root). Capture directly from the
105+
# existing .gcda instead.
106+
echo -e "${BLUE}Reusing existing build directory: $BUILD_DIR${NC}"
107+
cd "$BUILD_DIR"
108+
else
109+
# Clean previous build
110+
echo -e "${YELLOW}Cleaning previous build...${NC}"
111+
rm -rf "$BUILD_DIR"
112+
mkdir -p "$BUILD_DIR"
113+
cd "$BUILD_DIR"
114+
115+
# Configure with coverage enabled
116+
echo -e "${YELLOW}Configuring CMake with coverage enabled...${NC}"
117+
# Pass Conan toolchain if it exists
118+
TOOLCHAIN_ARG=""
119+
if [ -f "$PROJECT_ROOT/build/conan-deps/conan_toolchain.cmake" ]; then
120+
TOOLCHAIN_ARG="-DCMAKE_TOOLCHAIN_FILE=$PROJECT_ROOT/build/conan-deps/conan_toolchain.cmake"
121+
fi
122+
cmake -DENABLE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug -G Ninja $TOOLCHAIN_ARG "$PROJECT_ROOT"
123+
124+
if [[ $? -ne 0 ]]; then
125+
echo -e "${RED}CMake configuration failed${NC}"
126+
exit 1
127+
fi
128+
129+
# Build
130+
echo -e "${YELLOW}Building project...${NC}"
131+
ninja
132+
133+
if [[ $? -ne 0 ]]; then
134+
echo -e "${RED}Build failed${NC}"
135+
exit 1
136+
fi
137+
138+
# Reset coverage counters (fresh build only — the reuse path keeps the
139+
# .gcda produced by the upstream make test.debug.coverage step).
140+
echo -e "${YELLOW}Resetting coverage counters...${NC}"
141+
lcov --zerocounters --directory . $GCOV_TOOL_ARG
142+
143+
# Run tests (continue even if some fail to get partial coverage)
144+
echo -e "${YELLOW}Running tests...${NC}"
145+
ctest --output-on-failure || echo -e "${YELLOW}Warning: Some tests failed, but continuing with coverage generation${NC}"
98146
fi
99-
cmake -DENABLE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug -G Ninja $TOOLCHAIN_ARG "$PROJECT_ROOT"
100-
101-
if [[ $? -ne 0 ]]; then
102-
echo -e "${RED}CMake configuration failed${NC}"
103-
exit 1
104-
fi
105-
106-
# Build
107-
echo -e "${YELLOW}Building project...${NC}"
108-
ninja
109-
110-
if [[ $? -ne 0 ]]; then
111-
echo -e "${RED}Build failed${NC}"
112-
exit 1
113-
fi
114-
115-
# Reset coverage counters
116-
echo -e "${YELLOW}Resetting coverage counters...${NC}"
117-
lcov --zerocounters --directory . $GCOV_TOOL_ARG
118-
119-
# Run tests (continue even if some fail to get partial coverage)
120-
echo -e "${YELLOW}Running tests...${NC}"
121-
ctest --output-on-failure || echo -e "${YELLOW}Warning: Some tests failed, but continuing with coverage generation${NC}"
122147

123148
# Capture coverage data.
124149
# Use llvm-cov gcov wrapper so Clang-built .gcda files are processed correctly

0 commit comments

Comments
 (0)