Skip to content

chore(ci): Graduate sanitizer jobs from Phase 0 to Phase 1 (#554) #685

chore(ci): Graduate sanitizer jobs from Phase 0 to Phase 1 (#554)

chore(ci): Graduate sanitizer jobs from Phase 0 to Phase 1 (#554) #685

Workflow file for this run

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