Last active
January 18, 2022 22:18
-
-
Save hcpadkins/9ec4b64c9756a5575b02db7751c7f8b6 to your computer and use it in GitHub Desktop.
JupyterLab start-up helper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# NOTE: This script enables the JupterLab Extension Manager and installs extensions. | |
# There are code-execution risks with this, so make sure you only use trusted | |
# extensions and you're comfortable with these extensions before running! | |
# | |
# This script wrappers the JupterLab Docker container. It defines a few Docker volume | |
# mounts to ensure that JupyterLab configuration and notebooks are persisted between | |
# restarts. | |
# | |
# Additionally, it defines a requirements.txt which is automatically pip installed at | |
# container start to ensure that all dependencies are available between restarts, and | |
# installs JupyterLab extensions at the same time. | |
# | |
# Finally, this script also installs and configures GopherNotes for Jupter, enabling | |
# development of Go lang notebooks. | |
# If the lab container version changes, plugins and their associated Python dependencies | |
# may need updating. | |
LAB_CONTAINER="jupyter/base-notebook:lab-3.1.10" | |
function wait_for_jupyterlab { | |
while true ; do | |
if curl -s -m5 -o /dev/null -f http://127.0.0.1:8888; then | |
break | |
fi | |
echo -n "." | |
sleep 1 | |
done | |
} | |
# Ensure the required directory structure exists. | |
mkdir -p "${PWD}/jupyterlab/work" | |
mkdir -p "${PWD}/jupyterlab/configuration" | |
mkdir -p "${PWD}/jupyterlab/start-up" | |
# Create the requirements list, if not present. | |
if [ ! -e "${PWD}/jupyterlab/requirements.txt" ]; then | |
cat > "${PWD}/jupyterlab/requirements.txt" <<EOF | |
requests | |
boto3 | |
jmespath | |
python-lsp-server[all] | |
black | |
isort | |
jupyterlab-lsp==3.8.1 | |
jupyterlab_code_formatter==1.4.10 | |
EOF | |
fi | |
# Create the package bootstrap script, if not present. | |
if [ ! -e "${PWD}/jupyterlab/start-up/packages.sh" ]; then | |
cat > "${PWD}/jupyterlab/start-up/packages.sh" <<EOF | |
#!/bin/sh | |
pip install -r /home/jupyter/requirements.txt | |
jupyter labextension install @ryantam626/[email protected] | |
jupyter labextension install @krassowski/[email protected] | |
EOF | |
chmod a+x "${PWD}/jupyterlab/start-up/packages.sh" | |
fi | |
# Create the GopherNotes bootstrap script, if not present. | |
if [ ! -e "${PWD}/jupyterlab/start-up/gophernotes.sh" ]; then | |
cat > "${PWD}/jupyterlab/start-up/gophernotes.sh" <<'EOF' | |
#!/bin/sh | |
JUPYTER_USER="jovyan" | |
JUPYTER_PATH="/home/${JUPYTER_USER}" | |
# Root is unfortunately required due to these dependencies. start-notebook should | |
# drop the user into NB_USER when started however. | |
apt update -y && apt install -y build-essential | |
# Fetch and install Go. | |
wget -qO /tmp/go1.17.6.linux-amd64.tar.gz https://go.dev/dl/go1.17.6.linux-amd64.tar.gz | |
tar -C /usr/local -xf /tmp/go1.17.6.linux-amd64.tar.gz | |
rm /tmp/go1.17.6.linux-amd64.tar.gz | |
# Ensure Go is in the user's path right away. | |
grep -Eqi '^export PATH.*go/bin$' /etc/profile.d/99-golang.sh || \ | |
echo 'export PATH=$PATH:/usr/local/go/bin' >> /etc/profile.d/99-golang.sh | |
source /etc/profile.d/99-golang.sh | |
# Ensure the kernels path exists. | |
mkdir -p "${JUPYTER_PATH}/.local/share/jupyter/kernels/gophernotes" | |
# Install GopherNotes. | |
go install github.com/gopherdata/[email protected] | |
cp "$(go env GOPATH)"/pkg/mod/github.com/gopherdata/[email protected]/kernel/* \ | |
"${JUPYTER_PATH}/.local/share/jupyter/kernels/gophernotes/" | |
chmod 660 "${JUPYTER_PATH}/.local/share/jupyter/kernels/gophernotes/kernel.json" | |
# Fix path to GopherNotes. | |
sed "s|gophernotes|$(go env GOPATH)/bin/gophernotes|" \ | |
< "${JUPYTER_PATH}/.local/share/jupyter/kernels/gophernotes/kernel.json.in" \ | |
> "${JUPYTER_PATH}/.local/share/jupyter/kernels/gophernotes/kernel.json" | |
# Fix permissions. | |
chown -R ${JUPYTER_USER}: "${JUPYTER_PATH}/.local/share/jupyter/" | |
# Clean up. | |
apt -y remove build-essential && \ | |
apt -y autoremove --purge && \ | |
apt -y clean | |
EOF | |
chmod a+x "${PWD}/jupyterlab/start-up/gophernotes.sh" | |
fi | |
# Flag whether a restart is required after start-up. This is used as some extensions | |
# require a full restart of JupterLab before they will function. We set this based on | |
# the installation of configuration, rather than the plugin itself, for simplicity but | |
# at the cost of accuracy if no configuration is added for a given plugin. | |
JL_RECONFIG=0 | |
# Install advanced settings, if not present. | |
CF_SETTINGS="${PWD}/jupyterlab/configuration/lab/user-settings/@ryantam626/jupyterlab_code_formatter/settings.jupyterlab-settings" | |
NE_SETTINGS="${PWD}/jupyterlab/configuration/lab/user-settings/@jupyterlab/notebook-extension/tracker.jupyterlab-settings" | |
EM_SETTINGS="${PWD}/jupyterlab/configuration/lab/user-settings/@jupyterlab/extensionmanager-extension/plugin.jupyterlab-settings" | |
if [ ! -e "${CF_SETTINGS}" ]; then | |
mkdir -p "$(dirname "${CF_SETTINGS}")" | |
JL_RECONFIG=1 | |
cat > "${CF_SETTINGS}" <<EOF | |
{ | |
"formatOnSave": true, | |
"preferences": { | |
"default_formatter": { | |
"python": ["isort", "black"] | |
} | |
} | |
} | |
EOF | |
fi | |
if [ ! -e "${NE_SETTINGS}" ]; then | |
mkdir -p "$(dirname "${NE_SETTINGS}")" | |
JL_RECONFIG=1 | |
cat > "${NE_SETTINGS}" <<EOF | |
{ | |
"codeCellConfig": { | |
"rulers": [88] | |
} | |
} | |
EOF | |
fi | |
if [ ! -e "${EM_SETTINGS}" ]; then | |
mkdir -p "$(dirname "${EM_SETTINGS}")" | |
JL_RECONFIG=1 | |
cat > "${EM_SETTINGS}" <<EOF | |
{ | |
"disclaimed": true | |
} | |
EOF | |
fi | |
# No container? Start one. | |
if ! docker start JupyterLab > /dev/null 2>&1; then | |
docker run \ | |
-itd \ | |
--name JupyterLab \ | |
--user root \ | |
-p 127.0.0.1:8888:8888 \ | |
-e GRANT_SUDO=yes \ | |
-e NB_USER=jupyter \ | |
-w /home/jupyter/ \ | |
-e CHOWN_HOME=yes \ | |
-e JUPYTER_ENABLE_LAB=yes \ | |
-e RESTARTABLE=yes \ | |
-v"${PWD}/jupyterlab/work":/home/jupyter/work \ | |
-v"${PWD}/jupyterlab/configuration":/home/jupyter/.jupyter \ | |
-v"${PWD}/jupyterlab/requirements.txt":/home/jupyter/requirements.txt \ | |
-v"${PWD}/jupyterlab/start-up":/usr/local/bin/start-notebook.d/ \ | |
"${LAB_CONTAINER}" | |
fi | |
# Get token and print to STDOUT. | |
echo -n 'INFO: Waiting for JupyterLab to start...' | |
wait_for_jupyterlab | |
echo "DONE" | |
# If the JupterLab configuration has been changed, restart the container to make sure | |
# everything is correct. | |
if [ $JL_RECONFIG -gt 0 ]; then | |
echo -n 'INFO: JupterLab restarting due to configuration change...' | |
docker restart JupyterLab > /dev/null 2>&1 | |
wait_for_jupyterlab | |
fi | |
echo "DONE" | |
# Grab the JupyterLab token / URL. | |
echo -n 'INFO: Fetching JupyterLab token from logs...' | |
JUPYTER_URL=$(docker logs JupyterLab | grep -Eio 'http.*?token=[0-9a-f]+' | tail -n 1) | |
echo "DONE" | |
# Finally good to go! | |
echo "INFO: JupyterLab available at ${JUPYTER_URL}" | |
echo 'INFO: Attempting to open JupyterLab in browser...' | |
if [ -n "$(which open)" ]; then | |
open "${JUPYTER_URL}" | |
else | |
echo "INFO: No 'open' on this system, not opening browser." | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment