-
Notifications
You must be signed in to change notification settings - Fork 1.4k
RFC: Add dev container configuration #12306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
edd8c9f
54b159b
7d5c6a2
533db90
88fb767
c2ef194
4e7d48f
c9fb83a
1d475dc
7732c1b
c2da3f2
42714c8
f758f91
c2ce81f
865484b
5b90440
7f64bae
78628b6
5727408
ae7afc8
831fcfa
b736ab4
a911699
436fa77
f74476c
7fb00e4
35d0ff2
77d33b8
b963171
90859fe
09bba5c
92ab0e6
e77ebb6
7f23b05
27f942d
74e3a1f
ed0738a
91ae09e
053f349
6c956e7
010f77e
c8c190f
be4b239
045dbeb
f661923
34e508c
17bfca4
a92aebf
b945edc
0d90a77
2e96695
0b7bd34
d317c0f
9043f31
0bee949
1c75ac8
d8f584d
69e2c78
87e307f
9c7fd8c
4727fd8
14ddd4c
405b9a6
98aa7ed
2ac5c68
3578193
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# ๐ Welcome to the MNE-Python Dev Container! | ||
|
||
It's so great to see you! ๐คฉ | ||
|
||
This appears to be the first time you're starting up the container, | ||
or you've restarted it after uninstalling MNE-Python. | ||
|
||
In any case, **we're currently running the MNE-Python installation | ||
procedure.** You can view progress by opening the terminal window | ||
with the the spinning icon (or exclamation mark, in some cases!) in the bottom-right of | ||
your screen! | ||
|
||
Once installation is finished, that terminal window will close and your browser will | ||
open to connect you to a VNC desktop. This is where interactive plots will appear. | ||
|
||
Enjoy, have a great day, and: **Happy hacking!** ๐๐๐ | ||
|
||
### Some technical background | ||
|
||
The Dev Container is based on Debian 12 ("bookworm") GNU/Linux. | ||
|
||
Python is installed in a `conda` environment (named `base`), together with a few | ||
dependencies that are currently not available from PyPI for all platforms: | ||
|
||
- `h5py` | ||
- `psutil` | ||
- `pyside6` | ||
- `vtk` | ||
|
||
Everything else is pulled and installed from PyPI through `uv`. Specifically, the command | ||
that is run to install MNE-Python is: | ||
|
||
```shell | ||
pipx run uv pip install -e ".[full-pyside6,dev,test_extra] | ||
``` | ||
It is totally acceptable and safe to install or update dependencies via `pip` if you | ||
wish. | ||
|
||
All `git` pre-commit hooks are automatically installed. | ||
|
||
The noVNC server (for connecting to the VNC desktop via a browser) is exposed on TCP | ||
port 6080. | ||
|
||
The host's `mne_data` directory is mounted at `~/mne_data` inside the container. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
{ | ||
"name": "mne-dev", | ||
// More info on the Debian base image we're using here: | ||
// https://github.com/devcontainers/images/tree/main/src/base-debian | ||
// All features listed in the repository's devcontainer configuration are actually | ||
// built into the image. | ||
"image": "mcr.microsoft.com/devcontainers/base:debian-12", | ||
"containerEnv": { | ||
"PYTHONNOUSERSITE": "true", // Make Python ignore the user's site-packages folder if it exists | ||
"XDG_RUNTIME_DIR": "/home/mne-user/.cache/xdgr", // For VNC | ||
"RUNNING_IN_DEV_CONTAINER": "true" | ||
}, | ||
"features": { | ||
// See https://containers.dev/features | ||
// User & shell setup | ||
"ghcr.io/devcontainers/features/common-utils:2": { | ||
"username": "mne-user", | ||
"configureZshAsDefaultShell": true | ||
}, | ||
// Desktop and VNC access | ||
"ghcr.io/devcontainers/features/desktop-lite:1": { | ||
"password": "noPassword" | ||
}, | ||
// Git | ||
"ghcr.io/devcontainers/features/git:1": { | ||
"version": "os-provided", | ||
"ppa": "true" | ||
}, | ||
// APT packages | ||
// We need those for 3D rendering and building the docs. | ||
"ghcr.io/rocker-org/devcontainer-features/apt-packages:1": { | ||
"packages": "mesa-utils,libegl1,^libxcb.*-dev,libx11-xcb-dev,libglu1-mesa-dev,libxrender-dev,libxi-dev,libxkbcommon-dev,libxkbcommon-x11-dev,optipng,graphviz" | ||
}, | ||
// Conda | ||
"ghcr.io/mamba-org/devcontainer-features/micromamba:1": { | ||
"channels": "conda-forge", | ||
"packages": "conda python h5py vtk pyside6 pipx" | ||
}, | ||
// Zsh plugins | ||
"ghcr.io/devcontainers-contrib/features/zsh-plugins:0": { | ||
"plugins": "git pip zsh-autosuggestions zsh-syntax-highlighting history-substring-search", | ||
"omzPlugins": "https://github.com/zsh-users/zsh-autosuggestions https://github.com/zsh-users/zsh-syntax-highlighting" | ||
} | ||
}, | ||
// Configure tool-specific properties. | ||
"customizations": { | ||
// Configure properties specific to VS Code. | ||
"vscode": { | ||
"settings": { | ||
Comment on lines
+47
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seems like fertile ground for bikeshedding. Is there a way for the container to pick up the user's local settings? If not, then can you indicate which of these settings are truly necessary for the container to work, which are not necessary but clearly a good idea (e.g., don't restore port forwards?), and which are your preferences (perhaps "format on type" fits here)? |
||
// Editor settings | ||
"editor.formatOnSave": true, | ||
"editor.formatOnSaveMode": "modificationsIfAvailable", | ||
"editor.renderWhitespace": "trailing", | ||
"editor.rulers": [88], | ||
// General Python settings | ||
"python.defaultInterpreterPath": "/opt/conda/bin/python", | ||
"python.analysis.typeCheckingMode": "basic", | ||
"python.testing.pytestEnabled": true, | ||
"python.testing.pytestArgs": ["--color=yes"], | ||
"ruff.nativeServer": false, | ||
"ruff.importStrategy": "fromEnvironment", | ||
"debugpy.debugJustMyCode": false, | ||
// Python modules and scripts | ||
"[python]": { | ||
"editor.defaultFormatter": "charliermarsh.ruff", | ||
"editor.codeActionsOnSave": { | ||
"source.fixAll": "explicit", | ||
"source.organizeImports": "explicit" | ||
} | ||
}, | ||
// Jupyter notebooks | ||
"notebook.formatOnSave.enabled": true, | ||
"notebook.codeActionsOnSave": { | ||
"source.fixAll": "explicit", | ||
"source.organizeImports": "explicit" | ||
}, | ||
// JavaScript | ||
"[javascript]": { | ||
"editor.defaultFormatter": "esbenp.prettier-vscode" | ||
}, | ||
// Git | ||
"git.allowNoVerifyCommit": true, // Allow omitting pre-commit hooks | ||
"git.allowForcePush": true, | ||
// Screencast settings | ||
"screencastMode.keyboardOverlayTimeout": 5000, | ||
"screencastMode.mouseIndicatorSize": 50, | ||
// Disable telemetry and experiments | ||
"telemetry.telemetryLevel": "off", | ||
"gitlens.telemetry.enabled": false, | ||
"redhat.telemetry.enabled": false, | ||
"workbench.enableExperiments": false, // Should not be necessary if telemetry is off, but let's make it explicit. | ||
// Avoid accumulation of unused forwarded ports. | ||
"remote.restoreForwardedPorts": false, | ||
// Always open Markdown files in the preview (i.e., rendered); double-clicking on a line openes the editor. | ||
"workbench.editorAssociations": { | ||
"*.md": "vscode.markdown.preview.editor" | ||
} | ||
}, | ||
// Add the IDs of extensions you want installed when the container is created. | ||
"extensions": [ | ||
// Python Development | ||
"ms-python.python", | ||
"ms-toolsai.jupyter", | ||
"charliermarsh.ruff", | ||
"samuelcolvin.jinjahtml", | ||
// MNE-Python snippets | ||
"hoechenberger.mne-python-extension", | ||
// JavaScript & TypeScript Development | ||
"dbaeumer.vscode-eslint", | ||
"esbenp.prettier-vscode", | ||
// Git | ||
"GitHub.vscode-pull-request-github", | ||
"eamodio.gitlens", | ||
// TOML | ||
"tamasfe.even-better-toml", | ||
// YAML | ||
"redhat.vscode-yaml", | ||
// Spell checking | ||
"streetsidesoftware.code-spell-checker", | ||
// Path / filename autocomplete | ||
"ionutvmi.path-autocomplete" | ||
] | ||
} | ||
}, | ||
// Use 'forwardPorts' to make a list of ports inside the container available locally. | ||
"forwardPorts": [6080], | ||
"portsAttributes": { | ||
"6080": { | ||
"label": "Web VNC" | ||
} | ||
}, | ||
// Set `remoteUser` to `root` to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. | ||
"remoteUser": "mne-user", | ||
"initializeCommand": { | ||
// These commands are run on the host. | ||
// | ||
// We will mount the mne_data directory from the host inside the container to avoid having | ||
// to dowload testing and sample data on container (re)creation. | ||
// Note: On Windows, the .ps1 suffix will be appended automatically to the script name. | ||
"create-mne-data-dir": ".devcontainer/scripts/create-data-dir" | ||
}, | ||
// Use 'postCreateCommand' to run commands after the container is created. | ||
"postCreateCommand": { | ||
// Initialize shells to use conda and mamba | ||
"init-conda-shell": "conda init --quiet zsh bash", | ||
// Force pip to always install "globally": This will work in a virtual environment, but fail with the system Python, which is | ||
// precisely what we want: prevent users from accidentally cluttering their user site-packages folder | ||
"disable-pip-user-installs": "pip config set install.system true", | ||
// Create XDG_RUNTIME_DIR | ||
"create-xdg-runtime-dir": "mkdir -p -m 0700 $XDG_RUNTIME_DIR" | ||
}, | ||
"postStartCommand": { | ||
// These commands are run inside the container. | ||
// | ||
// Do all git-config-related things here, otherwise we may run into problems; see: | ||
// https://github.com/microsoft/vscode-remote-release/issues/6810 | ||
"configure-git": "${containerWorkspaceFolder}/.devcontainer/scripts/configure-git.sh" | ||
}, | ||
"mounts": [ | ||
"source=${localEnv:HOME}${localEnv:USERPROFILE}/mne_data,target=/home/mne-user/mne_data,type=bind" | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#!/bin/bash | ||
|
||
set -ex | ||
|
||
# Set git default branch name | ||
git config --global init.defaultBranch main | ||
|
||
# Work around "dubious ownership in repository" error | ||
git config --global --add safe.directory /workspaces/* | ||
|
||
# Use VS Code as default git editor, diff, and merge tool | ||
git config --global core.editor 'code --wait --reuse-window' | ||
git config --global --replace-all difftool.default-difftool.cmd 'code --wait --diff $LOCAL $REMOTE' | ||
git config --global --replace-all diff.tool default-difftool | ||
git config --global --replace-all mergetool.code.cmd 'code --wait --merge $REMOTE $LOCAL $BASE $MERGED' | ||
git config --global --replace-all merge.tool code | ||
|
||
# Show indicator for "dirty" git repositories in the shell prompt. | ||
# This can be slow on large repositories and should be disabled in that case. | ||
git config --global --replace-all devcontainers-theme.show-dirty 1 | ||
|
||
# Make "git blame" ignore certain commits | ||
git config --local blame.ignoreRevsFile .git-blame-ignore-revs |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -eu | ||
|
||
# Path to the directory you want to create | ||
DIR=~/mne_data | ||
|
||
# Check if the directory already exists | ||
if [ -d "$DIR" ]; then | ||
echo "Found existing MNE data directory: $DIR" | ||
else | ||
# Create the directory since it does not exist | ||
mkdir -p "$DIR" | ||
# Check if the directory creation was successful | ||
if [ $? -eq 0 ]; then | ||
echo "MNE data directory created: $DIR" | ||
else | ||
echo "Failed to create MNE data directory: $DIR" | ||
exit 1 | ||
fi | ||
fi |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Get the path to the user's USERPROFILE folder | ||
$userProfilePath = [System.Environment]::GetFolderPath("UserProfile") | ||
|
||
# Define the path to the new directory | ||
$newDirectoryPath = "$userProfilePath\mne_data" | ||
|
||
# Check if the directory already exists | ||
if (Test-Path -Path $newDirectoryPath) { | ||
Write-Output "Found existing MNE data directory: $newDirectoryPath" | ||
} else { | ||
# Create the directory since it does not exist | ||
try { | ||
New-Item -ItemType Directory -Path $newDirectoryPath -Force | ||
# Verify if the directory was created successfully | ||
if (Test-Path -Path $newDirectoryPath) { | ||
Write-Output "MNE data directory created: $newDirectoryPath" | ||
} else { | ||
Write-Output "Failed to create directory: $newDirectoryPath" | ||
exit 1 | ||
} | ||
} catch { | ||
Write-Output "Error creating MNE data directory: $_" | ||
exit 1 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -eu | ||
|
||
# Exit immediately if not running inside a Dev Container | ||
if [ -z "${RUNNING_IN_DEV_CONTAINER+x}" ]; then | ||
echo -e "๐ Not running in dev container, not installing MNE-Python (dev).\n" | ||
exit | ||
fi | ||
|
||
package_name="MNE-Python (dev)" | ||
import_name="mne" | ||
|
||
# Run the import test outside of the repository, so we don't accidentally import the | ||
# `mne` directory from there. This is an annoyance caused by MNE-Python's not using a | ||
# src/ layout. | ||
orig_dir=$(pwd) | ||
cd ~ | ||
if python -c "import $import_name" &> /dev/null; then | ||
echo -e "โ $package_name is already installed.\n" | ||
cd "${orig_dir}" | ||
exit | ||
else | ||
cd "${orig_dir}" | ||
code .devcontainer/Welcome.md | ||
echo -e "๐ก $package_name is not installed. Installing now โฆ\n" | ||
pipx install uv | ||
uv pip install -e ".[full-pyside6,dev,test_extra]" | ||
echo -e "\nโ $package_name has been installed.\n" | ||
echo -e "๐ก Installing pre-commit hooks โฆ" | ||
pre-commit install --install-hooks | ||
echo -e "โ pre-commit hooks installed.\n" | ||
fi | ||
|
||
echo -e "\n๐ You're all set. Happy hacking!\n" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -eu | ||
|
||
# Exit immediately if not running inside a Dev Container | ||
if [ -z "${RUNNING_IN_DEV_CONTAINER+x}" ]; then | ||
echo -e "๐ Not running in dev container, not opening web browser.\n" | ||
exit | ||
fi | ||
|
||
echo -e "๐ Opening VNC desktop in web browser โฆ\n" | ||
xdg-open 'http://localhost:6080?autoconnect=true' | ||
echo -e "Welcome to the MNE-Python Dev Container!\nCreate a plot in VS Code and it will show up here." | xmessage -center -timeout 30 -title "Welcome to MNE-Python!" -file - |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -97,6 +97,7 @@ cover | |
.venv/ | ||
venv/ | ||
*.json | ||
!/.devcontainer/**/* | ||
!codemeta.json | ||
.hypothesis/ | ||
.ruff_cache/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": "Current File", | ||
"type": "debugpy", | ||
"request": "launch", | ||
"program": "${file}", | ||
"console": "integratedTerminal", | ||
"justMyCode": false, | ||
"jinja": true | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
// See https://go.microsoft.com/fwlink/?LinkId=733558 | ||
// for the documentation about the tasks.json format | ||
"version": "2.0.0", | ||
"tasks": [ | ||
{ | ||
"label": "Install MNE-Python (dev)", | ||
"type": "shell", | ||
"command": ".devcontainer/scripts/install.sh", | ||
"runOptions": { | ||
"runOn": "folderOpen" | ||
}, | ||
"presentation": { | ||
"showReuseMessage": false, | ||
"reveal": "silent", | ||
"close": true | ||
}, | ||
"problemMatcher": [] | ||
}, | ||
{ | ||
"label": "Open VNC Desktop in Browser", | ||
"type": "shell", | ||
"command": ".devcontainer/scripts/open-vnc-browser.sh", | ||
"runOptions": { | ||
"runOn": "folderOpen" | ||
}, | ||
"dependsOn": ["Install MNE-Python (dev)"], | ||
"presentation": { | ||
"showReuseMessage": false, | ||
"reveal": "silent", | ||
"close": true | ||
}, | ||
"problemMatcher": [] | ||
} | ||
] | ||
} |
Uh oh!
There was an error while loading. Please reload this page.