Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/_reusable-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# ---------------------------------------------------------------------------
# Reusable workflow: Build MNE-CPP (no tests, min/max Qt versions)
# Called by main.yml, staging.yml, and pull-request.yml
# ---------------------------------------------------------------------------
name: Reusable Build

on:
workflow_call:
inputs:
qt_version:
description: "Qt version to build with"
required: true
type: string

jobs:
Build:
runs-on: ${{ matrix.os }}

strategy:
fail-fast: false
max-parallel: 3
matrix:
os: [ubuntu-24.04, macos-26, windows-2025]

steps:
- name: Clone repository
uses: actions/checkout@v4

- name: Install Python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.10'
architecture: ${{ matrix.os == 'macos-26' && 'arm64' || 'x64' }}

- name: Install Qt (Linux|MacOS)
if: matrix.os == 'ubuntu-24.04' || matrix.os == 'macos-26'
uses: jurplel/install-qt-action@v3
with:
version: ${{ inputs.qt_version }}
modules: qtshadertools

- name: Install Qt (Windows)
if: matrix.os == 'windows-2025'
uses: jurplel/install-qt-action@v3
with:
version: ${{ inputs.qt_version }}
arch: win64_msvc2022_64
modules: qtshadertools

- name: Configure and compile MNE-CPP (Linux|MacOS)
if: matrix.os == 'ubuntu-24.04' || matrix.os == 'macos-26'
run: |
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build

- name: Configure and compile MNE-CPP (Windows)
if: matrix.os == 'windows-2025'
run: |
cmd.exe /c "call `"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat`" && set > %temp%\vcvars.txt"
Get-Content "$env:temp\vcvars.txt" | Foreach-Object { if ($_ -match "^(.*?)=(.*)$") { Set-Content "env:\$($matches[1])" $matches[2] } }
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release

- name: Deploy binaries
run: |
./tools/deploy.bat dynamic pack
246 changes: 246 additions & 0 deletions .github/workflows/_reusable-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
# ---------------------------------------------------------------------------
# Reusable workflow: Build & Test MNE-CPP
# Called by main.yml, staging.yml, and pull-request.yml
# ---------------------------------------------------------------------------
name: Reusable Tests

on:
workflow_call:
inputs:
with_code_coverage:
description: "Enable gcov code coverage instrumentation (Linux only)"
required: false
default: false
type: boolean
upload_codecov:
description: "Upload coverage to Codecov (requires CODECOV_TOKEN secret)"
required: false
default: false
type: boolean
coverage_threshold:
description: "Minimum coverage % — workflow fails if below this (0 = disabled)"
required: false
default: 0
type: number
fail_on_coverage_regression:
description: "Fail if coverage drops compared to previous baseline"
required: false
default: false
type: boolean
secrets:
CODECOV_TOKEN:
required: false

jobs:
Tests:
runs-on: ${{ matrix.os }}

strategy:
fail-fast: false
max-parallel: 3
matrix:
os: [ubuntu-24.04, macos-26, windows-2025]

steps:
- name: Clone repository
uses: actions/checkout@v4

- name: Clone mne-cpp test data
run: git clone https://github.com/mne-tools/mne-cpp-test-data.git resources/data/mne-cpp-test-data

- name: Install Python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.10'
architecture: ${{ matrix.os == 'macos-26' && 'arm64' || 'x64' }}

- name: Install Qt (Linux|MacOS)
if: matrix.os == 'ubuntu-24.04' || matrix.os == 'macos-26'
uses: jurplel/install-qt-action@v3
with:
version: 6.10.2
modules: qtshadertools

- name: Install Qt (Windows)
if: matrix.os == 'windows-2025'
uses: jurplel/install-qt-action@v3
with:
version: 6.10.2
arch: win64_msvc2022_64
modules: qtshadertools

- name: Install MNE-Python
run: |
pip install --upgrade pip
pip install mne numpy scipy

- name: Cache MNE sample data
id: cache-mne-data
uses: actions/cache@v4
with:
path: ~/mne_data/MNE-sample-data
key: mne-sample-data-v1

- name: Download MNE sample data
if: steps.cache-mne-data.outputs.cache-hit != 'true'
run: |
python -c "import mne; mne.datasets.sample.data_path()"

# ── Build ──────────────────────────────────────────────────────────────
- name: Configure and compile MNE-CPP (Linux)
if: matrix.os == 'ubuntu-24.04'
run: |
COV_FLAG=""
if [ "${{ inputs.with_code_coverage }}" = "true" ]; then
COV_FLAG="-DWITH_CODE_COV=ON"
fi
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON ${COV_FLAG}
cmake --build build

- name: Configure and compile MNE-CPP (MacOS)
if: matrix.os == 'macos-26'
run: |
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON
cmake --build build

- name: Configure and compile MNE-CPP (Windows)
if: matrix.os == 'windows-2025'
run: |
cmd.exe /c "call `"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat`" && set > %temp%\vcvars.txt"
Get-Content "$env:temp\vcvars.txt" | Foreach-Object { if ($_ -match "^(.*?)=(.*)$") { Set-Content "env:\$($matches[1])" $matches[2] } }
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON
cmake --build build --config Release

