Setting up a development environment#

This guide explains how to set up a development environment for your OrangeQS Juice extension. There are two main ways to set up a development environment for your extension:

Option 1: Using an existing OrangeQS Juice installation#

If you want to use an existing OrangeQS Juice installation for development, you must install your extension in editable mode. You can do this in two ways:

  1. By following the Installing a package in editable mode tutorial. This is the recommended way. This method is persistent and modifies the environments of the existing installation. This method requires you to make changes to the environment configuration files (e.g. pyproject.toml) and to rebuild relevant environments, but has the advantage that the change will persist on restart and will affect all relevant environments (e.g. all singleuser containers or all instances of a service).

  2. Using uv pip install.

    Warning

    This is an advanced technique and should be used with caution. It can lead to confusing situations where the state of the environment is different from the state defined in the configuration files. Make sure you understand the implications of this method before using it.

    This method applies only temporarily and has the least impact on the existing installation. It does not require any changes to environment configuration files (e.g. pyproject.toml) and does not require rebuilding any environments.

    However, the change will be lost on restart and will only affect the current environment (e.g. your singleuser container or a service)!

    • For installing in your singleuser container environment, open a local terminal from the JupyterLab launcher and run uv pip install -e <path_to_extension>. Next, restart your local notebook kernel or your dashboard to reflect the change.

      • To undo your changes, restart your singleuser container from the JupyterHub control panel available from JupyterLab at File > Hub Control Panel.

    • For installing in a service environment, open a IPython console connected to the service and run !uv pip install -e <path_to_extension>. Next, restart the process of the service from the Task manager page on the dashboard using the Restart Process button to reflect the change.

      • To undo your changes, restart the container of the service from the Task manager page on the dashboard using the Restart Container button.

No access to an existing OrangeQS Juice installation?

If you do not have access to an existing OrangeQS Juice installation, you can also use the development container of the OrangeQS Juice core repository. See Development environment for instructions on how to set up a development container for OrangeQS Juice core.

Once you have set up the dev container, you can develop your extension from the bundled JupyterLab or VS Code interface.

Option 2: Setting up a dedicated local development container#

Warning

The steps outlined in this method have not been tested in a while and may be outdated. Use with caution!

Setting up a development container#

We will set up the development environment in a development container, which runs the development environment in an isolated container and is supported on all operating systems. If you are using VS Code or any other IDE that supports dev containers you can let your development environment be set up automatically from a configuration file.

Start by creating a Dockerfile and a devcontainer.json configuration file for the dev container.

Click to see file contents

.devcontainer/devcontainer.json

{
    "build": {
        "dockerfile": "Dockerfile",
        "context": "."
    },
    "privileged": true,
    "overrideCommand": false,
    "capAdd": [
        "SYS_ADMIN",
        "SYS_PTRACE"
    ],
    "forwardPorts": [8086, 8000],
    "workspaceFolder": "/var/lib/juice/dist/$project",
    "mounts": [
        "source=/var/lib/containers,target=/var/lib/containers,type=bind"
    ],
    "runArgs": ["--name", "juice-extension-devcontainer"],
    "customizations": {
        "vscode": {
            "settings": {
                "cSpell.language": "en,en-US"
            },
            "extensions": [
                "ms-python.vscode-pylance",
                "ms-python.python",
                "ms-python.debugpy",
                "charliermarsh.ruff",
                "eamodio.gitlens",
                "tamasfe.even-better-toml"
            ]
        }
    },
    "postAttachCommand": "pip install -e ."
}

.devcontainer/Dockerfile

FROM quay.io/almalinuxorg/10-init
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8

# Configure to use iptables for containers instead of nftables
# Required for podman to work on WSL systems
RUN mkdir -p /etc/containers
RUN echo -e "[network]\nfirewall_driver = \"iptables\"" > /etc/containers/containers.conf

RUN /usr/bin/dnf install -y \
    git \
    rpm-build \
    ruby \
    sudo \
    podman \
    python3 \
    python3-pip \
    iputils \
    nano \
    iptables \
    && /usr/bin/dnf clean all

RUN /usr/bin/gem install fpm

ARG USERNAME=user
ARG USER_UID=1000
ARG USER_GID=$USER_UID

# Add a non-root user with passwordless sudo access
# This is user is not used by default, but can be used for development purposes.
RUN groupadd --gid $USER_GID $USERNAME \
    && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
    && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
    && chmod 0440 /etc/sudoers.d/$USERNAME

The folder structure of your extension should now look like this:

juice_extension_example
├── .devcontainer
│   ├── devcontainer.json
│   └── Dockerfile
└── other project files

Next, start the dev container. If you are using VS Code you can follow these instructions.

  1. Follow the dev container installation instructions for VS Code.

  2. Open the folder of the Juice repository in the dev container using the Dev Containers: Open folder in Container command in the Command Palette. You should not be prompted to select a container, it should use the provided configuration at .devcontainer/devcontainer.json by default.

  3. Wait until the container is built and the development dependencies are installed.

  4. Run juice --version in your terminal to confirm that the Juice orchestrator is correctly installed.

  5. Run pip list | grep juice_extension_example to confirm your extension is installed in editable mode.

If you encounter any issues with your dev container, remember that you can always rebuild your container to get back to a clean state. To do this in VS Code, use the Dev Containers: Rebuild Container command in the Command Palette.

Starting OrangeQS Juice#

Next we should start OrangeQS Juice such that we can access the installation from a JupyterHub user container. To set up and start a full OrangeQS Juice deployment (including JupyterHub, database, services, etc.) you can run juice install. Since your extension has been installed in editable mode in the same environment as the Juice orchestrator, your extension will be automatically installed in the environments used by the user containers and services. Installing from editable mode is only meant for development, in production you should add your extension to the environment configuration.

After juice install has finished, you can open http://localhost:8000 in your browser to access the JupyterHub interface. The password can be found using cat /etc/juice/jupyterhub/secrets.env in your dev container. Check whether your extension is installed correctly in your user container by opening a new notebook and executing import juice_extension_example.

If you make modifications to your extension, you should consider which parts of OrangeQS Juice need to be restarted to reflect your changes. Below is a guideline which lists some common cases:

  • Changing a Python file which is used in

    • a service: Restart the service by running systemctl restart juice-{service} in the dev container.

    • your notebook: Restart your notebook kernel.

  • Changing a dependency: Rebuild the environment by running juice install --rebuild --restart in the dev container.

Next, let’s continue by adding some functionality to the extension.