Skip to content

Commit 4d65200

Browse files
authored
Merge pull request #278 from nmfs-opensci/test
create script for geospatial r install; fix vscode extensions
2 parents 873f076 + 8bbd88e commit 4d65200

File tree

10 files changed

+89
-12
lines changed

10 files changed

+89
-12
lines changed

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LABEL org.opencontainers.image.author="eli.holmes@noaa.gov"
55
LABEL org.opencontainers.image.source=https://github.com/nmfs-opensci/py-rocket-base
66
LABEL org.opencontainers.image.description="Python (3.12), R (4.4.3), Desktop and Publishing tools"
77
LABEL org.opencontainers.image.licenses=Apache2.0
8-
LABEL org.opencontainers.image.version=2025.04.17
8+
LABEL org.opencontainers.image.version=2025.04.26
99

1010
USER root
1111

@@ -60,7 +60,7 @@ RUN wget -qO- https://raw.githubusercontent.com/retorquere/zotero-deb/master/ins
6060
RUN /pyrocket_scripts/install-apt-packages.sh ${REPO_DIR}/apt.txt
6161

6262
# Install some basic VS Code extensions
63-
RUN /pyrocket_scripts/install-vscode-extensions.sh ${REPO_DIR}/vscode-extensions.txt
63+
RUN /pyrocket_scripts/install-vscode-extensions.sh ${REPO_DIR}/vscode-extensions.txt || (echo "install-vscode-extensions.sh failed for some extensions" >&2 && exit 1)
6464

6565
# Re-enable man pages disabled in Ubuntu 18 minimal image
6666
# https://wiki.ubuntu.com/Minimal

README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# py-rocket-base: JupyterHub base image
22

