From 70db2b891d66512773a31edd13daaade961ed441 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 28 Aug 2026 00:27:25 +0200 Subject: [PATCH 1/3] Test new minimal commands upload site --- .github/workflows/tests.yml | 10 ++-- azure-pipelines.yml | 79 ++++++++++++++++++++++++++++++-- tools/github_actions_env_vars.sh | 2 +- tools/github_actions_test.sh | 3 +- 4 files changed, 83 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b598df23035..685e363cbf6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -104,8 +104,11 @@ jobs: python-version: ${{ matrix.python }} if: startswith(matrix.kind, 'pip') || matrix.kind == 'minimal' id: setup-python - # Workaround macOS path behavior with login shells (which puts system Python first) - - run: echo "export PATH=\"$(dirname ${{ steps.setup-python.outputs.python-path }}):$PATH\"" | tee -a ~/.bash_profile # zizmor: ignore[template-injection] + # Workaround macOS path behavior with login shells (which puts system Python first). + # \$PATH must stay escaped so that it is expanded when each login shell sources + # ~/.bash_profile: writing the expanded value would freeze PATH as of this step and + # silently discard anything a later step adds to $GITHUB_PATH. + - run: echo "export PATH=\"$(dirname ${{ steps.setup-python.outputs.python-path }}):\$PATH\"" | tee -a ~/.bash_profile # zizmor: ignore[template-injection] if: startswith(matrix.kind, 'pip') && startswith(matrix.os, 'macos') # Python (if conda) - uses: mamba-org/setup-micromamba@v3 @@ -140,7 +143,8 @@ jobs: # platforms that have no binaries (macOS arm64, Windows, Linux arm64), so it # only needs to be gated on the kinds that deliberately do without them. - name: Setup minimal commands - uses: mne-tools/mne-tools/actions/setup-minimal-commands@main + # WIP: revert to mne-tools/mne-tools/actions/setup-minimal-commands@main + uses: larsoner/mne-tools/actions/setup-minimal-commands@fix-dyld if: matrix.kind != 'minimal' && matrix.kind != 'old' && matrix.kind != 'pip-ft' - run: bash ./tools/github_actions_infos.sh - name: Check Qt import diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2e365dd744f..db6c3eb288e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -50,13 +50,14 @@ stages: steps: - bash: | set -xeo pipefail - git clone --depth 1 https://github.com/mne-tools/mne-tools.git "$MNE_TOOLS_DIR" + # WIP: revert to --depth 1 https://github.com/mne-tools/mne-tools.git + git clone --depth 1 -b fix-dyld https://github.com/larsoner/mne-tools.git "$MNE_TOOLS_DIR" displayName: Clone mne-tools - bash: $(MNE_TOOLS_DIR)/tools/setup_xvfb.sh displayName: Setup up Xvfb - task: Cache@2 inputs: - key: minimal-cmds-1 + key: '"minimal-cmds" | "$(Agent.OS)" | $(MNE_TOOLS_DIR)/tools/get_minimal_commands.sh' path: /home/vsts/minimal_cmds displayName: Cache minimal commands - bash: $(MNE_TOOLS_DIR)/tools/get_minimal_commands.sh @@ -108,7 +109,8 @@ stages: steps: - bash: | set -xeo pipefail - git clone --depth 1 https://github.com/mne-tools/mne-tools.git "$MNE_TOOLS_DIR" + # WIP: revert to --depth 1 https://github.com/mne-tools/mne-tools.git + git clone --depth 1 -b fix-dyld https://github.com/larsoner/mne-tools.git "$MNE_TOOLS_DIR" displayName: Clone mne-tools - bash: $(MNE_TOOLS_DIR)/tools/setup_xvfb.sh displayName: Setup up Xvfb @@ -166,6 +168,7 @@ stages: pool: vmImage: 'windows-latest' variables: + MNE_TOOLS_DIR: '$(Pipeline.Workspace)/mne-tools' MNE_LOGGING_LEVEL: 'warning' MNE_FORCE_SERIAL: 'true' PYTEST_XDIST_N: '2' # Microsoft-hosted agents have 2 cores @@ -181,17 +184,83 @@ stages: 3.14 pip: TEST_MODE: 'pip' PYTHON_VERSION: '3.14' - MNE_TEST_ALLOW_SKIP: "^.*(Requires (spm|brainstorm) dataset|Requires MNE-C|Requires FreeSurfer|CUDA not|on Windows|MNE_FORCE_SERIAL|PySide6 causes segfaults|SCIPY_ARRAY_API).*$" + MNE_TEST_ALLOW_SKIP: "^.*(Requires (spm|brainstorm) dataset|Requires FreeSurfer|CUDA not|on Windows|MNE_FORCE_SERIAL|PySide6 causes segfaults|SCIPY_ARRAY_API).*$" 3.14 pip pre: TEST_MODE: 'pip-pre' PYTHON_VERSION: '3.14' - MNE_TEST_ALLOW_SKIP: "^.*(Requires (spm|brainstorm) dataset|Requires MNE-C|Requires FreeSurfer|CUDA not|Numba not|on Windows|MNE_FORCE_SERIAL|PySide6 causes segfaults|SCIPY_ARRAY_API).*$" + MNE_TEST_ALLOW_SKIP: "^.*(Requires (spm|brainstorm) dataset|Requires FreeSurfer|CUDA not|Numba not|on Windows|MNE_FORCE_SERIAL|PySide6 causes segfaults|SCIPY_ARRAY_API).*$" steps: - task: UsePythonVersion@0 inputs: versionSpec: $(PYTHON_VERSION) architecture: 'x64' addToPath: true + - bash: | + set -xeo pipefail + # WIP: revert to --depth 1 https://github.com/mne-tools/mne-tools.git + git clone --depth 1 -b fix-dyld https://github.com/larsoner/mne-tools.git "$MNE_TOOLS_DIR" + displayName: Clone mne-tools + - task: Cache@2 + inputs: + key: '"minimal-cmds" | "$(Agent.OS)" | $(MNE_TOOLS_DIR)/tools/get_minimal_commands.sh' + path: C:\Users\VssAdministrator\minimal_cmds + displayName: Cache minimal commands + # MNE_TOOLS_DIR is a Windows path and these steps run under Git Bash, hence + # cygpath; backticks rather than $(...) so Azure does not read it as a macro + - bash: | + set -eo pipefail + MNE_TOOLS_UNIX=`cygpath -u "$MNE_TOOLS_DIR"` + bash "$MNE_TOOLS_UNIX/tools/get_minimal_commands.sh" + displayName: Get minimal commands + # WIP diagnostics for gh-14230: mne_do_forward_solution fails to launch on + # Windows (WinError 2) while other MNE-C commands work. It is the only one + # invoked that ships both an .exe and an extensionless /bin/sh script, so + # this pins down whether that shadows the .exe. Never fails the job. + - script: | + echo ===== environment ===== + echo MNE_ROOT=%MNE_ROOT% + echo FREESURFER_HOME=%FREESURFER_HOME% + echo PATHEXT=%PATHEXT% + echo PATH=%PATH% + echo ===== where (bare name, as CreateProcess resolves it) ===== + where mne_do_forward_solution + where mne_do_forward_solution.exe + where mne_setup_source_space + where mne_volume_source_space + where mne_process_raw + echo ===== bin contents ===== + dir /b "%MNE_ROOT%\bin" + echo ===== direct launch of the .exe (rules out a missing DLL) ===== + "%MNE_ROOT%\bin\mne_do_forward_solution.exe" --version + echo ===== exit code %ERRORLEVEL% ===== + exit /b 0 + displayName: 'Diagnostics: MNE-C lookup (cmd)' + continueOnError: true + # Same question from Python, which is what actually fails: bare name vs + # explicit .exe vs a command that works, reporting the winerror for each. + - bash: | + python - <<'EOF' + import shutil, subprocess + for name in ( + "mne_do_forward_solution", + "mne_do_forward_solution.exe", + "mne_setup_source_space", + "mne_volume_source_space", + ): + print(f"{name}:") + print(f" shutil.which -> {shutil.which(name)!r}") + try: + p = subprocess.run( + [name, "--version"], capture_output=True, text=True, timeout=60 + ) + print(f" launched rc={p.returncode}") + print(f" stdout={p.stdout.strip()[:200]!r}") + print(f" stderr={p.stderr.strip()[:200]!r}") + except OSError as exc: + print(f" OSError winerror={getattr(exc, 'winerror', None)} {exc}") + EOF + displayName: 'Diagnostics: MNE-C lookup (python)' + continueOnError: true - bash: | set -xeo pipefail git clone --depth 1 https://github.com/pyvista/setup-headless-display-action.git diff --git a/tools/github_actions_env_vars.sh b/tools/github_actions_env_vars.sh index a687363cd6f..f02ec36d90c 100755 --- a/tools/github_actions_env_vars.sh +++ b/tools/github_actions_env_vars.sh @@ -38,7 +38,7 @@ if [[ "$MNE_CI_KIND" == "pip"* ]]; then echo "MNE_QT_BACKEND=PySide6" | tee -a $GITHUB_ENV elif [[ "$MNE_CI_KIND" == "pip" ]]; then if [[ "${RUNNER_OS}" == "macOS" ]]; then - echo "MNE_TEST_ALLOW_SKIP=.*(Requires (spm|brainstorm|misc) dataset|SCIPY_ARRAY_API|FreeSurfer|MNE-C|CUDA not|macOS|PySide6 causes segfaults).*" | tee -a $GITHUB_ENV + echo "MNE_TEST_ALLOW_SKIP=.*(Requires (spm|brainstorm|misc) dataset|SCIPY_ARRAY_API|FreeSurfer|CUDA not|macOS|PySide6 causes segfaults).*" | tee -a $GITHUB_ENV else echo "MNE_TEST_ALLOW_SKIP=.*(Requires (spm|brainstorm|misc) dataset|SCIPY_ARRAY_API|CUDA not|PySide6 causes segfaults).*" | tee -a $GITHUB_ENV fi diff --git a/tools/github_actions_test.sh b/tools/github_actions_test.sh index 94fee65b89e..a906960b538 100755 --- a/tools/github_actions_test.sh +++ b/tools/github_actions_test.sh @@ -16,8 +16,7 @@ elif [[ "${CI_OS_NAME}" == "macos"* ]]; then if [[ "$(uname -m)" == "arm64" ]]; then CONDITION="not (ultraslowtest or pgtest)" else - # TODO: the MNE-C tools are on PATH during setup but not once the tests run (gh-14230) - CONDITION="not (slowtest or pgtest or mne_c)" + CONDITION="not (slowtest or pgtest)" fi elif [[ "${CI_OS_NAME}" == "windows"* ]]; then CONDITION="not (slowtest or pgtest)" From d20202ff773d709d26d273af03e5b343ea00804c Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 28 Aug 2026 13:55:11 +0200 Subject: [PATCH 2/3] WIP: set HOME on Azure Windows for MNE-C --- azure-pipelines.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index db6c3eb288e..c4cb9e73c95 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -285,6 +285,11 @@ stages: displayName: Cache testing data - bash: ./tools/github_actions_download.sh displayName: Download testing data + # Windows does not define HOME, and MNE-C dereferences it unconditionally: + # mne_forward_solution crashes with 0xC0000005 without it. Set after the + # steps above so that Git Bash keeps deriving its own MSYS-style HOME. + - script: 'echo ##vso[task.setvariable variable=HOME]%USERPROFILE%' + displayName: Set HOME for MNE-C - script: pytest -m "not (slowtest or pgtest)" -n $(PYTEST_XDIST_N) --dist loadscope --timeout=120 --timeout-method=thread -o faulthandler_timeout=110 --tb=short --cov=mne --cov-report=xml -vv mne displayName: Run tests - bash: bash <(curl -s https://codecov.io/bash) From 4a180f250b59fb3566f6a0ff962e997a71259dfb Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 28 Aug 2026 14:50:40 +0200 Subject: [PATCH 3/3] Point CI back at mne-tools main and drop WIP diagnostics --- .github/workflows/tests.yml | 3 +- azure-pipelines.yml | 58 ++----------------------------------- 2 files changed, 4 insertions(+), 57 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 685e363cbf6..81b481fb6d7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -143,8 +143,7 @@ jobs: # platforms that have no binaries (macOS arm64, Windows, Linux arm64), so it # only needs to be gated on the kinds that deliberately do without them. - name: Setup minimal commands - # WIP: revert to mne-tools/mne-tools/actions/setup-minimal-commands@main - uses: larsoner/mne-tools/actions/setup-minimal-commands@fix-dyld + uses: mne-tools/mne-tools/actions/setup-minimal-commands@main if: matrix.kind != 'minimal' && matrix.kind != 'old' && matrix.kind != 'pip-ft' - run: bash ./tools/github_actions_infos.sh - name: Check Qt import diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c4cb9e73c95..7b4fd72b4c1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -50,8 +50,7 @@ stages: steps: - bash: | set -xeo pipefail - # WIP: revert to --depth 1 https://github.com/mne-tools/mne-tools.git - git clone --depth 1 -b fix-dyld https://github.com/larsoner/mne-tools.git "$MNE_TOOLS_DIR" + git clone --depth 1 https://github.com/mne-tools/mne-tools.git "$MNE_TOOLS_DIR" displayName: Clone mne-tools - bash: $(MNE_TOOLS_DIR)/tools/setup_xvfb.sh displayName: Setup up Xvfb @@ -109,8 +108,7 @@ stages: steps: - bash: | set -xeo pipefail - # WIP: revert to --depth 1 https://github.com/mne-tools/mne-tools.git - git clone --depth 1 -b fix-dyld https://github.com/larsoner/mne-tools.git "$MNE_TOOLS_DIR" + git clone --depth 1 https://github.com/mne-tools/mne-tools.git "$MNE_TOOLS_DIR" displayName: Clone mne-tools - bash: $(MNE_TOOLS_DIR)/tools/setup_xvfb.sh displayName: Setup up Xvfb @@ -197,8 +195,7 @@ stages: addToPath: true - bash: | set -xeo pipefail - # WIP: revert to --depth 1 https://github.com/mne-tools/mne-tools.git - git clone --depth 1 -b fix-dyld https://github.com/larsoner/mne-tools.git "$MNE_TOOLS_DIR" + git clone --depth 1 https://github.com/mne-tools/mne-tools.git "$MNE_TOOLS_DIR" displayName: Clone mne-tools - task: Cache@2 inputs: @@ -212,55 +209,6 @@ stages: MNE_TOOLS_UNIX=`cygpath -u "$MNE_TOOLS_DIR"` bash "$MNE_TOOLS_UNIX/tools/get_minimal_commands.sh" displayName: Get minimal commands - # WIP diagnostics for gh-14230: mne_do_forward_solution fails to launch on - # Windows (WinError 2) while other MNE-C commands work. It is the only one - # invoked that ships both an .exe and an extensionless /bin/sh script, so - # this pins down whether that shadows the .exe. Never fails the job. - - script: | - echo ===== environment ===== - echo MNE_ROOT=%MNE_ROOT% - echo FREESURFER_HOME=%FREESURFER_HOME% - echo PATHEXT=%PATHEXT% - echo PATH=%PATH% - echo ===== where (bare name, as CreateProcess resolves it) ===== - where mne_do_forward_solution - where mne_do_forward_solution.exe - where mne_setup_source_space - where mne_volume_source_space - where mne_process_raw - echo ===== bin contents ===== - dir /b "%MNE_ROOT%\bin" - echo ===== direct launch of the .exe (rules out a missing DLL) ===== - "%MNE_ROOT%\bin\mne_do_forward_solution.exe" --version - echo ===== exit code %ERRORLEVEL% ===== - exit /b 0 - displayName: 'Diagnostics: MNE-C lookup (cmd)' - continueOnError: true - # Same question from Python, which is what actually fails: bare name vs - # explicit .exe vs a command that works, reporting the winerror for each. - - bash: | - python - <<'EOF' - import shutil, subprocess - for name in ( - "mne_do_forward_solution", - "mne_do_forward_solution.exe", - "mne_setup_source_space", - "mne_volume_source_space", - ): - print(f"{name}:") - print(f" shutil.which -> {shutil.which(name)!r}") - try: - p = subprocess.run( - [name, "--version"], capture_output=True, text=True, timeout=60 - ) - print(f" launched rc={p.returncode}") - print(f" stdout={p.stdout.strip()[:200]!r}") - print(f" stderr={p.stderr.strip()[:200]!r}") - except OSError as exc: - print(f" OSError winerror={getattr(exc, 'winerror', None)} {exc}") - EOF - displayName: 'Diagnostics: MNE-C lookup (python)' - continueOnError: true - bash: | set -xeo pipefail git clone --depth 1 https://github.com/pyvista/setup-headless-display-action.git