|
| 1 | + |
| 2 | +This document explains how to prepare a new development environment and update an existing environment, as necessary, for the development of *NiPreps*' components. |
| 3 | +Some components may deviate from these guidelines, in such a case, please follow the guidelines provided in their documentation. |
| 4 | + |
| 5 | +If you plan to contribute back to the community, making your code available via pull-request, please make sure to have read and understood the [Community Documents and Contributor Guidelines](../community/index.md). |
| 6 | +If you plan to distribute derived code, please follow our [licensing guidelines](../community/licensing.md). |
| 7 | + |
| 8 | +Development in Docker is encouraged, for the sake of consistency and portability. |
| 9 | +By default, work should be built off of [`nipreps/fmriprep:unstable`](https://hub.docker.com/r/nipreps/fmriprep/), which tracks the `master` branch, or `nipreps/fmriprep:latest`, which tracks the latest release version (see [BIDS-Apps execution guide](../apps/docker.md) for the basic procedure for running). |
| 10 | + |
| 11 | +It will be assumed the developer has a working repository in `$HOME/projects/fmriprep`, and examples are also given for [niworkflows](https://github.com/nipreps/niworkflows) and *NiPype*. |
| 12 | + |
| 13 | +## Patching a working copy into a Docker container |
| 14 | + |
| 15 | +In order to test new code without rebuilding the Docker image, it is possible to mount working repositories as source directories within the container. |
| 16 | +The [Docker wrapper](../apps/docker.md#running-a-niprep-with-a-lightweight-wrapper) script simplifies this for the most common repositories: |
| 17 | + |
| 18 | +```shell |
| 19 | + -f PATH, --patch-fmriprep PATH |
| 20 | + working fmriprep repository (default: None) |
| 21 | + -n PATH, --patch-niworkflows PATH |
| 22 | + working niworkflows repository (default: None) |
| 23 | + -p PATH, --patch-nipype PATH |
| 24 | + working nipype repository (default: None) |
| 25 | +``` |
| 26 | + |
| 27 | +For instance, if your repositories are contained in `$HOME/projects`: |
| 28 | + |
| 29 | +```shell |
| 30 | +$ fmriprep-docker -f $HOME/projects/fmriprep/fmriprep \ |
| 31 | + -n $HOME/projects/niworkflows/niworkflows \ |
| 32 | + -p $HOME/projects/nipype/nipype \ |
| 33 | + -i nipreps/fmriprep:latest \ |
| 34 | + $HOME/fullds005 $HOME/dockerout participant |
| 35 | +``` |
| 36 | + |
| 37 | +Note the `-i` flag allows you to specify an image. |
| 38 | + |
| 39 | +When invoking `docker` directly, the mount options must be specified |
| 40 | +with the `-v` flag: |
| 41 | + |
| 42 | +```shell |
| 43 | +-v $HOME/projects/fmriprep/fmriprep:/usr/local/miniconda/lib/python3.7/site-packages/fmriprep:ro |
| 44 | +-v $HOME/projects/niworkflows/niworkflows:/usr/local/miniconda/lib/python3.7/site-packages/niworkflows:ro |
| 45 | +-v $HOME/projects/nipype/nipype:/usr/local/miniconda/lib/python3.7/site-packages/nipype:ro |
| 46 | +``` |
| 47 | + |
| 48 | +For example, |
| 49 | + |
| 50 | +```shell |
| 51 | +$ docker run --rm -v $HOME/ds005:/data:ro -v $HOME/dockerout:/out \ |
| 52 | + -v $HOME/projects/fmriprep/fmriprep:/usr/local/miniconda/lib/python3.7/site-packages/fmriprep:ro \ |
| 53 | + nipreps/fmriprep:latest /data /out/out participant \ |
| 54 | + -w /out/work/ |
| 55 | +``` |
| 56 | + |
| 57 | +In order to work directly in the container, pass the `--shell` flag to `fmriprep-docker` |
| 58 | + |
| 59 | +```shell |
| 60 | +$ fmriprep-docker --shell $HOME/ds005 $HOME/dockerout participant |
| 61 | +``` |
| 62 | + |
| 63 | +This is the equivalent of using `--entrypoint=bash` and omitting the *fMRIPrep* arguments in a `docker` command: |
| 64 | + |
| 65 | +```shell |
| 66 | +$ docker run --rm -v $HOME/ds005:/data:ro -v $HOME/dockerout:/out \ |
| 67 | + -v $HOME/projects/fmriprep/fmriprep:/usr/local/miniconda/lib/python3.7/site-packages/fmriprep:ro --entrypoint=bash \ |
| 68 | + nipreps/fmriprep:latest |
| 69 | +``` |
| 70 | + |
| 71 | +Patching containers can be achieved in Singularity analogous to `docker` using the `--bind` (`-B`) option: |
| 72 | + |
| 73 | +```shell |
| 74 | +$ singularity run \ |
| 75 | + -B $HOME/projects/fmriprep/fmriprep:/usr/local/miniconda/lib/python3.7/site-packages/fmriprep \ |
| 76 | + fmriprep.img \ |
| 77 | + /scratch/dataset /scratch/out participant -w /out/work/ |
| 78 | +``` |
| 79 | + |
| 80 | +## Adding dependencies |
| 81 | + |
| 82 | +New dependencies to be inserted into the Docker image will either be Python or non-Python dependencies. |
| 83 | +Python dependencies may be added in three places, depending on whether the package is large or non-release versions are required. |
| 84 | +The image [must be rebuilt](#rebuilding-docker-image) after any dependency changes. |
| 85 | + |
| 86 | +Python dependencies should generally be included in the appropriate dependency metadata of the `setup.cfg` file found at the root of each repository. |
| 87 | +If some the dependency must be a particular version (or set thereof), it is possible to use version filters in this `setup.cfg` file. |
| 88 | + |
| 89 | +For large Python dependencies where there will be a benefit to pre-compiled binaries, [conda](https://github.com/conda/conda) packages may also be added to the `conda install` line in the [Dockerfile](https://github.com/nipreps/fmriprep/blob/29133e5e9f92aae4b23dd897f9733885a60be311/Dockerfile#L46). |
| 90 | + |
| 91 | +Non-Python dependencies must also be installed in the Dockerfile, via a `RUN` command. |
| 92 | +For example, installing an `apt` package may be done as follows: |
| 93 | + |
| 94 | +```Dockerfile |
| 95 | +RUN apt-get update && \ |
| 96 | + apt-get install -y <PACKAGE> |
| 97 | +``` |
| 98 | + |
| 99 | +## (Re)Building Docker image |
| 100 | + |
| 101 | +If it is necessary to (re)build the Docker image, a local image named `fmriprep` may be built from within the local repository. |
| 102 | +Let's assume it is located in `~/projects/fmriprep`: |
| 103 | + |
| 104 | +```shell |
| 105 | +~/projects/fmriprep$ VERSION=$( python get_version.py ) |
| 106 | +~/projects/fmriprep$ docker build -t fmriprep --build-arg VERSION=$VERSION . |
| 107 | +``` |
| 108 | + |
| 109 | +The `VERSION` build argument is necessary to ensure that help text can be reliably generated. |
| 110 | +The `get_version.py` tool constructs the version string from the current repository state. |
| 111 | + |
| 112 | +To work in this image, replace `nipreps/fmriprep:latest` with just `fmriprep` in any of the above commands. |
| 113 | +This image may be accessed by the [Docker wrapper](../apps/docker.md#running-a-niprep-with-a-lightweight-wrapper) via the `-i` flag, e.g.: |
| 114 | + |
| 115 | +```shell |
| 116 | +$ fmriprep-docker -i fmriprep --shell |
| 117 | +``` |
| 118 | + |
| 119 | +## Code-Server Development Environment (Experimental) |
| 120 | + |
| 121 | +To get the best of working with containers and having an interactive development environment, we have an experimental setup with [code-server](https://github.com/cdr/code-server). |
| 122 | + |
| 123 | +!!! important |
| 124 | + |
| 125 | + We have [a video walking through the process](https://youtu.be/bkZ-NyUaTvg) if you want a visual guide. |
| 126 | + |
| 127 | + |
| 128 | +**1. Build the Docker image**. |
| 129 | +We will use the `Dockerfile_devel` file to build our development docker image: |
| 130 | +```shell |
| 131 | +$ cd $HOME/projects/fmriprep |
| 132 | +$ docker build -t fmriprep_devel -f Dockerfile_devel . |
| 133 | +``` |
| 134 | + |
| 135 | +**2. Run the Docker image** |
| 136 | +We can start a docker container using the image we built (`fmriprep_devel`): |
| 137 | +```shell |
| 138 | +$ docker run -it -p 127.0.0.1:8445:8080 -v ${PWD}:/src/fmriprep fmriprep_devel:latest |
| 139 | +``` |
| 140 | + |
| 141 | +!!! important "Windows Users" |
| 142 | + |
| 143 | + If you are using windows shell, `${PWD}` may not be defined, instead use the absolute path to your repository. |
| 144 | + |
| 145 | +!!! important "Docker-Toolbox" |
| 146 | + |
| 147 | + If you are using Docker-Toolbox, you will need to change your virtualbox settings using [these steps as a guide](https://github.com/jdkent/tutDockerRstudio#additional-setup-for-docker-toolbox). For step 6, instead of `Name = rstudio; Host Port = 8787; Guest Port = 8787`, have `Name = code-server; Host Port = 8443; Guest Port = 8080`. Then in the docker command above, change `127.0.0.1:8445:8080` to `192.168.99.100:8445:8080`. |
| 148 | + |
| 149 | +If the container started correctly, you should see the following on your console: |
| 150 | + |
| 151 | +```shell |
| 152 | +INFO Server listening on http://localhost:8080 |
| 153 | +INFO - No authentication |
| 154 | +INFO - Not serving HTTPS |
| 155 | +``` |
| 156 | + |
| 157 | +Now you can switch to your favorite browser and go to: [127.0.0.1:8445](http://127.0.0.1:8445) (or [192.168.99.100:8445](http://192.168.99.100:8445) for Docker Toolbox). |
| 158 | + |
| 159 | +**3. Copy `fmriprep.egg-info` into your `fmriprep/` project directory** |
| 160 | +`fmriprep.egg-info` makes the package exacutable inside the docker container. |
| 161 | +Open a terminal in vscode and type the following: |
| 162 | + |
| 163 | +```shell |
| 164 | +$ cp -R /src/fmriprep.egg-info /src/fmriprep/ |
| 165 | +``` |
| 166 | + |
| 167 | +### Code-Server Development Environment Features |
| 168 | +- The editor is [vscode](https://code.visualstudio.com/docs) |
| 169 | + |
| 170 | +- There are several preconfigured debugging tests under the debugging icon in the activity bar |
| 171 | + - see [vscode debugging python](https://code.visualstudio.com/docs/python/debugging) for details. |
| 172 | + |
| 173 | +- The `gitlens` and `python` extensions are preinstalled to improve the development experience in vscode. |
0 commit comments