chore(ci): Graduate sanitizer jobs from Phase 0 to Phase 1 (#554) #685
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [ main, phase-* ] | |
| pull_request: | |
| branches: [ main ] | |
| jobs: | |
| # Guard against re-introduction of legacy error types in public API | |
| api-guard: | |
| name: API Guard - No Legacy Types | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Check for legacy types in public headers | |
| run: | | |
| echo "=== Checking for legacy thread::result/thread::error in include/ ===" | |
| # These types were removed in v3.0, use common::Result<T> and common::VoidResult instead | |
| if grep -rn '\bkcenon::thread::result\b\|\bkcenon::thread::error\b' include/; then | |
| echo "::error::Legacy types found in public headers!" | |
| echo "Use kcenon::common::Result<T> or kcenon::common::VoidResult instead" | |
| exit 1 | |
| fi | |
| echo "No legacy types found in public headers" | |
| build: | |
| name: ${{ matrix.os }} / ${{ matrix.compiler }} | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Ubuntu 24.04 has GCC 13+ with full std::format support | |
| - os: ubuntu-24.04 | |
| compiler: gcc | |
| - os: ubuntu-24.04 | |
| compiler: clang | |
| - os: macos-latest | |
| compiler: clang | |
| # Windows 2022 has MSVC 19.3x+ with full std::format support | |
| # Note: Uses version-based detection (not compile tests) due to Ninja+MSVC flag issues | |
| - os: windows-2022 | |
| compiler: msvc | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Install dependencies (Ubuntu) | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| # Install build tools and compilers (C++20 std::format requires GCC 13+ or Clang 17+) | |
| sudo apt-get install -y cmake ninja-build g++ clang libc++-dev libc++abi-dev | |
| # GTest is now fetched via CMake FetchContent | |
| - name: Install dependencies (macOS) | |
| if: runner.os == 'macOS' | |
| run: | | |
| brew install ninja | |
| # GTest is now fetched via CMake FetchContent | |
| # Note: macOS has built-in std::format support via Apple Clang | |
| - name: Install dependencies (Windows) | |
| if: runner.os == 'Windows' | |
| run: | | |
| choco install ninja -y | |
| # GTest is now fetched via CMake FetchContent | |
| # Note: MSVC 19.29+ has full std::format support (version-based detection) | |
| - name: Setup MSVC Developer Command Prompt | |
| if: runner.os == 'Windows' | |
| uses: ilammy/msvc-dev-cmd@v1 | |
| - name: Set up compiler (GCC) | |
| if: matrix.compiler == 'gcc' | |
| run: | | |
| echo "CC=gcc" >> $GITHUB_ENV | |
| echo "CXX=g++" >> $GITHUB_ENV | |
| - name: Set up compiler (Clang - Linux) | |
| if: matrix.compiler == 'clang' && runner.os == 'Linux' | |
| run: | | |
| echo "CC=clang" >> $GITHUB_ENV | |
| echo "CXX=clang++" >> $GITHUB_ENV | |
| # Use libc++ for proper std::format support | |
| echo "CXXFLAGS=-stdlib=libc++" >> $GITHUB_ENV | |
| echo "LDFLAGS=-stdlib=libc++ -lc++abi" >> $GITHUB_ENV | |
| - name: Set up compiler (Clang - macOS) | |
| if: matrix.compiler == 'clang' && runner.os == 'macOS' | |
| run: | | |
| echo "CC=clang" >> $GITHUB_ENV | |
| echo "CXX=clang++" >> $GITHUB_ENV | |
| - name: Checkout common_system | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: kcenon/common_system | |
| path: common_system | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Configure CMake (Unix) | |
| if: runner.os != 'Windows' | |
| run: | | |
| echo "=== Build Configuration ===" | |
| echo "Compiler: $CC / $CXX" | |
| echo "Common System: ON (required dependency)" | |
| echo "===========================" | |
| cmake -B build -G Ninja \ | |
| -DCMAKE_BUILD_TYPE=Debug \ | |
| -DBUILD_WITH_COMMON_SYSTEM=ON \ | |
| -DBUILD_INTEGRATION_TESTS=ON \ | |
| -DCMAKE_PREFIX_PATH="/usr;/usr/local" || { | |
| echo "::error::CMake configuration failed" | |
| cat build/CMakeFiles/CMakeError.log 2>/dev/null || echo "No error log found" | |
| exit 1 | |
| } | |
| - name: Configure CMake (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| Write-Host "=== Build Configuration ===" | |
| Write-Host "Compiler: MSVC" | |
| Write-Host "Common System: ON (required dependency)" | |
| Write-Host "===========================" | |
| # Use Ninja with MSVC - feature detection uses version-based check | |
| cmake -B build -G Ninja ` | |
| -DCMAKE_BUILD_TYPE=Debug ` | |
| -DCMAKE_CXX_STANDARD=20 ` | |
| -DCMAKE_CXX_FLAGS="/std:c++20 /EHsc /Zc:__cplusplus /permissive-" ` | |
| -DBUILD_WITH_COMMON_SYSTEM=ON ` | |
| -DBUILD_INTEGRATION_TESTS=ON | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Host "::error::CMake configuration failed" | |
| if (Test-Path "build/CMakeFiles/CMakeError.log") { | |
| Get-Content "build/CMakeFiles/CMakeError.log" | |
| } | |
| exit 1 | |
| } | |
| - name: Build (Unix) | |
| if: runner.os != 'Windows' | |
| run: cmake --build build --config Debug | |
| - name: Build (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: cmake --build build --config Debug | |
| - name: Test (Unix) | |
| if: runner.os != 'Windows' | |
| timeout-minutes: 10 | |
| run: | | |
| cd build | |
| if [ -f "bin/thread_base_unit" ]; then | |
| echo "Running unit tests..." | |
| find bin -name "*_unit" -executable 2>/dev/null | while read test; do | |
| echo "Running $test..." | |
| timeout 300 ./$test || echo "Test $test failed but continuing..." | |
| done | |
| else | |
| echo "No unit tests found, running ctest..." | |
| # Debug builds run 2-3x slower, use 300s timeout for integration tests | |
| ctest -C Debug --output-on-failure --timeout 300 || true | |
| fi | |
| - name: Test (Windows) | |
| if: runner.os == 'Windows' | |
| timeout-minutes: 10 | |
| shell: pwsh | |
| run: | | |
| cd build | |
| Write-Host "Running ctest..." | |
| # Debug builds run 2-3x slower, use 300s timeout for integration tests | |
| ctest -C Debug --output-on-failure --timeout 300 | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Host "Some tests failed but continuing..." | |
| } | |
| # Exit with success to match Unix behavior (|| true) | |
| exit 0 | |
| - name: Upload build artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-${{ matrix.os }}-${{ matrix.compiler }}-logs | |
| path: | | |
| build/CMakeFiles/*.log | |
| build/Testing/Temporary/ | |
| # Phase 1: Sanitizer builds (failures block merge) | |
| sanitizer: | |
| name: Sanitizer / ${{ matrix.sanitizer }} | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| sanitizer: [thread, address, undefined] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| # C++20 std::format requires Clang with libc++ on Linux | |
| sudo apt-get install -y cmake ninja-build clang libc++-dev libc++abi-dev | |
| # GTest is now fetched via CMake FetchContent | |
| - name: Checkout common_system | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: kcenon/common_system | |
| path: common_system | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Configure CMake with ${{ matrix.sanitizer }} sanitizer | |
| run: | | |
| echo "=== Build Configuration ===" | |
| echo "Sanitizer: ${{ matrix.sanitizer }}" | |
| echo "Common System: ON (required dependency)" | |
| echo "===========================" | |
| cmake -B build -G Ninja \ | |
| -DCMAKE_BUILD_TYPE=Debug \ | |
| -DCMAKE_CXX_COMPILER=clang++ \ | |
| -DCMAKE_C_COMPILER=clang \ | |
| -DCMAKE_CXX_FLAGS="-stdlib=libc++ -fsanitize=${{ matrix.sanitizer }} -fno-omit-frame-pointer -g" \ | |
| -DCMAKE_EXE_LINKER_FLAGS="-stdlib=libc++ -lc++abi" \ | |
| -DBUILD_WITH_COMMON_SYSTEM=ON \ | |
| -DBUILD_INTEGRATION_TESTS=ON \ | |
| -DCMAKE_PREFIX_PATH="/usr" || { | |
| echo "::error::CMake configuration failed for ${{ matrix.sanitizer }} sanitizer" | |
| cat build/CMakeFiles/CMakeError.log 2>/dev/null || echo "No error log found" | |
| exit 1 | |
| } | |
| - name: Build with sanitizer | |
| run: cmake --build build --config Debug | |
| - name: Run tests with sanitizer | |
| timeout-minutes: 10 | |
| run: | | |
| cd build | |
| export ASAN_OPTIONS=detect_leaks=1:alloc_dealloc_mismatch=0 | |
| export UBSAN_OPTIONS=print_stacktrace=1 | |
| export TSAN_OPTIONS=second_deadlock_stack=1 | |
| if [ -f "bin/thread_base_unit" ]; then | |
| echo "Running unit tests with ${{ matrix.sanitizer }}..." | |
| find bin -name "*_unit" -executable 2>/dev/null | while read test; do | |
| echo "Running $test..." | |
| timeout 120 ./$test | |
| done | |
| else | |
| echo "Running ctest with ${{ matrix.sanitizer }}..." | |
| ctest -C Debug --output-on-failure --timeout 120 -LE performance | |
| fi | |
| - name: Upload sanitizer results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sanitizer-${{ matrix.sanitizer }}-results | |
| path: | | |
| build/Testing/Temporary/ | |
| build/**/*.log |