From 1ff1dbe9d20ae058f6920d879ef4608d60e67cc7 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:37:35 +0200 Subject: [PATCH 01/17] ci: consolidate and accelerate checks --- .github/ci/integration-conda.txt | 3 + .github/ci/integration-requirements.txt | 5 + .github/workflows/changelog.yml | 30 - .github/workflows/ci_build.yml | 271 --------- .github/workflows/clang_format.yml | 57 -- .github/workflows/extended_ci.yml | 349 ++++++++++++ .github/workflows/jekyll-gh-pages.yml | 2 +- .github/workflows/license_check.yml | 115 ---- .github/workflows/lint.yml | 80 --- .github/workflows/perf.yml | 154 ------ .github/workflows/pr_gate.yml | 582 ++++++++++++++++++++ changes/developer/ci.consolidate-pr-gate.md | 2 + scripts/clang_tidy.sh | 95 +++- 13 files changed, 1014 insertions(+), 731 deletions(-) create mode 100644 .github/ci/integration-conda.txt create mode 100644 .github/ci/integration-requirements.txt delete mode 100644 .github/workflows/changelog.yml delete mode 100644 .github/workflows/ci_build.yml delete mode 100644 .github/workflows/clang_format.yml create mode 100644 .github/workflows/extended_ci.yml delete mode 100644 .github/workflows/license_check.yml delete mode 100644 .github/workflows/lint.yml delete mode 100644 .github/workflows/perf.yml create mode 100644 .github/workflows/pr_gate.yml create mode 100644 changes/developer/ci.consolidate-pr-gate.md diff --git a/.github/ci/integration-conda.txt b/.github/ci/integration-conda.txt new file mode 100644 index 000000000..a9e14a134 --- /dev/null +++ b/.github/ci/integration-conda.txt @@ -0,0 +1,3 @@ +pip +conda-forge::dftbplus +conda-forge::xtb diff --git a/.github/ci/integration-requirements.txt b/.github/ci/integration-requirements.txt new file mode 100644 index 000000000..6533e4f07 --- /dev/null +++ b/.github/ci/integration-requirements.txt @@ -0,0 +1,5 @@ +ase +pqanalysis +pyscf==2.9.0 +pytest +pytest-cov diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml deleted file mode 100644 index b1b9469e8..000000000 --- a/.github/workflows/changelog.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Changelog Fragment Check - -on: - pull_request: - branches: - - dev - types: [opened, reopened, synchronize] - -jobs: - check-changelog-fragment: - if: >- - github.event.pull_request.head.repo.full_name != github.repository || - github.event.pull_request.head.ref != 'main' - runs-on: ubuntu-latest - - steps: - - name: Checkout pull request - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - - - name: Test changelog tooling - run: python3 -m unittest discover -s scripts/tests -p 'test_*.py' - - - name: Check changelog fragment - run: >- - python3 scripts/check_changelog_fragment.py - ${{ github.event.pull_request.base.sha }} - ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/ci_build.yml b/.github/workflows/ci_build.yml deleted file mode 100644 index 660de56f2..000000000 --- a/.github/workflows/ci_build.yml +++ /dev/null @@ -1,271 +0,0 @@ -name: BUILD - -on: - pull_request: - branches: - - '*' - paths: - - '.github/workflows/ci_build.yml' - - '.cmake/**' - - 'apps/**' - - 'include/**' - - 'src/**' - - 'integration_tests/**' - - 'tests/**' - - 'benchmarks/CMakeLists.txt' - - 'benchmarks/src/**' - - 'CMakeLists.txt' - push: - branches: - - dev - - main - paths: - - '.github/workflows/ci_build.yml' - - '.cmake/**' - - 'apps/**' - - 'include/**' - - 'src/**' - - 'integration_tests/**' - - 'tests/**' - - 'benchmarks/CMakeLists.txt' - - 'benchmarks/src/**' - - 'CMakeLists.txt' - schedule: - - cron: '0 2 * * *' - workflow_dispatch: - -jobs: - build: - if: github.event_name != 'schedule' - strategy: - matrix: - os: [ubuntu-24.04, ubuntu-24.04-arm] - build: [Release, Debug] - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v4 - - - name: install gcc13 - run: | - sudo apt update - sudo apt install gcc-13 g++-13 mold - shell: bash - - # safe because CI builds with -DBUILD_WITH_NATIVE=Off (portable objects) - - name: setup ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ matrix.os }}-${{ matrix.build }} - - - name: setup python ubuntu - uses: actions/setup-python@v3 - with: - python-version: '3.12' - - - name: install python dependencies - run: | - python -m pip install numpy - shell: bash - - - name: install coverage - if : ${{ matrix.os == 'ubuntu-24.04' && matrix.build == 'Debug' }} - run: | - python -m pip install coverage - python -m pip install gcovr - shell: bash - - - name: Build and Test Project - if : ${{ matrix.os != 'ubuntu-24.04' || (matrix.os == 'ubuntu-24.04' && matrix.build != 'Debug') }} - run: | - mkdir build - cd build - cmake -DCMAKE_BUILD_TYPE=${{ matrix.build }} -DBUILD_WITH_NATIVE=Off .. - make -j$(nproc) - make test - env: - CC: gcc-13 - CXX: g++-13 - - - name: Build and Test Project with coverage - if : ${{ matrix.os == 'ubuntu-24.04' && matrix.build == 'Debug' }} - run: | - mkdir build - cd build - cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_WITH_GCOVR=ON -DBUILD_WITH_NATIVE=Off .. - make -j$(nproc) - make coverage_xml - env: - CC: gcc-13 - CXX: g++-13 - - - name: Upload coverage to Codecov - if : ${{ matrix.os == 'ubuntu-24.04' && matrix.build == 'Debug' }} - uses: codecov/codecov-action@v3 - with: - fail_ci_if_error: true # optional (default = false) - gcov: true - gcov_executable: gcov-13 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - - name: Setup Conda Environment - if: ${{matrix.build == 'Release'}} - uses: conda-incubator/setup-miniconda@v3 - with: - auto-update-conda: true - python-version: '3.12' - activate-environment: test-env - channels: default, conda-forge - auto-activate-base: false - - - name: Cache conda env - if: ${{matrix.build == 'Release'}} - id: cache-conda-env - uses: actions/cache@v4 - with: - path: ${{ env.CONDA }}/envs/test-env - key: conda-env-${{ matrix.os }}-${{ hashFiles('.github/workflows/ci_build.yml') }} - - - name: Install Integration Test Dependencies in Conda Env - if: ${{matrix.build == 'Release' && steps.cache-conda-env.outputs.cache-hit != 'true'}} - run: | - conda install -n test-env -y pip conda-forge::dftbplus conda-forge::xtb - conda run -n test-env python -m pip install pytest pytest-cov pqanalysis ase pyscf==2.9.0 - - - name: Integration Tests - if: ${{matrix.build == 'Release' }} - run: | - conda run -n test-env pytest integration_tests -v - - benchmark: - if: github.event_name != 'schedule' - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v4 - - - name: install gcc13 - run: | - sudo apt update - sudo apt install gcc-13 g++-13 - shell: bash - - - name: Build benchmark suite - run: | - cmake -S . -B build-benchmark \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_WITH_BENCHMARKING=On \ - -DBUILD_WITH_TESTS=Off \ - -DBUILD_WITH_ASE=Off \ - -DBUILD_WITH_DOCS=Off \ - -DBUILD_WITH_NATIVE=Off - cmake --build build-benchmark --target google_benchmarks -j$(nproc) - env: - CC: gcc-13 - CXX: g++-13 - - - name: Run benchmark smoke tests - run: | - ctest --test-dir build-benchmark -L benchmark --output-on-failure - - build-static-lto: - if: github.event_name != 'schedule' - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v4 - - - name: Install GCC 13 - run: | - sudo apt update - sudo apt install gcc-13 g++-13 mold ninja-build - - - name: Build and test static LTO - run: | - cmake -S . -B build -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=OFF \ - -DBUILD_WITH_ASE=OFF \ - -DBUILD_WITH_LTO=ON \ - -DBUILD_WITH_NATIVE=OFF - cmake --build build --parallel - ctest --test-dir build --output-on-failure -j1 - env: - CC: gcc-13 - CXX: g++-13 - - static-singularity-build: - if: >- - github.event_name == 'schedule' || - github.event_name == 'workflow_dispatch' - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v4 - - - name: Install build dependencies - run: | - sudo apt update - sudo apt install gcc-13 g++-13 mold - - - name: Setup ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: static-singularity - - - name: Build project - run: | - cmake -S . -B build \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=OFF \ - -DBUILD_WITH_SINGULARITY=ON \ - -DBUILD_WITH_TESTS=ON \ - -DBUILD_WITH_ASE=OFF \ - -DBUILD_WITH_NATIVE=OFF - cmake --build build --parallel "$(nproc)" - env: - CC: gcc-13 - CXX: g++-13 - - - name: Run tests - run: ctest --test-dir build --output-on-failure -j1 - - mpi-build: - if: github.event_name != 'schedule' - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v4 - - - name: Install build dependencies - run: | - sudo apt update - sudo apt install \ - gcc-13 \ - g++-13 \ - mold \ - openmpi-bin \ - libopenmpi-dev \ - python-is-python3 - - - name: Setup ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: mpi-release - - - name: Build project - run: | - cmake -S . -B build \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_WITH_MPI=ON \ - -DBUILD_WITH_ASE=OFF \ - -DBUILD_WITH_NATIVE=OFF \ - -DBUILD_WITH_TESTS=ON - cmake --build build --parallel "$(nproc)" - env: - CC: gcc-13 - CXX: g++-13 - - - name: Run tests - run: ctest --test-dir build --output-on-failure -j1 diff --git a/.github/workflows/clang_format.yml b/.github/workflows/clang_format.yml deleted file mode 100644 index cc55f415f..000000000 --- a/.github/workflows/clang_format.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Clang Format Check - -on: - pull_request: - branches: - - main - - dev - paths: - - '.clang-format' - - '.github/workflows/clang_format.yml' - - '**/*.c' - - '**/*.cc' - - '**/*.cpp' - - '**/*.cxx' - - '**/*.h' - - '**/*.hh' - - '**/*.hpp' - - '**/*.hxx' - - '!external/**' - workflow_dispatch: - -jobs: - clang-format: - runs-on: ubuntu-24.04 - name: Check Changed C++ Formatting - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install clang-format - run: python -m pip install clang-format==20.1.3 - - - name: Check formatting - run: | - base_sha="${{ github.event.pull_request.base.sha }}" - head_sha="${{ github.event.pull_request.head.sha }}" - if [[ -z "$base_sha" ]]; then - base_sha="HEAD^" - head_sha="HEAD" - fi - - clang-format --style=file --dump-config > /dev/null - git-clang-format \ - --diff \ - --binary "$(command -v clang-format)" \ - --extensions c,cc,cpp,cxx,h,hh,hpp,hxx \ - "$base_sha" \ - "$head_sha" \ - -- apps benchmarks include src tests diff --git a/.github/workflows/extended_ci.yml b/.github/workflows/extended_ci.yml new file mode 100644 index 000000000..ea133951d --- /dev/null +++ b/.github/workflows/extended_ci.yml @@ -0,0 +1,349 @@ +name: Extended CI + +on: + push: + branches: + - dev + paths: + - '.clang-tidy' + - '.cmake/**' + - '.github/ci/**' + - '.github/workflows/extended_ci.yml' + - '.gitmodules' + - 'apps/**' + - 'benchmarks/**' + - 'external/**' + - 'include/**' + - 'integration_tests/**' + - 'scripts/**' + - 'src/**' + - 'tests/**' + - 'CMakeLists.txt' + pull_request: + branches: + - main + types: [opened, reopened, synchronize] + schedule: + - cron: '0 2 * * *' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: extended-ci-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name != 'schedule' }} + +jobs: + full-lint: + name: Full C++ lint + runs-on: ubuntu-24.04 + timeout-minutes: 25 + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'schedule' && 'dev' || '' }} + + - name: Install build and lint tools + run: | + sudo apt-get update + sudo apt-get install -y \ + clangd-20 gcc-13 g++-13 mold ninja-build + sudo update-alternatives \ + --install /usr/bin/clangd clangd /usr/bin/clangd-20 100 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: python -m pip install clangd-tidy numpy + + - name: Configure compile database + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Debug \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_NATIVE=OFF + env: + CC: gcc-13 + CXX: g++-13 + + - name: Lint all production C++ files + run: | + set -euo pipefail + mapfile -t files < <( + git ls-files apps include src | + grep -E '\.(c|cc|cpp|cxx|h|hh|hpp|hxx|tpp)$' + ) + clangd-tidy "${files[@]}" -p=build --tqdm -j1 + + static-lto: + name: Full static LTO + runs-on: ubuntu-24.04 + timeout-minutes: 25 + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'schedule' && 'dev' || '' }} + + - name: Install build tools + run: | + sudo apt-get update + sudo apt-get install -y gcc-13 g++-13 mold ninja-build + + - name: Set up ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: static-lto + + - name: Build and test static LTO + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_WITH_ASE=OFF \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_LTO=ON \ + -DBUILD_WITH_NATIVE=OFF + cmake --build build --parallel "$(nproc)" + ctest --test-dir build --output-on-failure --parallel "$(nproc)" + env: + CC: gcc-13 + CXX: g++-13 + + arm-debug: + name: ARM Debug + runs-on: ubuntu-24.04-arm + timeout-minutes: 20 + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'schedule' && 'dev' || '' }} + + - name: Install build tools + run: | + sudo apt-get update + sudo apt-get install -y gcc-13 g++-13 mold ninja-build + + - name: Set up ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: extended-arm-debug + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install build dependencies + run: python -m pip install numpy + + - name: Build and run unit tests + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Debug \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_NATIVE=OFF + cmake --build build --parallel "$(nproc)" + ctest --test-dir build --output-on-failure --parallel "$(nproc)" + env: + CC: gcc-13 + CXX: g++-13 + + - name: Set up Conda + uses: conda-incubator/setup-miniconda@v3 + with: + auto-update-conda: false + python-version: '3.12' + activate-environment: test-env + channels: default, conda-forge + auto-activate-base: false + + - name: Restore integration environment + id: conda-cache + uses: actions/cache@v4 + with: + path: ${{ env.CONDA }}/envs/test-env + key: >- + conda-integration-${{ runner.arch }}-${{ + hashFiles('.github/ci/integration-conda.txt', + '.github/ci/integration-requirements.txt') }} + + - name: Install integration dependencies + if: steps.conda-cache.outputs.cache-hit != 'true' + run: | + conda install -n test-env -y --file .github/ci/integration-conda.txt + conda run -n test-env \ + python -m pip install -r .github/ci/integration-requirements.txt + + - name: Run ARM integration tests + run: conda run -n test-env pytest integration_tests -v + + mpi: + name: MPI build and tests + runs-on: ubuntu-24.04 + timeout-minutes: 15 + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'schedule' && 'dev' || '' }} + + - name: Install build dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + gcc-13 \ + g++-13 \ + libopenmpi-dev \ + mold \ + ninja-build \ + openmpi-bin \ + python-is-python3 + + - name: Set up ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: extended-mpi-release + + - name: Build and run tests + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_WITH_ASE=OFF \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_MPI=ON \ + -DBUILD_WITH_NATIVE=OFF \ + -DBUILD_WITH_TESTS=ON + cmake --build build --parallel "$(nproc)" + ctest --test-dir build --output-on-failure --parallel "$(nproc)" + env: + CC: gcc-13 + CXX: g++-13 + + benchmarks: + name: Benchmark build and smoke + runs-on: ubuntu-24.04 + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'schedule' && 'dev' || '' }} + + - name: Install build tools + run: | + sudo apt-get update + sudo apt-get install -y gcc-13 g++-13 ninja-build + + - name: Set up ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: extended-benchmarks + + - name: Build and run benchmark smoke tests + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_WITH_ASE=OFF \ + -DBUILD_WITH_BENCHMARKING=ON \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_NATIVE=OFF \ + -DBUILD_WITH_TESTS=OFF + cmake --build build --target google_benchmarks --parallel "$(nproc)" + ctest --test-dir build -L benchmark --output-on-failure + env: + CC: gcc-13 + CXX: g++-13 + + static-singularity: + name: Static Singularity build + if: >- + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' + runs-on: ubuntu-24.04 + timeout-minutes: 25 + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'schedule' && 'dev' || '' }} + + - name: Install build tools + run: | + sudo apt-get update + sudo apt-get install -y gcc-13 g++-13 mold ninja-build + + - name: Set up ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: static-singularity + + - name: Build and run tests + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_WITH_ASE=OFF \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_NATIVE=OFF \ + -DBUILD_WITH_SINGULARITY=ON \ + -DBUILD_WITH_TESTS=ON + cmake --build build --parallel "$(nproc)" + ctest --test-dir build --output-on-failure --parallel "$(nproc)" + env: + CC: gcc-13 + CXX: g++-13 + + result: + name: Extended result + if: always() + needs: + - full-lint + - static-lto + - arm-debug + - mpi + - benchmarks + - static-singularity + runs-on: ubuntu-latest + timeout-minutes: 2 + + steps: + - name: Verify extended job results + env: + ARM_DEBUG: ${{ needs.arm-debug.result }} + BENCHMARKS: ${{ needs.benchmarks.result }} + FULL_LINT: ${{ needs.full-lint.result }} + MPI: ${{ needs.mpi.result }} + STATIC_LTO: ${{ needs.static-lto.result }} + STATIC_SINGULARITY: ${{ needs.static-singularity.result }} + run: | + set -euo pipefail + + failed=false + results=( + "full-lint=$FULL_LINT" + "static-lto=$STATIC_LTO" + "arm-debug=$ARM_DEBUG" + "mpi=$MPI" + "benchmarks=$BENCHMARKS" + "static-singularity=$STATIC_SINGULARITY" + ) + + for entry in "${results[@]}"; do + name="${entry%%=*}" + result="${entry#*=}" + echo "$name: $result" + if [[ "$result" != "success" && "$result" != "skipped" ]]; then + failed=true + fi + done + + if [[ "$failed" == "true" ]]; then + exit 1 + fi diff --git a/.github/workflows/jekyll-gh-pages.yml b/.github/workflows/jekyll-gh-pages.yml index 4c6d57065..b70f581a1 100644 --- a/.github/workflows/jekyll-gh-pages.yml +++ b/.github/workflows/jekyll-gh-pages.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Requirements run: sudo apt-get install -y doxygen diff --git a/.github/workflows/license_check.yml b/.github/workflows/license_check.yml deleted file mode 100644 index 69115df78..000000000 --- a/.github/workflows/license_check.yml +++ /dev/null @@ -1,115 +0,0 @@ -name: License Header Check - -on: - pull_request: - branches: - - '*' - paths: - - '**/*.cpp' - - '**/*.hpp' - - '**/*.c' - - '**/*.h' - - '.github/workflows/license_check.yml' - - 'scripts/addLicense.sh' - - 'config/licenseHeader.txt' - push: - branches: - - main - - master - paths: - - '**/*.cpp' - - '**/*.hpp' - - '**/*.c' - - '**/*.h' - - '.github/workflows/license_check.yml' - - 'scripts/addLicense.sh' - - 'config/licenseHeader.txt' - workflow_dispatch: - -jobs: - license-check: - runs-on: ubuntu-latest - name: Check License Headers - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Check for license headers in C++ files - run: | - echo "Checking for exact GPL license headers in C++ files..." - - # Verify license header template exists - if [ ! -f "config/licenseHeader.txt" ]; then - echo "❌ License header template not found at config/licenseHeader.txt" - exit 1 - fi - - # Get the expected license header content - license_header=$(cat config/licenseHeader.txt) - header_line_count=$(wc -l < config/licenseHeader.txt) - - echo "Expected license header has $header_line_count lines" - echo "" - - # Find all C++ files in the relevant directories (excluding external dependencies) - missing_count=0 - total_files=0 - - find src/ tests/ include/ apps/ benchmarks/ -regex ".*\.\(hpp\|cpp\|c\|h\)$" -print0 2>/dev/null | while IFS= read -r -d '' file; do - total_files=$((total_files + 1)) - echo "Checking: $file" - - # Extract the first N lines from the file (where N = number of lines in license header) - file_header=$(head -n "$header_line_count" "$file" 2>/dev/null) - - # Compare the extracted header with the expected license header - if [ "$file_header" = "$license_header" ]; then - echo "✅ Exact license header match: $file" - else - echo "❌ License header mismatch: $file" - echo "$file" >> /tmp/missing_files.txt - fi - done - - # Count missing files - if [ -f /tmp/missing_files.txt ]; then - missing_count=$(wc -l < /tmp/missing_files.txt) - else - missing_count=0 - fi - - # Get total file count - total_files=$(find src/ tests/ include/ apps/ benchmarks/ -regex ".*\.\(hpp\|cpp\|c\|h\)$" 2>/dev/null | wc -l) - - echo "" - echo "=== License Check Summary ===" - echo "Total files checked: $total_files" - echo "Files with missing/invalid license headers: $missing_count" - - if [ $missing_count -gt 0 ]; then - echo "" - echo "Files with incorrect license headers:" - cat /tmp/missing_files.txt | while read -r file; do - echo " - $file" - done - echo "" - echo "To fix these issues, you can run the addLicense.sh script:" - echo " bash scripts/addLicense.sh" - echo "" - echo "Or manually ensure each file starts with the exact content from:" - echo " config/licenseHeader.txt" - exit 1 - else - echo "✅ All files have the exact license header!" - fi - shell: bash - - - name: Show license header template - run: | - echo "=== License Header Template ===" - echo "Using template from: config/licenseHeader.txt" - echo "Template content:" - cat config/licenseHeader.txt - echo "=== End of Template ===" - shell: bash diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index ea03c8beb..000000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: LINT - -on: - pull_request: - branches: - - '*' - paths: - - '.github/workflows/ci_build.yml' - - '.cmake/**' - - 'apps/**' - - 'include/**' - - 'src/**' - - 'integration_tests/**' - - 'tests/**' - - 'benchmarks/CMakeLists.txt' - - 'benchmarks/src/**' - - 'CMakeLists.txt' - - '.clang-tidy' - - 'scripts/clang_tidy.sh' - - '.github/workflows/lint.yml' - push: - branches: - - dev - - main - paths: - - '.github/workflows/ci_build.yml' - - '.cmake/**' - - 'apps/**' - - 'include/**' - - 'src/**' - - 'integration_tests/**' - - 'CMakeLists.txt' - - '.clang-tidy' - - 'scripts/clang_tidy.sh' - - '.github/workflows/lint.yml' - - 'benchmarks/src/**' - - 'CMakeLists.txt' - workflow_dispatch: - -jobs: - lint: - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v4 - - - name: Install build tools - run: | - sudo apt update - sudo apt install -y gcc-13 g++-13 mold ninja-build clangd-20 - sudo update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-20 100 - shell: bash - - - name: Setup python - uses: actions/setup-python@v3 - with: - python-version: '3.12' - - - name: install python dependencies - run: | - python -m pip install numpy - shell: bash - - - name: Install clangd-tidy - run: python -m pip install clangd-tidy - shell: bash - - - name: Configure (generate compile_commands.json) - run: | - cmake -S . -B build -G Ninja \ - -DCMAKE_BUILD_TYPE=Debug \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -DBUILD_WITH_NATIVE=Off - env: - CC: gcc-13 - CXX: g++-13 - - - name: Run clangd-tidy - run: | - clangd-tidy $(find src/ apps/ include/ -name "*.cpp" -o -name "*.hpp" -o -name "*.tpp" 2>/dev/null) -p=build diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml deleted file mode 100644 index ff72e7ca6..000000000 --- a/.github/workflows/perf.yml +++ /dev/null @@ -1,154 +0,0 @@ -name: Performance Gate - -# Builds the fixed-work perf benchmarks for the PR and for the base branch, -# runs each under callgrind, and fails if any regressed by more than the -# threshold. Instruction counts are deterministic, so this is not flaky. - -on: - pull_request: - branches: - - main - - dev - # Only run when something that can actually affect instruction counts - # changes — code, benchmarks, build config, submodules, or the gate - # itself. Doc / changelog / unrelated-workflow PRs skip the gate. - paths: - - 'src/**' - - 'include/**' - - 'apps/**' - - 'benchmarks/**' - - 'external/**' - - 'CMakeLists.txt' - - '.cmake/**' - - '.github/workflows/perf.yml' - - 'scripts/perf_gate.sh' - workflow_dispatch: - -permissions: - contents: read - pull-requests: write - -jobs: - perf-regression: - runs-on: ubuntu-24.04 - steps: - - name: Checkout PR - uses: actions/checkout@v4 - with: - submodules: recursive - - # Callgrind instruction counts are a pure function of the binary, which - # is a pure function of the base commit SHA. So we cache the measured - # base Ir counts keyed on that SHA: on a hit we skip the base checkout, - # build, and base callgrind run entirely (~half the workflow). - - name: Restore cached base instruction counts - id: base-ir-cache - uses: actions/cache@v4 - with: - path: base_ir.tsv - key: perf-base-ir-${{ github.event.pull_request.base.sha || github.sha }} - - - name: Checkout base branch - if: steps.base-ir-cache.outputs.cache-hit != 'true' - uses: actions/checkout@v4 - with: - ref: ${{ github.base_ref || 'dev' }} - submodules: recursive - path: base - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y gcc-13 g++-13 valgrind mold - shell: bash - - - name: Setup ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: perf-${{ github.base_ref || 'dev' }} - - - name: Build PR benchmarks - run: | - cmake -S . -B build-pr \ - -DCMAKE_BUILD_TYPE=Release -DBUILD_WITH_NATIVE=Off \ - -DBUILD_WITH_PERF_BENCH=On -DBUILD_WITH_ASE=Off \ - -DBUILD_WITH_TESTS=Off -DBUILD_WITH_DOCS=Off - cmake --build build-pr --target perf_benchmarks -j$(nproc) - env: - CC: gcc-13 - CXX: g++-13 - - - name: Build base benchmarks (baseline) - if: steps.base-ir-cache.outputs.cache-hit != 'true' - run: | - cmake -S base -B build-base \ - -DCMAKE_BUILD_TYPE=Release -DBUILD_WITH_NATIVE=Off \ - -DBUILD_WITH_PERF_BENCH=On -DBUILD_WITH_ASE=Off \ - -DBUILD_WITH_TESTS=Off -DBUILD_WITH_DOCS=Off || true - cmake --build build-base --target perf_benchmarks -j$(nproc) \ - || echo "base branch has no perf benchmarks yet - baseline skipped" - env: - CC: gcc-13 - CXX: g++-13 - - - name: Compare instruction counts (gate) - id: gate - continue-on-error: true - env: - # The script loads base counts from this file if it exists (cache - # hit) and (re)writes the freshly-known counts to it at the end so - # actions/cache@v4 saves them at job end (cache miss path). - PERF_BASE_IR_FILE: base_ir.tsv - run: bash scripts/perf_gate.sh build-pr/benchmarks/perf build-base/benchmarks/perf 2 - - - name: Mint PQ Perf Bot installation token - id: app-token - if: always() && github.event_name == 'pull_request' - continue-on-error: true - uses: actions/create-github-app-token@v1 - with: - app-id: ${{ secrets.PQ_PERF_BOT_APP_ID }} - private-key: ${{ secrets.PQ_PERF_BOT_PRIVATE_KEY }} - - - name: Post / update performance comment - if: always() && github.event_name == 'pull_request' - continue-on-error: true - uses: actions/github-script@v7 - with: - # Comment author is pq-perf-bot[bot] when the token mint succeeds, - # else falls back to github-actions[bot] so the comment still posts. - github-token: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }} - script: | - const fs = require('fs'); - const marker = ''; - const botLogin = 'pq-perf-bot[bot]'; - let body; - try { body = fs.readFileSync('perf_report.md', 'utf8'); } - catch (e) { core.info('no perf_report.md, skipping comment'); return; } - const { owner, repo } = context.repo; - const issue_number = context.issue.number; - const { data: comments } = - await github.rest.issues.listComments({ owner, repo, issue_number }); - const existing = comments.find(c => c.body && c.body.includes(marker)); - // GitHub fixes a comment's author at creation time; if an older - // comment exists under a different author (e.g. github-actions[bot] - // before the App was wired in), delete and recreate so the visible - // author becomes pq-perf-bot[bot]. - if (existing && existing.user.login !== botLogin) { - await github.rest.issues.deleteComment( - { owner, repo, comment_id: existing.id }); - await github.rest.issues.createComment( - { owner, repo, issue_number, body }); - } else if (existing) { - await github.rest.issues.updateComment( - { owner, repo, comment_id: existing.id, body }); - } else { - await github.rest.issues.createComment( - { owner, repo, issue_number, body }); - } - - - name: Fail on regression - if: steps.gate.outcome == 'failure' - run: | - echo "performance regression detected (see the comment / summary above)" - exit 1 diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml new file mode 100644 index 000000000..6ae66c615 --- /dev/null +++ b/.github/workflows/pr_gate.yml @@ -0,0 +1,582 @@ +name: PR Gate + +on: + pull_request: + branches: + - dev + - main + types: [opened, reopened, synchronize] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: pr-gate-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + preflight: + name: Preflight + runs-on: ubuntu-24.04 + timeout-minutes: 5 + outputs: + base_sha: ${{ steps.changes.outputs.base_sha }} + build: ${{ steps.changes.outputs.build }} + head_sha: ${{ steps.changes.outputs.head_sha }} + lto: ${{ steps.changes.outputs.lto }} + perf: ${{ steps.changes.outputs.perf }} + + steps: + - name: Checkout pull request + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Classify changed paths + id: changes + env: + EVENT_BASE_SHA: ${{ github.event.pull_request.base.sha }} + EVENT_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + set -euo pipefail + + base_sha="$EVENT_BASE_SHA" + head_sha="$EVENT_HEAD_SHA" + if [[ -z "$base_sha" || -z "$head_sha" ]]; then + base_sha="$(git rev-parse HEAD^)" + head_sha="$(git rev-parse HEAD)" + fi + + build=false + cpp=false + lto=false + perf=false + workflow=false + changed_file="$RUNNER_TEMP/pq-changed-files.txt" + git diff --name-only "$base_sha...$head_sha" > "$changed_file" + + while IFS= read -r path; do + [[ -n "$path" ]] || continue + + case "$path" in + .github/workflows/pr_gate.yml|.github/workflows/extended_ci.yml) + build=true + cpp=true + lto=true + perf=true + ;; + esac + + case "$path" in + .github/workflows/*) + workflow=true + ;; + esac + + case "$path" in + .cmake/*|.github/ci/*|.gitmodules|apps/*|benchmarks/CMakeLists.txt|benchmarks/src/*|external/*|include/*|integration_tests/*|src/*|tests/*|CMakeLists.txt|scripts/clang_tidy.sh) + build=true + ;; + esac + + case "$path" in + .cmake/*|.gitmodules|apps/*|external/*|include/*|src/*|CMakeLists.txt) + lto=true + ;; + esac + + case "$path" in + *.c|*.cc|*.cpp|*.cxx|*.h|*.hh|*.hpp|*.hxx|*.tpp|.clang-format|config/licenseHeader.txt) + cpp=true + ;; + esac + + case "$path" in + .cmake/*|apps/*|benchmarks/*|external/*|include/*|src/*|CMakeLists.txt|scripts/perf_gate.sh) + perf=true + ;; + esac + done < "$changed_file" + + { + echo "base_sha=$base_sha" + echo "build=$build" + echo "cpp=$cpp" + echo "head_sha=$head_sha" + echo "lto=$lto" + echo "perf=$perf" + echo "workflow=$workflow" + } >> "$GITHUB_OUTPUT" + + { + echo "### Changed paths" + echo + echo "build=$build, cpp=$cpp, lto=$lto, perf=$perf, workflow=$workflow" + } >> "$GITHUB_STEP_SUMMARY" + + - name: Test changelog tooling + run: python3 -m unittest discover -s scripts/tests -p 'test_*.py' + + - name: Check workflow syntax + if: steps.changes.outputs.workflow == 'true' + uses: docker://rhysd/actionlint:1.7.12 + with: + args: -color + + - name: Check changelog fragment + if: >- + github.event_name == 'pull_request' && + github.base_ref == 'dev' && + (github.event.pull_request.head.repo.full_name != github.repository || + github.head_ref != 'main') + env: + BASE_SHA: ${{ steps.changes.outputs.base_sha }} + HEAD_SHA: ${{ steps.changes.outputs.head_sha }} + run: python3 scripts/check_changelog_fragment.py "$BASE_SHA" "$HEAD_SHA" + + - name: Set up Python for formatting + if: steps.changes.outputs.cpp == 'true' + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install clang-format + if: steps.changes.outputs.cpp == 'true' + run: python -m pip install clang-format==20.1.3 + + - name: Check changed C++ formatting + if: steps.changes.outputs.cpp == 'true' + env: + BASE_SHA: ${{ steps.changes.outputs.base_sha }} + HEAD_SHA: ${{ steps.changes.outputs.head_sha }} + run: | + set -euo pipefail + clang-format --style=file --dump-config >/dev/null + git-clang-format \ + --diff \ + --binary "$(command -v clang-format)" \ + --extensions c,cc,cpp,cxx,h,hh,hpp,hxx,tpp \ + "$BASE_SHA" \ + "$HEAD_SHA" \ + -- apps benchmarks include src tests + + - name: Check license headers + if: steps.changes.outputs.cpp == 'true' + run: | + set -euo pipefail + + header_lines="$(wc -l < config/licenseHeader.txt)" + missing=() + while IFS= read -r file; do + if ! cmp -s config/licenseHeader.txt \ + <(head -n "$header_lines" "$file"); then + missing+=("$file") + fi + done < <( + git ls-files apps benchmarks include src tests | + grep -E '\.(c|cc|cpp|cxx|h|hh|hpp|hxx|tpp)$' + ) + + if (( ${#missing[@]} > 0 )); then + printf 'Files with missing or invalid license headers:\n' >&2 + printf ' %s\n' "${missing[@]}" >&2 + exit 1 + fi + + echo "All tracked C++ files have the expected license header." + + release: + name: x86 Release and integration + if: ${{ always() && needs.preflight.outputs.build == 'true' }} + needs: preflight + runs-on: ubuntu-24.04 + timeout-minutes: 20 + + steps: + - uses: actions/checkout@v4 + + - name: Install build tools + run: | + sudo apt-get update + sudo apt-get install -y gcc-13 g++-13 mold ninja-build + + - name: Set up ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: pr-x86-release + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install build dependencies + run: python -m pip install numpy + + - name: Build and run unit tests + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_NATIVE=OFF + cmake --build build --parallel "$(nproc)" + ctest --test-dir build --output-on-failure --parallel "$(nproc)" + env: + CC: gcc-13 + CXX: g++-13 + + - name: Set up Conda + uses: conda-incubator/setup-miniconda@v3 + with: + auto-update-conda: false + python-version: '3.12' + activate-environment: test-env + channels: default, conda-forge + auto-activate-base: false + + - name: Restore integration environment + id: conda-cache + uses: actions/cache@v4 + with: + path: ${{ env.CONDA }}/envs/test-env + key: >- + conda-integration-${{ runner.arch }}-${{ + hashFiles('.github/ci/integration-conda.txt', + '.github/ci/integration-requirements.txt') }} + + - name: Install integration dependencies + if: steps.conda-cache.outputs.cache-hit != 'true' + run: | + conda install -n test-env -y --file .github/ci/integration-conda.txt + conda run -n test-env \ + python -m pip install -r .github/ci/integration-requirements.txt + + - name: Run integration tests + run: conda run -n test-env pytest integration_tests -v + + debug-quality: + name: x86 Debug, coverage, and changed lint + if: ${{ always() && needs.preflight.outputs.build == 'true' }} + needs: preflight + runs-on: ubuntu-24.04 + timeout-minutes: 20 + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install build and lint tools + run: | + sudo apt-get update + sudo apt-get install -y \ + clangd-20 gcc-13 g++-13 mold ninja-build + sudo update-alternatives \ + --install /usr/bin/clangd clangd /usr/bin/clangd-20 100 + + - name: Set up ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: pr-x86-debug-quality + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: python -m pip install clangd-tidy coverage gcovr numpy + + - name: Build tests and coverage + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Debug \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_GCOVR=ON \ + -DBUILD_WITH_NATIVE=OFF + cmake --build build --target coverage_xml --parallel "$(nproc)" + env: + CC: gcc-13 + CXX: g++-13 + + - name: Lint changed C++ files + env: + BASE_SHA: ${{ needs.preflight.outputs.base_sha }} + run: >- + scripts/clang_tidy.sh + --base "$BASE_SHA" + --build-dir build + --jobs 2 + + - name: Upload coverage + uses: codecov/codecov-action@v7 + with: + disable_search: true + fail_ci_if_error: true + files: build/coverage_xml.xml + token: ${{ secrets.CODECOV_TOKEN }} + + arm-release: + name: ARM Release + if: ${{ always() && needs.preflight.outputs.build == 'true' }} + needs: preflight + runs-on: ubuntu-24.04-arm + timeout-minutes: 20 + + steps: + - uses: actions/checkout@v4 + + - name: Install build tools + run: | + sudo apt-get update + sudo apt-get install -y gcc-13 g++-13 mold ninja-build + + - name: Set up ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: pr-arm-release + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install build dependencies + run: python -m pip install numpy + + - name: Build and run unit tests + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_NATIVE=OFF + cmake --build build --parallel "$(nproc)" + ctest --test-dir build --output-on-failure --parallel "$(nproc)" + env: + CC: gcc-13 + CXX: g++-13 + + lto-smoke: + name: Static LTO smoke + if: ${{ always() && needs.preflight.outputs.lto == 'true' }} + needs: preflight + runs-on: ubuntu-24.04 + timeout-minutes: 15 + + steps: + - uses: actions/checkout@v4 + + - name: Install build tools + run: | + sudo apt-get update + sudo apt-get install -y gcc-13 g++-13 mold ninja-build + + - name: Set up ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: static-lto + + - name: Build executable with static LTO + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_WITH_ASE=OFF \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_LTO=ON \ + -DBUILD_WITH_NATIVE=OFF \ + -DBUILD_WITH_TESTS=OFF + cmake --build build --target PQ --parallel "$(nproc)" + build/apps/PQ --version + env: + CC: gcc-13 + CXX: g++-13 + + performance: + name: Performance regression + if: ${{ always() && needs.preflight.outputs.perf == 'true' }} + needs: preflight + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout pull request + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Restore cached base instruction counts + id: base-ir-cache + uses: actions/cache@v4 + with: + path: base_ir.tsv + key: perf-base-ir-${{ needs.preflight.outputs.base_sha }} + + - name: Checkout exact base commit + if: steps.base-ir-cache.outputs.cache-hit != 'true' + uses: actions/checkout@v4 + with: + ref: ${{ needs.preflight.outputs.base_sha }} + submodules: recursive + path: base + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y gcc-13 g++-13 valgrind mold + + - name: Set up ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: perf-${{ github.base_ref || 'dev' }} + + - name: Build pull-request benchmarks + run: | + cmake -S . -B build-pr \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_WITH_ASE=OFF \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_NATIVE=OFF \ + -DBUILD_WITH_PERF_BENCH=ON \ + -DBUILD_WITH_TESTS=OFF + cmake --build build-pr --target perf_benchmarks --parallel "$(nproc)" + env: + CC: gcc-13 + CXX: g++-13 + + - name: Build base benchmarks + if: steps.base-ir-cache.outputs.cache-hit != 'true' + run: | + cmake -S base -B build-base \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_WITH_ASE=OFF \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_NATIVE=OFF \ + -DBUILD_WITH_PERF_BENCH=ON \ + -DBUILD_WITH_TESTS=OFF || true + cmake --build build-base --target perf_benchmarks \ + --parallel "$(nproc)" || \ + echo "Base commit has no performance benchmarks; baseline skipped." + env: + CC: gcc-13 + CXX: g++-13 + + - name: Compare instruction counts + id: gate + continue-on-error: true + env: + PERF_BASE_IR_FILE: base_ir.tsv + run: >- + bash scripts/perf_gate.sh + build-pr/benchmarks/perf + build-base/benchmarks/perf + 2 + + - name: Mint PQ Perf Bot token + id: app-token + if: always() && github.event_name == 'pull_request' + continue-on-error: true + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.PQ_PERF_BOT_APP_ID }} + private-key: ${{ secrets.PQ_PERF_BOT_PRIVATE_KEY }} + + - name: Post or update performance comment + if: always() && github.event_name == 'pull_request' + continue-on-error: true + uses: actions/github-script@v7 + with: + github-token: >- + ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + const marker = ''; + const botLogin = 'pq-perf-bot[bot]'; + let body; + try { + body = fs.readFileSync('perf_report.md', 'utf8'); + } catch (error) { + core.info('no perf_report.md, skipping comment'); + return; + } + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + const { data: comments } = + await github.rest.issues.listComments({ owner, repo, issue_number }); + const existing = comments.find( + comment => comment.body && comment.body.includes(marker) + ); + if (existing && existing.user.login !== botLogin) { + await github.rest.issues.deleteComment( + { owner, repo, comment_id: existing.id } + ); + await github.rest.issues.createComment( + { owner, repo, issue_number, body } + ); + } else if (existing) { + await github.rest.issues.updateComment( + { owner, repo, comment_id: existing.id, body } + ); + } else { + await github.rest.issues.createComment( + { owner, repo, issue_number, body } + ); + } + + - name: Fail on regression + if: steps.gate.outcome == 'failure' + run: | + echo "Performance regression detected; see the PR comment or summary." + exit 1 + + result: + name: Required result + if: always() + needs: + - preflight + - release + - debug-quality + - arm-release + - lto-smoke + - performance + runs-on: ubuntu-latest + timeout-minutes: 2 + + steps: + - name: Verify required job results + env: + ARM_RELEASE: ${{ needs.arm-release.result }} + DEBUG_QUALITY: ${{ needs.debug-quality.result }} + LTO_SMOKE: ${{ needs.lto-smoke.result }} + PERFORMANCE: ${{ needs.performance.result }} + PREFLIGHT: ${{ needs.preflight.result }} + RELEASE: ${{ needs.release.result }} + run: | + set -euo pipefail + + failed=false + results=( + "preflight=$PREFLIGHT" + "release=$RELEASE" + "debug-quality=$DEBUG_QUALITY" + "arm-release=$ARM_RELEASE" + "lto-smoke=$LTO_SMOKE" + "performance=$PERFORMANCE" + ) + + for entry in "${results[@]}"; do + name="${entry%%=*}" + result="${entry#*=}" + echo "$name: $result" + if [[ "$result" != "success" && "$result" != "skipped" ]]; then + failed=true + fi + done + + if [[ "$failed" == "true" ]]; then + exit 1 + fi diff --git a/changes/developer/ci.consolidate-pr-gate.md b/changes/developer/ci.consolidate-pr-gate.md new file mode 100644 index 000000000..64d524182 --- /dev/null +++ b/changes/developer/ci.consolidate-pr-gate.md @@ -0,0 +1,2 @@ +- Consolidate pull-request checks into one cancellable gate and move exhaustive build variants to extended CI. +- Update coverage and documentation workflows to supported action runtimes. diff --git a/scripts/clang_tidy.sh b/scripts/clang_tidy.sh index 2d1573566..7b599287e 100755 --- a/scripts/clang_tidy.sh +++ b/scripts/clang_tidy.sh @@ -1,56 +1,105 @@ #!/usr/bin/env bash -set -o pipefail +set -eo pipefail -LOGFILE="clangd-tidy-report.log" +all_files=false +base_ref="origin/dev" +build_dir="." +jobs=1 -# check if log file exists and make a backup if it does -if [[ -f "$LOGFILE" ]]; then - mv "$LOGFILE" "${LOGFILE}.bak" -fi +usage() { + cat <<'EOF' +Usage: scripts/clang_tidy.sh [options] -# Only stdout goes to the log file; stderr (where --tqdm draws its -# progress bar via carriage returns) stays on the terminal only, so -# the log file doesn't fill up with \r-based redraw noise. -exec > >(tee "$LOGFILE") +Options: + --all Check every tracked C/C++ file outside external/. + --base Compare HEAD with this revision (default: origin/dev). + --build-dir Directory containing compile_commands.json (default: .). + --jobs Number of concurrent clangd-tidy workers (default: 1). + -h, --help Show this help. +EOF +} -echo "Clangd-Tidy:" - -all_files=false while [[ $# -gt 0 ]]; do case "$1" in --all) all_files=true shift ;; + --base) + [[ $# -ge 2 ]] || { echo "--base requires a revision" >&2; exit 2; } + base_ref="$2" + shift 2 + ;; + --build-dir) + [[ $# -ge 2 ]] || { echo "--build-dir requires a path" >&2; exit 2; } + build_dir="$2" + shift 2 + ;; + --jobs) + [[ $# -ge 2 ]] || { echo "--jobs requires a count" >&2; exit 2; } + jobs="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; *) - echo "Unknown option: $1" - exit 1 + echo "Unknown option: $1" >&2 + usage >&2 + exit 2 ;; esac done +if [[ ! "$jobs" =~ ^[1-9][0-9]*$ ]]; then + echo "--jobs must be a positive integer" >&2 + exit 2 +fi + +if [[ ! -f "$build_dir/compile_commands.json" ]]; then + echo "Missing $build_dir/compile_commands.json; configure CMake first." >&2 + exit 2 +fi + +LOGFILE="clangd-tidy-report.log" +if [[ -f "$LOGFILE" ]]; then + mv "$LOGFILE" "${LOGFILE}.bak" +fi + +# Keep tqdm redraws on stderr instead of filling the report with carriage returns. +exec > >(tee "$LOGFILE") + +echo "Clangd-Tidy:" + files=() if $all_files; then echo " Mode: all tracked C++ files" while IFS= read -r f; do [[ -f "$f" ]] && files+=("$f") - done < <(git ls-files '*.cpp' '*.cxx' '*.cc' '*.c' '*.h' '*.hpp' '*.hxx' -- ':!external') + done < <( + git ls-files \ + '*.cpp' '*.cxx' '*.cc' '*.c' \ + '*.h' '*.hpp' '*.hxx' '*.tpp' \ + -- ':!external/**' + ) else - echo " Mode: changed files since origin/dev" - while IFS=$'\t' read -r status old new; do + merge_base="$(git merge-base HEAD "$base_ref")" + echo " Mode: changed files since $base_ref" + while IFS=$'\t' read -r status first second; do case "$status" in D) ;; - R*) [[ -f "$new" ]] && files+=("$new") ;; - *) [[ -f "$old" ]] && files+=("$old") ;; + R*) [[ -f "$second" ]] && files+=("$second") ;; + *) [[ -f "$first" ]] && files+=("$first") ;; esac - done < <(git diff --name-status "$(git merge-base HEAD origin/dev)") + done < <(git diff --name-status --find-renames "$merge_base...HEAD") # Filter to C++ files only (changed mode may include non-source files) # and exclude anything under external/ cpp_files=() for f in "${files[@]}"; do [[ "$f" == external/* ]] && continue - [[ "$f" =~ \.(cpp|cxx|cc|c|h|hpp|hxx)$ ]] && cpp_files+=("$f") + [[ "$f" =~ \.(cpp|cxx|cc|c|h|hpp|hxx|tpp)$ ]] && cpp_files+=("$f") done files=("${cpp_files[@]}") fi @@ -61,4 +110,4 @@ if [[ ${#files[@]} -eq 0 ]]; then fi echo " Files: ${#files[@]}" -clangd-tidy "${files[@]}" -p=. --tqdm -j1 +clangd-tidy "${files[@]}" -p="$build_dir" --tqdm -j"$jobs" From 98fd35674f0e8e9963110988fe2f8587994859a8 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:41:00 +0200 Subject: [PATCH 02/17] ci: limit actionlint to workflow syntax --- .github/workflows/pr_gate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml index 6ae66c615..2f9648e6f 100644 --- a/.github/workflows/pr_gate.yml +++ b/.github/workflows/pr_gate.yml @@ -122,7 +122,7 @@ jobs: if: steps.changes.outputs.workflow == 'true' uses: docker://rhysd/actionlint:1.7.12 with: - args: -color + args: -color -shellcheck= - name: Check changelog fragment if: >- From 298c3d0ca9e022c1c9472dfddffb47a5bae2c5da Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:42:10 +0200 Subject: [PATCH 03/17] ci: keep coverage on make generator --- .github/workflows/pr_gate.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml index 2f9648e6f..8a95095a6 100644 --- a/.github/workflows/pr_gate.yml +++ b/.github/workflows/pr_gate.yml @@ -271,7 +271,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y \ - clangd-20 gcc-13 g++-13 mold ninja-build + clangd-20 gcc-13 g++-13 mold sudo update-alternatives \ --install /usr/bin/clangd clangd /usr/bin/clangd-20 100 @@ -290,12 +290,13 @@ jobs: - name: Build tests and coverage run: | - cmake -S . -B build -G Ninja \ + cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_WITH_DOCS=OFF \ -DBUILD_WITH_GCOVR=ON \ -DBUILD_WITH_NATIVE=OFF - cmake --build build --target coverage_xml --parallel "$(nproc)" + cmake --build build --parallel "$(nproc)" + cmake --build build --target coverage_xml env: CC: gcc-13 CXX: g++-13 From d726aca9f6f9a077e045e4f6396ca6dd97532893 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:47:37 +0200 Subject: [PATCH 04/17] ci: keep stateful tests serial --- .github/workflows/extended_ci.yml | 8 ++++---- .github/workflows/pr_gate.yml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/extended_ci.yml b/.github/workflows/extended_ci.yml index ea133951d..ac381b006 100644 --- a/.github/workflows/extended_ci.yml +++ b/.github/workflows/extended_ci.yml @@ -110,7 +110,7 @@ jobs: -DBUILD_WITH_LTO=ON \ -DBUILD_WITH_NATIVE=OFF cmake --build build --parallel "$(nproc)" - ctest --test-dir build --output-on-failure --parallel "$(nproc)" + ctest --test-dir build --output-on-failure env: CC: gcc-13 CXX: g++-13 @@ -150,7 +150,7 @@ jobs: -DBUILD_WITH_DOCS=OFF \ -DBUILD_WITH_NATIVE=OFF cmake --build build --parallel "$(nproc)" - ctest --test-dir build --output-on-failure --parallel "$(nproc)" + ctest --test-dir build --output-on-failure env: CC: gcc-13 CXX: g++-13 @@ -221,7 +221,7 @@ jobs: -DBUILD_WITH_NATIVE=OFF \ -DBUILD_WITH_TESTS=ON cmake --build build --parallel "$(nproc)" - ctest --test-dir build --output-on-failure --parallel "$(nproc)" + ctest --test-dir build --output-on-failure env: CC: gcc-13 CXX: g++-13 @@ -295,7 +295,7 @@ jobs: -DBUILD_WITH_SINGULARITY=ON \ -DBUILD_WITH_TESTS=ON cmake --build build --parallel "$(nproc)" - ctest --test-dir build --output-on-failure --parallel "$(nproc)" + ctest --test-dir build --output-on-failure env: CC: gcc-13 CXX: g++-13 diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml index 8a95095a6..9a0c77956 100644 --- a/.github/workflows/pr_gate.yml +++ b/.github/workflows/pr_gate.yml @@ -221,7 +221,7 @@ jobs: -DBUILD_WITH_DOCS=OFF \ -DBUILD_WITH_NATIVE=OFF cmake --build build --parallel "$(nproc)" - ctest --test-dir build --output-on-failure --parallel "$(nproc)" + ctest --test-dir build --output-on-failure env: CC: gcc-13 CXX: g++-13 @@ -353,7 +353,7 @@ jobs: -DBUILD_WITH_DOCS=OFF \ -DBUILD_WITH_NATIVE=OFF cmake --build build --parallel "$(nproc)" - ctest --test-dir build --output-on-failure --parallel "$(nproc)" + ctest --test-dir build --output-on-failure env: CC: gcc-13 CXX: g++-13 From 65cc13659e6cc4e3399472bb1ba2f560c7765da5 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:51:10 +0200 Subject: [PATCH 05/17] ci: preserve license header semantics --- .github/workflows/pr_gate.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml index 9a0c77956..60318be46 100644 --- a/.github/workflows/pr_gate.yml +++ b/.github/workflows/pr_gate.yml @@ -167,10 +167,11 @@ jobs: set -euo pipefail header_lines="$(wc -l < config/licenseHeader.txt)" + expected_header="$(cat config/licenseHeader.txt)" missing=() while IFS= read -r file; do - if ! cmp -s config/licenseHeader.txt \ - <(head -n "$header_lines" "$file"); then + file_header="$(head -n "$header_lines" "$file")" + if [[ "$file_header" != "$expected_header" ]]; then missing+=("$file") fi done < <( From 0f749b23029b9ea087b4530fe2a7c0c766cf9e11 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Tue, 11 Aug 2026 23:11:25 +0200 Subject: [PATCH 06/17] ci: cancel superseded build jobs --- .github/workflows/pr_gate.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml index 60318be46..bd592a066 100644 --- a/.github/workflows/pr_gate.yml +++ b/.github/workflows/pr_gate.yml @@ -189,7 +189,7 @@ jobs: release: name: x86 Release and integration - if: ${{ always() && needs.preflight.outputs.build == 'true' }} + if: ${{ needs.preflight.outputs.build == 'true' }} needs: preflight runs-on: ubuntu-24.04 timeout-minutes: 20 @@ -258,7 +258,7 @@ jobs: debug-quality: name: x86 Debug, coverage, and changed lint - if: ${{ always() && needs.preflight.outputs.build == 'true' }} + if: ${{ needs.preflight.outputs.build == 'true' }} needs: preflight runs-on: ubuntu-24.04 timeout-minutes: 20 @@ -321,7 +321,7 @@ jobs: arm-release: name: ARM Release - if: ${{ always() && needs.preflight.outputs.build == 'true' }} + if: ${{ needs.preflight.outputs.build == 'true' }} needs: preflight runs-on: ubuntu-24.04-arm timeout-minutes: 20 @@ -361,7 +361,7 @@ jobs: lto-smoke: name: Static LTO smoke - if: ${{ always() && needs.preflight.outputs.lto == 'true' }} + if: ${{ needs.preflight.outputs.lto == 'true' }} needs: preflight runs-on: ubuntu-24.04 timeout-minutes: 15 @@ -397,7 +397,7 @@ jobs: performance: name: Performance regression - if: ${{ always() && needs.preflight.outputs.perf == 'true' }} + if: ${{ needs.preflight.outputs.perf == 'true' }} needs: preflight runs-on: ubuntu-24.04 timeout-minutes: 10 @@ -480,7 +480,7 @@ jobs: - name: Mint PQ Perf Bot token id: app-token - if: always() && github.event_name == 'pull_request' + if: github.event_name == 'pull_request' continue-on-error: true uses: actions/create-github-app-token@v1 with: @@ -488,7 +488,7 @@ jobs: private-key: ${{ secrets.PQ_PERF_BOT_PRIVATE_KEY }} - name: Post or update performance comment - if: always() && github.event_name == 'pull_request' + if: github.event_name == 'pull_request' continue-on-error: true uses: actions/github-script@v7 with: From afce55365cafc840c0df785eb82eee650d61ec65 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Tue, 11 Aug 2026 23:24:12 +0200 Subject: [PATCH 07/17] ci: preserve gcc coverage processing --- .github/workflows/pr_gate.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml index bd592a066..c7fb632a2 100644 --- a/.github/workflows/pr_gate.yml +++ b/.github/workflows/pr_gate.yml @@ -317,6 +317,7 @@ jobs: disable_search: true fail_ci_if_error: true files: build/coverage_xml.xml + gcov_executable: gcov-13 token: ${{ secrets.CODECOV_TOKEN }} arm-release: From 5bf494970db47ad7f149aae3e4a08659585776cf Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Tue, 11 Aug 2026 23:36:21 +0200 Subject: [PATCH 08/17] ci: collect generated gcov reports --- .github/workflows/pr_gate.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml index c7fb632a2..cf431a8d9 100644 --- a/.github/workflows/pr_gate.yml +++ b/.github/workflows/pr_gate.yml @@ -314,7 +314,6 @@ jobs: - name: Upload coverage uses: codecov/codecov-action@v7 with: - disable_search: true fail_ci_if_error: true files: build/coverage_xml.xml gcov_executable: gcov-13 From 18812acb7d8b09bb64eb1dea590fce923d08def2 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Tue, 11 Aug 2026 23:48:14 +0200 Subject: [PATCH 09/17] ci: preserve raw gcc coverage semantics --- .github/workflows/pr_gate.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml index cf431a8d9..ff4a7ac71 100644 --- a/.github/workflows/pr_gate.yml +++ b/.github/workflows/pr_gate.yml @@ -287,17 +287,16 @@ jobs: python-version: '3.12' - name: Install dependencies - run: python -m pip install clangd-tidy coverage gcovr numpy + run: python -m pip install clangd-tidy numpy - name: Build tests and coverage run: | cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_WITH_DOCS=OFF \ - -DBUILD_WITH_GCOVR=ON \ -DBUILD_WITH_NATIVE=OFF cmake --build build --parallel "$(nproc)" - cmake --build build --target coverage_xml + ctest --test-dir build --output-on-failure env: CC: gcc-13 CXX: g++-13 @@ -315,7 +314,6 @@ jobs: uses: codecov/codecov-action@v7 with: fail_ci_if_error: true - files: build/coverage_xml.xml gcov_executable: gcov-13 token: ${{ secrets.CODECOV_TOKEN }} From 351fa1844688fab0621b68f57e6a8187fc7fd712 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Wed, 12 Aug 2026 00:01:37 +0200 Subject: [PATCH 10/17] ci: preserve existing codecov baseline --- .github/workflows/pr_gate.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml index ff4a7ac71..09b97648e 100644 --- a/.github/workflows/pr_gate.yml +++ b/.github/workflows/pr_gate.yml @@ -122,7 +122,9 @@ jobs: if: steps.changes.outputs.workflow == 'true' uses: docker://rhysd/actionlint:1.7.12 with: - args: -color -shellcheck= + args: >- + -color -shellcheck= + -ignore=runner.*codecov/codecov-action@v3.*too.old - name: Check changelog fragment if: >- @@ -287,16 +289,17 @@ jobs: python-version: '3.12' - name: Install dependencies - run: python -m pip install clangd-tidy numpy + run: python -m pip install clangd-tidy coverage gcovr numpy - name: Build tests and coverage run: | cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_GCOVR=ON \ -DBUILD_WITH_NATIVE=OFF cmake --build build --parallel "$(nproc)" - ctest --test-dir build --output-on-failure + cmake --build build --target coverage_xml env: CC: gcc-13 CXX: g++-13 @@ -311,11 +314,13 @@ jobs: --jobs 2 - name: Upload coverage - uses: codecov/codecov-action@v7 + uses: codecov/codecov-action@v3 with: fail_ci_if_error: true + gcov: true gcov_executable: gcov-13 - token: ${{ secrets.CODECOV_TOKEN }} + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} arm-release: name: ARM Release From 1956a044a62bab7a0513deb6b267c55764e8728d Mon Sep 17 00:00:00 2001 From: Jakob Gamper <97gamjak@gmail.com> Date: Wed, 12 Aug 2026 08:29:39 +0200 Subject: [PATCH 11/17] refactor: move path filtering logic from on to steps -- skipped jobs appear as a failed check in a ruleset --- .github/workflows/ci_build.yml | 265 +++------------------------- .github/workflows/clang_format.yml | 49 +++-- .github/workflows/license_check.yml | 71 +++++--- .github/workflows/lint.yml | 66 ++++--- 4 files changed, 146 insertions(+), 305 deletions(-) diff --git a/.github/workflows/ci_build.yml b/.github/workflows/ci_build.yml index 660de56f2..4b54fb7c0 100644 --- a/.github/workflows/ci_build.yml +++ b/.github/workflows/ci_build.yml @@ -4,39 +4,41 @@ on: pull_request: branches: - '*' - paths: - - '.github/workflows/ci_build.yml' - - '.cmake/**' - - 'apps/**' - - 'include/**' - - 'src/**' - - 'integration_tests/**' - - 'tests/**' - - 'benchmarks/CMakeLists.txt' - - 'benchmarks/src/**' - - 'CMakeLists.txt' push: branches: - dev - main - paths: - - '.github/workflows/ci_build.yml' - - '.cmake/**' - - 'apps/**' - - 'include/**' - - 'src/**' - - 'integration_tests/**' - - 'tests/**' - - 'benchmarks/CMakeLists.txt' - - 'benchmarks/src/**' - - 'CMakeLists.txt' schedule: - cron: '0 2 * * *' workflow_dispatch: jobs: + changes: + if: github.event_name != 'schedule' + runs-on: ubuntu-24.04 + outputs: + relevant: ${{ steps.filter.outputs.relevant }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + relevant: + - '.github/workflows/ci_build.yml' + - '.cmake/**' + - 'apps/**' + - 'include/**' + - 'src/**' + - 'integration_tests/**' + - 'tests/**' + - 'benchmarks/CMakeLists.txt' + - 'benchmarks/src/**' + - 'CMakeLists.txt' + build: if: github.event_name != 'schedule' + needs: changes strategy: matrix: os: [ubuntu-24.04, ubuntu-24.04-arm] @@ -47,225 +49,10 @@ jobs: - uses: actions/checkout@v4 - name: install gcc13 + if: needs.changes.outputs.relevant == 'true' run: | sudo apt update sudo apt install gcc-13 g++-13 mold shell: bash - # safe because CI builds with -DBUILD_WITH_NATIVE=Off (portable objects) - - name: setup ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ matrix.os }}-${{ matrix.build }} - - - name: setup python ubuntu - uses: actions/setup-python@v3 - with: - python-version: '3.12' - - - name: install python dependencies - run: | - python -m pip install numpy - shell: bash - - - name: install coverage - if : ${{ matrix.os == 'ubuntu-24.04' && matrix.build == 'Debug' }} - run: | - python -m pip install coverage - python -m pip install gcovr - shell: bash - - - name: Build and Test Project - if : ${{ matrix.os != 'ubuntu-24.04' || (matrix.os == 'ubuntu-24.04' && matrix.build != 'Debug') }} - run: | - mkdir build - cd build - cmake -DCMAKE_BUILD_TYPE=${{ matrix.build }} -DBUILD_WITH_NATIVE=Off .. - make -j$(nproc) - make test - env: - CC: gcc-13 - CXX: g++-13 - - - name: Build and Test Project with coverage - if : ${{ matrix.os == 'ubuntu-24.04' && matrix.build == 'Debug' }} - run: | - mkdir build - cd build - cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_WITH_GCOVR=ON -DBUILD_WITH_NATIVE=Off .. - make -j$(nproc) - make coverage_xml - env: - CC: gcc-13 - CXX: g++-13 - - - name: Upload coverage to Codecov - if : ${{ matrix.os == 'ubuntu-24.04' && matrix.build == 'Debug' }} - uses: codecov/codecov-action@v3 - with: - fail_ci_if_error: true # optional (default = false) - gcov: true - gcov_executable: gcov-13 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - - name: Setup Conda Environment - if: ${{matrix.build == 'Release'}} - uses: conda-incubator/setup-miniconda@v3 - with: - auto-update-conda: true - python-version: '3.12' - activate-environment: test-env - channels: default, conda-forge - auto-activate-base: false - - - name: Cache conda env - if: ${{matrix.build == 'Release'}} - id: cache-conda-env - uses: actions/cache@v4 - with: - path: ${{ env.CONDA }}/envs/test-env - key: conda-env-${{ matrix.os }}-${{ hashFiles('.github/workflows/ci_build.yml') }} - - - name: Install Integration Test Dependencies in Conda Env - if: ${{matrix.build == 'Release' && steps.cache-conda-env.outputs.cache-hit != 'true'}} - run: | - conda install -n test-env -y pip conda-forge::dftbplus conda-forge::xtb - conda run -n test-env python -m pip install pytest pytest-cov pqanalysis ase pyscf==2.9.0 - - - name: Integration Tests - if: ${{matrix.build == 'Release' }} - run: | - conda run -n test-env pytest integration_tests -v - - benchmark: - if: github.event_name != 'schedule' - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v4 - - - name: install gcc13 - run: | - sudo apt update - sudo apt install gcc-13 g++-13 - shell: bash - - - name: Build benchmark suite - run: | - cmake -S . -B build-benchmark \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_WITH_BENCHMARKING=On \ - -DBUILD_WITH_TESTS=Off \ - -DBUILD_WITH_ASE=Off \ - -DBUILD_WITH_DOCS=Off \ - -DBUILD_WITH_NATIVE=Off - cmake --build build-benchmark --target google_benchmarks -j$(nproc) - env: - CC: gcc-13 - CXX: g++-13 - - - name: Run benchmark smoke tests - run: | - ctest --test-dir build-benchmark -L benchmark --output-on-failure - - build-static-lto: - if: github.event_name != 'schedule' - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v4 - - - name: Install GCC 13 - run: | - sudo apt update - sudo apt install gcc-13 g++-13 mold ninja-build - - - name: Build and test static LTO - run: | - cmake -S . -B build -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=OFF \ - -DBUILD_WITH_ASE=OFF \ - -DBUILD_WITH_LTO=ON \ - -DBUILD_WITH_NATIVE=OFF - cmake --build build --parallel - ctest --test-dir build --output-on-failure -j1 - env: - CC: gcc-13 - CXX: g++-13 - - static-singularity-build: - if: >- - github.event_name == 'schedule' || - github.event_name == 'workflow_dispatch' - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v4 - - - name: Install build dependencies - run: | - sudo apt update - sudo apt install gcc-13 g++-13 mold - - - name: Setup ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: static-singularity - - - name: Build project - run: | - cmake -S . -B build \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=OFF \ - -DBUILD_WITH_SINGULARITY=ON \ - -DBUILD_WITH_TESTS=ON \ - -DBUILD_WITH_ASE=OFF \ - -DBUILD_WITH_NATIVE=OFF - cmake --build build --parallel "$(nproc)" - env: - CC: gcc-13 - CXX: g++-13 - - - name: Run tests - run: ctest --test-dir build --output-on-failure -j1 - - mpi-build: - if: github.event_name != 'schedule' - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v4 - - - name: Install build dependencies - run: | - sudo apt update - sudo apt install \ - gcc-13 \ - g++-13 \ - mold \ - openmpi-bin \ - libopenmpi-dev \ - python-is-python3 - - - name: Setup ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: mpi-release - - - name: Build project - run: | - cmake -S . -B build \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_WITH_MPI=ON \ - -DBUILD_WITH_ASE=OFF \ - -DBUILD_WITH_NATIVE=OFF \ - -DBUILD_WITH_TESTS=ON - cmake --build build --parallel "$(nproc)" - env: - CC: gcc-13 - CXX: g++-13 - - - name: Run tests - run: ctest --test-dir build --output-on-failure -j1 + # ... every subsequent step gets `if: needs.changes.outputs.relevant == 'true'` ... diff --git a/.github/workflows/clang_format.yml b/.github/workflows/clang_format.yml index cc55f415f..078a99275 100644 --- a/.github/workflows/clang_format.yml +++ b/.github/workflows/clang_format.yml @@ -5,22 +5,35 @@ on: branches: - main - dev - paths: - - '.clang-format' - - '.github/workflows/clang_format.yml' - - '**/*.c' - - '**/*.cc' - - '**/*.cpp' - - '**/*.cxx' - - '**/*.h' - - '**/*.hh' - - '**/*.hpp' - - '**/*.hxx' - - '!external/**' workflow_dispatch: jobs: + changes: + runs-on: ubuntu-24.04 + outputs: + relevant: ${{ steps.filter.outputs.relevant }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + relevant: + - '.clang-format' + - '.github/workflows/clang_format.yml' + - '**/*.c' + - '**/*.cc' + - '**/*.cpp' + - '**/*.cxx' + - '**/*.h' + - '**/*.hh' + - '**/*.hpp' + - '**/*.hxx' + - '!external/**' + clang-format: + needs: changes + if: needs.changes.outputs.relevant == 'true' runs-on: ubuntu-24.04 name: Check Changed C++ Formatting @@ -55,3 +68,15 @@ jobs: "$base_sha" \ "$head_sha" \ -- apps benchmarks include src tests + + clang-format-gate: + if: always() + needs: [changes, clang-format] + runs-on: ubuntu-24.04 + steps: + - name: Check clang-format result + run: | + if [[ "${{ needs.clang-format.result }}" == "failure" ]]; then + exit 1 + fi + echo "OK — clang-format passed or was skipped (no relevant paths changed)" diff --git a/.github/workflows/license_check.yml b/.github/workflows/license_check.yml index 69115df78..0d934e4c8 100644 --- a/.github/workflows/license_check.yml +++ b/.github/workflows/license_check.yml @@ -4,30 +4,35 @@ on: pull_request: branches: - '*' - paths: - - '**/*.cpp' - - '**/*.hpp' - - '**/*.c' - - '**/*.h' - - '.github/workflows/license_check.yml' - - 'scripts/addLicense.sh' - - 'config/licenseHeader.txt' push: branches: - main - master - paths: - - '**/*.cpp' - - '**/*.hpp' - - '**/*.c' - - '**/*.h' - - '.github/workflows/license_check.yml' - - 'scripts/addLicense.sh' - - 'config/licenseHeader.txt' workflow_dispatch: jobs: + changes: + runs-on: ubuntu-24.04 + outputs: + relevant: ${{ steps.filter.outputs.relevant }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + relevant: + - '**/*.cpp' + - '**/*.hpp' + - '**/*.c' + - '**/*.h' + - '.github/workflows/license_check.yml' + - 'scripts/addLicense.sh' + - 'config/licenseHeader.txt' + license-check: + needs: changes + if: needs.changes.outputs.relevant == 'true' runs-on: ubuntu-latest name: Check License Headers @@ -38,31 +43,31 @@ jobs: - name: Check for license headers in C++ files run: | echo "Checking for exact GPL license headers in C++ files..." - + # Verify license header template exists if [ ! -f "config/licenseHeader.txt" ]; then echo "❌ License header template not found at config/licenseHeader.txt" exit 1 fi - + # Get the expected license header content license_header=$(cat config/licenseHeader.txt) header_line_count=$(wc -l < config/licenseHeader.txt) - + echo "Expected license header has $header_line_count lines" echo "" - + # Find all C++ files in the relevant directories (excluding external dependencies) missing_count=0 total_files=0 - + find src/ tests/ include/ apps/ benchmarks/ -regex ".*\.\(hpp\|cpp\|c\|h\)$" -print0 2>/dev/null | while IFS= read -r -d '' file; do total_files=$((total_files + 1)) echo "Checking: $file" - + # Extract the first N lines from the file (where N = number of lines in license header) file_header=$(head -n "$header_line_count" "$file" 2>/dev/null) - + # Compare the extracted header with the expected license header if [ "$file_header" = "$license_header" ]; then echo "✅ Exact license header match: $file" @@ -71,22 +76,22 @@ jobs: echo "$file" >> /tmp/missing_files.txt fi done - + # Count missing files if [ -f /tmp/missing_files.txt ]; then missing_count=$(wc -l < /tmp/missing_files.txt) else missing_count=0 fi - + # Get total file count total_files=$(find src/ tests/ include/ apps/ benchmarks/ -regex ".*\.\(hpp\|cpp\|c\|h\)$" 2>/dev/null | wc -l) - + echo "" echo "=== License Check Summary ===" echo "Total files checked: $total_files" echo "Files with missing/invalid license headers: $missing_count" - + if [ $missing_count -gt 0 ]; then echo "" echo "Files with incorrect license headers:" @@ -113,3 +118,15 @@ jobs: cat config/licenseHeader.txt echo "=== End of Template ===" shell: bash + + license-check-gate: + if: always() + needs: [changes, license-check] + runs-on: ubuntu-24.04 + steps: + - name: Check license-check result + run: | + if [[ "${{ needs.license-check.result }}" == "failure" ]]; then + exit 1 + fi + echo "OK — license check passed or was skipped (no relevant paths changed)" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ea03c8beb..d87f27263 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,41 +4,41 @@ on: pull_request: branches: - '*' - paths: - - '.github/workflows/ci_build.yml' - - '.cmake/**' - - 'apps/**' - - 'include/**' - - 'src/**' - - 'integration_tests/**' - - 'tests/**' - - 'benchmarks/CMakeLists.txt' - - 'benchmarks/src/**' - - 'CMakeLists.txt' - - '.clang-tidy' - - 'scripts/clang_tidy.sh' - - '.github/workflows/lint.yml' push: branches: - dev - main - paths: - - '.github/workflows/ci_build.yml' - - '.cmake/**' - - 'apps/**' - - 'include/**' - - 'src/**' - - 'integration_tests/**' - - 'CMakeLists.txt' - - '.clang-tidy' - - 'scripts/clang_tidy.sh' - - '.github/workflows/lint.yml' - - 'benchmarks/src/**' - - 'CMakeLists.txt' workflow_dispatch: jobs: + changes: + runs-on: ubuntu-24.04 + outputs: + relevant: ${{ steps.filter.outputs.relevant }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + relevant: + - '.github/workflows/ci_build.yml' + - '.cmake/**' + - 'apps/**' + - 'include/**' + - 'src/**' + - 'integration_tests/**' + - 'tests/**' + - 'benchmarks/CMakeLists.txt' + - 'benchmarks/src/**' + - 'CMakeLists.txt' + - '.clang-tidy' + - 'scripts/clang_tidy.sh' + - '.github/workflows/lint.yml' + lint: + needs: changes + if: needs.changes.outputs.relevant == 'true' runs-on: ubuntu-24.04 steps: @@ -78,3 +78,15 @@ jobs: - name: Run clangd-tidy run: | clangd-tidy $(find src/ apps/ include/ -name "*.cpp" -o -name "*.hpp" -o -name "*.tpp" 2>/dev/null) -p=build + + lint-gate: + if: always() + needs: [changes, lint] + runs-on: ubuntu-24.04 + steps: + - name: Check lint result + run: | + if [[ "${{ needs.lint.result }}" == "failure" ]]; then + exit 1 + fi + echo "OK — lint passed or was skipped (no relevant paths changed)" From 91761bf2e400004f7eea6523816c3d8e2f9624f5 Mon Sep 17 00:00:00 2001 From: Jakob Gamper <97gamjak@gmail.com> Date: Wed, 12 Aug 2026 08:33:03 +0200 Subject: [PATCH 12/17] chore: fix lost ci file --- .github/workflows/ci_build.yml | 242 ++++++++++++++++++++++++++++++++- 1 file changed, 239 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_build.yml b/.github/workflows/ci_build.yml index 4b54fb7c0..20e8e778f 100644 --- a/.github/workflows/ci_build.yml +++ b/.github/workflows/ci_build.yml @@ -37,7 +37,7 @@ jobs: - 'CMakeLists.txt' build: - if: github.event_name != 'schedule' + if: needs.changes.outputs.relevant == 'true' && github.event_name != 'schedule' needs: changes strategy: matrix: @@ -49,10 +49,246 @@ jobs: - uses: actions/checkout@v4 - name: install gcc13 - if: needs.changes.outputs.relevant == 'true' run: | sudo apt update sudo apt install gcc-13 g++-13 mold shell: bash - # ... every subsequent step gets `if: needs.changes.outputs.relevant == 'true'` ... + # safe because CI builds with -DBUILD_WITH_NATIVE=Off (portable objects) + - name: setup ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ matrix.os }}-${{ matrix.build }} + + - name: setup python ubuntu + uses: actions/setup-python@v3 + with: + python-version: '3.12' + + - name: install python dependencies + run: | + python -m pip install numpy + shell: bash + + - name: install coverage + if : ${{ matrix.os == 'ubuntu-24.04' && matrix.build == 'Debug' }} + run: | + python -m pip install coverage + python -m pip install gcovr + shell: bash + + - name: Build and Test Project + if : ${{ matrix.os != 'ubuntu-24.04' || (matrix.os == 'ubuntu-24.04' && matrix.build != 'Debug') }} + run: | + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=${{ matrix.build }} -DBUILD_WITH_NATIVE=Off .. + make -j$(nproc) + make test + env: + CC: gcc-13 + CXX: g++-13 + + - name: Build and Test Project with coverage + if : ${{ matrix.os == 'ubuntu-24.04' && matrix.build == 'Debug' }} + run: | + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_WITH_GCOVR=ON -DBUILD_WITH_NATIVE=Off .. + make -j$(nproc) + make coverage_xml + env: + CC: gcc-13 + CXX: g++-13 + + - name: Upload coverage to Codecov + if : ${{ matrix.os == 'ubuntu-24.04' && matrix.build == 'Debug' }} + uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: true # optional (default = false) + gcov: true + gcov_executable: gcov-13 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + - name: Setup Conda Environment + if: ${{matrix.build == 'Release'}} + uses: conda-incubator/setup-miniconda@v3 + with: + auto-update-conda: true + python-version: '3.12' + activate-environment: test-env + channels: default, conda-forge + auto-activate-base: false + + - name: Cache conda env + if: ${{matrix.build == 'Release'}} + id: cache-conda-env + uses: actions/cache@v4 + with: + path: ${{ env.CONDA }}/envs/test-env + key: conda-env-${{ matrix.os }}-${{ hashFiles('.github/workflows/ci_build.yml') }} + + - name: Install Integration Test Dependencies in Conda Env + if: ${{matrix.build == 'Release' && steps.cache-conda-env.outputs.cache-hit != 'true'}} + run: | + conda install -n test-env -y pip conda-forge::dftbplus conda-forge::xtb + conda run -n test-env python -m pip install pytest pytest-cov pqanalysis ase pyscf==2.9.0 + + - name: Integration Tests + if: ${{matrix.build == 'Release' }} + run: | + conda run -n test-env pytest integration_tests -v + + benchmark: + if: needs.changes.outputs.relevant == 'true' && github.event_name != 'schedule' + needs: changes + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: install gcc13 + run: | + sudo apt update + sudo apt install gcc-13 g++-13 + shell: bash + + - name: Build benchmark suite + run: | + cmake -S . -B build-benchmark \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_WITH_BENCHMARKING=On \ + -DBUILD_WITH_TESTS=Off \ + -DBUILD_WITH_ASE=Off \ + -DBUILD_WITH_DOCS=Off \ + -DBUILD_WITH_NATIVE=Off + cmake --build build-benchmark --target google_benchmarks -j$(nproc) + env: + CC: gcc-13 + CXX: g++-13 + + - name: Run benchmark smoke tests + run: | + ctest --test-dir build-benchmark -L benchmark --output-on-failure + + build-static-lto: + if: needs.changes.outputs.relevant == 'true' && github.event_name != 'schedule' + needs: changes + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Install GCC 13 + run: | + sudo apt update + sudo apt install gcc-13 g++-13 mold ninja-build + + - name: Build and test static LTO + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_WITH_ASE=OFF \ + -DBUILD_WITH_LTO=ON \ + -DBUILD_WITH_NATIVE=OFF + cmake --build build --parallel + ctest --test-dir build --output-on-failure -j1 + env: + CC: gcc-13 + CXX: g++-13 + + static-singularity-build: + if: >- + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Install build dependencies + run: | + sudo apt update + sudo apt install gcc-13 g++-13 mold + + - name: Setup ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: static-singularity + + - name: Build project + run: | + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_WITH_SINGULARITY=ON \ + -DBUILD_WITH_TESTS=ON \ + -DBUILD_WITH_ASE=OFF \ + -DBUILD_WITH_NATIVE=OFF + cmake --build build --parallel "$(nproc)" + env: + CC: gcc-13 + CXX: g++-13 + + - name: Run tests + run: ctest --test-dir build --output-on-failure -j1 + + mpi-build: + if: needs.changes.outputs.relevant == 'true' && github.event_name != 'schedule' + needs: changes + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Install build dependencies + run: | + sudo apt update + sudo apt install \ + gcc-13 \ + g++-13 \ + mold \ + openmpi-bin \ + libopenmpi-dev \ + python-is-python3 + + - name: Setup ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: mpi-release + + - name: Build project + run: | + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_WITH_MPI=ON \ + -DBUILD_WITH_ASE=OFF \ + -DBUILD_WITH_NATIVE=OFF \ + -DBUILD_WITH_TESTS=ON + cmake --build build --parallel "$(nproc)" + env: + CC: gcc-13 + CXX: g++-13 + + - name: Run tests + run: ctest --test-dir build --output-on-failure -j1 + + ci-build-gate: + if: always() + needs: [changes, build, benchmark, build-static-lto, mpi-build] + runs-on: ubuntu-24.04 + steps: + - name: Check CI build results + run: | + for result in \ + "${{ needs.build.result }}" \ + "${{ needs.benchmark.result }}" \ + "${{ needs.build-static-lto.result }}" \ + "${{ needs.mpi-build.result }}"; do + if [[ "$result" == "failure" ]]; then + exit 1 + fi + done + echo "OK — all CI build jobs passed or were skipped (no relevant paths changed)" From 6823c964c669f9ebd6d6f63c7db5a84c3d9c6b46 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Wed, 12 Aug 2026 08:57:54 +0200 Subject: [PATCH 13/17] ci: tolerate coverage rounding noise --- codecov.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/codecov.yml b/codecov.yml index c75bebdc0..d33efc600 100644 --- a/codecov.yml +++ b/codecov.yml @@ -11,5 +11,11 @@ ignore: - src/engine/*.cpp # just one combining all engine methods - tested by integration tests - include/engine/*.hpp # just one combining all engine methods - tested by integration tests - src/QM/** +coverage: + status: + project: + default: + target: auto + threshold: 0.01 codecov: token: bf914a3c-fad4-4ea0-936e-5fdf8a80e211 From 098378ad04497bfd3d283d8e384fc7a87bf04828 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Wed, 12 Aug 2026 15:22:53 +0200 Subject: [PATCH 14/17] ci: preserve hardened gate filtering --- .github/workflows/pr_gate.yml | 31 +++++++++++++++------ changes/developer/ci.consolidate-pr-gate.md | 1 + 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml index 09b97648e..a43845e05 100644 --- a/.github/workflows/pr_gate.yml +++ b/.github/workflows/pr_gate.yml @@ -24,6 +24,7 @@ jobs: base_sha: ${{ steps.changes.outputs.base_sha }} build: ${{ steps.changes.outputs.build }} head_sha: ${{ steps.changes.outputs.head_sha }} + lint_all: ${{ steps.changes.outputs.lint_all }} lto: ${{ steps.changes.outputs.lto }} perf: ${{ steps.changes.outputs.perf }} @@ -50,6 +51,7 @@ jobs: build=false cpp=false + lint_all=false lto=false perf=false workflow=false @@ -75,11 +77,17 @@ jobs: esac case "$path" in - .cmake/*|.github/ci/*|.gitmodules|apps/*|benchmarks/CMakeLists.txt|benchmarks/src/*|external/*|include/*|integration_tests/*|src/*|tests/*|CMakeLists.txt|scripts/clang_tidy.sh) + .clang-tidy|.cmake/*|.github/ci/*|.gitmodules|apps/*|benchmarks/CMakeLists.txt|benchmarks/src/*|external/*|include/*|integration_tests/*|src/*|tests/*|CMakeLists.txt|scripts/clang_tidy.sh) build=true ;; esac + case "$path" in + .clang-tidy|scripts/clang_tidy.sh) + lint_all=true + ;; + esac + case "$path" in .cmake/*|.gitmodules|apps/*|external/*|include/*|src/*|CMakeLists.txt) lto=true @@ -87,7 +95,7 @@ jobs: esac case "$path" in - *.c|*.cc|*.cpp|*.cxx|*.h|*.hh|*.hpp|*.hxx|*.tpp|.clang-format|config/licenseHeader.txt) + *.c|*.cc|*.cpp|*.cxx|*.h|*.hh|*.hpp|*.hxx|*.tpp|.clang-format|config/licenseHeader.txt|scripts/addLicense.sh) cpp=true ;; esac @@ -104,6 +112,7 @@ jobs: echo "build=$build" echo "cpp=$cpp" echo "head_sha=$head_sha" + echo "lint_all=$lint_all" echo "lto=$lto" echo "perf=$perf" echo "workflow=$workflow" @@ -112,7 +121,7 @@ jobs: { echo "### Changed paths" echo - echo "build=$build, cpp=$cpp, lto=$lto, perf=$perf, workflow=$workflow" + echo "build=$build, cpp=$cpp, lint_all=$lint_all, lto=$lto, perf=$perf, workflow=$workflow" } >> "$GITHUB_STEP_SUMMARY" - name: Test changelog tooling @@ -307,11 +316,17 @@ jobs: - name: Lint changed C++ files env: BASE_SHA: ${{ needs.preflight.outputs.base_sha }} - run: >- - scripts/clang_tidy.sh - --base "$BASE_SHA" - --build-dir build - --jobs 2 + LINT_ALL: ${{ needs.preflight.outputs.lint_all }} + run: | + set -euo pipefail + mode=(--base "$BASE_SHA") + if [[ "$LINT_ALL" == "true" ]]; then + mode=(--all) + fi + scripts/clang_tidy.sh \ + "${mode[@]}" \ + --build-dir build \ + --jobs 2 - name: Upload coverage uses: codecov/codecov-action@v3 diff --git a/changes/developer/ci.consolidate-pr-gate.md b/changes/developer/ci.consolidate-pr-gate.md index 64d524182..22a7b905d 100644 --- a/changes/developer/ci.consolidate-pr-gate.md +++ b/changes/developer/ci.consolidate-pr-gate.md @@ -1,2 +1,3 @@ - Consolidate pull-request checks into one cancellable gate and move exhaustive build variants to extended CI. +- Keep the required gate present for every pull request and filter heavy work in its preflight job. - Update coverage and documentation workflows to supported action runtimes. From bae7d5b94c0a983f104e55872bd03dbb28982074 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Wed, 12 Aug 2026 15:43:27 +0200 Subject: [PATCH 15/17] ci: allow full lint gate to complete --- .github/workflows/pr_gate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml index a43845e05..b6e469ded 100644 --- a/.github/workflows/pr_gate.yml +++ b/.github/workflows/pr_gate.yml @@ -272,7 +272,7 @@ jobs: if: ${{ needs.preflight.outputs.build == 'true' }} needs: preflight runs-on: ubuntu-24.04 - timeout-minutes: 20 + timeout-minutes: 30 steps: - uses: actions/checkout@v4 From 9c62a6bbd89d41191d86587d90e6fe16d96e6ab7 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Wed, 12 Aug 2026 16:09:45 +0200 Subject: [PATCH 16/17] ci: isolate configuration-wide lint --- .github/workflows/pr_gate.yml | 64 ++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/.github/workflows/pr_gate.yml b/.github/workflows/pr_gate.yml index b6e469ded..6ded5912d 100644 --- a/.github/workflows/pr_gate.yml +++ b/.github/workflows/pr_gate.yml @@ -272,7 +272,7 @@ jobs: if: ${{ needs.preflight.outputs.build == 'true' }} needs: preflight runs-on: ubuntu-24.04 - timeout-minutes: 30 + timeout-minutes: 20 steps: - uses: actions/checkout@v4 @@ -316,17 +316,11 @@ jobs: - name: Lint changed C++ files env: BASE_SHA: ${{ needs.preflight.outputs.base_sha }} - LINT_ALL: ${{ needs.preflight.outputs.lint_all }} - run: | - set -euo pipefail - mode=(--base "$BASE_SHA") - if [[ "$LINT_ALL" == "true" ]]; then - mode=(--all) - fi - scripts/clang_tidy.sh \ - "${mode[@]}" \ - --build-dir build \ - --jobs 2 + run: >- + scripts/clang_tidy.sh + --base "$BASE_SHA" + --build-dir build + --jobs 2 - name: Upload coverage uses: codecov/codecov-action@v3 @@ -337,6 +331,49 @@ jobs: env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + lint-config: + name: Full lint for lint configuration + if: ${{ needs.preflight.outputs.lint_all == 'true' }} + needs: preflight + runs-on: ubuntu-24.04 + timeout-minutes: 20 + + steps: + - uses: actions/checkout@v4 + + - name: Install build and lint tools + run: | + sudo apt-get update + sudo apt-get install -y \ + clangd-20 gcc-13 g++-13 mold ninja-build + sudo update-alternatives \ + --install /usr/bin/clangd clangd /usr/bin/clangd-20 100 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: python -m pip install clangd-tidy numpy tqdm + + - name: Configure compile database + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Debug \ + -DBUILD_WITH_DOCS=OFF \ + -DBUILD_WITH_NATIVE=OFF + env: + CC: gcc-13 + CXX: g++-13 + + - name: Lint all production C++ files + run: >- + scripts/clang_tidy.sh + --all + --build-dir build + --jobs 2 + arm-release: name: ARM Release if: ${{ needs.preflight.outputs.build == 'true' }} @@ -560,6 +597,7 @@ jobs: - preflight - release - debug-quality + - lint-config - arm-release - lto-smoke - performance @@ -571,6 +609,7 @@ jobs: env: ARM_RELEASE: ${{ needs.arm-release.result }} DEBUG_QUALITY: ${{ needs.debug-quality.result }} + LINT_CONFIG: ${{ needs.lint-config.result }} LTO_SMOKE: ${{ needs.lto-smoke.result }} PERFORMANCE: ${{ needs.performance.result }} PREFLIGHT: ${{ needs.preflight.result }} @@ -583,6 +622,7 @@ jobs: "preflight=$PREFLIGHT" "release=$RELEASE" "debug-quality=$DEBUG_QUALITY" + "lint-config=$LINT_CONFIG" "arm-release=$ARM_RELEASE" "lto-smoke=$LTO_SMOKE" "performance=$PERFORMANCE" From 4c4df9e3d167f8ca9cc69dd571a6acca9a0d20f9 Mon Sep 17 00:00:00 2001 From: "Josef M. Gallmetzer" <64498081+galjos@users.noreply.github.com> Date: Wed, 12 Aug 2026 16:31:01 +0200 Subject: [PATCH 17/17] ci: align full lint with production scope --- scripts/clang_tidy.sh | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/scripts/clang_tidy.sh b/scripts/clang_tidy.sh index 7b599287e..ad14241b7 100755 --- a/scripts/clang_tidy.sh +++ b/scripts/clang_tidy.sh @@ -11,7 +11,7 @@ usage() { Usage: scripts/clang_tidy.sh [options] Options: - --all Check every tracked C/C++ file outside external/. + --all Check every tracked production C/C++ file. --base Compare HEAD with this revision (default: origin/dev). --build-dir Directory containing compile_commands.json (default: .). --jobs Number of concurrent clangd-tidy workers (default: 1). @@ -74,15 +74,11 @@ echo "Clangd-Tidy:" files=() if $all_files; then - echo " Mode: all tracked C++ files" + echo " Mode: all tracked production C++ files" while IFS= read -r f; do + [[ "$f" =~ \.(c|cc|cpp|cxx|h|hh|hpp|hxx|tpp)$ ]] || continue [[ -f "$f" ]] && files+=("$f") - done < <( - git ls-files \ - '*.cpp' '*.cxx' '*.cc' '*.c' \ - '*.h' '*.hpp' '*.hxx' '*.tpp' \ - -- ':!external/**' - ) + done < <(git ls-files apps include src) else merge_base="$(git merge-base HEAD "$base_ref")" echo " Mode: changed files since $base_ref" @@ -94,12 +90,14 @@ else esac done < <(git diff --name-status --find-renames "$merge_base...HEAD") - # Filter to C++ files only (changed mode may include non-source files) - # and exclude anything under external/ + # Match the full-lint scope: production C++ files only. cpp_files=() for f in "${files[@]}"; do - [[ "$f" == external/* ]] && continue - [[ "$f" =~ \.(cpp|cxx|cc|c|h|hpp|hxx|tpp)$ ]] && cpp_files+=("$f") + case "$f" in + apps/*|include/*|src/*) ;; + *) continue ;; + esac + [[ "$f" =~ \.(c|cc|cpp|cxx|h|hh|hpp|hxx|tpp)$ ]] && cpp_files+=("$f") done files=("${cpp_files[@]}") fi