ci: capture build exit code under bash -e #12
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: MiniSolver CI Pipeline | |
| # Trigger mechanism: When code is pushed to the main branch, or a Pull Request is submitted | |
| on: | |
| push: | |
| branches: [ "main", "master" ] | |
| pull_request: | |
| branches: [ "main", "master" ] | |
| # Allow manual trigger on GitHub Actions page | |
| workflow_dispatch: | |
| jobs: | |
| # ========================================================= | |
| # Task 1: Code Style Check | |
| # Goal: Ensure all submitted code conforms to .clang-format specification | |
| # ========================================================= | |
| style-check: | |
| name: Check Code Style | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Install Clang-Format | |
| run: | | |
| sudo apt-get update | |
| # Pin version to avoid ubuntu-latest drift and keep it consistent with .pre-commit-config.yaml (v16.0.6). | |
| sudo apt-get install -y clang-format-16 | |
| - name: Verify C++ Formatting | |
| run: | | |
| echo ">> Verifying C++ code style against .clang-format..." | |
| # 1. Find all .h, .cpp, .cu files | |
| # 2. grep -v exclude 'generated' directory (automatically generated code should not be forced to format) | |
| # 3. clang-format parameters: | |
| # -style=file: use .clang-format in project root | |
| # --dry-run: only check, do not modify | |
| # --Werror: report format errors directly (Exit Code 1) | |
| find include src examples tests tools -type f \( -name "*.h" -o -name "*.cpp" -o -name "*.cu" \) \ | |
| | grep -v "generated" \ | |
| | xargs clang-format-16 -style=file --dry-run --Werror | |
| echo ">> Style check passed! ✨" | |
| # ========================================================= | |
| # Task 2: Build and Test | |
| # ========================================================= | |
| build-and-test: | |
| name: ${{ matrix.os }} - ${{ matrix.backend }} - ${{ matrix.build_type }} | |
| # Only run build if style check passes <<< | |
| needs: style-check | |
| runs-on: ${{ matrix.os }} | |
| # Matrix strategy: Automatically combine different operating systems, backends, and build types for parallel testing | |
| strategy: | |
| fail-fast: false # If one task fails, do not immediately cancel other tasks | |
| matrix: | |
| os: [ubuntu-latest] # Can expand to windows-latest, macos-latest | |
| build_type: [Release, Debug] | |
| backend: [Eigen, CustomMatrix] | |
| include: | |
| # Set CMake parameters for different backends | |
| - backend: Eigen | |
| cmake_args: "-DUSE_EIGEN=ON" | |
| - backend: CustomMatrix | |
| cmake_args: "-DUSE_CUSTOM_MATRIX=ON" | |
| steps: | |
| # 1. Pull code | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| # 2. Prepare Python environment (for code generation) | |
| - name: Set up Python 3.x | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.10' | |
| cache: 'pip' # Cache pip dependencies to speed up build | |
| # 3. Install dependencies (SymPy & System Tools) | |
| - name: Install Dependencies | |
| run: | | |
| # Update apt and install build tools | |
| sudo apt-get update | |
| sudo apt-get install -y cmake g++ make unzip | |
| # Install dependencies from requirements.txt if it exists | |
| if [ -f "requirements.txt" ]; then | |
| pip install -r requirements.txt | |
| else | |
| pip install sympy | |
| fi | |
| # 4. Execute code generation (simulate build.sh logic) | |
| - name: Generate Model Code | |
| run: | | |
| echo ">> Generating C++ headers from Python models..." | |
| # Set PYTHONPATH to find minisolver module | |
| export PYTHONPATH=$PYTHONPATH:$(pwd)/python | |
| # Run vehicle model generation script | |
| python3 examples/01_car_tutorial/generate_model.py | |
| # Run advanced bicycle model generation script | |
| python3 examples/02_advanced_bicycle/generate_advanced_model.py | |
| # 5. Configure CMake | |
| - name: Configure CMake | |
| run: | | |
| # Only enable sanitizers in Debug builds. | |
| # Note: do NOT embed quotes inside a variable with spaces; it will be split into multiple args. | |
| if [ "${{ matrix.build_type }}" == "Debug" ]; then | |
| cmake -B build \ | |
| -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ | |
| ${{ matrix.cmake_args }} \ | |
| -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" | |
| else | |
| cmake -B build \ | |
| -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ | |
| ${{ matrix.cmake_args }} | |
| fi | |
| # 6. Compile project | |
| - name: Build Project | |
| run: | | |
| # Use multi-core compilation to speed up | |
| # Use native build tool parallelism for maximum CMake compatibility. | |
| set -o pipefail | |
| # The runner executes bash with -e; disable it temporarily so we can capture the build exit code. | |
| set +e | |
| cmake --build build --config ${{ matrix.build_type }} -- -j"$(nproc)" 2>&1 | tee build.log | |
| status=${PIPESTATUS[0]} | |
| set -e | |
| if [ "$status" -ne 0 ]; then | |
| echo "Build failed (exit=$status). Extracting error lines for GitHub annotations..." | |
| msg="$(grep -n \"error:\" build.log | head -n 20 || true)" | |
| if [ -z \"$msg\" ]; then | |
| msg=\"$(tail -n 80 build.log)\" | |
| fi | |
| # Escape for workflow commands. | |
| msg=\"$(printf '%s' \"$msg\" | sed -e 's/%/%25/g' -e 's/\\r/%0D/g' -e 's/\\n/%0A/g')\" | |
| echo \"::error::$msg\" | |
| exit \"$status\" | |
| fi | |
| # 7. Run unit tests | |
| - name: Run Unit Tests | |
| working-directory: build | |
| run: | | |
| echo ">> Running GTest Suite..." | |
| # --output-on-failure: Output detailed logs when tests fail for debugging | |
| ctest --output-on-failure -C ${{ matrix.build_type }} | |
| # 8. (Optional) Run Benchmark to ensure performance does not drop significantly | |
| # Only run in Release mode to avoid misleading performance data in Debug mode | |
| - name: Run Benchmark Check | |
| if: matrix.build_type == 'Release' | |
| working-directory: build | |
| run: | | |
| if [ -f "./tools/benchmark_suite/benchmark_suite" ]; then | |
| echo ">> Running Benchmark Suite..." | |
| ./tools/benchmark_suite/benchmark_suite | |
| else | |
| echo "Benchmark executable not found, skipping." | |
| fi |