Github Actions
MNE-CPP uses GitHub Actions for all continuous integration (CI) work. GitHub Actions operates on repository events — pushes, pull requests, releases, and timers — which trigger workflows defined as YAML files in .github/workflows/. Each workflow contains one or more jobs, and each job runs a series of steps on a fresh virtual machine (runner). You can read more about the terminology in the GitHub Actions documentation.
Workflow Overview
MNE-CPP's CI is organized around the Main – Staging – Feature branch model. The following table summarizes every CI workflow:
| Event type | Workflow Name | Workflow Script | Effect |
|---|---|---|---|
Pull Requests → staging or main | PullRequest | pull-request.yml | Build and tests on all platforms (maximum Qt), plus minimum Qt on Linux (on all platforms for PRs into main), and the WebAssembly build. PR cannot be merged until all jobs pass. |
Pushes to staging, nightly | Staging | staging.yml | On push: tests with code coverage threshold/regression checks on all platforms with minimum and maximum Qt, quality and documentation gates, and the WebAssembly build. On pushes that touch the build or packaging, nightly and on manual dispatch: also dev release binaries and dev website + Doxygen deployment. |
Pushes to main | Main | main.yml | Builds, tests, stable website + Doxygen deployment. |
Tag v* on main | Main (release jobs) | main.yml | Full stable release: platform binaries (Linux, macOS, Windows), installers, WebAssembly, Codecov upload. |
Pushes to the wasm branch | WasmTest | wasmtest.yml | Creates new versions of the WebAssembly capable MNE-CPP applications and makes them accessible via the repository's gh-pages branch. |
| Timer runs out | Coverity | coverity.yml | Triggers every two days to run Coverity static code analysis tools. |
Pushes to the generateqt branch | BuildQtBinaries | buildqtbinaries.yml | Triggers builds of all needed Qt versions and makes them accessible as artifacts via the Github Actions interface. |
Reusable Workflows
Common build and test logic is extracted into reusable workflows to avoid duplication:
| Workflow | Script | Purpose |
|---|---|---|
| Reusable Build | _reusable-build.yml | Cross-platform build parameterized by Qt version |
| Reusable Tests | _reusable-tests.yml | Test execution with configurable code coverage (Codecov upload, threshold check, or plain) |
These are called by the main pipeline workflows using workflow_call.
How the Pull Request Workflow Works
When a contributor opens or updates a pull request targeting staging (or main), pull-request.yml runs:
- The static quality gates.
- Builds and tests with the maximum supported Qt version on Linux, macOS, and Windows, and with the minimum supported Qt version on Linux, via the reusable test workflow (without coverage uploads).
- Builds the documentation to verify it compiles without errors.
- Builds WebAssembly (without deploying it).
The PR cannot be merged until all matrix jobs pass. This ensures that every change is validated on Linux, macOS, and Windows before it reaches the staging branch.
How the Staging Workflow Works
staging.yml is the active development pipeline. Every push to staging runs:
- The static quality gates
- Tests with code coverage on all platforms — enforcing a minimum threshold (50%) and detecting coverage regressions — plus tests against the minimum Qt version on all platforms
- The documentation gate (API docs, screenshots, website build)
- The WebAssembly build (the dev deployment waits for the release run)
GitHub-hosted runners are shared by every repository in the mne-tools organization, so the release part of the pipeline runs only where it can be affected: nightly when staging has changed since the last nightly, on manual dispatch (Actions → Staging → Run workflow), and after a push that changed the build or packaging since the last dev build (a CMakeLists.txt or cmake/ outside the tests, scripts/build/, scripts/ci/, scripts/deploy/, or the staging and release workflows). Such a push starts the release as a separate run, which later pushes do not cancel. Once every test configuration compiles (failing tests do not block it), it:
- Creates or updates the
dev_buildpre-release on GitHub with the latest development binaries - Invokes the platform-specific release workflows (
release-linux.yml,release-macos.yml,release-windows.yml,release-installer.yml) to produce development binaries tagged asdev_build - Deploys the dev version of the documentation website to
/dev/and Doxygen API docs to/dev/
A failing nightly opens a "Nightly staging build failed" issue, which closes automatically when a later nightly passes. CodeQL security analysis, on a full build, runs weekly, on main and on manual dispatch (codeql.yml).
How the Main / Release Workflow Works
main.yml handles two scenarios:
Pushes to main (no tag)
On every push to main, the workflow:
- Builds with minimum and maximum Qt versions
- Runs tests (with Codecov upload)
- Deploys the stable website and Doxygen API documentation (preserving the dev subdirectory)
Tagged releases (v*)
When a version tag is pushed (e.g., v2.0.0), the workflow additionally:
- Creates a GitHub Release from the tag
- Invokes platform-specific release workflows to build and upload binaries for Linux, macOS, and Windows
- Builds and uploads platform installers (
.run,.dmg,.exe) - Builds and deploys WebAssembly applications
- Uploads the Doxygen
.qchfile as a release asset
Each platform job follows the same high-level pattern: install Qt, configure CMake, build, deploy (bundle Qt libraries), package into an archive, and upload to the GitHub release.
Workflow File Structure
All workflow YAML files live in .github/workflows/ and follow a common structure:
name: WorkflowName
on:
push:
branches: [staging]
pull_request:
branches: [staging, main]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-24.04, macos-26, windows-2025]
steps:
- uses: actions/checkout@v4
- name: Install Qt
# ...
- name: Configure
run: cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build --parallel
- name: Test
run: cd build && ctest --output-on-failure
Key elements include the trigger (on:), the matrix strategy for cross-platform builds, and the steps that install dependencies, compile, test, and optionally deploy.
Secrets and Environment Variables
Some workflows require GitHub repository secrets (configured in Settings → Secrets and variables → Actions):
| Secret | Used by | Purpose |
|---|---|---|
GITHUB_TOKEN | All release workflows | Automatically provided; used to upload release assets |
GH_PAT | Release workflows | Personal access token for cross-repo operations (optional fallback) |
GIT_CREDENTIALS | Website + Doxygen deploy | Authentication for pushing to gh-pages branches |
CODECOV_TOKEN | Main workflow | Token for uploading coverage to Codecov |
COVERITY_TOKEN | coverity.yml | Authentication token for Coverity Scan |
Adding or Modifying a Workflow
To add a new workflow:
- Create a new
.ymlfile in.github/workflows/. - Define the trigger event (
on:) — push, pull_request, schedule, or workflow_dispatch. - Define one or more jobs with the appropriate runner (
runs-on:) and steps. - Test the workflow by pushing to a feature branch and opening a PR against
staging.
To modify an existing workflow, edit the corresponding .yml file and push the change. If the modification affects the PR workflow, open a PR to see the results immediately.