Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2e9305f
doc: Rename Main workflow badge to Release
chdinh Mar 9, 2026
7dbe757
fix: Resolve 7 test failures across macOS and Ubuntu
chdinh Mar 9, 2026
dafdd78
Fix CI test failures across all platforms
chdinh Mar 10, 2026
6e4fb63
perf: Cache inverse operator in test_inverse_data to avoid timeout
chdinh Mar 10, 2026
6dc5abb
fix: Skip inverse operator write/read roundtrip that exceeds CI timeout
chdinh Mar 10, 2026
3de3614
fix: Sphere simplex test convergence and inverse roundtrip prep
chdinh Mar 10, 2026
28046b3
fix: Skip inverse operator write/read roundtrip that exceeds CI timeout
chdinh Mar 10, 2026
3de2823
Fix FIFF double I/O, make_dir EOF, write_cov packing, and source_cov …
chdinh Mar 10, 2026
6605d58
fix: Initialize all members in MNECTFCompDataSet copy constructor
chdinh Mar 10, 2026
b919df5
fix: Coverage build uses -O0 and simplify source_cov kind fix
chdinh Mar 10, 2026
5a7aa6b
Fix 6 failing Windows CI tests
chdinh Mar 10, 2026
3786c60
refactored inverse lib
chdinh Mar 11, 2026
3a95ffb
Fix 3 remaining test failures and enable 13 tests on macOS/Linux
chdinh Mar 11, 2026
16db72d
Fix three failing macOS tests
chdinh Mar 11, 2026
8cdf519
fix coverage computation
chdinh Mar 12, 2026
75432ae
switch to gh cli for uploading binaries results
chdinh Mar 12, 2026
7f1ae41
refactor and cleanup fwd library
chdinh Mar 12, 2026
f778b1f
move mne forward solution from mne to fwd library
chdinh Mar 13, 2026
d2f2a55
Fix Tests
chdinh Mar 13, 2026
18e46fb
fix inline declaration
chdinh Mar 14, 2026
231abd3
fix windows tests
chdinh Mar 14, 2026
537b490
update transformation file path in generate_fieldmap_reference.py and…
chdinh Mar 14, 2026
a46cf7d
FIX: prevent crash in make_inverse_operator with non-surface-oriented…
chdinh Mar 14, 2026
67bf50d
ENH: optimize build and coverage settings in CI workflows
chdinh Mar 14, 2026
0b56650
REF: simplify coverage analysis in CI workflow by removing CheckCover…
chdinh Mar 14, 2026
d4f03d2
REF: refine coverage reports to focus on MNE-CPP libraries and adjust…
chdinh Mar 14, 2026
984610e
FIX: adjust coverage regression check to allow up to 2% drop in coverage
chdinh Mar 14, 2026
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
60 changes: 37 additions & 23 deletions .github/workflows/_reusable-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,27 +95,27 @@ jobs:
COV_FLAG="-DWITH_CODE_COV=ON"
fi
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON ${COV_FLAG}
cmake --build build
cmake --build build -j $(nproc)

- 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
cmake --build build -j $(sysctl -n hw.logicalcpu)

- 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
cmake --build build --config Release --parallel

# ── Run tests ──────────────────────────────────────────────────────────
- name: Run tests (Linux)
if: matrix.os == 'ubuntu-24.04'
env:
QTEST_FUNCTION_TIMEOUT: 900000
QTEST_FUNCTION_TIMEOUT: ${{ inputs.with_code_coverage && 1800000 || 900000 }}
QT_QPA_PLATFORM: offscreen
MNE_DATA: ~/mne_data
run: |
Expand Down Expand Up @@ -150,26 +150,33 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}

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

- name: Generate lcov report
- name: Generate coverage report (fastcov)
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 "=== Coverage diagnostics ==="
echo "gcno files: $(find build -name '*.gcno' 2>/dev/null | wc -l)"
echo "gcda files: $(find build -name '*.gcda' 2>/dev/null | wc -l)"
# Collect coverage data with fastcov (10-100x faster than lcov)
fastcov -d build -o coverage.json
# Full report in lcov format
fastcov -C coverage.json --lcov -o coverage.info
# Filtered report: only MNE-CPP libraries (no apps, tests, examples, external, Qt, Eigen)
fastcov -C coverage.json --lcov -o coverage_filtered.info \
--include src/libraries/ \
--exclude /usr Qt eigen
# Extract total line coverage percentage from lcov format
TOTAL_LINES=$(grep -c "^DA:" coverage_filtered.info 2>/dev/null || echo "0")
HIT_LINES=$(grep "^DA:" coverage_filtered.info | awk -F, '$2 > 0' | wc -l 2>/dev/null || echo "0")
if [ "$TOTAL_LINES" -gt 0 ]; then
COVERAGE=$(awk "BEGIN {printf \"%.2f\", $HIT_LINES * 100.0 / $TOTAL_LINES}")
else
COVERAGE="0"
fi
echo "Total line coverage: ${COVERAGE}%"
echo "coverage=${COVERAGE}" >> "$GITHUB_OUTPUT"