3-
[![Build and push container image](https://github.com/nmfs-opensci/py-rocket-base/actions/workflows/build.yaml/badge.svg)](https://github.com/nmfs-opensci/py-rocket-base/actions/workflows/build.yaml)[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.13942617.svg)](https://doi.org/10.5281/zenodo.13942617)[![GitHub Release](https://img.shields.io/github/v/release/nmfs-opensci/py-rocket-base)](https://github.com/nmfs-opensci/py-rocket-base/releases)
3+
[![Build and push container image](https://github.com/nmfs-opensci/py-rocket-base/actions/workflows/build.yaml/badge.svg)](https://github.com/nmfs-opensci/py-rocket-base/actions/workflows/build.yaml) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.13942617.svg)](https://doi.org/10.5281/zenodo.13942617) [![GitHub Release](https://img.shields.io/github/v/release/nmfs-opensci/py-rocket-base)](https://github.com/nmfs-opensci/py-rocket-base/releases)
44

55
The py-rocket-base image is a base image for the JupyterHubs with Python and RStudio. The py-rocket-base image uses the [Pangeo base-image](https://github.com/pangeo-data/pangeo-docker-images/tree/master/base-image) (minus the `ONBUILD` commands) as the base image (stored in `py-rocket-base/base-image`) and the [pangeo-notebook metapackage](https://github.com/conda-forge/pangeo-notebook-feedstock/blob/main/recipe/meta.yaml) to setup the core JupyterHub environment (following [Pangeo base-notebook](https://github.com/pangeo-data/pangeo-docker-images/blob/master/base-notebook/environment.yml). [Additional Python packages](https://github.com/nmfs-opensci/py-rocket-base/blob/main/environment.yml) are installed to provide a fuller JupyterLab, RStudio, Desktop, and VSCode environment. The R environment is installed with [Rocker](https://rocker-project.org/) installation scripts. You can scroll through the Rocker [installation scripts](https://github.com/rocker-org/rocker-versioned2/blob/master/scripts/install_rstudio.sh) to see how that environment is set up.
66

@@ -14,9 +14,12 @@ The base image has the following support:
1414
* Python + JupyterLab with mamba handling package installation
1515
* R + RStudio with the rocker scripts and other functions available
1616
* Desktop ready for installing applications, including Java-based applications
17-
* VSCode
17+
* VSCode + R and Python extensions pre-installed
18+
* Publishing infrastructure installed (TexLive, Quarto, JupyterBook, MyST, pandoc).
1819

19-
*There are many ways to install R and RStudio into an image designed for JupyterHubs.* The objective of py-rocket-base is to create a JupyterHub (or binder) image such when you click the RStudio button in the JupyterLab UI to enter the RStudio UI (`/rstudio`), you **enter an environment that is the same as if you had used a Rocker image** but if you are in the JupyterLab UI (`/lab`), the **environment is the same as if you had used repo2docker** to create the environment. There are many other ways to install R and RStudio in a JupyterHub image. See History below for other approaches we have used.
20+
py-rocket-base is the base image for the NMFS OpenSci specialized images, specifically [py-rocket-geospatial](https://nmfs-opensci.github.io/container-images/).
21+
22+
*There are many ways to install R and RStudio into an image designed for JupyterHubs.* The objective of py-rocket-base is to create a JupyterHub (or binder) image such when you click the RStudio button in the JupyterLab UI to enter the RStudio UI (`/rstudio`), you **enter an environment that is the same as if you had used a Rocker image** but if you are in the JupyterLab UI (`/lab`), the **environment is the same as if you had used the Pangeo base image** to create the environment. See History below for other approaches we have used to create py-rocket over the years (and why this current approach is used).
2023

2124
## Using this in a JupyterHub
2225

@@ -34,9 +37,9 @@ py-rocket-base has pyrocket and rocket scripts that you can use to help customiz
3437

3538
## History and motivation
3639

37-
The original [py-rocket 1.0](https://github.com/NASA-Openscapes/py-rocket) was developed by Luis Lopez and was built off a Rocker base image. Carl Boettiger and Eli Holmes later altered the image (py-rocket 2.0) so that the Python environment matched the Pangeo image structure but the image was still built off a Rocker image. Subsequently, Carl Boettiger developed [repo2docker-r](https://github.com/boettiger-lab/repo2docker-r) that creates a JupyterHub-compatible image that uses a [Jupyter docker stack image](https://jupyter-docker-stacks.readthedocs.io/en/latest/) as base. For py-rocker 3.0, Eli Holmes used Carl's ideas but used [repo2docker](https://repo2docker.readthedocs.io/en/latest/) and [repo2docker-action](https://github.com/jupyterhub/repo2docker-action) to build the base image. To do this, the [CryoCloud hub image](https://github.com/CryoInTheCloud/hub-image) repo was used for the basic structure and approach. Eli added the `rocker.sh` script and `appendix` modifications to install R and RStudio via the Rocker scripts (rather than using a Rocker image as base). Yuvi Panda (repo2docker) gave input throughout the process as snags were hit.
40+
The original [py-rocket 1.0](https://github.com/NASA-Openscapes/py-rocket) was developed by Luis Lopez and was built off a Rocker base image. Carl Boettiger and Eli Holmes later altered the image (py-rocket 2.0) so that the Python environment matched the Pangeo image structure but the image was still built off a Rocker image. Subsequently, Carl Boettiger developed [repo2docker-r](https://github.com/boettiger-lab/repo2docker-r) that creates a JupyterHub-compatible image that uses a [Jupyter docker stack image](https://jupyter-docker-stacks.readthedocs.io/en/latest/) as base. For py-rocker 3.0, Eli Holmes used Carl's ideas but used [repo2docker](https://repo2docker.readthedocs.io/en/latest/) and [repo2docker-action](https://github.com/jupyterhub/repo2docker-action) to build the base image. To do this, the [CryoCloud hub image](https://github.com/CryoInTheCloud/hub-image) repo was used for the basic structure and approach. Eli added the `rocker.sh` script and `appendix` modifications to install R and RStudio via the Rocker scripts (rather than using a Rocker image as base). Yuvi Panda (repo2docker) gave input throughout the process as snags were hit. For py-rocker 4.0, current approach, repo2docker was abandoned and the base image was created by using the Pangeo base image with the `ONBUILD` parts removed. This approach was taken after discussions with Docker image mainainers at NASA who had quickly run into the need for more tailored control of the build process when customizing images. And in fact, the need for customizing the build process became an issue quickly and I was resorting to many "hacks" to circumvent repo2docker default behavior. Given the close relationship between Pangeo base image and repo2docker developers, using the Pangeo base image Docker file still results in a base image that is very similar to that created with repo2docker.
3841

39-
**Why Rocker for the R/RStudio environment?** The Rocker images are the standard for R/RStudio contanier images. They are heavily tested and regularly updated. There is a large developer community that fixes problems and bugs. The stack has gone through major revisions to improve modularity and they constantly innovating (integration for machine-learning, CUDA, BLAS, spatial, etc., etc.). py-rocket is building off that work without using the images directly. Instead it uses the Docker file code and the installation scripts. There are many other approaches to adding R and RStudio to images that work in JupyterHubs. See [repo2docker-r](https://github.com/boettiger-lab/repo2docker-r) that Carl developed and [r-conda](https://github.com/binder-examples/r-conda) for a conda native approach using repo2docker. py-rocket is not intended to create small images; it is intended to create images that emulate Rocker in the `/rstudio` environment on a JupyterHub.
42+
**Why Rocker for the R/RStudio environment?** The Rocker images are the standard for R/RStudio contanier images. They are heavily tested and regularly updated. There is a large developer community that fixes problems and bugs. The stack has gone through major revisions to improve modularity and they constantly innovating (integration for machine-learning, CUDA, BLAS, spatial, etc., etc.). py-rocket is building off that work without using the images directly. Instead it uses the Docker file code and the installation scripts. There are many other approaches to adding R and RStudio to images that work in JupyterHubs. See [repo2docker-r](https://github.com/boettiger-lab/repo2docker-r) that Carl developed and [r-conda](https://github.com/binder-examples/r-conda) for a conda native approach using repo2docker. py-rocket is not intended to create small images; it is intended to create images that emulate Rocker (verse and geospatial) in the R environments (whether in Jupyter Lab, RStudio or VSCode) on a JupyterHub.
4043

4144
## Building the documentation
4245

book/customizing.qmd

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ How to use the helper scripts is shown in [configuration files](configuration_fi
1111
* `install-conda-packages.sh`
1212
* `install-pip-packages.sh`
1313
* `install-r-packages.sh`
14+
* `install-geospatial-r.sh`
1415
* `install-apt-packages.sh`
1516
* `install-vscode-extensions.sh`
1617
* `install-desktop.sh`
@@ -33,12 +34,23 @@ RUN /pyrocket_scripts/install-conda-packages.sh environment.yml && \
3334
rm environment.yml
3435
```
3536

36-
Rocker scripts do not take arguments. Note that PATH must be given since rocker installation scripts will fail with conda on the path. The path specification will apply only within the specific RUN context and must be in the same RUN that the rocker script is run. The scripts should be run as root.
37+
If you have multiple files in the repo:
38+
```markdown
39+
COPY . /tmp2/
40+
RUN /pyrocket_scripts/install-conda-packages.sh /tmp2/environment.yml || echo "install-conda-packages.sh failed" || true
41+
RUN /pyrocket_scripts/install-r-packages.sh /tmp2/install.R || echo "install-r-package.sh failed" || true
42+
RUN /pyrocket_scripts/install-apt-packages.sh /tmp2/apt.txt || echo "install-apt-packages.sh failed" || true
43+
RUN /pyrocket_scripts/install-desktop.sh /tmp2/Desktop|| echo "setup-desktop.sh failed" || true
44+
RUN /pyrocket_scripts/install-geospatial-r.sh || echo "setup-desktop.sh failed" || true
45+
RUN rm -rf /tmp2
46+
```
47+
48+
Rocker scripts do not take arguments. Note that PATH must be given since rocker installation scripts will fail with conda on the path. The path specification will apply only within the specific RUN context and must be in the same RUN that the rocker script is run. The scripts should be run as root. Getting R packages to install to the system library, as opposed to /home which will be removed in a Jupyter Hub, can be tricky. See the `/pyrocket_scripts/install-geospatial-r.sh` script for an example.
3749

3850
```markdown
3951
USER root
4052
RUN PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && \
41-
/rocker_scripts/install_geospatial.sh
53+
/rocker_scripts/install_texlive.sh
4254
USER ${NB_USER}
4355
```
4456

extensions/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Download extensions here as a backup if open-vsx.org is down

extensions/quarto-1.120.0.vsix

8.51 MB
Binary file not shown.

scripts/install-geospatial-r.sh

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Ensure script is being run as root
5+
if [ "$(id -u)" -ne 0 ]; then
6+
echo "pyrocket_scripts/install-geospatial-r.sh script must be run as root." >&2
7+
exit 1
8+
fi
9+
10+
# Temporary R profile setup for system-wide library path
11+
echo '.libPaths("/usr/local/lib/R/site-library")' > /etc/littler.r
12+
echo '.libPaths("/usr/local/lib/R/site-library")' > /tmp/rprofile.site
13+
14+
# Run rocker-provided geospatial install script with temporary profile
15+
R_PROFILE=/tmp/rprofile.site \
16+
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
17+
/rocker_scripts/install_geospatial.sh
18+
19+
# Cleanup
20+
rm -f /etc/littler.r /tmp/rprofile.site

scripts/install-rocker.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ mkdir -p "${R_LIBS_USER}" && chown ${NB_USER}:staff "${R_LIBS_USER}"
142142
echo 'if (!dir.exists(Sys.getenv("R_LIBS_USER"))) dir.create(Sys.getenv("R_LIBS_USER"), recursive = TRUE)' >> "$RPROFILE_SITE"
143143
echo '.libPaths(c(Sys.getenv("R_LIBS_USER"), .libPaths()))' >> "$RPROFILE_SITE"
144144

145+
# Install the R kernel for JupyterLab and VSCode so they point to the rocker installed R
145146
Rscript - <<EOF
146147
install.packages('IRkernel', lib = .Library) # install in system library
147148
Sys.setenv(PATH = paste("/srv/conda/envs/notebook/bin", Sys.getenv("PATH"), sep = ":"))
@@ -167,6 +168,9 @@ setHook(packageEvent("reticulate", "attach"), function(...) {
167168
})
168169
EOF
169170

171+
# Install for VSCode. languageserver is required for VSCode. others make R nicer in VSCode and JLab.
172+
Rscript -e "install.packages(c('languageserver', 'httpgd', 'quarto'), lib = .Library)"
173+
170174
# Ensure jovyan can modify Rprofile.site and Renviron.site because start will need this, and allow user to alter rserver.conf
171175
# to set the gh-scoped-cred variables if they are present
172176
chown ${NB_USER}:staff /etc/rstudio/rserver.conf

scripts/install-vscode-extensions.sh

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#!/bin/bash
2-
# Required User: NB_USER
2+
set -euo pipefail
3+
IFS=$'\n\t'
4+
# Required User: root
35

46
# Install VSCode extensions.
5-
# These get installed to $CONDA_PREFIXshare/code-server/extensions/
7+
# These get installed to ${NB_PYTHON_PREFIX}/share/code-server/extensions/
68

79
# Check if a filename argument is provided
810
if [ -z "$1" ]; then
@@ -11,7 +13,7 @@ if [ -z "$1" ]; then
1113
exit 1
1214
fi
1315

14-
# Check if the script is run as root; folders will be made in /home and there are issues with this if user is jovyan
16+
# Check if the script is run as root;
1517
# Since some prior installs might have created .local as root. Easier just to install vscode extensions as root
1618
if [[ $(id -u) -ne 0 ]]; then
1719
echo "Error: install-vscode-extensions.sh must be run as root. Please use 'USER root' in your Dockerfile before running this script."
@@ -36,6 +38,7 @@ EXT_DIR="${NB_PYTHON_PREFIX}/share/code-server/extensions"
3638
install -o ${NB_USER} -g ${NB_USER} -m 755 -d "${EXT_DIR}"
3739

3840
# Install each extension listed in the file; skip empty lines or comments
41+
FAILED=0
3942
while IFS= read -r EXT; do
4043
# Remove comments and leading/trailing whitespace
4144
EXT=$(echo "$EXT" | sed 's/#.*//; s/^[[:space:]]*//; s/[[:space:]]*$//')
@@ -47,5 +50,16 @@ while IFS= read -r EXT; do
4750
echo " Successfully installed extension: $EXT"
4851
else
4952
echo " Failed to install extension: $EXT" >&2
53+
FAILED=1
5054
fi
5155
done < "$ext_file"
56+
57+
# Fix ownership so user can install/uninstall extensions later
58+
echo "Fixing ownership of installed extensions..."
59+
chown -R ${NB_USER}:${NB_USER} "${EXT_DIR}"
60+
chmod -R u+rwX,go+rX "${EXT_DIR}"
61+
62+
if [ "$FAILED" -ne 0 ]; then
63+
echo "One or more VSCode extensions failed to install. Exiting." >&2
64+
exit 1
65+
fi

start

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,25 @@ if [ -d "${REPO_DIR}/childstart/" ]; then
3232
fi
3333
done
3434
fi
35+
36+
# --- Set default VSCode code-server settings if missing ---
37+
DEFAULT_SETTINGS_DIR="/usr/local/share/code-server/User"
38+
USER_SETTINGS_DIR="/home/jovyan/.local/share/code-server/User"
39+
40+
# Ensure User settings directory exists
41+
mkdir -p "${USER_SETTINGS_DIR}"
42+
43+
if [ ! -f "${USER_SETTINGS_DIR}/settings.json" ]; then
44+
echo "Creating default VSCode settings.json..."
45+
cat > "${USER_SETTINGS_DIR}/settings.json" <<'EOF'
46+
{
47+
"remote.autoForwardPorts": true,
48+
"remote.autoForwardPortsSource": "process",
49+
"remote.restoreForwardedPorts": false
50+
}
51+
EOF
52+
chown -R jovyan:jovyan "${USER_SETTINGS_DIR}"
53+
else
54+
echo "User already has VSCode settings.json, not overwriting."
55+
fi
3556
exec "$@"

vscode-extensions.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
ms-python.python
2+
ms-toolsai.jupyter
23
quarto.quarto
4+
anwar.papyrus-pdf

0 commit comments

Comments
 (0)