diff options
-rw-r--r-- | .github/workflows/test-all.yaml | 1 | ||||
-rw-r--r-- | .github/workflows/test-pr.yaml | 1 | ||||
-rw-r--r-- | src/jupyterlab/devcontainer-feature.json | 33 | ||||
-rw-r--r-- | src/jupyterlab/install.sh | 71 | ||||
-rw-r--r-- | src/python/devcontainer-feature.json | 10 | ||||
-rwxr-xr-x | src/python/install.sh | 130 | ||||
-rw-r--r-- | test-scenarios/install_jupyterlab.sh | 12 | ||||
-rw-r--r-- | test-scenarios/scenarios.json | 19 | ||||
-rw-r--r-- | test/jupyterlab/test.sh | 12 | ||||
-rw-r--r-- | v1/feature-scripts.env | 3 |
10 files changed, 126 insertions, 166 deletions
diff --git a/.github/workflows/test-all.yaml b/.github/workflows/test-all.yaml index 31a664f..be86dc8 100644 --- a/.github/workflows/test-all.yaml +++ b/.github/workflows/test-all.yaml @@ -27,7 +27,6 @@ jobs: "go", "hugo", "java", - "python jupyterlab", # Install 'python', then 'jupyterlab' "kubectl-helm-minikube", "node", "oryx", diff --git a/.github/workflows/test-pr.yaml b/.github/workflows/test-pr.yaml index 22c04ef..e242ad1 100644 --- a/.github/workflows/test-pr.yaml +++ b/.github/workflows/test-pr.yaml @@ -27,7 +27,6 @@ jobs: go: ./**/go/** hugo: ./**/hugo/** java: ./**/java/** - 'python jupyterlab': ./**/jupyterlab/** kubectl-helm-minikube: ./**/kubectl-helm-minikube/** node: ./**/node/** oryx: ./**/oryx/** diff --git a/src/jupyterlab/devcontainer-feature.json b/src/jupyterlab/devcontainer-feature.json deleted file mode 100644 index 8546925..0000000 --- a/src/jupyterlab/devcontainer-feature.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id": "jupyterlab", - "name": "Jupyter Lab", - "options": { - "version": { - "type": "string", - "proposals": [ - "latest", - "3.6.2" - ], - "default": "latest", - "description": "Select or enter a jupyterlab version." - }, - "python_binary": { - "type": "string", - "proposals": [ - "python", - "/usr/local/python/bin/python" - ], - "default": "python", - "description": "Select or enter the python binary path." - } - }, - "extensions": [ - "ms-python.python", - "ms-python.vscode-pylance", - "ms-toolsai.jupyter" - ], - "install": { - "app": "", - "file": "install.sh" - } -}
\ No newline at end of file diff --git a/src/jupyterlab/install.sh b/src/jupyterlab/install.sh deleted file mode 100644 index f6c30d3..0000000 --- a/src/jupyterlab/install.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash -#------------------------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information -#------------------------------------------------------------------------------------------------------------- -# -# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/jupyterlab.md -# Maintainer: The VS Code and Codespaces Teams - -set -ex - -VERSION=${VERSION:-"latest"} -PYTHON=${PYTHON_BINARY:-"python"} - -USERNAME=${USERNAME:-"automatic"} -ALLOW_ALL_ORIGINS=${ALLOW_ALL_ORIGINS:-""} - -if [ "$(id -u)" -ne 0 ]; then - echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' - exit 1 -fi - -# If in automatic mode, determine if a user already exists, if not use vscode -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=vscode - fi -elif [ "${USERNAME}" = "none" ]; then - USERNAME=root - USER_UID=0 - USER_GID=0 -fi - -addToJupyterConfig() { - JUPYTER_DIR="/home/${USERNAME}/.jupyter" - JUPYTER_CONFIG="${JUPYTER_DIR}/jupyter_notebook_config.py" - - # Make sure the config file exists - test -d ${JUPYTER_DIR} || mkdir ${JUPYTER_DIR} - test -f ${JUPYTER_CONFIG} || touch ${JUPYTER_CONFIG} - - # Don't write the same line more than once - grep -q ${1} ${JUPYTER_CONFIG} || echo ${1} >> ${JUPYTER_CONFIG} -} - -# Make sure that Python is available -if ! ${PYTHON} --version > /dev/null ; then - echo "You need to install Python before installing JupyterLab." - exit 1 -fi - -# pip skips installation if JupyterLab is already installed -echo "Installing JupyterLab..." -if [ "${VERSION}" = "latest" ]; then - ${PYTHON} -m pip install jupyterlab --no-cache-dir -else - ${PYTHON} -m pip install jupyterlab=="${VERSION}" --no-cache-dir -fi - -if [ "${ALLOW_ALL_ORIGINS}" = 'true' ]; then - addToJupyterConfig "c.ServerApp.allow_origin = '*'" - addToJupyterConfig "c.NotebookApp.allow_origin = '*'" -fi
\ No newline at end of file diff --git a/src/python/devcontainer-feature.json b/src/python/devcontainer-feature.json index 238b8d1..c32e18a 100644 --- a/src/python/devcontainer-feature.json +++ b/src/python/devcontainer-feature.json @@ -37,6 +37,16 @@ "type": "boolean", "default": "true", "description": "If true, overrides existing version (if any) of python on the PATH" + }, + "install_jupyterlab": { + "type": "boolean", + "default": false, + "description": "Install JupyterLab, a web-based interactive development environment for notebooks" + }, + "configure_jupyterlab_allow_origin": { + "type": "string", + "default": "", + "description": "Configure JupyterLab to accept HTTP requests from the specified origin" } }, "containerEnv": { diff --git a/src/python/install.sh b/src/python/install.sh index a3516da..c976671 100755 --- a/src/python/install.sh +++ b/src/python/install.sh @@ -19,6 +19,9 @@ USERNAME=${USERNAME:-"automatic"} UPDATE_RC=${UPDATE_RC:-"true"} USE_ORYX_IF_AVAILABLE=${USE_ORYX_IF_AVAILABLE:-"true"} +INSTALL_JUPYTERLAB=${INSTALL_JUPYTERLAB:-"false"} +CONFIGURE_JUPYTERLAB_ALLOW_ORIGIN=${CONFIGURE_JUPYTERLAB_ALLOW_ORIGIN:-""} + DEFAULT_UTILS=("pylint" "flake8" "autopep8" "black" "yapf" "mypy" "pydocstyle" "pycodestyle" "bandit" "pipenv" "virtualenv") PYTHON_SOURCE_GPG_KEYS="64E628F8D684696D B26995E310250568 2D347EA6AA65421D FB9921286F5E1540 3A5CA953F73C700D 04C367C218ADD4FF 0EDDC5F26A45C816 6AF053F07D9DC8D2 C9BE28DEE6DF025C 126EB563A74B06BF D9866941EA5BBD71 ED9D77D5" GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80 @@ -301,6 +304,32 @@ install_using_oryx() { add_symlink } +sudo_if() { + COMMAND="$*" + if [ "$(id -u)" -eq 0 ] && [ "$USERNAME" != "root" ]; then + su - "$USERNAME" -c "$COMMAND" + else + "$COMMAND" + fi +} + +install_user_package() { + PACKAGE="$1" + sudo_if "$INSTALL_PATH/bin/python3" -m pip install --user --upgrade --no-cache-dir "$PACKAGE" +} + +add_user_jupyter_config() { + CONFIG_DIR="/home/$USERNAME/.jupyter" + CONFIG_FILE="$CONFIG_DIR/jupyter_notebook_config.py" + + # Make sure the config file exists or create it with proper permissions + test -d "$CONFIG_DIR" || sudo_if mkdir "$CONFIG_DIR" + test -f "$CONFIG_FILE" || sudo_if touch "$CONFIG_FILE" + + # Don't write the same config more than once + grep -q "$1" "$CONFIG_FILE" || echo "$1" >> "$CONFIG_FILE" +} + # Ensure apt is in non-interactive to avoid prompts export DEBIAN_FRONTEND=noninteractive @@ -310,7 +339,7 @@ check_packages curl ca-certificates gnupg2 tar make gcc libssl-dev zlib1g-dev li libxmlsec1-dev libsqlite3-dev libffi-dev liblzma-dev uuid-dev -# Install python from source if needed +# Install Python from source if needed if [ "${PYTHON_VERSION}" != "none" ]; then CURRENT_PATH="${PYTHON_INSTALL_PATH}/current" # If the os-provided versions are "good enough", detect that and bail out. @@ -330,53 +359,62 @@ if [ "${PYTHON_VERSION}" != "none" ]; then updaterc "if [[ \"\${PATH}\" != *\"${CURRENT_PATH}/bin\"* ]]; then export PATH=${CURRENT_PATH}/bin:\${PATH}; fi" fi -# If not installing python tools, exit -if [ "${INSTALL_PYTHON_TOOLS}" != "true" ]; then - echo "Done!" - exit 0 -fi +# Install Python tools if needed +if [ "${INSTALL_PYTHON_TOOLS}" = "true" ]; then + echo 'Installing Python tools...' + export PIPX_BIN_DIR="${PIPX_HOME}/bin" + export PATH="${CURRENT_PATH}/bin:${PIPX_BIN_DIR}:${PATH}" -export PIPX_BIN_DIR="${PIPX_HOME}/bin" -export PATH="${CURRENT_PATH}/bin:${PIPX_BIN_DIR}:${PATH}" + # Create pipx group, dir, and set sticky bit + if ! cat /etc/group | grep -e "^pipx:" > /dev/null 2>&1; then + groupadd -r pipx + fi + usermod -a -G pipx ${USERNAME} + umask 0002 + mkdir -p ${PIPX_BIN_DIR} + chown :pipx ${PIPX_HOME} ${PIPX_BIN_DIR} + chmod g+s ${PIPX_HOME} ${PIPX_BIN_DIR} + + # Update pip if not using os provided python + if [ ${PYTHON_VERSION} != "os-provided" ] && [ ${PYTHON_VERSION} != "system" ] && [ ${PYTHON_VERSION} != "none" ]; then + echo "Updating pip..." + "${INSTALL_PATH}/bin/python3" -m pip install --no-cache-dir --upgrade pip + fi -# Create pipx group, dir, and set sticky bit -if ! cat /etc/group | grep -e "^pipx:" > /dev/null 2>&1; then - groupadd -r pipx -fi -usermod -a -G pipx ${USERNAME} -umask 0002 -mkdir -p ${PIPX_BIN_DIR} -chown :pipx ${PIPX_HOME} ${PIPX_BIN_DIR} -chmod g+s ${PIPX_HOME} ${PIPX_BIN_DIR} - -# Update pip if not using os provided python -if [ ${PYTHON_VERSION} != "os-provided" ] && [ ${PYTHON_VERSION} != "system" ] && [ ${PYTHON_VERSION} != "none" ]; then - echo "Updating pip..." - ${INSTALL_PATH}/bin/python3 -m pip install --no-cache-dir --upgrade pip -fi + # Install tools + echo "Installing Python tools..." + export PYTHONUSERBASE=/tmp/pip-tmp + export PIP_CACHE_DIR=/tmp/pip-tmp/cache + PIPX_DIR="" + if ! type pipx > /dev/null 2>&1; then + pip3 install --disable-pip-version-check --no-cache-dir --user pipx 2>&1 + /tmp/pip-tmp/bin/pipx install --pip-args=--no-cache-dir pipx + PIPX_DIR="/tmp/pip-tmp/bin" + fi + for util in "${DEFAULT_UTILS[@]}"; do + if ! type ${util} > /dev/null 2>&1; then + "${PIPX_DIR}/pipx" install --system-site-packages --pip-args '--no-cache-dir --force-reinstall' ${util} + else + echo "${util} already installed. Skipping." + fi + done + rm -rf /tmp/pip-tmp -# Install tools -echo "Installing Python tools..." -export PYTHONUSERBASE=/tmp/pip-tmp -export PIP_CACHE_DIR=/tmp/pip-tmp/cache -pipx_path="" -if ! type pipx > /dev/null 2>&1; then - pip3 install --disable-pip-version-check --no-cache-dir --user pipx 2>&1 - /tmp/pip-tmp/bin/pipx install --pip-args=--no-cache-dir pipx - pipx_path="/tmp/pip-tmp/bin/" + updaterc "export PIPX_HOME=\"${PIPX_HOME}\"" + updaterc "export PIPX_BIN_DIR=\"${PIPX_BIN_DIR}\"" + updaterc "if [[ \"\${PATH}\" != *\"\${PIPX_BIN_DIR}\"* ]]; then export PATH=\"\${PATH}:\${PIPX_BIN_DIR}\"; fi" fi -for util in "${DEFAULT_UTILS[@]}"; do - if ! type ${util} > /dev/null 2>&1; then - ${pipx_path}pipx install --system-site-packages --pip-args '--no-cache-dir --force-reinstall' ${util} - else - echo "${util} already installed. Skipping." + +# Install JupyterLab if needed +if [ "${INSTALL_JUPYTERLAB}" = "true" ]; then + install_user_package jupyterlab + + # Configure JupyterLab if needed + # TODO: True if it's not empty + if [ -n "${CONFIGURE_JUPYTERLAB_ALLOW_ORIGIN}" ]; then + add_user_jupyter_config "c.ServerApp.allow_origin = '${CONFIGURE_JUPYTERLAB_ALLOW_ORIGIN}'" + add_user_jupyter_config "c.NotebookApp.allow_origin = '${CONFIGURE_JUPYTERLAB_ALLOW_ORIGIN}'" fi -done -rm -rf /tmp/pip-tmp - -updaterc "$(cat << EOF -export PIPX_HOME="${PIPX_HOME}" -export PIPX_BIN_DIR="${PIPX_BIN_DIR}" -if [[ "\${PATH}" != *"\${PIPX_BIN_DIR}"* ]]; then export PATH="\${PATH}:\${PIPX_BIN_DIR}"; fi -EOF -)" +fi + +echo "Done!" diff --git a/test-scenarios/install_jupyterlab.sh b/test-scenarios/install_jupyterlab.sh new file mode 100644 index 0000000..2a4aaa3 --- /dev/null +++ b/test-scenarios/install_jupyterlab.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +check "version" jupyter lab --version +check "config" grep ".*.allow_origin = '*'" /home/vscode/.jupyter/jupyter_notebook_config.py + +# Report result +reportResults diff --git a/test-scenarios/scenarios.json b/test-scenarios/scenarios.json index e97b853..bbb61e7 100644 --- a/test-scenarios/scenarios.json +++ b/test-scenarios/scenarios.json @@ -16,5 +16,24 @@ } } ] + }, + "install_jupyterlab": { + "image": "mcr.microsoft.com/vscode/devcontainers/base:focal", + "remoteUser": "vscode", + "features": [ + { + "id": "common", + "options": { + "username": "vscode" + } + }, + { + "id": "python", + "options": { + "install_jupyterlab": true, + "configure_jupyterlab_allow_origin": "*" + } + } + ] } }
\ No newline at end of file diff --git a/test/jupyterlab/test.sh b/test/jupyterlab/test.sh deleted file mode 100644 index 67249bf..0000000 --- a/test/jupyterlab/test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -set -e - -# Optional: Import test library -source dev-container-features-test-lib - -# Definition specific tests -check "version" jupyter --version - -# Report result -reportResults
\ No newline at end of file diff --git a/v1/feature-scripts.env b/v1/feature-scripts.env index 7503ce5..c3e9936 100644 --- a/v1/feature-scripts.env +++ b/v1/feature-scripts.env @@ -10,7 +10,7 @@ _BUILD_ARG_AWS_CLI="./aws-cli/install.sh ${_B _BUILD_ARG_AZURE_CLI="./az-cli/install.sh ${_BUILD_ARG_AZURE_CLI_VERSION:-latest}" _BUILD_ARG_SSHD="./sshd/install.sh" _BUILD_ARG_NODE="./node/install.sh ${_BUILD_ARG_NODE_NVMINSTALLPATH:-/usr/local/share/nvm} ${_BUILD_ARG_NODE_VERSION:-lts/*} automatic true ${_BUILD_ARG_NODE_NODEGYPDEPENDENCIES:-true}" -_BUILD_ARG_PYTHON="./python/install.sh ${_BUILD_ARG_PYTHON_VERSION:-latest} ${_BUILD_ARG_PYTHON_INSTALLPATH:-/usr/local/python} /usr/local/py-utils automatic true ${_BUILD_ARG_PYTHON_INSTALLTOOLS:-true} true ${_BUILD_ARG_PYTHON_OPTIMIZE:-false} ${_BUILD_ARG_PYTHON_OVERRIDEDEFAULTVERSION:-true}" +_BUILD_ARG_PYTHON="./python/install.sh ${_BUILD_ARG_PYTHON_VERSION:-latest} ${_BUILD_ARG_PYTHON_INSTALLPATH:-/usr/local/python} /usr/local/py-utils automatic true ${_BUILD_ARG_PYTHON_INSTALLTOOLS:-true} true ${_BUILD_ARG_PYTHON_OPTIMIZE:-false} ${_BUILD_ARG_PYTHON_OVERRIDEDEFAULTVERSION:-true} ${_BUILD_ARG_PYTHON_INSTALLJUPYTERLAB:-false} ${_BUILD_ARG_PYTHON_CONFIGUREJUPYTERLABALLOWORIGIN:-''}" _BUILD_ARG_GO="./go/install.sh ${_BUILD_ARG_GOLANG_VERSION:-latest}" _BUILD_ARG_JAVA="./java/wrapper.sh ${_BUILD_ARG_JAVA_VERSION:-latest}" _BUILD_ARG_GRADLE="./gradle/install.sh ${_BUILD_ARG_GRADLE_VERSION:-latest}" @@ -20,7 +20,6 @@ _BUILD_ARG_RUST="./rust/install.sh /usr _BUILD_ARG_POWERSHELL="./powershell/install.sh ${_BUILD_ARG_POWERSHELL_VERSION:-latest}" _BUILD_ARG_DESKTOP_LITE="./desktop-lite/install.sh automatic ${_BUILD_ARG_DESKTOP_LITE_PASSWORD:-vscode} true ${_BUILD_ARG_DESKTOP_LITE_VNCPORT:-5901} ${_BUILD_ARG_DESKTOP_LITE_WEBPORT:-6080}" _BUILD_ARG_DOTNET="./dotnet/install.sh ${_BUILD_ARG_DOTNET_VERSION:-latest} ${_BUILD_ARG_DOTNET_RUNTIMEONLY:-false} automatic true /usr/local/dotnet dotnet ${_BUILD_ARG_DOTNET_OVERRIDEDEFAULTVERSION:-true} ${_BUILD_ARG_DOTNET_INSTALLUSINGAPT:-true}" -_BUILD_ARG_JUPYTERLAB="./jupyterlab/install.sh ${_BUILD_ARG_JUPYTERLAB_VERSION:-latest} automatic ${_BUILD_ARG_JUPYTERLAB_PYTHONBINARY:-python} true" _BUILD_ARG_PHP="./php/install.sh ${_BUILD_ARG_PHP_VERSION:-latest} /usr/local/php ${_BUILD_ARG_PHP_INSTALLCOMPOSER:-true} automatic true ${_BUILD_ARG_PHP_OVERRIDEDEFAULTVERSION:-true}" _BUILD_ARG_ORYX="./oryx/install.sh automatic true" _BUILD_ARG_HUGO="./hugo/install.sh ${_BUILD_ARG_HUGO_VERSION:-latest} automatic true" |