Expand Down Expand Up @@ -206,16 +213,23 @@ jobs:
fi
fi

# --- Regression check ---
# --- Regression check (allow up to 2% drop) ---
if [ "$CHECK_REGRESSION" = "true" ] && [ -f .coverage-baseline ]; then
PREVIOUS=$(cat .coverage-baseline)
echo " Previous coverage: ${PREVIOUS}%"
if (( $(echo "${CURRENT} < ${PREVIOUS}" | bc -l) )); then
ALLOWED_DROP="2.0"
LIMIT=$(echo "${PREVIOUS} - ${ALLOWED_DROP}" | bc -l)
if (( $(echo "${CURRENT} < ${LIMIT}" | bc -l) )); then
DELTA=$(echo "${PREVIOUS} - ${CURRENT}" | bc -l)
echo " ❌ FAIL: Coverage dropped by ${DELTA}% (${PREVIOUS}% → ${CURRENT}%)"
echo " ❌ FAIL: Coverage dropped by ${DELTA}% (${PREVIOUS}% → ${CURRENT}%), exceeds ${ALLOWED_DROP}% tolerance"
FAILED=true
else
echo " ✅ PASS: Coverage did not regress"
if (( $(echo "${CURRENT} < ${PREVIOUS}" | bc -l) )); then
DELTA=$(echo "${PREVIOUS} - ${CURRENT}" | bc -l)
echo " ⚠️ WARN: Coverage dropped by ${DELTA}% (within ${ALLOWED_DROP}% tolerance)"
else
echo " ✅ PASS: Coverage did not regress"
fi
fi
fi

Expand Down
26 changes: 11 additions & 15 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#
# Branch model: Feature → Staging → Main
# ═══════════════════════════════════════════════════════════════════════════
name: Main
name: Release

on:
push:
Expand Down Expand Up @@ -84,13 +84,11 @@ jobs:

