Newer
Older
ARG BUILD_ON_IMAGE=glcr.b-data.ch/r/ver
ARG NB_USER=jovyan
ARG NB_UID=1000
ARG CODE_BUILTIN_EXTENSIONS_DIR=/opt/code-server/lib/vscode/extensions
FROM ${BUILD_ON_IMAGE}:${R_VERSION}${CUDA_IMAGE_FLAVOR:+-}${CUDA_IMAGE_FLAVOR} AS files
COPY conf/ipython /files
COPY conf/jupyter /files
COPY conf${CUDA_IMAGE:+/cuda}/shell /files
## Copy content of skel directory to backup
RUN cp -a /files/etc/skel/. /files/var/backups/skel \
&& chown -R ${NB_UID}:${NB_GID} /files/var/backups/skel \
## Copy custom fonts
&& mkdir -p /files/usr/local/share/jupyter/lab/static/assets \
&& cp -a /files/opt/code-server/src/browser/media/css \
/files/usr/local/share/jupyter/lab/static/assets \
&& cp -a /files/opt/code-server/src/browser/media/fonts \
/files/usr/local/share/jupyter/lab/static/assets \
&& mkdir -p /files/etc/rstudio/fonts \
&& cp "/files/opt/code-server/src/browser/media/fonts/MesloLGS NF Regular.woff" \
"/files/etc/rstudio/fonts/MesloLGS NF.woff" \
&& cp "/files/opt/code-server/src/browser/media/fonts/MesloLGS NF Regular.woff2" \
"/files/etc/rstudio/fonts/MesloLGS NF.woff2" \
&& if [ -n "${CUDA_VERSION}" ]; then \
## Use standard R terminal for CUDA images
## radian forces usage of /usr[/local]/bin/python
sed -i 's|/usr/local/bin/radian|/usr/local/bin/R|g' \
/files/var/backups/skel/.local/share/code-server/User/settings.json; \
## Use entrypoint of CUDA image
mv /opt/nvidia/entrypoint.d /opt/nvidia/nvidia_entrypoint.sh \
/files/usr/local/bin; \
mv /files/usr/local/bin/start.sh \
/files/usr/local/bin/entrypoint.d/99-start.sh; \
mv /files/usr/local/bin/nvidia_entrypoint.sh \
/files/usr/local/bin/start.sh; \
&& if [ -z "${RSTUDIO_VERSION}" ]; then \
rm -rf /files/etc/rstudio \
/files/usr/local/bin/before-notebook.d/*-rstudio.sh \
/files/var/backups/skel/.config; \
fi \
## Ensure file modes are correct when using CI
## Otherwise set to 777 in the target image
&& find /files -type d -exec chmod 755 {} \; \
&& find /files -type f -exec chmod 644 {} \; \
&& find /files/usr/local/bin -type f -exec chmod 755 {} \; \
&& find /files/etc/profile.d -type f -exec chmod 755 {} \;
FROM glcr.b-data.ch/neovim/nvsi:${NEOVIM_VERSION} AS nvsi
FROM glcr.b-data.ch/git/gsi/${GIT_VERSION}/${BASE_IMAGE}:${BASE_IMAGE_TAG} AS gsi
FROM glcr.b-data.ch/git-lfs/glfsi:${GIT_LFS_VERSION} AS glfsi
FROM ${BUILD_ON_IMAGE}:${R_VERSION}${CUDA_IMAGE_FLAVOR:+-}${CUDA_IMAGE_FLAVOR}
ARG NB_USER
ARG NB_UID
ARG JUPYTERHUB_VERSION
ARG JUPYTERLAB_VERSION
ARG GIT_VERSION
ARG GIT_LFS_VERSION
ARG PANDOC_VERSION
ARG CUDA_IMAGE_LICENSE=${CUDA_VERSION:+"NVIDIA Deep Learning Container License"}
ARG IMAGE_LICENSE=${CUDA_IMAGE_LICENSE:-"MIT"}
ARG IMAGE_SOURCE=https://gitlab.b-data.ch/jupyterlab/r/docker-stack
ARG IMAGE_VENDOR="b-data GmbH"
ARG IMAGE_AUTHORS="Olivier Benz <olivier.benz@b-data.ch>"
LABEL org.opencontainers.image.licenses="$IMAGE_LICENSE" \
org.opencontainers.image.source="$IMAGE_SOURCE" \
org.opencontainers.image.vendor="$IMAGE_VENDOR" \
org.opencontainers.image.authors="$IMAGE_AUTHORS"
ENV PARENT_IMAGE=${BUILD_ON_IMAGE}:${R_VERSION}${CUDA_IMAGE_FLAVOR:+-}${CUDA_IMAGE_FLAVOR} \
NB_UID=${NB_UID} \
JUPYTERHUB_VERSION=${JUPYTERHUB_VERSION} \
JUPYTERLAB_VERSION=${JUPYTERLAB_VERSION} \
CODE_SERVER_VERSION=${CODE_SERVER_VERSION} \
RSTUDIO_VERSION=${RSTUDIO_VERSION} \
PANDOC_VERSION=${PANDOC_VERSION} \
BUILD_DATE=${BUILD_START}
## Installing V8 on Linux, the alternative way
## https://ropensci.org/blog/2020/11/12/installing-v8
ENV DOWNLOAD_STATIC_LIBV8=1
## Disable prompt to install miniconda
ENV RETICULATE_MINICONDA_ENABLED=0
## Install Neovim
COPY --from=nvsi /usr/local /usr/local
## Install Git LFS
COPY --from=glfsi /usr/local /usr/local
RUN dpkgArch="$(dpkg --print-architecture)" \
## Unminimise if the system has been minimised
&& if [ $(command -v unminimize) ]; then \
yes | unminimize; \
fi \
bash-completion \
build-essential \
## Neovim: Additional runtime recommendations
ripgrep \
## Git: Additional runtime dependencies
## Git: Additional runtime recommendations
## Python: Additional dev dependencies
&& if [ -z "$PYTHON_VERSION" ]; then \
apt-get -y install --no-install-recommends \
python3-dev \
## Install Python package installer
## (dep: python3-distutils, python3-setuptools and python3-wheel)
python3-pip \
## make some useful symlinks that are expected to exist
## ("/usr/bin/python" and friends)
for src in pydoc3 python3 python3-config; do \
dst="$(echo "$src" | tr -d 3)"; \
if [ -s "/usr/bin/$src" ] && [ ! -e "/usr/bin/$dst" ]; then \
ln -svT "$src" "/usr/bin/$dst"; \
fi \
else \
## Force update pip, setuptools and wheel
pip \
setuptools \
wheel; \
## Install font MesloLGS NF
&& mkdir -p /usr/share/fonts/truetype/meslo \
&& curl -sL https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Regular.ttf -o "/usr/share/fonts/truetype/meslo/MesloLGS NF Regular.ttf" \
&& curl -sL https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Bold.ttf -o "/usr/share/fonts/truetype/meslo/MesloLGS NF Bold.ttf" \
&& curl -sL https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Italic.ttf -o "/usr/share/fonts/truetype/meslo/MesloLGS NF Italic.ttf" \
&& curl -sL https://github.com/romkatv/powerlevel10k-media/raw/master/MesloLGS%20NF%20Bold%20Italic.ttf -o "/usr/share/fonts/truetype/meslo/MesloLGS NF Bold Italic.ttf" \
## Git: Set default branch name to main
&& git config --system init.defaultBranch main \
## Git: Store passwords for one hour in memory
&& git config --system credential.helper "cache --timeout=3600" \
## Git: Merge the default branch from the default remote when "git pull" is run
&& git config --system pull.rebase false \
&& curl -sLO https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-1-${dpkgArch}.deb \
&& dpkg -i pandoc-${PANDOC_VERSION}-1-${dpkgArch}.deb \
&& rm pandoc-${PANDOC_VERSION}-1-${dpkgArch}.deb \
## Delete potential user with UID 1000
&& if grep -q 1000 /etc/passwd; then \
userdel --remove $(id -un 1000); \
## Do not set user limits for sudo/sudo-i
&& sed -i 's/.*pam_limits.so/#&/g' /etc/pam.d/sudo \
&& if [ -f "/etc/pam.d/sudo-i" ]; then \
sed -i 's/.*pam_limits.so/#&/g' /etc/pam.d/sudo-i; \
fi \
&& useradd -l -m -s $(which zsh) -N -u ${NB_UID} ${NB_USER} \
## Mark home directory as populated
&& touch /home/${NB_USER}/.populated \
&& chown ${NB_UID}:${NB_GID} /home/${NB_USER}/.populated \
&& chmod go+w /home/${NB_USER}/.populated \
## Create backup directory for home directory
&& mkdir -p /var/backups/skel \
&& chown ${NB_UID}:${NB_GID} /var/backups/skel \
## Install Tini
&& curl -sL https://github.com/krallin/tini/releases/download/v0.19.0/tini-${dpkgArch} -o /usr/local/bin/tini \
&& chmod +x /usr/local/bin/tini \
ENV PATH=/opt/code-server/bin:$PATH \
CS_DISABLE_GETTING_STARTED_OVERRIDE=1
&& curl -sL https://github.com/coder/code-server/releases/download/v${CODE_SERVER_VERSION}/code-server-${CODE_SERVER_VERSION}-linux-$(dpkg --print-architecture).tar.gz | tar zxf - --no-same-owner --strip-components=1 \
## Copy custom fonts
&& mkdir -p /opt/code-server/src/browser/media/fonts \
&& cp -a /usr/share/fonts/truetype/meslo/*.ttf /opt/code-server/src/browser/media/fonts \
&& sed -i 's|</head>| <link rel="preload" href="{{BASE}}/_static/src/browser/media/fonts/MesloLGS NF Regular.woff2" as="font" type="font/woff2" crossorigin="anonymous">\n </head>|g' /opt/code-server/lib/vscode/out/vs/code/browser/workbench/workbench.html \
&& sed -i 's|</head>| <link rel="preload" href="{{BASE}}/_static/src/browser/media/fonts/MesloLGS NF Italic.woff2" as="font" type="font/woff2" crossorigin="anonymous">\n </head>|g' /opt/code-server/lib/vscode/out/vs/code/browser/workbench/workbench.html \
&& sed -i 's|</head>| <link rel="preload" href="{{BASE}}/_static/src/browser/media/fonts/MesloLGS NF Bold.woff2" as="font" type="font/woff2" crossorigin="anonymous">\n </head>|g' /opt/code-server/lib/vscode/out/vs/code/browser/workbench/workbench.html \
&& sed -i 's|</head>| <link rel="preload" href="{{BASE}}/_static/src/browser/media/fonts/MesloLGS NF Bold Italic.woff2" as="font" type="font/woff2" crossorigin="anonymous">\n </head>|g' /opt/code-server/lib/vscode/out/vs/code/browser/workbench/workbench.html \
&& sed -i 's|</head>| <link rel="stylesheet" type="text/css" href="{{BASE}}/_static/src/browser/media/css/fonts.css">\n </head>|g' /opt/code-server/lib/vscode/out/vs/code/browser/workbench/workbench.html \
## Install code-server extensions
&& cd /tmp \
&& curl -sLO https://dl.b-data.ch/vsix/piotrpalarz.vscode-gitignore-generator-1.0.3.vsix \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension piotrpalarz.vscode-gitignore-generator-1.0.3.vsix \
&& curl -sLO https://dl.b-data.ch/vsix/mutantdino.resourcemonitor-1.0.7.vsix \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension mutantdino.resourcemonitor-1.0.7.vsix \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension alefragnani.project-manager \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension GitHub.vscode-pull-request-github \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension GitLab.gitlab-workflow \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension ms-python.python \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension ms-toolsai.jupyter \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension christian-kohler.path-intellisense \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension eamodio.gitlens@11.7.0 \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension mhutchie.git-graph \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension redhat.vscode-yaml \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension grapecity.gc-excelviewer \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension editorconfig.editorconfig \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension DavidAnson.vscode-markdownlint \
&& cd /opt/code-server/lib/vscode/extensions/ms-toolsai.jupyter-* \
## Install RStudio
RUN if [ -n "${RSTUDIO_VERSION}" ]; then \
dpkgArch="$(dpkg --print-architecture)"; \
. /etc/os-release; \
apt-get update; \
## Install lsb-release
if [ "$ID" = "debian" ]; then \
dpkg --compare-versions "$VERSION_ID" lt "12"; \
condDebian=$?; \
fi; \
if [ "$ID" = "ubuntu" ]; then \
dpkg --compare-versions "$VERSION_ID" lt "23.04"; \
condUbuntu=$?; \
fi; \
if [ "$condDebian" = "0" ]; then \
http://mirrors.kernel.org/debian/pool/main/l/lsb-release-minimal/lsb-release_12.0-1_all.deb \
-o lsb-release.deb; \
dpkg -i lsb-release.deb; \
rm lsb-release.deb; \
elif [ "$condUbuntu" = "0" ]; then \
curl -sL \
http://mirrors.kernel.org/ubuntu/pool/main/l/lsb-release-minimal/lsb-release_12.0-2_all.deb \
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
-o lsb-release.deb; \
dpkg -i lsb-release.deb; \
rm lsb-release.deb; \
else \
apt-get -y install --no-install-recommends lsb-release; \
fi; \
## Map Ubuntu codename
if [ "$ID" = "debian" ]; then \
case "$VERSION_CODENAME" in \
bullseye) UBUNTU_CODENAME="focal" ;; \
bookworm) UBUNTU_CODENAME="jammy" ;; \
*) echo "error: Debian $VERSION unsupported"; exit 1 ;; \
esac; \
fi; \
## Install RStudio
apt-get -y install --no-install-recommends \
libapparmor1 \
libpq5 \
libssl-dev; \
rm -rf /var/lib/apt/lists/*; \
DOWNLOAD_FILE=rstudio-server-$(echo $RSTUDIO_VERSION | tr + -)-$dpkgArch.deb; \
wget --progress=dot:mega \
"https://download2.rstudio.org/server/$UBUNTU_CODENAME/$dpkgArch/$DOWNLOAD_FILE" || \
wget --progress=dot:mega \
"https://s3.amazonaws.com/rstudio-ide-build/server/$UBUNTU_CODENAME/$dpkgArch/$DOWNLOAD_FILE"; \
dpkg -i "$DOWNLOAD_FILE"; \
rm "$DOWNLOAD_FILE"; \
## Enable rstudio-server and rserver system-wide
ln -fs /usr/lib/rstudio-server/bin/rstudio-server /usr/local/bin; \
ln -fs /usr/lib/rstudio-server/bin/rserver /usr/local/bin; \
## Copy custom fonts
cp "/opt/code-server/src/browser/media/fonts/MesloLGS NF Regular.ttf" \
"/etc/rstudio/fonts/MesloLGS NF.ttf"; \
fi
RUN export PIP_BREAK_SYSTEM_PACKAGES=1 \
&& pip install \
jupyter-server-proxy \
jupyterhub==${JUPYTERHUB_VERSION} \
jupyterlab==${JUPYTERLAB_VERSION} \
jupyterlab-git \
jupyterlab-lsp \
notebook \
${RSTUDIO_VERSION:+jupyter-rsession-proxy} \
## Jupyter Server Proxy: Set maximum allowed HTTP body size to 10 GiB
&& sed -i 's/AsyncHTTPClient(/AsyncHTTPClient(max_body_size=10737418240, /g' \
/usr/local/lib/python*/*-packages/jupyter_server_proxy/handlers.py \
## Jupyter Server Proxy: Set maximum allowed websocket message size to 1 GiB
&& sed -i 's/"_default_max_message_size",.*$/"_default_max_message_size", 1024 \* 1024 \* 1024/g' \
/usr/local/lib/python*/*-packages/jupyter_server_proxy/websocket.py \
&& sed -i 's/_default_max_message_size =.*$/_default_max_message_size = 1024 \* 1024 \* 1024/g' \
/usr/local/lib/python*/*-packages/tornado/websocket.py \
## Copy custom fonts
&& mkdir -p /usr/local/share/jupyter/lab/static/assets/fonts \
&& cp -a /usr/share/fonts/truetype/meslo/*.ttf /usr/local/share/jupyter/lab/static/assets/fonts \
&& sed -i 's|</head>|<link rel="preload" href="{{page_config.fullStaticUrl}}/assets/fonts/MesloLGS NF Regular.woff2" as="font" type="font/woff2" crossorigin="anonymous"></head>|g' /usr/local/share/jupyter/lab/static/index.html \
&& sed -i 's|</head>|<link rel="preload" href="{{page_config.fullStaticUrl}}/assets/fonts/MesloLGS NF Italic.woff2" as="font" type="font/woff2" crossorigin="anonymous"></head>|g' /usr/local/share/jupyter/lab/static/index.html \
&& sed -i 's|</head>|<link rel="preload" href="{{page_config.fullStaticUrl}}/assets/fonts/MesloLGS NF Bold.woff2" as="font" type="font/woff2" crossorigin="anonymous"></head>|g' /usr/local/share/jupyter/lab/static/index.html \
&& sed -i 's|</head>|<link rel="preload" href="{{page_config.fullStaticUrl}}/assets/fonts/MesloLGS NF Bold Italic.woff2" as="font" type="font/woff2" crossorigin="anonymous"></head>|g' /usr/local/share/jupyter/lab/static/index.html \
&& sed -i 's|</head>|<link rel="stylesheet" type="text/css" href="{{page_config.fullStaticUrl}}/assets/css/fonts.css"></head>|g' /usr/local/share/jupyter/lab/static/index.html \
## Clean up
&& rm -rf /tmp/* \
## Install R related stuff
RUN apt-get update \
&& apt-get -y install --no-install-recommends \
## Current ZeroMQ library for R pbdZMQ
libzmq3-dev \
## Required for R extension
libcurl4-openssl-dev \
libfontconfig1-dev \
libssl-dev \
## Enabled at runtime and only if nvidia-smi and at least one GPU are present
&& if [ ! -z "$CUDA_IMAGE" ]; then \
nvblasLib="$(cd $CUDA_HOME/lib* && ls libnvblas.so* | head -n 1)"; \
cp -a $(which radian) $(which radian)_; \
echo '#!/bin/bash' > $(which radian)_; \
echo "command -v nvidia-smi >/dev/null && nvidia-smi -L | grep 'GPU[[:space:]]\?[[:digit:]]\+' >/dev/null && export LD_PRELOAD=$nvblasLib" \
>> $(which radian)_; \
echo "$(which radian) \"\${@}\"" >> $(which radian)_; \
## Install the R kernel for Jupyter, languageserver and httpgd
&& install2.r --error --deps TRUE --skipinstalled -n $NCPUS \
&& Rscript -e "IRkernel::installspec(user = FALSE, displayname = paste('R', Sys.getenv('R_VERSION')))" \
## Get rid of libcairo2-dev and its dependencies (incl. python3)
&& apt-get -y purge libcairo2-dev \
&& apt-get -y autoremove \
## IRkernel: Enable 'image/svg+xml' instead of 'image/png' for plot display
## IRkernel: Enable 'application/pdf' for PDF conversion
&& echo "options(jupyter.plot_mimetypes = c('text/plain', 'image/svg+xml', 'application/pdf'))" \
## IRkernel: Include user's private bin in PATH
&& echo "if (dir.exists(file.path(Sys.getenv('HOME'), 'bin')) &&" \
>> $(R RHOME)/etc/Rprofile.site \
&& echo ' !grepl(file.path(Sys.getenv('\''HOME'\''), '\''bin'\''), Sys.getenv('\''PATH'\''))) {' \
>> $(R RHOME)/etc/Rprofile.site \
&& echo " Sys.setenv(PATH = paste(file.path(Sys.getenv('HOME'), 'bin'), Sys.getenv('PATH')," \
>> $(R RHOME)/etc/Rprofile.site \
&& echo " sep = .Platform\$path.sep))}" \
>> $(R RHOME)/etc/Rprofile.site \
&& echo "if (dir.exists(file.path(Sys.getenv('HOME'), '.local', 'bin')) &&" \
>> $(R RHOME)/etc/Rprofile.site \
&& echo ' !grepl(file.path(Sys.getenv('\''HOME'\''), '\''.local'\'', '\''bin'\''), Sys.getenv('\''PATH'\''))) {' \
>> $(R RHOME)/etc/Rprofile.site \
&& echo " Sys.setenv(PATH = paste(file.path(Sys.getenv('HOME'), '.local', 'bin'), Sys.getenv('PATH')," \
>> $(R RHOME)/etc/Rprofile.site \
&& echo " sep = .Platform\$path.sep))}" \
>> $(R RHOME)/etc/Rprofile.site \
&& code-server --extensions-dir ${CODE_BUILTIN_EXTENSIONS_DIR} --install-extension REditorSupport.r \
## REditorSupport.r: Disable help panel and revert to old behaviour
&& echo "options(vsc.helpPanel = FALSE)" >> $(R RHOME)/etc/Rprofile.site \
## Change ownership and permission of $(R RHOME)/etc/*.site
&& chown :"$NB_GID" "$(R RHOME)/etc" "$(R RHOME)/etc/"*.site \
&& chmod g+w "$(R RHOME)/etc" "$(R RHOME)/etc/"*.site \
## Strip libraries of binary packages installed from P3M
&& RLS=$(Rscript -e "cat(Sys.getenv('R_LIBS_SITE'))") \
&& strip ${RLS}/*/libs/*.so \
${HOME}/.cache \
${HOME}/.config \
${HOME}/.ipython \
${HOME}/.local
## Switch back to ${NB_USER} to avoid accidental container runs as root
USER ${NB_USER}
ENV HOME=/home/${NB_USER} \
## Install Oh My Zsh with Powerlevel10k theme
RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended \
&& git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${HOME}/.oh-my-zsh/custom/themes/powerlevel10k \
&& ${HOME}/.oh-my-zsh/custom/themes/powerlevel10k/gitstatus/install -f \
&& sed -i 's/ZSH="\/home\/jovyan\/.oh-my-zsh"/ZSH="$HOME\/.oh-my-zsh"/g' ${HOME}/.zshrc \
&& sed -i 's/ZSH_THEME="robbyrussell"/ZSH_THEME="powerlevel10k\/powerlevel10k"/g' ${HOME}/.zshrc \
&& echo "\n# set PATH so it includes user's private bin if it exists\nif [ -d \"\$HOME/bin\" ] && [[ \"\$PATH\" != *\"\$HOME/bin\"* ]] ; then\n PATH=\"\$HOME/bin:\$PATH\"\nfi" | tee -a ${HOME}/.bashrc ${HOME}/.zshrc \
&& echo "\n# set PATH so it includes user's private bin if it exists\nif [ -d \"\$HOME/.local/bin\" ] && [[ \"\$PATH\" != *\"\$HOME/.local/bin\"* ]] ; then\n PATH=\"\$HOME/.local/bin:\$PATH\"\nfi" | tee -a ${HOME}/.bashrc ${HOME}/.zshrc \
&& echo "\n# Update last-activity timestamps while in screen/tmux session\nif [ ! -z \"\$TMUX\" -o ! -z \"\$STY\" ] ; then\n busy &\nfi" >> ${HOME}/.bashrc \
&& echo "\n# Update last-activity timestamps while in screen/tmux session\nif [ ! -z \"\$TMUX\" -o ! -z \"\$STY\" ] ; then\n setopt nocheckjobs\n busy &\nfi" >> ${HOME}/.zshrc \
&& echo "\n# To customize prompt, run \`p10k configure\` or edit ~/.p10k.zsh." >> ${HOME}/.zshrc \
&& echo "[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh" >> ${HOME}/.zshrc \
## Record population timestamp
&& date -uIseconds > ${HOME}/.populated \
## Copy files as late as possible to avoid cache busting
EXPOSE 8888
## Configure container startup