# ── Run tests ──────────────────────────────────────────────────────────
- name: Run tests (Linux)
if: matrix.os == 'ubuntu-24.04'
env:
QTEST_FUNCTION_TIMEOUT: 900000
QT_QPA_PLATFORM: offscreen
MNE_DATA: ~/mne_data
run: |
if [ "${{ inputs.with_code_coverage }}" = "true" ]; then
./tools/test_all.bat verbose withCoverage
else
./tools/test_all.bat verbose
fi

- name: Run tests (MacOS)
if: matrix.os == 'macos-26'
env:
QTEST_FUNCTION_TIMEOUT: 900000
QT_QPA_PLATFORM: offscreen
MNE_DATA: ~/mne_data
run: |
./tools/test_all.bat Release verbose

- name: Run tests (Windows)
if: matrix.os == 'windows-2025'
env:
QTEST_FUNCTION_TIMEOUT: 900000
MNE_DATA: ~/mne_data
run: |
./tools/test_all.bat Release verbose

# ── Coverage: Codecov upload (main branch) ─────────────────────────────
- name: Upload coverage to Codecov
if: matrix.os == 'ubuntu-24.04' && inputs.upload_codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}

# ── Coverage: threshold & regression check (staging branch) ────────────
- name: Install lcov
if: matrix.os == 'ubuntu-24.04' && inputs.with_code_coverage && !inputs.upload_codecov
run: sudo apt-get install -y -qq lcov

- name: Generate lcov report
id: lcov
if: matrix.os == 'ubuntu-24.04' && inputs.with_code_coverage && !inputs.upload_codecov
run: |
lcov --capture --directory build \
--output-file coverage.info \
--ignore-errors mismatch 2>/dev/null || true
# Remove external/test code from the report
lcov --remove coverage.info \
'*/src/external/*' '*/src/examples/*' '*/src/testframes/*' \
'/usr/*' '*/Qt/*' \
--output-file coverage_filtered.info \
--ignore-errors unused 2>/dev/null || true
# Extract total line coverage percentage
COVERAGE=$(lcov --summary coverage_filtered.info 2>&1 \
| grep -oP 'lines\.*:\s*\K[0-9]+(\.[0-9]+)?' || echo "0")
echo "Total line coverage: ${COVERAGE}%"
echo "coverage=${COVERAGE}" >> "$GITHUB_OUTPUT"

- name: Restore previous coverage baseline
id: prev-coverage
if: matrix.os == 'ubuntu-24.04' && inputs.fail_on_coverage_regression
uses: actions/cache/restore@v4
with:
path: .coverage-baseline
key: coverage-baseline-${{ github.ref_name }}

- name: Check coverage threshold & regression
if: matrix.os == 'ubuntu-24.04' && inputs.with_code_coverage && !inputs.upload_codecov
env:
CURRENT: ${{ steps.lcov.outputs.coverage }}
THRESHOLD: ${{ inputs.coverage_threshold }}
CHECK_REGRESSION: ${{ inputs.fail_on_coverage_regression }}
run: |
echo "═══════════════════════════════════════════════════"
echo " Coverage Report"
echo "═══════════════════════════════════════════════════"
echo " Current coverage : ${CURRENT}%"
echo " Minimum threshold: ${THRESHOLD}%"

FAILED=false

# --- Threshold check ---
if [ "$THRESHOLD" != "0" ]; then
if (( $(echo "${CURRENT} < ${THRESHOLD}" | bc -l) )); then
echo " ❌ FAIL: Coverage ${CURRENT}% is below threshold ${THRESHOLD}%"
FAILED=true
else
echo " ✅ PASS: Coverage is above threshold"
fi
fi

# --- Regression check ---
if [ "$CHECK_REGRESSION" = "true" ] && [ -f .coverage-baseline ]; then
PREVIOUS=$(cat .coverage-baseline)
echo " Previous coverage: ${PREVIOUS}%"
if (( $(echo "${CURRENT} < ${PREVIOUS}" | bc -l) )); then
DELTA=$(echo "${PREVIOUS} - ${CURRENT}" | bc -l)
echo " ❌ FAIL: Coverage dropped by ${DELTA}% (${PREVIOUS}% → ${CURRENT}%)"
FAILED=true
else
echo " ✅ PASS: Coverage did not regress"
fi
fi

echo "═══════════════════════════════════════════════════"

# Update baseline for next run
echo "${CURRENT}" > .coverage-baseline

if [ "$FAILED" = "true" ]; then
exit 1
fi

- name: Save coverage baseline
if: matrix.os == 'ubuntu-24.04' && inputs.fail_on_coverage_regression && always()
uses: actions/cache/save@v4
with:
path: .coverage-baseline
key: coverage-baseline-${{ github.ref_name }}-${{ github.run_id }}

- name: Upload coverage report artifact
if: matrix.os == 'ubuntu-24.04' && inputs.with_code_coverage
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: |
coverage.info
coverage_filtered.info
retention-days: 30
5 changes: 2 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ name: "CodeQL"

on:
push:
branches: [ "main", "v2.0-dev" ]
branches: [ "main", "staging" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
branches: [ "main", "staging" ]
schedule:
- cron: '28 15 * * 1'

Expand Down
32 changes: 0 additions & 32 deletions .github/workflows/docutest.yml

This file was deleted.

Loading
Loading