- name: Deploy qch to GitHub Release
if: startsWith(github.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: doc/doxygen/qt-creator_doc/mne-cpp.qch
asset_name: mne-cpp-${{ github.ref_name }}-doc-qtcreator.qch
tag: ${{ github.ref }}
overwrite: true
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp doc/doxygen/qt-creator_doc/mne-cpp.qch "mne-cpp-${{ github.ref_name }}-doc-qtcreator.qch"
gh release upload "${{ github.ref_name }}" "mne-cpp-${{ github.ref_name }}-doc-qtcreator.qch" --clobber

- name: Deploy Doxygen to GitHub Pages (production)
env:
Expand Down Expand Up @@ -294,13 +292,11 @@ jobs:
tar cfz mne-cpp-wasm.tar.gz -C out/wasm .

- name: Upload WASM to GitHub Release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: mne-cpp-wasm.tar.gz
asset_name: mne-cpp-${{ github.ref_name }}-wasm.tar.gz
tag: ${{ github.ref }}
overwrite: true
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp mne-cpp-wasm.tar.gz "mne-cpp-${{ github.ref_name }}-wasm.tar.gz"
gh release upload "${{ github.ref_name }}" "mne-cpp-${{ github.ref_name }}-wasm.tar.gz" --clobber

- name: Deploy WASM to mne-cpp/wasm repository
env:
Expand Down
40 changes: 16 additions & 24 deletions .github/workflows/release-installer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -205,14 +205,11 @@ jobs:
ls -lh MNE-CPP-*.run

- name: Upload installer
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: MNE-CPP-*-Linux.run
file_glob: true
asset_name: ${{ steps.tag.outputs.asset_prefix }}-linux-installer-x86_64.run
tag: ${{ steps.tag.outputs.tag }}
overwrite: true
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp MNE-CPP-*-Linux.run "${{ steps.tag.outputs.asset_prefix }}-linux-installer-x86_64.run"
gh release upload "${{ steps.tag.outputs.tag }}" "${{ steps.tag.outputs.asset_prefix }}-linux-installer-x86_64.run" --clobber

# =========================================================================
# macOS Installer (.dmg)
Expand Down Expand Up @@ -540,14 +537,11 @@ jobs:
security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true

- name: Upload installer
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: MNE-CPP-*-Darwin.dmg
file_glob: true
asset_name: ${{ steps.tag.outputs.asset_prefix }}-macos-installer-arm64.dmg
tag: ${{ steps.tag.outputs.tag }}
overwrite: true
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp MNE-CPP-*-Darwin.dmg "${{ steps.tag.outputs.asset_prefix }}-macos-installer-arm64.dmg"
gh release upload "${{ steps.tag.outputs.tag }}" "${{ steps.tag.outputs.asset_prefix }}-macos-installer-arm64.dmg" --clobber

# =========================================================================
# Windows Installer (.exe)
Expand Down Expand Up @@ -734,11 +728,9 @@ jobs:
Get-ChildItem MNE-CPP-*-win64.exe

- name: Upload installer
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: MNE-CPP-*-win64.exe
file_glob: true
asset_name: ${{ steps.tag.outputs.asset_prefix }}-windows-installer-x86_64.exe
tag: ${{ steps.tag.outputs.tag }}
overwrite: true
shell: bash
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp MNE-CPP-*-win64.exe "${{ steps.tag.outputs.asset_prefix }}-windows-installer-x86_64.exe"
gh release upload "${{ steps.tag.outputs.tag }}" "${{ steps.tag.outputs.asset_prefix }}-windows-installer-x86_64.exe" --clobber
24 changes: 10 additions & 14 deletions .github/workflows/release-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,11 @@ jobs:
./tools/deploy.bat dynamic pack
- name: Upload release binaries
if: steps.release-info.outputs.should_upload == 'true'
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: mne-cpp-linux-dynamic-x86_64.tar.gz
asset_name: ${{ steps.release-info.outputs.prefix }}-linux-dynamic-x86_64.tar.gz
tag: ${{ steps.release-info.outputs.tag }}
overwrite: true
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp mne-cpp-linux-dynamic-x86_64.tar.gz "${{ steps.release-info.outputs.prefix }}-linux-dynamic-x86_64.tar.gz"
gh release upload "${{ steps.release-info.outputs.tag }}" "${{ steps.release-info.outputs.prefix }}-linux-dynamic-x86_64.tar.gz" --clobber


LinuxStatic:
Expand Down Expand Up @@ -141,10 +139,8 @@ jobs:
./tools/deploy.bat static pack
- name: Upload release binaries
if: steps.release-info.outputs.should_upload == 'true'
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: mne-cpp-linux-static-x86_64.tar.gz
asset_name: ${{ steps.release-info.outputs.prefix }}-linux-static-x86_64.tar.gz
tag: ${{ steps.release-info.outputs.tag }}
overwrite: true
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp mne-cpp-linux-static-x86_64.tar.gz "${{ steps.release-info.outputs.prefix }}-linux-static-x86_64.tar.gz"
gh release upload "${{ steps.release-info.outputs.tag }}" "${{ steps.release-info.outputs.prefix }}-linux-static-x86_64.tar.gz" --clobber
24 changes: 10 additions & 14 deletions .github/workflows/release-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,11 @@ jobs:
fi
- name: Upload release binaries
if: steps.release-info.outputs.should_upload == 'true'
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: mne-cpp-macos-dynamic-arm64.tar.gz
asset_name: ${{ steps.release-info.outputs.prefix }}-macos-dynamic-arm64.tar.gz
tag: ${{ steps.release-info.outputs.tag }}
overwrite: true
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp mne-cpp-macos-dynamic-arm64.tar.gz "${{ steps.release-info.outputs.prefix }}-macos-dynamic-arm64.tar.gz"
gh release upload "${{ steps.release-info.outputs.tag }}" "${{ steps.release-info.outputs.prefix }}-macos-dynamic-arm64.tar.gz" --clobber


MacOSStatic:
Expand Down Expand Up @@ -159,10 +157,8 @@ jobs:
./tools/deploy.bat static pack
- name: Upload release binaries
if: steps.release-info.outputs.should_upload == 'true'
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: mne-cpp-macos-static-arm64.tar.gz
asset_name: ${{ steps.release-info.outputs.prefix }}-macos-static-arm64.tar.gz
tag: ${{ steps.release-info.outputs.tag }}
overwrite: true
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp mne-cpp-macos-static-arm64.tar.gz "${{ steps.release-info.outputs.prefix }}-macos-static-arm64.tar.gz"
gh release upload "${{ steps.release-info.outputs.tag }}" "${{ steps.release-info.outputs.prefix }}-macos-static-arm64.tar.gz" --clobber
26 changes: 12 additions & 14 deletions .github/workflows/release-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,12 @@ jobs:
}
- name: Upload release binaries
if: steps.release-info.outputs.should_upload == 'true'
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: mne-cpp-windows-dynamic-x86_64.zip
asset_name: ${{ steps.release-info.outputs.prefix }}-windows-dynamic-x86_64.zip
tag: ${{ steps.release-info.outputs.tag }}
overwrite: true
shell: bash
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp mne-cpp-windows-dynamic-x86_64.zip "${{ steps.release-info.outputs.prefix }}-windows-dynamic-x86_64.zip"
gh release upload "${{ steps.release-info.outputs.tag }}" "${{ steps.release-info.outputs.prefix }}-windows-dynamic-x86_64.zip" --clobber


WinStatic:
Expand Down Expand Up @@ -156,10 +155,9 @@ jobs:
./tools/deploy.bat static pack
- name: Upload release binaries
if: steps.release-info.outputs.should_upload == 'true'
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: mne-cpp-windows-static-x86_64.zip
asset_name: ${{ steps.release-info.outputs.prefix }}-windows-static-x86_64.zip
tag: ${{ steps.release-info.outputs.tag }}
overwrite: true
shell: bash
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp mne-cpp-windows-static-x86_64.zip "${{ steps.release-info.outputs.prefix }}-windows-static-x86_64.zip"
gh release upload "${{ steps.release-info.outputs.tag }}" "${{ steps.release-info.outputs.prefix }}-windows-static-x86_64.zip" --clobber
36 changes: 17 additions & 19 deletions .github/workflows/staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ concurrency:
cancel-in-progress: true

env:
# Minimum acceptable line coverage percentage.
# Minimum acceptable line coverage percentage (libraries only).
# The pipeline will FAIL if coverage drops below this.
COVERAGE_THRESHOLD: 50
COVERAGE_THRESHOLD: 35

jobs:

Expand Down Expand Up @@ -100,13 +100,15 @@ jobs:
qt_version: '6.10.2'

# ── Tests with coverage threshold & regression checks ────────────────────
# Add [skip-coverage] to your commit message to skip coverage analysis.
# Coverage is only collected on Ubuntu; macOS/Windows always run without it.
Tests:
uses: ./.github/workflows/_reusable-tests.yml
with:
with_code_coverage: true
with_code_coverage: ${{ !contains(github.event.head_commit.message, '[skip-coverage]') }}
upload_codecov: false
coverage_threshold: 50
fail_on_coverage_regression: true
coverage_threshold: ${{ contains(github.event.head_commit.message, '[skip-coverage]') && 0 || 35 }}
fail_on_coverage_regression: ${{ !contains(github.event.head_commit.message, '[skip-coverage]') }}

# ── Doxygen API documentation (dev) ──────────────────────────────────────
Doxygen:
Expand All @@ -131,13 +133,11 @@ jobs:
doxygen mne-cpp_doxyfile

- name: Deploy qch to dev_build release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: doc/doxygen/qt-creator_doc/mne-cpp.qch
asset_name: mne-cpp-dev-doc-qtcreator.qch
tag: dev_build
overwrite: true
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp doc/doxygen/qt-creator_doc/mne-cpp.qch mne-cpp-dev-doc-qtcreator.qch
gh release upload dev_build mne-cpp-dev-doc-qtcreator.qch --clobber

- name: Deploy Doxygen to GitHub Pages (dev subdirectory)
env:
Expand Down Expand Up @@ -262,10 +262,8 @@ jobs:
tar cfz mne-cpp-wasm.tar.gz -C out/wasm .

- name: Upload WASM to dev_build release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
file: mne-cpp-wasm.tar.gz
asset_name: mne-cpp-dev-wasm.tar.gz
tag: dev_build
overwrite: true
env:
GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
run: |
cp mne-cpp-wasm.tar.gz mne-cpp-dev-wasm.tar.gz
gh release upload dev_build mne-cpp-dev-wasm.tar.gz --clobber
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
</p>
<p align="center">
<a href="https://github.com/mne-tools/mne-cpp/actions/workflows/main.yml" target="_blank">
<img src="https://github.com/mne-tools/mne-cpp/actions/workflows/main.yml/badge.svg?branch=main" alt="Main">
<img src="https://github.com/mne-tools/mne-cpp/actions/workflows/main.yml/badge.svg?branch=main" alt="Release">
</a>
<a href="https://github.com/mne-tools/mne-cpp/actions/workflows/staging.yml" target="_blank">
<img src="https://github.com/mne-tools/mne-cpp/actions/workflows/staging.yml/badge.svg?branch=staging" alt="Staging">
Expand Down
Loading
Loading