diff --git a/book/desktop.qmd b/book/desktop.qmd index e3b7ab3..370666b 100644 --- a/book/desktop.qmd +++ b/book/desktop.qmd @@ -1,14 +1,16 @@ # Desktop -py-rocket-base includes the [xfce4 Desktop UI](https://www.xfce.org/) where you can run applications. A common application is to run QGIS or java-based applications. py-rocket-base -looks for files with the ending `.desktop`, `.xml` (optional), and `.png` (optional) associated with an application in a directory called Desktop in your repo. +py-rocket-base includes the [xfce4 Desktop UI](https://www.xfce.org/) where you can run applications. It is built off [freedesktop](https://www.freedesktop.org/wiki/Software/) (which is helpful to know if you are debugging and configuring). A common application is to run QGIS or java-based applications. py-rocket-base +looks for files with the ending `.desktop`, `.xml` (optional), and `.png` or `.svg` (optional) associated with an application in a directory called Desktop in your repo. + +py-rocket-base puts these `.desktop` files in `/usr/share/Desktop`. Typically these desktop files are in `~/Desktop`. But in a JupyterHub, the home directory is often persistent and py-rocket-base tries not to alter the user home directory. Also there might be orphaned desktop files in `~/Desktop` and so the user desktop UI would be inconsistent between users. Using `/usr/share/Desktop` does mean that users cannot add their own persistent applications to xfce4 Desktop but this would be difficult anyhow without root access in many cases. ## Adding an application in your child docker image ### Create the Desktop directory -Create the directory and add the .desktop and optional .png and .xml files. +Create the directory and add the .desktop and optional .png and .xml files. py-rocket-base will move them to the correct places (`/usr/share/applications` and `/usr/share/Desktop`, `/usr/share/mime/packages` and icon locations). ``` your-repo/ @@ -44,9 +46,19 @@ Keywords=map;globe; You can specify the mime types via xml. -#### .png +#### icons + +You can include a png or svg for the icon. py-rocket-base will place this in `/usr/share/icons/packages`. If you put your icon file in the Desktop directory in your repo, then in your desktop file, use the file name without the extension. + +``` +Icon=cdat +``` -You can include a png for the icon. py-rocket-base will place this in `/srv/repo/Desktop/` so use a location like `/srv/repo/Desktop/my.png` for your png that you include in your Desktop folder. +You can also use an absolute file path. + +``` +Icon=/srv/conda/envs/notebook/share/qgis/images/icons/qgis-icon-512x512.png +``` ### Install the application @@ -115,7 +127,8 @@ Add `cdat.png` icon to Desktop directory. To add new desktop applications, one needs to do the following. py-rocket-base does these steps automatically (via the start script) so that the user only has to put files into a Desktop directory in the docker build repo. * Install the application. See examples. -* Add a `.desktop` file to `${HOME}/.local/share/applications/`. -* To have an icon on the Desktop, you create a folder Desktop in the `${HOME}` and create a soft link to the `.desktop` files in `${HOME}/.local/share/applications/`. +* Add a `.desktop` file to an application directory. py-rocket-base puts these in `/usr/share/applications` but you will also see `${HOME}/.local/share/applications/`. +* To have an icon on the Desktop, you create a folder Desktop and tell XDG what directory to use. The directory is specified in `~/.config/user-dirs.dirs` which XDG sets. By default (XDG), this folder is `~/Desktop` but you can set it to something else. py-rocket-base sets a default value in `/etc/xdg/user-dirs.defaults`. This updates `~/.config/user-dirs.dirs` when the `/etc/xdg/xfce4/xinitrc` start script is run (when the `/desktop` button is clicked). +* XDG looks for .desktop files in the Desktop directory. py-rocket-base creates a soft link to the `.desktop` files in `/usr/share/applications/` in `/usr/share/Desktop`. See this [Medium article](https://medium.com/@zoldin/how-to-add-new-application-to-xfce-menu-list-d90955e101d5) for a description. \ No newline at end of file diff --git a/book/developers.qmd b/book/developers.qmd index d4303ea..82dcd15 100644 --- a/book/developers.qmd +++ b/book/developers.qmd @@ -96,28 +96,30 @@ most of the work in py-rocket-base is done here. appendix calls rocker.sh and in Each file is described below. -### apt2.txt +## apt2.txt This is not named `apt.txt` because these packages need to be installed after R is installed because the R scripts uninstall packages as part of cleanup. There are some packages that are required for Desktop (`/desktop`) to operate correctly. Packages needed for R and RStudio building (`/rstudio`) are installed via the rocker install scripts. -### environment.yml +## environment.yml These are added to the notebook conda environment and in py-rocket-base the basic packages needed for Jupyter Lab, RStudio and Desktop are added. Scientific packages are not added here. They will be added via child images that use py-rocket-base as the base image (in the FROM line). -### appendix +## appendix This a long file with many pieces. The pieces are explained below. Click on the number next to code to read about what that code block does. - ```r USER root # <1> -# repo2docker does not set this. This is the default env in repo2docker type images +# Set env variables # <2> +# This is the default env in repo2docker type images # <2> ENV CONDA_ENV=notebook # <2> +# Tell applications where to open desktop apps # <2> +DISPLAY=":1.0" # <2> # Install R, RStudio via Rocker scripts -ENV R_VERSION="4.4.1" # <3> +ENV R_VERSION="4.4.1" # <3> ENV R_DOCKERFILE="verse_${R_VERSION}" # <3> # This is in the rocker script but will not run since ${NB_USER} already exists # <3> # Needed because rocker scripts set permissions based on the staff group # <3> @@ -165,19 +167,22 @@ ONBUILD USER ${NB_USER} # <10> # copy all the files in the repo (i.e. ".") into ${REPO_DIR} ONBUILD COPY --chown=${NB_USER}:${NB_USER} . ${REPO_DIR}/childimage # <11> +# Desktop and apt.txt installs need to be done by root +ONBUILD USER root -# Copy Desktop files into ${REPO_DIR}/Desktop if they exist. start will copy to Application dir and Desktop -# Will not fail if Desktop dir exists but is empty -ONBUILD RUN echo "Checking for 'Desktop directory'..." \ # <13> - ; cd "${REPO_DIR}/childimage/" \ # <13> - ; if test -d Desktop ; then \ # <13> - mkdir -p "${REPO_DIR}/Desktop" && \ # <13> - [ "$(ls -A Desktop 2>/dev/null)" ] && cp -r Desktop/* "${REPO_DIR}/Desktop/"; \ # <13> - fi # <13> +# Copy Desktop files into ${REPO_DIR}/Desktop if they exist. start will copy to Application dir and Desktop # <13> +# Will not fail if Desktop dir exists but is empty # <13> +ONBUILD RUN echo "Checking for 'Desktop directory'..." \ # <13> + ; cd "${REPO_DIR}/childimage/" \ # <13> + ; if test -d Desktop ; then \ # <13> + mkdir -p "${REPO_DIR}/Desktop" && \ # <13> + cp -r Desktop/* "${REPO_DIR}/Desktop/" 2>/dev/null && \ # <13> + chmod +x "${REPO_DIR}/desktop.sh" ; \ # <13> + fi \ # <13> + ; "${REPO_DIR}/desktop.sh" # <13> # Install apt packages specified in a apt.txt file if it exists. # blank lines and comments are supported in apt.txt -ONBUILD USER root # <14> ONBUILD RUN echo "Checking for 'apt.txt'..." \ # <14> ; cd "${REPO_DIR}/childimage/" \ # <14> ; if test -f "apt.txt" ; then \ # <14> @@ -188,6 +193,7 @@ ONBUILD RUN echo "Checking for 'apt.txt'..." \ # <14> && apt-get clean \ # <14> && rm -rf /var/lib/apt/lists/* \ # <14> ; fi + ONBUILD USER ${NB_USER} # <15> # Add the conda environment @@ -256,9 +262,9 @@ USER ${NB_USER} # <21> WORKDIR ${HOME} # <21> ``` 1. Some commands need to be run as root, such as installing linux packages with `apt-get` -2. repo2docker does not set this environment variable and it is useful for child builds +2. Set variables. CONDA_ENV is useful for child builds 3. This section runs the script `rocker.sh` which installs R and RStudio using rocker scripts. -4. The rocker scripts build R from source and as part of clean up in the script, linux packages are removed that are not needed. repo2docker installs the packages in `apt.txt` automatically before the code in `appendix` thus the needed linux packages (which include packages for desktop) are put in `apt2.txt` (which repo2docker will not detect) and are installed separately here. The `grep -v ` etc code is processing `apt2.txt` and removing comments and blank lines. +4. The rocker scripts build R from source and as part of clean up in the script, linux packages are removed that are not needed. repo2docker installs the packages in `apt.txt` automatically before the code in `appendix` thus the needed linux packages (which include packages for the Xfce Desktop Environment in `\desktop`) are put in `apt2.txt`. repo2docker will not detect this file and we can install the packages here after R is built. The `grep -v ` etc code is processing `apt2.txt` and removing comments and blank lines. 5. Ubuntu does not have man pages installed by default. These lines activate `man` so users have the common help files. 6. This is some custom jupyter config to allow hidden files to be listed in the folder browser. 7. `book` and `docs` are the documentation files and are not needed in the image. @@ -267,7 +273,7 @@ WORKDIR ${HOME} # <21> 10. Set the user to NB_USER. Not strictly necessary but helps ensure that we don't accidentally create files that jovyan (NB_USER) cannot access. 11. Copy the child build context (files with the Docker file) into `${REPO_DIR}`. Make sure that jovyan owns the directory. Note, jovyan owns `${REPO_DIR}` (this is set by repo2docker). 12. empty -13. The Desktop files are put in a directory called Desktop. Copy them into `${REPO_DIR}/Desktop`. The start script will copy these into the correct location for the Desktop server. +13. The Desktop files are put in a directory called Desktop. Copy them into `${REPO_DIR}/Desktop`. The `desktop.sh` script will copy these into the correct location for the Desktop server. 14. If `apt.txt` is present, then install the packages. The code processes any comments or blank lines in `apt.txt`. This must be run as root so we switch to root to install. 15. Switch back to jovyan so we don't accidentally make files as belonging to root. 16. If `environment.yml` is present, install these into the conda environment and do some clean-up. Sometimes package solving will get rid of pip installed packages. We need to make sure that jupyter-remote-desktop-proxy does not disappear. @@ -342,9 +348,7 @@ fi # <13> ## start -Within a JupyterHub, the user home directory `$HOME` is typically re-mapped to the user persistent home directory. That means that the image build process cannot put things into `$HOME`, they would just be lost when `$HOME` is re-mapped. If a process needs to have something in the home directory, e.g. in some local user configuration, this must be done in the `start` script. The repo2docker docker image specifies that the start script is `${REPO_DIR}/start`. - -For py-rocket-base, the start script is used to move the Desktop files (`*.desktop`) to where the Desktop server expects them, which is `${HOME}/.local/share/applications/` and `${HOME}/Desktop`. Any `*.desktop` files found there will appear as clickable icons in `\desktop`. This must be done in start, since the files need to be put in `${HOME}` which not available until after the server starts. +Within a JupyterHub, the user home directory `$HOME` is typically re-mapped to the user persistent home directory. That means that the image build process cannot put things into `$HOME`, they would just be lost when `$HOME` is re-mapped. If a process needs to have something in the home directory, e.g. in some local user configuration, this must be done in the `start` script. The repo2docker docker image specifies that the start script is `${REPO_DIR}/start`. In py-rocket-base, the start scripts in a child docker file is souces in a subshell from the py-rocket-base start script. ``` #!/bin/bash @@ -353,49 +357,72 @@ set -euo pipefail # Start - Set any environment variables here # <1> # These are inherited by all processes, *except* RStudio # <1> # USE export =value # <1> -# Tell applications where to open desktop apps - this allows notebooks to pop open GUIs # <1> -export DISPLAY=":1.0" # <1> # source this file to get the variables defined in the rocker Dockerfile # <1> source ${REPO_DIR}/env.txt # <1> # End - Set any environment variables here # <1> -# The for loops will fail if they return null (no files). Set shell option nullglob -shopt -s nullglob # <2> - -# Add any .desktop files to the application database and desktop. This is done -# at startup-time because it's expected that a remote filesystem will be -# mounted at $HOME, which would overwrite the data if it was created at -# build-time. -APPLICATIONS_DIR="${HOME}/.local/share/applications" # <3> -DESKTOP_DIR="${HOME}/Desktop" # <3> -# Remove DESKTOP_DIR if it exists to avoid leftover files -if [ -d "${DESKTOP_DIR}" ]; then # <4> - rm -rf "${DESKTOP_DIR}" # <4> -fi # <4> -mkdir -p "${APPLICATIONS_DIR}" # <5> -mkdir -p "${DESKTOP_DIR}" # <5> -for desktop_file_path in ${REPO_DIR}/Desktop/*.desktop; do # <6> - cp "${desktop_file_path}" "${APPLICATIONS_DIR}/." # <6> - - # Symlink application to desktop and set execute permission so xfce (desktop) doesn't complain - desktop_file_name="$(basename ${desktop_file_path})" # <7> - # Set execute permissions on the copied .desktop file # <7> - chmod +x "${APPLICATIONS_DIR}/${desktop_file_name}" # <7> - ln -sf "${APPLICATIONS_DIR}/${desktop_file_name}" "${DESKTOP_DIR}/${desktop_file_name}" # <8> -done -update-desktop-database "${APPLICATIONS_DIR}" # <9> - -# Add MIME Type data from XML files to the MIME database. -MIME_DIR="${HOME}/.local/share/mime" # <10> -MIME_PACKAGES_DIR="${MIME_DIR}/packages" # <10> -mkdir -p "${MIME_PACKAGES_DIR}" # <10> -for mime_file_path in ${REPO_DIR}/Desktop/*.xml; do # <10> - cp "${mime_file_path}" "${MIME_PACKAGES_DIR}/." # <10> -done # <10> -update-mime-database "${MIME_DIR}" # <10> - # Run child start in a subshell to contain its environment -[ -f ${REPO_DIR}/childimage/start ] && ( source ${REPO_DIR}/childimage/start ) # <11> +[ -f ${REPO_DIR}/childimage/start ] && ( source ${REPO_DIR}/childimage/start ) # <2> exec "$@" -``` \ No newline at end of file +``` +1. In a Docker file so no way to dynamically set environmental variables, so the `env.txt` file with the `export =` are source at start up. +2. Run any child start script in a subshell. Run in a subshell to contain any `set` statements or similar. + +## desktop.sh + +The default for XDG and xfce4 is for Desktop files to be in ~/Desktop but this leads to a variety of problems. First we are altering the user directiory which seems rude, second orphan desktop files might be in ~/Desktop so who knows what the user Desktop experience with be, here the Desktop dir is set to /usr/share/Desktop so is part of the image. Users that really want to customize Desktop can change `~/.config/user-dirs.dirs`. Though py-rocket-base might not respect that. Not sure why you'd do that instead of just using a different image that doesn't have the py-rocket-base behavior. + +``` +#!/bin/bash +set -e + +# Copy in the Desktop files +APPLICATIONS_DIR=/usr/share/applications # <1> +DESKTOP_DIR=/usr/share/Desktop # <2> +mkdir -p "${DESKTOP_DIR}" # <2> +chown :staff /usr/share/Desktop # <2> +chmod 775 /usr/share/Desktop # <2> +# set the Desktop dir default for XDG +echo 'XDG_DESKTOP_DIR="${DESKTOP_DIR}"' > /etc/xdg/user-dirs.defaults # <3> + +# The for loops will fail if they return null (no files). Set shell option nullglob +shopt -s nullglob + +for desktop_file_path in ${REPO_DIR}/Desktop/*.desktop; do # <4> + cp "${desktop_file_path}" "${APPLICATIONS_DIR}/." # <4> + # Symlink application to desktop and set execute permission so xfce (desktop) doesn't complain # <4> + desktop_file_name="$(basename ${desktop_file_path})" # <4> + # Set execute permissions on the copied .desktop file # <4> + chmod +x "${APPLICATIONS_DIR}/${desktop_file_name}" # <4> + ln -sf "${APPLICATIONS_DIR}/${desktop_file_name}" "${DESKTOP_DIR}/${desktop_file_name}" # <4> +done # <4> +update-desktop-database "${APPLICATIONS_DIR}" # <4> + +# Add MIME Type data from XML files to the MIME database. # <5> +MIME_DIR="/usr/share/mime" # <5> +MIME_PACKAGES_DIR="${MIME_DIR}/packages" # <5> +mkdir -p "${MIME_PACKAGES_DIR}" # <5> +for mime_file_path in ${REPO_DIR}/Desktop/*.xml; do # <5> + cp "${mime_file_path}" "${MIME_PACKAGES_DIR}/." # <5> +done # <5> +update-mime-database "${MIME_DIR}" # <5> + +# Add icons # <6> +ICON_DIR="/usr/share/icons" # <6> +ICON_PACKAGES_DIR="${ICON_DIR}/packages" # <6> +mkdir -p "${ICON_PACKAGES_DIR}" # <6> +for icon_file_path in "${REPO_DIR}"/Desktop/*.png; do # <6> + cp "${icon_file_path}" "${ICON_PACKAGES_DIR}/" || echo "Failed to copy ${icon_file_path}" # <6> +done # <6> +for icon_file_path in "${REPO_DIR}"/Desktop/*.svg; do # <6> + cp "${icon_file_path}" "${ICON_PACKAGES_DIR}/" || echo "Failed to copy ${icon_file_path}" # <6> +done # <6> +gtk-update-icon-cache "${ICON_DIR}" # <6> +``` +1. This is the default local for system applications. +2. Create the Desktop directory and make sure jovyan can put files there. This is mainly for debugging. +3. Set up the default XDG_DESKTOP_DIR value. This will be copied to the `~.config` (by xinitrc). +4. Copy the .desktop file in the Desktop directory into the applications directory and make a symlink to the Desktop directory. The former means that the applications will appear in the menu in xfce4 desktop and the latter means there will be a desktop icon. +5. Add any mime xml files to the mime folder and update the mime database. +6. Add any png or svg icon files to the icon folder and update the icon database. \ No newline at end of file diff --git a/docs/desktop.html b/docs/desktop.html index a849bd2..917e6f4 100644 --- a/docs/desktop.html +++ b/docs/desktop.html @@ -205,12 +205,13 @@

4  xfce4 Desktop UI where you can run applications. A common application is to run QGIS or java-based applications. py-rocket-base looks for files with the ending .desktop, .xml (optional), and .png (optional) associated with an application in a directory called Desktop in your repo.

+

py-rocket-base includes the xfce4 Desktop UI where you can run applications. It is built off freedesktop (which is helpful to know if you are debugging and configuring). A common application is to run QGIS or java-based applications. py-rocket-base looks for files with the ending .desktop, .xml (optional), and .png or .svg (optional) associated with an application in a directory called Desktop in your repo.

+

py-rocket-base puts these .desktop files in /usr/share/Desktop. Typically these desktop files are in ~/Desktop. But in a JupyterHub, the home directory is often persistent and py-rocket-base tries not to alter the user home directory. Also there might be orphaned desktop files in ~/Desktop and so the user desktop UI would be inconsistent between users. Using /usr/share/Desktop does mean that users cannot add their own persistent applications to xfce4 Desktop but this would be difficult anyhow without root access in many cases.

4.1 Adding an application in your child docker image

4.1.1 Create the Desktop directory

-

Create the directory and add the .desktop and optional .png and .xml files.

+

Create the directory and add the .desktop and optional .png and .xml files. py-rocket-base will move them to the correct places (/usr/share/applications and /usr/share/Desktop, /usr/share/mime/packages and icon locations).

your-repo/
 ├── Dockerfile
 ├── optional extra files
@@ -238,9 +239,12 @@ 

4.1.1.2 .xml

You can specify the mime types via xml.

-
-

4.1.1.3 .png

-

You can include a png for the icon. py-rocket-base will place this in /srv/repo/Desktop/ so use a location like /srv/repo/Desktop/my.png for your png that you include in your Desktop folder.

+
+

4.1.1.3 icons

+

You can include a png or svg for the icon. py-rocket-base will place this in /usr/share/icons/packages. If you put your icon file in the Desktop directory in your repo, then in your desktop file, use the file name without the extension.

+
Icon=cdat
+

You can also use an absolute file path.

+
Icon=/srv/conda/envs/notebook/share/qgis/images/icons/qgis-icon-512x512.png
@@ -299,8 +303,9 @@

To add new desktop applications, one needs to do the following. py-rocket-base does these steps automatically (via the start script) so that the user only has to put files into a Desktop directory in the docker build repo.

  • Install the application. See examples.
  • -
  • Add a .desktop file to ${HOME}/.local/share/applications/.
  • -
  • To have an icon on the Desktop, you create a folder Desktop in the ${HOME} and create a soft link to the .desktop files in ${HOME}/.local/share/applications/.
  • +
  • Add a .desktop file to an application directory. py-rocket-base puts these in /usr/share/applications but you will also see ${HOME}/.local/share/applications/.
  • +
  • To have an icon on the Desktop, you create a folder Desktop and tell XDG what directory to use. The directory is specified in ~/.config/user-dirs.dirs which XDG sets. By default (XDG), this folder is ~/Desktop but you can set it to something else. py-rocket-base sets a default value in /etc/xdg/user-dirs.defaults. This updates ~/.config/user-dirs.dirs when the /etc/xdg/xfce4/xinitrc start script is run (when the /desktop button is clicked).
  • +
  • XDG looks for .desktop files in the Desktop directory. py-rocket-base creates a soft link to the .desktop files in /usr/share/applications/ in /usr/share/Desktop.

See this Medium article for a description.

diff --git a/docs/developers.html b/docs/developers.html index 2b48b46..0e1641c 100644 --- a/docs/developers.html +++ b/docs/developers.html @@ -219,14 +219,13 @@

Table of contents

-
  • 7.6 Basic structure of py-rocket-base -
  • -
  • 7.7 rocker.sh
  • -
  • 7.8 start
  • +
  • 7.6 Basic structure of py-rocket-base
  • +
  • 7.7 apt2.txt
  • +
  • 7.8 environment.yml
  • +
  • 7.9 appendix
  • +
  • 7.10 rocker.sh
  • +
  • 7.11 start
  • +
  • 7.12 desktop.sh
  • @@ -327,160 +326,168 @@

    -

    7.6.1 apt2.txt

    +

    +
    +

    7.7 apt2.txt

    This is not named apt.txt because these packages need to be installed after R is installed because the R scripts uninstall packages as part of cleanup. There are some packages that are required for Desktop (/desktop) to operate correctly. Packages needed for R and RStudio building (/rstudio) are installed via the rocker install scripts.

    -
    -

    7.6.2 environment.yml

    +
    +

    7.8 environment.yml

    These are added to the notebook conda environment and in py-rocket-base the basic packages needed for Jupyter Lab, RStudio and Desktop are added. Scientific packages are not added here. They will be added via child images that use py-rocket-base as the base image (in the FROM line).

    -
    -

    7.6.3 appendix

    +
    +

    7.9 appendix

    This a long file with many pieces. The pieces are explained below. Click on the number next to code to read about what that code block does.

    USER root
     
    -# repo2docker does not set this. This is the default env in repo2docker type images
    -ENV CONDA_ENV=notebook
    -
    -# Install R, RStudio via Rocker scripts
    -ENV R_VERSION="4.4.1"
    -ENV R_DOCKERFILE="verse_${R_VERSION}"
    -# This is in the rocker script but will not run since ${NB_USER} already exists
    -# Needed because rocker scripts set permissions based on the staff group
    -RUN usermod -a -G staff "${NB_USER}"
    -RUN PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && \
    -  chmod +x ${REPO_DIR}/rocker.sh && \
    -  ${REPO_DIR}/rocker.sh
    -
    -# Install linux packages after R installation since the R install scripts get rid of packages
    -# The package_list part is reading the file and doing clean-up to just have the list of packages
    -RUN package_list=$(grep -v '^\s*#' ${REPO_DIR}/apt2.txt | grep -v '^\s*$' | sed 's/\r//g; s/#.*//; s/^[[:space:]]*//; s/[[:space:]]*$//' | awk '{$1=$1};1') && \
    -  apt-get update && \
    -  apt-get install --yes --no-install-recommends $package_list && \
    -  apt-get autoremove --purge && \
    -  apt-get clean && \
    -  rm -rf /var/lib/apt/lists/*
    -  
    -# Re-enable man pages disabled in Ubuntu 18 minimal image
    -# https://wiki.ubuntu.com/Minimal
    -RUN yes | unminimize
    -# NOTE: $NB_PYTHON_PREFIX is the same as $CONDA_PREFIX at run-time.
    -# $CONDA_PREFIX isn't available in this context.
    -# NOTE: Prepending ensures a working path; if $MANPATH was previously empty,
    -# the trailing colon ensures that system paths are searched.
    -ENV MANPATH="${NB_PYTHON_PREFIX}/share/man:${MANPATH}"
    -RUN mandb
    -
    -# Add custom jupyter config. You can also put config.py files in the same place
    -COPY custom_jupyter_server_config.json ${NB_PYTHON_PREFIX}/etc/jupyter/jupyter_server_config.d/
    -COPY custom_jupyter_server_config.json ${NB_PYTHON_PREFIX}/etc/jupyter/jupyter_notebook_config.d/
    -
    -# Clean up extra files in ${REPO_DIR}
    -RUN rm -rf ${REPO_DIR}/book ${REPO_DIR}/docs
    +# Set env variables
    +# This is the default env in repo2docker type images
    +ENV CONDA_ENV=notebook
    +# Tell applications where to open desktop apps
    +DISPLAY=":1.0"
    +
    +# Install R, RStudio via Rocker scripts
    +ENV R_VERSION="4.4.1"
    +ENV R_DOCKERFILE="verse_${R_VERSION}"
    +# This is in the rocker script but will not run since ${NB_USER} already exists
    +# Needed because rocker scripts set permissions based on the staff group
    +RUN usermod -a -G staff "${NB_USER}"
    +RUN PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && \
    +  chmod +x ${REPO_DIR}/rocker.sh && \
    +  ${REPO_DIR}/rocker.sh
    +
    +# Install linux packages after R installation since the R install scripts get rid of packages
    +# The package_list part is reading the file and doing clean-up to just have the list of packages
    +RUN package_list=$(grep -v '^\s*#' ${REPO_DIR}/apt2.txt | grep -v '^\s*$' | sed 's/\r//g; s/#.*//; s/^[[:space:]]*//; s/[[:space:]]*$//' | awk '{$1=$1};1') && \
    +  apt-get update && \
    +  apt-get install --yes --no-install-recommends $package_list && \
    +  apt-get autoremove --purge && \
    +  apt-get clean && \
    +  rm -rf /var/lib/apt/lists/*
    +  
    +# Re-enable man pages disabled in Ubuntu 18 minimal image
    +# https://wiki.ubuntu.com/Minimal
    +RUN yes | unminimize
    +# NOTE: $NB_PYTHON_PREFIX is the same as $CONDA_PREFIX at run-time.
    +# $CONDA_PREFIX isn't available in this context.
    +# NOTE: Prepending ensures a working path; if $MANPATH was previously empty,
    +# the trailing colon ensures that system paths are searched.
    +ENV MANPATH="${NB_PYTHON_PREFIX}/share/man:${MANPATH}"
    +RUN mandb
    +
    +# Add custom jupyter config. You can also put config.py files in the same place
    +COPY custom_jupyter_server_config.json ${NB_PYTHON_PREFIX}/etc/jupyter/jupyter_server_config.d/
    +COPY custom_jupyter_server_config.json ${NB_PYTHON_PREFIX}/etc/jupyter/jupyter_notebook_config.d/
     
    -###################
    -# Set up behavior for child dockerfiles
    -# Convert NB_USER to ENV (from ARG) so that it passes to the child dockerfile
    -ENV NB_USER=${NB_USER}
    -
    -## ONBUILD section. These commands are run in child Dockerfiles. These are run right after the FROM image is loaded
    -
    -ONBUILD USER ${NB_USER}
    -
    -# ${REPO_DIR} is owned by ${NB_USER}
    -# copy all the files in the repo (i.e. ".") into ${REPO_DIR}
    -ONBUILD COPY --chown=${NB_USER}:${NB_USER} . ${REPO_DIR}/childimage
    -
    -
    -# Copy Desktop files into ${REPO_DIR}/Desktop if they exist. start will copy to Application dir and Desktop
    -# Will not fail if Desktop dir exists but is empty
    -ONBUILD RUN echo "Checking for 'Desktop directory'..." \
    -        ; cd "${REPO_DIR}/childimage/" \
    -        ; if test -d Desktop ; then \
    -            mkdir -p "${REPO_DIR}/Desktop" && \
    -            [ "$(ls -A Desktop 2>/dev/null)" ] && cp -r Desktop/* "${REPO_DIR}/Desktop/"; \
    -        fi
    -
    -# Install apt packages specified in a apt.txt file if it exists.
    -# blank lines and comments are supported in apt.txt
    -ONBUILD USER root
    -ONBUILD RUN echo "Checking for 'apt.txt'..." \
    -        ; cd "${REPO_DIR}/childimage/" \
    -        ; if test -f "apt.txt" ; then \
    -        package_list=$(grep -v '^\s*#' apt.txt | grep -v '^\s*$' | sed 's/\r//g; s/#.*//; s/^[[:space:]]*//; s/[[:space:]]*$//' | awk '{$1=$1};1') \
    -        && apt-get update --fix-missing > /dev/null \
    -        && apt-get install --yes --no-install-recommends $package_list \
    -        && apt-get autoremove --purge \
    -        && apt-get clean \
    -        && rm -rf /var/lib/apt/lists/* \
    -        ; fi
    -ONBUILD USER ${NB_USER}
    -
    -# Add the conda environment
    -# sometimes package solving will get rid of pip installed packages. Make sure jupyter-remote-desktop-proxy does not disappear
    -ONBUILD RUN echo "Checking for 'conda-lock.yml' or 'environment.yml'..." \
    -        ; cd "${REPO_DIR}/childimage/" \
    -        ; if test -f "conda-lock.yml" ; then echo "Using conda-lock.yml" & \
    -        conda-lock install --name ${CONDA_ENV} \
    -        && pip install --no-deps jupyter-remote-desktop-proxy \
    -        ; elif test -f "environment.yml" ; then echo "Using environment.yml" & \
    -        mamba env update --name ${CONDA_ENV} -f environment.yml  \
    -        && pip install --no-deps jupyter-remote-desktop-proxy \
    -        ; fi \
    -        && mamba clean -yaf \
    -        && find ${CONDA_DIR} -follow -type f -name '*.a' -delete \
    -        && find ${CONDA_DIR} -follow -type f -name '*.js.map' -delete \
    -        ; if ls ${NB_PYTHON_PREFIX}/lib/python*/site-packages/bokeh/server/static > /dev/null 2>&1; then \
    -        find ${NB_PYTHON_PREFIX}/lib/python*/site-packages/bokeh/server/static -follow -type f -name '*.js' ! -name '*.min.js' -delete \
    -        ; fi
    -
    -# If a requirements.txt file exists, use pip to install packages
    -# listed there. We don't want to save cached wheels in the image
    -# to avoid wasting space.
    -ONBUILD RUN echo "Checking for pip 'requirements.txt'..." \
    -        ; cd "${REPO_DIR}/childimage/" \
    -        ; if test -f "requirements.txt" ; then \
    -        ${NB_PYTHON_PREFIX}/bin/pip install --no-cache -r requirements.txt \
    -        ; fi
    -
    -# Add the r packages
    -ONBUILD RUN echo "Checking for 'install.R" \
    +# Clean up extra files in ${REPO_DIR}
    +RUN rm -rf ${REPO_DIR}/book ${REPO_DIR}/docs
    +
    +###################
    +# Set up behavior for child dockerfiles
    +# Convert NB_USER to ENV (from ARG) so that it passes to the child dockerfile
    +ENV NB_USER=${NB_USER}
    +
    +## ONBUILD section. These commands are run in child Dockerfiles. These are run right after the FROM image is loaded
    +
    +ONBUILD USER ${NB_USER}
    +
    +# ${REPO_DIR} is owned by ${NB_USER}
    +# copy all the files in the repo (i.e. ".") into ${REPO_DIR}
    +ONBUILD COPY --chown=${NB_USER}:${NB_USER} . ${REPO_DIR}/childimage
    +
    +# Desktop and apt.txt installs need to be done by root
    +ONBUILD USER root 
    +
    +# Copy Desktop files into ${REPO_DIR}/Desktop if they exist. start will copy to Application dir and Desktop
    +# Will not fail if Desktop dir exists but is empty
    +ONBUILD RUN echo "Checking for 'Desktop directory'..." \
    +        ; cd "${REPO_DIR}/childimage/" \
    +        ; if test -d Desktop ; then \
    +            mkdir -p "${REPO_DIR}/Desktop" && \
    +            cp -r Desktop/* "${REPO_DIR}/Desktop/" 2>/dev/null && \
    +            chmod +x "${REPO_DIR}/desktop.sh" ; \
    +        fi \
    +        ; "${REPO_DIR}/desktop.sh"
    +
    +# Install apt packages specified in a apt.txt file if it exists.
    +# blank lines and comments are supported in apt.txt
    +ONBUILD RUN echo "Checking for 'apt.txt'..." \
    +        ; cd "${REPO_DIR}/childimage/" \
    +        ; if test -f "apt.txt" ; then \
    +        package_list=$(grep -v '^\s*#' apt.txt | grep -v '^\s*$' | sed 's/\r//g; s/#.*//; s/^[[:space:]]*//; s/[[:space:]]*$//' | awk '{$1=$1};1') \
    +        && apt-get update --fix-missing > /dev/null \
    +        && apt-get install --yes --no-install-recommends $package_list \
    +        && apt-get autoremove --purge \
    +        && apt-get clean \
    +        && rm -rf /var/lib/apt/lists/* \
    +        ; fi
    +        
    +ONBUILD USER ${NB_USER}
    +
    +# Add the conda environment
    +# sometimes package solving will get rid of pip installed packages. Make sure jupyter-remote-desktop-proxy does not disappear
    +ONBUILD RUN echo "Checking for 'conda-lock.yml' or 'environment.yml'..." \
    +        ; cd "${REPO_DIR}/childimage/" \
    +        ; if test -f "conda-lock.yml" ; then echo "Using conda-lock.yml" & \
    +        conda-lock install --name ${CONDA_ENV} \
    +        && pip install --no-deps jupyter-remote-desktop-proxy \
    +        ; elif test -f "environment.yml" ; then echo "Using environment.yml" & \
    +        mamba env update --name ${CONDA_ENV} -f environment.yml  \
    +        && pip install --no-deps jupyter-remote-desktop-proxy \
    +        ; fi \
    +        && mamba clean -yaf \
    +        && find ${CONDA_DIR} -follow -type f -name '*.a' -delete \
    +        && find ${CONDA_DIR} -follow -type f -name '*.js.map' -delete \
    +        ; if ls ${NB_PYTHON_PREFIX}/lib/python*/site-packages/bokeh/server/static > /dev/null 2>&1; then \
    +        find ${NB_PYTHON_PREFIX}/lib/python*/site-packages/bokeh/server/static -follow -type f -name '*.js' ! -name '*.min.js' -delete \
    +        ; fi
    +
    +# If a requirements.txt file exists, use pip to install packages
    +# listed there. We don't want to save cached wheels in the image
    +# to avoid wasting space.
    +ONBUILD RUN echo "Checking for pip 'requirements.txt'..." \
             ; cd "${REPO_DIR}/childimage/" \
    -        ; if test -f "install.R" ; then echo "Using install.R" & \
    -        Rscript install.R \
    +        ; if test -f "requirements.txt" ; then \
    +        ${NB_PYTHON_PREFIX}/bin/pip install --no-cache -r requirements.txt \
             ; fi
     
    -# If a start file exists, put that under ${REPO_DIR}/childimage/start. 
    -# This is sourced in ${REPO_DIR}/start
    -ONBUILD RUN echo "Checking for 'start'..." \
    -        ; cd "${REPO_DIR}/childimage/" \
    -        ; if test -f "start" ; then \
    -        chmod +x start \
    -        ; fi
    -
    -# If a postBuild file exists, run it!
    -# After it's done, we try to remove any possible cruft commands there
    -# left behind under $HOME - particularly stuff that jupyterlab extensions
    -# leave behind.
    -ONBUILD RUN echo "Checking for 'postBuild'..." \
    -        ; cd "${REPO_DIR}/childimage/" \
    -        ; if test -f "postBuild" ; then \
    -        chmod +x postBuild \
    -        && ./postBuild \
    -        && rm -rf /tmp/* \
    -        && rm -rf ${HOME}/.cache ${HOME}/.npm ${HOME}/.yarn \
    -        && rm -rf ${NB_PYTHON_PREFIX}/share/jupyter/lab/staging \
    -        && find ${CONDA_DIR} -follow -type f -name '*.a' -delete \
    -        && find ${CONDA_DIR} -follow -type f -name '*.js.map' -delete \
    -        ; fi
    -
    -## End ONBUILD section for child images
    -################################
    -
    -# Revert to default user and home as pwd
    -USER ${NB_USER}
    -WORKDIR ${HOME}
    +# Add the r packages +ONBUILD RUN echo "Checking for 'install.R" \ + ; cd "${REPO_DIR}/childimage/" \ + ; if test -f "install.R" ; then echo "Using install.R" & \ + Rscript install.R \ + ; fi + +# If a start file exists, put that under ${REPO_DIR}/childimage/start. +# This is sourced in ${REPO_DIR}/start +ONBUILD RUN echo "Checking for 'start'..." \ + ; cd "${REPO_DIR}/childimage/" \ + ; if test -f "start" ; then \ + chmod +x start \ + ; fi + +# If a postBuild file exists, run it! +# After it's done, we try to remove any possible cruft commands there +# left behind under $HOME - particularly stuff that jupyterlab extensions +# leave behind. +ONBUILD RUN echo "Checking for 'postBuild'..." \ + ; cd "${REPO_DIR}/childimage/" \ + ; if test -f "postBuild" ; then \ + chmod +x postBuild \ + && ./postBuild \ + && rm -rf /tmp/* \ + && rm -rf ${HOME}/.cache ${HOME}/.npm ${HOME}/.yarn \ + && rm -rf ${NB_PYTHON_PREFIX}/share/jupyter/lab/staging \ + && find ${CONDA_DIR} -follow -type f -name '*.a' -delete \ + && find ${CONDA_DIR} -follow -type f -name '*.js.map' -delete \ + ; fi + +## End ONBUILD section for child images +################################ + +# Revert to default user and home as pwd +USER ${NB_USER} +WORKDIR ${HOME}
    1
    @@ -488,85 +495,84 @@

    2
    -repo2docker does not set this environment variable and it is useful for child builds +Set variables. CONDA_ENV is useful for child builds
    3
    -This section runs the script rocker.sh which installs R and RStudio using rocker scripts. +This section runs the script rocker.sh which installs R and RStudio using rocker scripts.
    4
    -The rocker scripts build R from source and as part of clean up in the script, linux packages are removed that are not needed. repo2docker installs the packages in apt.txt automatically before the code in appendix thus the needed linux packages (which include packages for desktop) are put in apt2.txt (which repo2docker will not detect) and are installed separately here. The grep -v etc code is processing apt2.txt and removing comments and blank lines. +The rocker scripts build R from source and as part of clean up in the script, linux packages are removed that are not needed. repo2docker installs the packages in apt.txt automatically before the code in appendix thus the needed linux packages (which include packages for the Xfce Desktop Environment in \desktop) are put in apt2.txt. repo2docker will not detect this file and we can install the packages here after R is built. The grep -v etc code is processing apt2.txt and removing comments and blank lines.
    5
    -Ubuntu does not have man pages installed by default. These lines activate man so users have the common help files. +Ubuntu does not have man pages installed by default. These lines activate man so users have the common help files.
    6
    -This is some custom jupyter config to allow hidden files to be listed in the folder browser. +This is some custom jupyter config to allow hidden files to be listed in the folder browser.
    7
    -book and docs are the documentation files and are not needed in the image. +book and docs are the documentation files and are not needed in the image.
    8
    -The NB_USER environmental variable is not exported by repo2docker (it is an argument confined to the parent build) but is very useful for child builds. So it is converted to an environmental variable. +The NB_USER environmental variable is not exported by repo2docker (it is an argument confined to the parent build) but is very useful for child builds. So it is converted to an environmental variable.
    9
    -This next section is a series of ONBUILD commands. These are only run if py-rocker-base is used in the FROM line in a child docker file. +This next section is a series of ONBUILD commands. These are only run if py-rocker-base is used in the FROM line in a child docker file.
    10
    -Set the user to NB_USER. Not strictly necessary but helps ensure that we don’t accidentally create files that jovyan (NB_USER) cannot access. +Set the user to NB_USER. Not strictly necessary but helps ensure that we don’t accidentally create files that jovyan (NB_USER) cannot access.
    11
    -Copy the child build context (files with the Docker file) into ${REPO_DIR}. Make sure that jovyan owns the directory. Note, jovyan owns ${REPO_DIR} (this is set by repo2docker). +Copy the child build context (files with the Docker file) into ${REPO_DIR}. Make sure that jovyan owns the directory. Note, jovyan owns ${REPO_DIR} (this is set by repo2docker).
    13
    -The Desktop files are put in a directory called Desktop. Copy them into ${REPO_DIR}/Desktop. The start script will copy these into the correct location for the Desktop server. +The Desktop files are put in a directory called Desktop. Copy them into ${REPO_DIR}/Desktop. The desktop.sh script will copy these into the correct location for the Desktop server.
    14
    -If apt.txt is present, then install the packages. The code processes any comments or blank lines in apt.txt. This must be run as root so we switch to root to install. +If apt.txt is present, then install the packages. The code processes any comments or blank lines in apt.txt. This must be run as root so we switch to root to install.
    15
    -Switch back to jovyan so we don’t accidentally make files as belonging to root. +Switch back to jovyan so we don’t accidentally make files as belonging to root.
    16
    -If environment.yml is present, install these into the conda environment and do some clean-up. Sometimes package solving will get rid of pip installed packages. We need to make sure that jupyter-remote-desktop-proxy does not disappear. +If environment.yml is present, install these into the conda environment and do some clean-up. Sometimes package solving will get rid of pip installed packages. We need to make sure that jupyter-remote-desktop-proxy does not disappear.
    17
    -If requirements.txt is present, install with pip and do some clean-up. +If requirements.txt is present, install with pip and do some clean-up.
    18
    -install.R is an R script where the user can specify how to install packages or run any other R code. +install.R is an R script where the user can specify how to install packages or run any other R code.
    19
    -start is run in the ${REPO_DIR}/start command in a subshell. The ${REPO_DIR}/start command cannot be replaced since it contains code to move the Desktop files into the correct place. +start is run in the ${REPO_DIR}/start command in a subshell. The ${REPO_DIR}/start command cannot be replaced since it contains code to move the Desktop files into the correct place.
    20
    -postBuild is a script. If present, run it and then do some clean-up. It is common to use postBuild to apply extensions or install packages that cannot be installed with apt. +postBuild is a script. If present, run it and then do some clean-up. It is common to use postBuild to apply extensions or install packages that cannot be installed with apt.
    21
    -The parent docker build completes by setting the user to jovyan and the working directory to ${HOME}. Within a JupyterHub deployment, ${HOME} will often be re-mapped to the user persistent memory so it is important not to write anything that needs to be persistent to ${HOME}, for example configuration. You can do this in the start script since that runs after the user directory is mapped or you can put configuration files in some place other than ${HOME}. +The parent docker build completes by setting the user to jovyan and the working directory to ${HOME}. Within a JupyterHub deployment, ${HOME} will often be re-mapped to the user persistent memory so it is important not to write anything that needs to be persistent to ${HOME}, for example configuration. You can do this in the start script since that runs after the user directory is mapped or you can put configuration files in some place other than ${HOME}.

    -
    -
    -

    7.7 rocker.sh

    +
    +

    7.10 rocker.sh

    This script will copy in the rocker scripts from rocker-versioned2 into ${REPO_DIR} to install things. It will read in one of the rocker docker files using R_DOCKERFILE defined in the appendix file (which is inserted into the main docker file). Variables defined here will only be available in this script. Click on the numbers in the script to learn what each section is doing.

    #!/bin/bash
     set -e
    @@ -665,40 +671,54 @@ 

    -

    7.8 start

    -

    Within a JupyterHub, the user home directory $HOME is typically re-mapped to the user persistent home directory. That means that the image build process cannot put things into $HOME, they would just be lost when $HOME is re-mapped. If a process needs to have something in the home directory, e.g. in some local user configuration, this must be done in the start script. The repo2docker docker image specifies that the start script is ${REPO_DIR}/start.

    -

    For py-rocket-base, the start script is used to move the Desktop files (*.desktop) to where the Desktop server expects them, which is ${HOME}/.local/share/applications/ and ${HOME}/Desktop. Any *.desktop files found there will appear as clickable icons in \desktop. This must be done in start, since the files need to be put in ${HOME} which not available until after the server starts.

    +
    +

    7.11 start

    +

    Within a JupyterHub, the user home directory $HOME is typically re-mapped to the user persistent home directory. That means that the image build process cannot put things into $HOME, they would just be lost when $HOME is re-mapped. If a process needs to have something in the home directory, e.g. in some local user configuration, this must be done in the start script. The repo2docker docker image specifies that the start script is ${REPO_DIR}/start. In py-rocket-base, the start scripts in a child docker file is souces in a subshell from the py-rocket-base start script.

    #!/bin/bash
     set -euo pipefail
     
     # Start - Set any environment variables here
     # These are inherited by all processes, *except* RStudio
     # USE export <parname>=value
    -# Tell applications where to open desktop apps - this allows notebooks to pop open GUIs
    -export DISPLAY=":1.0"
     # source this file to get the variables defined in the rocker Dockerfile
     source ${REPO_DIR}/env.txt
     # End - Set any environment variables here
     
    +# Run child start in a subshell to contain its environment
    +[ -f ${REPO_DIR}/childimage/start ] && ( source ${REPO_DIR}/childimage/start )
    +
    +exec "$@"
    +
    +
    1
    +
    +In a Docker file so no way to dynamically set environmental variables, so the env.txt file with the export <var>=<value> are source at start up. +
    +
    2
    +
    +Run any child start script in a subshell. Run in a subshell to contain any set statements or similar. +
    +
    +
    +
    +

    7.12 desktop.sh

    +

    The default for XDG and xfce4 is for Desktop files to be in ~/Desktop but this leads to a variety of problems. First we are altering the user directiory which seems rude, second orphan desktop files might be in ~/Desktop so who knows what the user Desktop experience with be, here the Desktop dir is set to /usr/share/Desktop so is part of the image. Users that really want to customize Desktop can change ~/.config/user-dirs.dirs. Though py-rocket-base might not respect that. Not sure why you’d do that instead of just using a different image that doesn’t have the py-rocket-base behavior.

    +
    #!/bin/bash
    +set -e
    +
    +# Copy in the Desktop files
    +APPLICATIONS_DIR=/usr/share/applications
    +DESKTOP_DIR=/usr/share/Desktop
    +mkdir -p "${DESKTOP_DIR}"
    +chown :staff /usr/share/Desktop
    +chmod 775 /usr/share/Desktop
    +# set the Desktop dir default for XDG
    +echo 'XDG_DESKTOP_DIR="${DESKTOP_DIR}"' > /etc/xdg/user-dirs.defaults
    +
     # The for loops will fail if they return null (no files). Set shell option nullglob
     shopt -s nullglob
     
    -# Add any .desktop files to the application database and desktop. This is done
    -# at startup-time because it's expected that a remote filesystem will be
    -# mounted at $HOME, which would overwrite the data if it was created at
    -# build-time.
    -APPLICATIONS_DIR="${HOME}/.local/share/applications"
    -DESKTOP_DIR="${HOME}/Desktop"
    -# Remove DESKTOP_DIR if it exists to avoid leftover files
    -if [ -d "${DESKTOP_DIR}" ]; then
    -    rm -rf "${DESKTOP_DIR}"
    -fi
    -mkdir -p "${APPLICATIONS_DIR}"
    -mkdir -p "${DESKTOP_DIR}"
     for desktop_file_path in ${REPO_DIR}/Desktop/*.desktop; do
         cp "${desktop_file_path}" "${APPLICATIONS_DIR}/."
    -
         # Symlink application to desktop and set execute permission so xfce (desktop) doesn't complain
         desktop_file_name="$(basename ${desktop_file_path})"
         # Set execute permissions on the copied .desktop file
    @@ -708,7 +728,7 @@ 

    +# Add icons +ICON_DIR="/usr/share/icons" +ICON_PACKAGES_DIR="${ICON_DIR}/packages" +mkdir -p "${ICON_PACKAGES_DIR}" +for icon_file_path in "${REPO_DIR}"/Desktop/*.png; do + cp "${icon_file_path}" "${ICON_PACKAGES_DIR}/" || echo "Failed to copy ${icon_file_path}" +done +for icon_file_path in "${REPO_DIR}"/Desktop/*.svg; do + cp "${icon_file_path}" "${ICON_PACKAGES_DIR}/" || echo "Failed to copy ${icon_file_path}" +done +gtk-update-icon-cache "${ICON_DIR}"

    +
    +
    1
    +
    +This is the default local for system applications. +
    +
    2
    +
    +Create the Desktop directory and make sure jovyan can put files there. This is mainly for debugging. +
    +
    3
    +
    +Set up the default XDG_DESKTOP_DIR value. This will be copied to the ~.config (by xinitrc). +
    +
    4
    +
    +Copy the .desktop file in the Desktop directory into the applications directory and make a symlink to the Desktop directory. The former means that the applications will appear in the menu in xfce4 desktop and the latter means there will be a desktop icon. +
    +
    5
    +
    +Add any mime xml files to the mime folder and update the mime database. +
    +
    6
    +
    +Add any png or svg icon files to the icon folder and update the icon database. +
    +
    diff --git a/docs/search.json b/docs/search.json index b6bf9b7..536ed60 100644 --- a/docs/search.json +++ b/docs/search.json @@ -154,7 +154,7 @@ "href": "desktop.html#adding-an-application-in-your-child-docker-image", "title": "4  Desktop", "section": "", - "text": "4.1.1 Create the Desktop directory\nCreate the directory and add the .desktop and optional .png and .xml files.\nyour-repo/\n├── Dockerfile\n├── optional extra files\n├── Desktop/\n│ ├── qgis.desktop\n│ ├── qgis.xml\n│ └── qgis.png\n\n4.1.1.1 .desktop file\nThe .desktop file is a configuration file that describes how an application is launched. The required parts are Name, Exec and Type. MimeType specifies what types of files the application can use (optional).\ncdat.desktop\n[Desktop Entry]\nType=Application\nName=CWUtils\nGenericName=CoastWatch Data Analysis Tool\nIcon=/srv/repo/Desktop/cdat.png\nTryExec=cdat\nExec=cdat %F\nTerminal=false\nStartupNotify=false\nCategories=Qt;Education;Science;Geography;\nKeywords=map;globe;\n\n\n4.1.1.2 .xml\nYou can specify the mime types via xml.\n\n\n4.1.1.3 .png\nYou can include a png for the icon. py-rocket-base will place this in /srv/repo/Desktop/ so use a location like /srv/repo/Desktop/my.png for your png that you include in your Desktop folder.\n\n\n\n4.1.2 Install the application\nHow you install the application really varies. Here are 2 examples.\nQGIS\n\nadd qgis to environment.yml\nadd libgl1-mesa-glx to apt.txt\nadd the .desktop file to Desktop directory\n\nqgis.desktop\n# From: https://github.com/qgis/QGIS/blob/ltr-3_28/linux/org.qgis.qgis.desktop.in\n[Desktop Entry]\nType=Application\nName=QGIS Desktop\nGenericName=Geographic Information System\nIcon=/srv/conda/envs/notebook/share/qgis/images/icons/qgis-icon-512x512.png\nTryExec=qgis\nExec=qgis %F\nTerminal=false\nStartupNotify=false\nCategories=Qt;Education;Science;Geography;\nMimeType=application/x-qgis-project;application/x-qgis-project-container;application/x-qgis-layer-settings;application/x-qgis-layer-definition;application/x-qgis-composer-template;image/tiff;image/jpeg;image/jp2;application/x-raster-aig;application/x-raster-ecw;application/x-raster-mrsid;application/x-mapinfo-mif;application/x-esri-shape;application/vnd.google-earth.kml+xml;application/vnd.google-earth.kmz;application/geopackage+sqlite3;\nKeywords=map;globe;postgis;wms;wfs;ogc;osgeo;\nStartupWMClass=QGIS3\nCoastWatch Utilities\nAdd this to the docker file to install\nUSER root\n# Install cwutils\nRUN cd /tmp && \\\n wget https://www.star.nesdis.noaa.gov/socd/coastwatch/cwf/cwutils-4_0_0_198-linux-x86_64.tar.gz && \\\n tar -zxf cwutils-4_0_0_198-linux-x86_64.tar.gz && \\\n rm -rf cwutils-4_0_0_198-linux-x86_64.tar.gz\nENV PATH=${PATH}:/tmp/cwutils_4.0.0.198/bin\nENV MANPATH=${MANPATH}:/tmp/cwutils_4.0.0.198/doc/man\nENV INSTALL4J ADD VM PARAMS=-Dsun.java2d.uiScale=2.0\nUSER ${NB_USER}\nAdd this cdat.desktop file to Desktop directory\n[Desktop Entry]\nType=Application\nName=CWUtils\nGenericName=CoastWatch Data Analysis Tool\nIcon=/srv/repo/Desktop/cdat.png\nTryExec=cdat\nExec=cdat %F\nTerminal=false\nStartupNotify=false\nCategories=Qt;Education;Science;Geography;\nKeywords=map;globe;\nAdd cdat.png icon to Desktop directory.", + "text": "4.1.1 Create the Desktop directory\nCreate the directory and add the .desktop and optional .png and .xml files. py-rocket-base will move them to the correct places (/usr/share/applications and /usr/share/Desktop, /usr/share/mime/packages and icon locations).\nyour-repo/\n├── Dockerfile\n├── optional extra files\n├── Desktop/\n│ ├── qgis.desktop\n│ ├── qgis.xml\n│ └── qgis.png\n\n4.1.1.1 .desktop file\nThe .desktop file is a configuration file that describes how an application is launched. The required parts are Name, Exec and Type. MimeType specifies what types of files the application can use (optional).\ncdat.desktop\n[Desktop Entry]\nType=Application\nName=CWUtils\nGenericName=CoastWatch Data Analysis Tool\nIcon=/srv/repo/Desktop/cdat.png\nTryExec=cdat\nExec=cdat %F\nTerminal=false\nStartupNotify=false\nCategories=Qt;Education;Science;Geography;\nKeywords=map;globe;\n\n\n4.1.1.2 .xml\nYou can specify the mime types via xml.\n\n\n4.1.1.3 icons\nYou can include a png or svg for the icon. py-rocket-base will place this in /usr/share/icons/packages. If you put your icon file in the Desktop directory in your repo, then in your desktop file, use the file name without the extension.\nIcon=cdat\nYou can also use an absolute file path.\nIcon=/srv/conda/envs/notebook/share/qgis/images/icons/qgis-icon-512x512.png\n\n\n\n4.1.2 Install the application\nHow you install the application really varies. Here are 2 examples.\nQGIS\n\nadd qgis to environment.yml\nadd libgl1-mesa-glx to apt.txt\nadd the .desktop file to Desktop directory\n\nqgis.desktop\n# From: https://github.com/qgis/QGIS/blob/ltr-3_28/linux/org.qgis.qgis.desktop.in\n[Desktop Entry]\nType=Application\nName=QGIS Desktop\nGenericName=Geographic Information System\nIcon=/srv/conda/envs/notebook/share/qgis/images/icons/qgis-icon-512x512.png\nTryExec=qgis\nExec=qgis %F\nTerminal=false\nStartupNotify=false\nCategories=Qt;Education;Science;Geography;\nMimeType=application/x-qgis-project;application/x-qgis-project-container;application/x-qgis-layer-settings;application/x-qgis-layer-definition;application/x-qgis-composer-template;image/tiff;image/jpeg;image/jp2;application/x-raster-aig;application/x-raster-ecw;application/x-raster-mrsid;application/x-mapinfo-mif;application/x-esri-shape;application/vnd.google-earth.kml+xml;application/vnd.google-earth.kmz;application/geopackage+sqlite3;\nKeywords=map;globe;postgis;wms;wfs;ogc;osgeo;\nStartupWMClass=QGIS3\nCoastWatch Utilities\nAdd this to the docker file to install\nUSER root\n# Install cwutils\nRUN cd /tmp && \\\n wget https://www.star.nesdis.noaa.gov/socd/coastwatch/cwf/cwutils-4_0_0_198-linux-x86_64.tar.gz && \\\n tar -zxf cwutils-4_0_0_198-linux-x86_64.tar.gz && \\\n rm -rf cwutils-4_0_0_198-linux-x86_64.tar.gz\nENV PATH=${PATH}:/tmp/cwutils_4.0.0.198/bin\nENV MANPATH=${MANPATH}:/tmp/cwutils_4.0.0.198/doc/man\nENV INSTALL4J ADD VM PARAMS=-Dsun.java2d.uiScale=2.0\nUSER ${NB_USER}\nAdd this cdat.desktop file to Desktop directory\n[Desktop Entry]\nType=Application\nName=CWUtils\nGenericName=CoastWatch Data Analysis Tool\nIcon=/srv/repo/Desktop/cdat.png\nTryExec=cdat\nExec=cdat %F\nTerminal=false\nStartupNotify=false\nCategories=Qt;Education;Science;Geography;\nKeywords=map;globe;\nAdd cdat.png icon to Desktop directory.", "crumbs": [ "4  Desktop" ] @@ -164,7 +164,7 @@ "href": "desktop.html#whats-happening", "title": "4  Desktop", "section": "4.2 What’s happening", - "text": "4.2 What’s happening\nTo add new desktop applications, one needs to do the following. py-rocket-base does these steps automatically (via the start script) so that the user only has to put files into a Desktop directory in the docker build repo.\n\nInstall the application. See examples.\nAdd a .desktop file to ${HOME}/.local/share/applications/.\nTo have an icon on the Desktop, you create a folder Desktop in the ${HOME} and create a soft link to the .desktop files in ${HOME}/.local/share/applications/.\n\nSee this Medium article for a description.", + "text": "4.2 What’s happening\nTo add new desktop applications, one needs to do the following. py-rocket-base does these steps automatically (via the start script) so that the user only has to put files into a Desktop directory in the docker build repo.\n\nInstall the application. See examples.\nAdd a .desktop file to an application directory. py-rocket-base puts these in /usr/share/applications but you will also see ${HOME}/.local/share/applications/.\nTo have an icon on the Desktop, you create a folder Desktop and tell XDG what directory to use. The directory is specified in ~/.config/user-dirs.dirs which XDG sets. By default (XDG), this folder is ~/Desktop but you can set it to something else. py-rocket-base sets a default value in /etc/xdg/user-dirs.defaults. This updates ~/.config/user-dirs.dirs when the /etc/xdg/xfce4/xinitrc start script is run (when the /desktop button is clicked).\nXDG looks for .desktop files in the Desktop directory. py-rocket-base creates a soft link to the .desktop files in /usr/share/applications/ in /usr/share/Desktop.\n\nSee this Medium article for a description.", "crumbs": [ "4  Desktop" ] @@ -294,7 +294,37 @@ "href": "developers.html#basic-structure-of-py-rocket-base", "title": "7  Developer notes", "section": "7.6 Basic structure of py-rocket-base", - "text": "7.6 Basic structure of py-rocket-base\nThe py-rocket-base docker build has the following structure:\n# base image and environment\nrepo2docker sets this up\nrepo2docker-action sets the directory where the build files \n are put to /srv/repo (via $REPO_DIR in GitHub Action)\n and ownership is set to jovyan (via $NB_USER)\n \n# environment.yml\nrepo2docker adds these packages to the conda notebook environment\n\n# start\nrepo2docker points the Docker image entrypoint (command run on start) to this file\n${REPO_DIR}/start\n\n# appendix\nrepo2docker-action adds the Docker commands here to the end of Dockerfile\nmost of the work in py-rocket-base is done here. appendix calls rocker.sh and install packages in apt2.txt\nEach file is described below.\n\n7.6.1 apt2.txt\nThis is not named apt.txt because these packages need to be installed after R is installed because the R scripts uninstall packages as part of cleanup. There are some packages that are required for Desktop (/desktop) to operate correctly. Packages needed for R and RStudio building (/rstudio) are installed via the rocker install scripts.\n\n\n7.6.2 environment.yml\nThese are added to the notebook conda environment and in py-rocket-base the basic packages needed for Jupyter Lab, RStudio and Desktop are added. Scientific packages are not added here. They will be added via child images that use py-rocket-base as the base image (in the FROM line).\n\n\n7.6.3 appendix\nThis a long file with many pieces. The pieces are explained below. Click on the number next to code to read about what that code block does.\n1USER root\n\n# repo2docker does not set this. This is the default env in repo2docker type images\n2ENV CONDA_ENV=notebook\n\n# Install R, RStudio via Rocker scripts\n3ENV R_VERSION=\"4.4.1\"\nENV R_DOCKERFILE=\"verse_${R_VERSION}\"\n# This is in the rocker script but will not run since ${NB_USER} already exists\n# Needed because rocker scripts set permissions based on the staff group\nRUN usermod -a -G staff \"${NB_USER}\"\nRUN PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && \\\n chmod +x ${REPO_DIR}/rocker.sh && \\\n ${REPO_DIR}/rocker.sh\n\n4# Install linux packages after R installation since the R install scripts get rid of packages\n# The package_list part is reading the file and doing clean-up to just have the list of packages\nRUN package_list=$(grep -v '^\\s*#' ${REPO_DIR}/apt2.txt | grep -v '^\\s*$' | sed 's/\\r//g; s/#.*//; s/^[[:space:]]*//; s/[[:space:]]*$//' | awk '{$1=$1};1') && \\\n apt-get update && \\\n apt-get install --yes --no-install-recommends $package_list && \\\n apt-get autoremove --purge && \\\n apt-get clean && \\\n rm -rf /var/lib/apt/lists/*\n \n# Re-enable man pages disabled in Ubuntu 18 minimal image\n# https://wiki.ubuntu.com/Minimal\n5RUN yes | unminimize\n# NOTE: $NB_PYTHON_PREFIX is the same as $CONDA_PREFIX at run-time.\n# $CONDA_PREFIX isn't available in this context.\n# NOTE: Prepending ensures a working path; if $MANPATH was previously empty,\n# the trailing colon ensures that system paths are searched.\nENV MANPATH=\"${NB_PYTHON_PREFIX}/share/man:${MANPATH}\"\nRUN mandb\n\n# Add custom jupyter config. You can also put config.py files in the same place\n6COPY custom_jupyter_server_config.json ${NB_PYTHON_PREFIX}/etc/jupyter/jupyter_server_config.d/\nCOPY custom_jupyter_server_config.json ${NB_PYTHON_PREFIX}/etc/jupyter/jupyter_notebook_config.d/\n\n# Clean up extra files in ${REPO_DIR}\n7RUN rm -rf ${REPO_DIR}/book ${REPO_DIR}/docs\n\n###################\n# Set up behavior for child dockerfiles\n# Convert NB_USER to ENV (from ARG) so that it passes to the child dockerfile\n8ENV NB_USER=${NB_USER}\n\n9## ONBUILD section. These commands are run in child Dockerfiles. These are run right after the FROM image is loaded\n\n10ONBUILD USER ${NB_USER}\n\n# ${REPO_DIR} is owned by ${NB_USER}\n# copy all the files in the repo (i.e. \".\") into ${REPO_DIR}\n11ONBUILD COPY --chown=${NB_USER}:${NB_USER} . ${REPO_DIR}/childimage\n\n\n# Copy Desktop files into ${REPO_DIR}/Desktop if they exist. start will copy to Application dir and Desktop\n# Will not fail if Desktop dir exists but is empty\n13ONBUILD RUN echo \"Checking for 'Desktop directory'...\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -d Desktop ; then \\\n mkdir -p \"${REPO_DIR}/Desktop\" && \\\n [ \"$(ls -A Desktop 2>/dev/null)\" ] && cp -r Desktop/* \"${REPO_DIR}/Desktop/\"; \\\n fi\n\n# Install apt packages specified in a apt.txt file if it exists.\n# blank lines and comments are supported in apt.txt\n14ONBUILD USER root\nONBUILD RUN echo \"Checking for 'apt.txt'...\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -f \"apt.txt\" ; then \\\n package_list=$(grep -v '^\\s*#' apt.txt | grep -v '^\\s*$' | sed 's/\\r//g; s/#.*//; s/^[[:space:]]*//; s/[[:space:]]*$//' | awk '{$1=$1};1') \\\n && apt-get update --fix-missing > /dev/null \\\n && apt-get install --yes --no-install-recommends $package_list \\\n && apt-get autoremove --purge \\\n && apt-get clean \\\n && rm -rf /var/lib/apt/lists/* \\\n ; fi\n15ONBUILD USER ${NB_USER}\n\n# Add the conda environment\n# sometimes package solving will get rid of pip installed packages. Make sure jupyter-remote-desktop-proxy does not disappear\n16ONBUILD RUN echo \"Checking for 'conda-lock.yml' or 'environment.yml'...\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -f \"conda-lock.yml\" ; then echo \"Using conda-lock.yml\" & \\\n conda-lock install --name ${CONDA_ENV} \\\n && pip install --no-deps jupyter-remote-desktop-proxy \\\n ; elif test -f \"environment.yml\" ; then echo \"Using environment.yml\" & \\\n mamba env update --name ${CONDA_ENV} -f environment.yml \\\n && pip install --no-deps jupyter-remote-desktop-proxy \\\n ; fi \\\n && mamba clean -yaf \\\n && find ${CONDA_DIR} -follow -type f -name '*.a' -delete \\\n && find ${CONDA_DIR} -follow -type f -name '*.js.map' -delete \\\n ; if ls ${NB_PYTHON_PREFIX}/lib/python*/site-packages/bokeh/server/static > /dev/null 2>&1; then \\\n find ${NB_PYTHON_PREFIX}/lib/python*/site-packages/bokeh/server/static -follow -type f -name '*.js' ! -name '*.min.js' -delete \\\n ; fi\n\n# If a requirements.txt file exists, use pip to install packages\n# listed there. We don't want to save cached wheels in the image\n# to avoid wasting space.\n17ONBUILD RUN echo \"Checking for pip 'requirements.txt'...\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -f \"requirements.txt\" ; then \\\n ${NB_PYTHON_PREFIX}/bin/pip install --no-cache -r requirements.txt \\\n ; fi\n\n# Add the r packages\n18ONBUILD RUN echo \"Checking for 'install.R\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -f \"install.R\" ; then echo \"Using install.R\" & \\\n Rscript install.R \\\n ; fi\n\n# If a start file exists, put that under ${REPO_DIR}/childimage/start. \n# This is sourced in ${REPO_DIR}/start\n19ONBUILD RUN echo \"Checking for 'start'...\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -f \"start\" ; then \\\n chmod +x start \\\n ; fi\n\n# If a postBuild file exists, run it!\n# After it's done, we try to remove any possible cruft commands there\n# left behind under $HOME - particularly stuff that jupyterlab extensions\n# leave behind.\n20ONBUILD RUN echo \"Checking for 'postBuild'...\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -f \"postBuild\" ; then \\\n chmod +x postBuild \\\n && ./postBuild \\\n && rm -rf /tmp/* \\\n && rm -rf ${HOME}/.cache ${HOME}/.npm ${HOME}/.yarn \\\n && rm -rf ${NB_PYTHON_PREFIX}/share/jupyter/lab/staging \\\n && find ${CONDA_DIR} -follow -type f -name '*.a' -delete \\\n && find ${CONDA_DIR} -follow -type f -name '*.js.map' -delete \\\n ; fi\n\n## End ONBUILD section for child images\n################################\n\n# Revert to default user and home as pwd\n21USER ${NB_USER}\nWORKDIR ${HOME}\n\n1\n\nSome commands need to be run as root, such as installing linux packages with apt-get\n\n2\n\nrepo2docker does not set this environment variable and it is useful for child builds\n\n3\n\nThis section runs the script rocker.sh which installs R and RStudio using rocker scripts.\n\n4\n\nThe rocker scripts build R from source and as part of clean up in the script, linux packages are removed that are not needed. repo2docker installs the packages in apt.txt automatically before the code in appendix thus the needed linux packages (which include packages for desktop) are put in apt2.txt (which repo2docker will not detect) and are installed separately here. The grep -v etc code is processing apt2.txt and removing comments and blank lines.\n\n5\n\nUbuntu does not have man pages installed by default. These lines activate man so users have the common help files.\n\n6\n\nThis is some custom jupyter config to allow hidden files to be listed in the folder browser.\n\n7\n\nbook and docs are the documentation files and are not needed in the image.\n\n8\n\nThe NB_USER environmental variable is not exported by repo2docker (it is an argument confined to the parent build) but is very useful for child builds. So it is converted to an environmental variable.\n\n9\n\nThis next section is a series of ONBUILD commands. These are only run if py-rocker-base is used in the FROM line in a child docker file.\n\n10\n\nSet the user to NB_USER. Not strictly necessary but helps ensure that we don’t accidentally create files that jovyan (NB_USER) cannot access.\n\n11\n\nCopy the child build context (files with the Docker file) into ${REPO_DIR}. Make sure that jovyan owns the directory. Note, jovyan owns ${REPO_DIR} (this is set by repo2docker).\n\n13\n\nThe Desktop files are put in a directory called Desktop. Copy them into ${REPO_DIR}/Desktop. The start script will copy these into the correct location for the Desktop server.\n\n14\n\nIf apt.txt is present, then install the packages. The code processes any comments or blank lines in apt.txt. This must be run as root so we switch to root to install.\n\n15\n\nSwitch back to jovyan so we don’t accidentally make files as belonging to root.\n\n16\n\nIf environment.yml is present, install these into the conda environment and do some clean-up. Sometimes package solving will get rid of pip installed packages. We need to make sure that jupyter-remote-desktop-proxy does not disappear.\n\n17\n\nIf requirements.txt is present, install with pip and do some clean-up.\n\n18\n\ninstall.R is an R script where the user can specify how to install packages or run any other R code.\n\n19\n\nstart is run in the ${REPO_DIR}/start command in a subshell. The ${REPO_DIR}/start command cannot be replaced since it contains code to move the Desktop files into the correct place.\n\n20\n\npostBuild is a script. If present, run it and then do some clean-up. It is common to use postBuild to apply extensions or install packages that cannot be installed with apt.\n\n21\n\nThe parent docker build completes by setting the user to jovyan and the working directory to ${HOME}. Within a JupyterHub deployment, ${HOME} will often be re-mapped to the user persistent memory so it is important not to write anything that needs to be persistent to ${HOME}, for example configuration. You can do this in the start script since that runs after the user directory is mapped or you can put configuration files in some place other than ${HOME}.", + "text": "7.6 Basic structure of py-rocket-base\nThe py-rocket-base docker build has the following structure:\n# base image and environment\nrepo2docker sets this up\nrepo2docker-action sets the directory where the build files \n are put to /srv/repo (via $REPO_DIR in GitHub Action)\n and ownership is set to jovyan (via $NB_USER)\n \n# environment.yml\nrepo2docker adds these packages to the conda notebook environment\n\n# start\nrepo2docker points the Docker image entrypoint (command run on start) to this file\n${REPO_DIR}/start\n\n# appendix\nrepo2docker-action adds the Docker commands here to the end of Dockerfile\nmost of the work in py-rocket-base is done here. appendix calls rocker.sh and install packages in apt2.txt\nEach file is described below.", + "crumbs": [ + "7  Developer notes" + ] + }, + { + "objectID": "developers.html#apt2.txt", + "href": "developers.html#apt2.txt", + "title": "7  Developer notes", + "section": "7.7 apt2.txt", + "text": "7.7 apt2.txt\nThis is not named apt.txt because these packages need to be installed after R is installed because the R scripts uninstall packages as part of cleanup. There are some packages that are required for Desktop (/desktop) to operate correctly. Packages needed for R and RStudio building (/rstudio) are installed via the rocker install scripts.", + "crumbs": [ + "7  Developer notes" + ] + }, + { + "objectID": "developers.html#environment.yml", + "href": "developers.html#environment.yml", + "title": "7  Developer notes", + "section": "7.8 environment.yml", + "text": "7.8 environment.yml\nThese are added to the notebook conda environment and in py-rocket-base the basic packages needed for Jupyter Lab, RStudio and Desktop are added. Scientific packages are not added here. They will be added via child images that use py-rocket-base as the base image (in the FROM line).", + "crumbs": [ + "7  Developer notes" + ] + }, + { + "objectID": "developers.html#appendix", + "href": "developers.html#appendix", + "title": "7  Developer notes", + "section": "7.9 appendix", + "text": "7.9 appendix\nThis a long file with many pieces. The pieces are explained below. Click on the number next to code to read about what that code block does.\n1USER root\n\n2# Set env variables\n# This is the default env in repo2docker type images\nENV CONDA_ENV=notebook\n# Tell applications where to open desktop apps\nDISPLAY=\":1.0\"\n\n# Install R, RStudio via Rocker scripts\n3ENV R_VERSION=\"4.4.1\"\nENV R_DOCKERFILE=\"verse_${R_VERSION}\"\n# This is in the rocker script but will not run since ${NB_USER} already exists\n# Needed because rocker scripts set permissions based on the staff group\nRUN usermod -a -G staff \"${NB_USER}\"\nRUN PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && \\\n chmod +x ${REPO_DIR}/rocker.sh && \\\n ${REPO_DIR}/rocker.sh\n\n4# Install linux packages after R installation since the R install scripts get rid of packages\n# The package_list part is reading the file and doing clean-up to just have the list of packages\nRUN package_list=$(grep -v '^\\s*#' ${REPO_DIR}/apt2.txt | grep -v '^\\s*$' | sed 's/\\r//g; s/#.*//; s/^[[:space:]]*//; s/[[:space:]]*$//' | awk '{$1=$1};1') && \\\n apt-get update && \\\n apt-get install --yes --no-install-recommends $package_list && \\\n apt-get autoremove --purge && \\\n apt-get clean && \\\n rm -rf /var/lib/apt/lists/*\n \n# Re-enable man pages disabled in Ubuntu 18 minimal image\n# https://wiki.ubuntu.com/Minimal\n5RUN yes | unminimize\n# NOTE: $NB_PYTHON_PREFIX is the same as $CONDA_PREFIX at run-time.\n# $CONDA_PREFIX isn't available in this context.\n# NOTE: Prepending ensures a working path; if $MANPATH was previously empty,\n# the trailing colon ensures that system paths are searched.\nENV MANPATH=\"${NB_PYTHON_PREFIX}/share/man:${MANPATH}\"\nRUN mandb\n\n# Add custom jupyter config. You can also put config.py files in the same place\n6COPY custom_jupyter_server_config.json ${NB_PYTHON_PREFIX}/etc/jupyter/jupyter_server_config.d/\nCOPY custom_jupyter_server_config.json ${NB_PYTHON_PREFIX}/etc/jupyter/jupyter_notebook_config.d/\n\n# Clean up extra files in ${REPO_DIR}\n7RUN rm -rf ${REPO_DIR}/book ${REPO_DIR}/docs\n\n###################\n# Set up behavior for child dockerfiles\n# Convert NB_USER to ENV (from ARG) so that it passes to the child dockerfile\n8ENV NB_USER=${NB_USER}\n\n9## ONBUILD section. These commands are run in child Dockerfiles. These are run right after the FROM image is loaded\n\n10ONBUILD USER ${NB_USER}\n\n# ${REPO_DIR} is owned by ${NB_USER}\n# copy all the files in the repo (i.e. \".\") into ${REPO_DIR}\n11ONBUILD COPY --chown=${NB_USER}:${NB_USER} . ${REPO_DIR}/childimage\n\n# Desktop and apt.txt installs need to be done by root\nONBUILD USER root \n\n13# Copy Desktop files into ${REPO_DIR}/Desktop if they exist. start will copy to Application dir and Desktop\n# Will not fail if Desktop dir exists but is empty\nONBUILD RUN echo \"Checking for 'Desktop directory'...\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -d Desktop ; then \\\n mkdir -p \"${REPO_DIR}/Desktop\" && \\\n cp -r Desktop/* \"${REPO_DIR}/Desktop/\" 2>/dev/null && \\\n chmod +x \"${REPO_DIR}/desktop.sh\" ; \\\n fi \\\n ; \"${REPO_DIR}/desktop.sh\"\n\n# Install apt packages specified in a apt.txt file if it exists.\n# blank lines and comments are supported in apt.txt\n14ONBUILD RUN echo \"Checking for 'apt.txt'...\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -f \"apt.txt\" ; then \\\n package_list=$(grep -v '^\\s*#' apt.txt | grep -v '^\\s*$' | sed 's/\\r//g; s/#.*//; s/^[[:space:]]*//; s/[[:space:]]*$//' | awk '{$1=$1};1') \\\n && apt-get update --fix-missing > /dev/null \\\n && apt-get install --yes --no-install-recommends $package_list \\\n && apt-get autoremove --purge \\\n && apt-get clean \\\n && rm -rf /var/lib/apt/lists/* \\\n ; fi\n \n15ONBUILD USER ${NB_USER}\n\n# Add the conda environment\n# sometimes package solving will get rid of pip installed packages. Make sure jupyter-remote-desktop-proxy does not disappear\n16ONBUILD RUN echo \"Checking for 'conda-lock.yml' or 'environment.yml'...\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -f \"conda-lock.yml\" ; then echo \"Using conda-lock.yml\" & \\\n conda-lock install --name ${CONDA_ENV} \\\n && pip install --no-deps jupyter-remote-desktop-proxy \\\n ; elif test -f \"environment.yml\" ; then echo \"Using environment.yml\" & \\\n mamba env update --name ${CONDA_ENV} -f environment.yml \\\n && pip install --no-deps jupyter-remote-desktop-proxy \\\n ; fi \\\n && mamba clean -yaf \\\n && find ${CONDA_DIR} -follow -type f -name '*.a' -delete \\\n && find ${CONDA_DIR} -follow -type f -name '*.js.map' -delete \\\n ; if ls ${NB_PYTHON_PREFIX}/lib/python*/site-packages/bokeh/server/static > /dev/null 2>&1; then \\\n find ${NB_PYTHON_PREFIX}/lib/python*/site-packages/bokeh/server/static -follow -type f -name '*.js' ! -name '*.min.js' -delete \\\n ; fi\n\n# If a requirements.txt file exists, use pip to install packages\n# listed there. We don't want to save cached wheels in the image\n# to avoid wasting space.\n17ONBUILD RUN echo \"Checking for pip 'requirements.txt'...\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -f \"requirements.txt\" ; then \\\n ${NB_PYTHON_PREFIX}/bin/pip install --no-cache -r requirements.txt \\\n ; fi\n\n# Add the r packages\n18ONBUILD RUN echo \"Checking for 'install.R\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -f \"install.R\" ; then echo \"Using install.R\" & \\\n Rscript install.R \\\n ; fi\n\n# If a start file exists, put that under ${REPO_DIR}/childimage/start. \n# This is sourced in ${REPO_DIR}/start\n19ONBUILD RUN echo \"Checking for 'start'...\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -f \"start\" ; then \\\n chmod +x start \\\n ; fi\n\n# If a postBuild file exists, run it!\n# After it's done, we try to remove any possible cruft commands there\n# left behind under $HOME - particularly stuff that jupyterlab extensions\n# leave behind.\n20ONBUILD RUN echo \"Checking for 'postBuild'...\" \\\n ; cd \"${REPO_DIR}/childimage/\" \\\n ; if test -f \"postBuild\" ; then \\\n chmod +x postBuild \\\n && ./postBuild \\\n && rm -rf /tmp/* \\\n && rm -rf ${HOME}/.cache ${HOME}/.npm ${HOME}/.yarn \\\n && rm -rf ${NB_PYTHON_PREFIX}/share/jupyter/lab/staging \\\n && find ${CONDA_DIR} -follow -type f -name '*.a' -delete \\\n && find ${CONDA_DIR} -follow -type f -name '*.js.map' -delete \\\n ; fi\n\n## End ONBUILD section for child images\n################################\n\n# Revert to default user and home as pwd\n21USER ${NB_USER}\nWORKDIR ${HOME}\n\n1\n\nSome commands need to be run as root, such as installing linux packages with apt-get\n\n2\n\nSet variables. CONDA_ENV is useful for child builds\n\n3\n\nThis section runs the script rocker.sh which installs R and RStudio using rocker scripts.\n\n4\n\nThe rocker scripts build R from source and as part of clean up in the script, linux packages are removed that are not needed. repo2docker installs the packages in apt.txt automatically before the code in appendix thus the needed linux packages (which include packages for the Xfce Desktop Environment in \\desktop) are put in apt2.txt. repo2docker will not detect this file and we can install the packages here after R is built. The grep -v etc code is processing apt2.txt and removing comments and blank lines.\n\n5\n\nUbuntu does not have man pages installed by default. These lines activate man so users have the common help files.\n\n6\n\nThis is some custom jupyter config to allow hidden files to be listed in the folder browser.\n\n7\n\nbook and docs are the documentation files and are not needed in the image.\n\n8\n\nThe NB_USER environmental variable is not exported by repo2docker (it is an argument confined to the parent build) but is very useful for child builds. So it is converted to an environmental variable.\n\n9\n\nThis next section is a series of ONBUILD commands. These are only run if py-rocker-base is used in the FROM line in a child docker file.\n\n10\n\nSet the user to NB_USER. Not strictly necessary but helps ensure that we don’t accidentally create files that jovyan (NB_USER) cannot access.\n\n11\n\nCopy the child build context (files with the Docker file) into ${REPO_DIR}. Make sure that jovyan owns the directory. Note, jovyan owns ${REPO_DIR} (this is set by repo2docker).\n\n13\n\nThe Desktop files are put in a directory called Desktop. Copy them into ${REPO_DIR}/Desktop. The desktop.sh script will copy these into the correct location for the Desktop server.\n\n14\n\nIf apt.txt is present, then install the packages. The code processes any comments or blank lines in apt.txt. This must be run as root so we switch to root to install.\n\n15\n\nSwitch back to jovyan so we don’t accidentally make files as belonging to root.\n\n16\n\nIf environment.yml is present, install these into the conda environment and do some clean-up. Sometimes package solving will get rid of pip installed packages. We need to make sure that jupyter-remote-desktop-proxy does not disappear.\n\n17\n\nIf requirements.txt is present, install with pip and do some clean-up.\n\n18\n\ninstall.R is an R script where the user can specify how to install packages or run any other R code.\n\n19\n\nstart is run in the ${REPO_DIR}/start command in a subshell. The ${REPO_DIR}/start command cannot be replaced since it contains code to move the Desktop files into the correct place.\n\n20\n\npostBuild is a script. If present, run it and then do some clean-up. It is common to use postBuild to apply extensions or install packages that cannot be installed with apt.\n\n21\n\nThe parent docker build completes by setting the user to jovyan and the working directory to ${HOME}. Within a JupyterHub deployment, ${HOME} will often be re-mapped to the user persistent memory so it is important not to write anything that needs to be persistent to ${HOME}, for example configuration. You can do this in the start script since that runs after the user directory is mapped or you can put configuration files in some place other than ${HOME}.", "crumbs": [ "7  Developer notes" ] @@ -303,8 +333,8 @@ "objectID": "developers.html#rocker.sh", "href": "developers.html#rocker.sh", "title": "7  Developer notes", - "section": "7.7 rocker.sh", - "text": "7.7 rocker.sh\nThis script will copy in the rocker scripts from rocker-versioned2 into ${REPO_DIR} to install things. It will read in one of the rocker docker files using R_DOCKERFILE defined in the appendix file (which is inserted into the main docker file). Variables defined here will only be available in this script. Click on the numbers in the script to learn what each section is doing.\n#!/bin/bash\nset -e\n\n# Copy in the rocker files. Work in ${REPO_DIR} to make sure I don't clobber anything\ncd ${REPO_DIR}\nwget https://github.com/rocker-org/rocker-versioned2/archive/refs/tags/R${R_VERSION}.tar.gz\ntar zxvf R${R_VERSION}.tar.gz && \\\nmv rocker-versioned2-R${R_VERSION}/scripts /rocker_scripts && \\\nROCKER_DOCKERFILE_NAME=\"${R_DOCKERFILE}.Dockerfile\"\nmv rocker-versioned2-R${R_VERSION}/dockerfiles/${ROCKER_DOCKERFILE_NAME} /rocker_scripts/original.Dockerfile && \\\nrm R${R_VERSION}.tar.gz && \\\nrm -rf rocker-versioned2-R${R_VERSION}\n\ncd /\n# Read the Dockerfile and process each line\nwhile IFS= read -r line; do\n # Check if the line starts with ENV or RUN\n if [[ \"$line\" == ENV* ]]; then\n # Assign variable\n var_assignment=$(echo \"$line\" | sed 's/^ENV //g')\n # Replace ENV DEFAULT_USER=\"jovyan\"\n if [[ \"$var_assignment\" == DEFAULT_USER* ]]; then\n var_assignment=\"DEFAULT_USER=${NB_USER}\"\n fi\n # Run this way eval \"export ...\" otherwise the \" will get turned to %22\n eval \"export $var_assignment\"\n # Write the exported variable to env.txt\n echo \"export $var_assignment\" >> ${REPO_DIR}/env.txt\n elif [[ \"$line\" == RUN* ]]; then\n # Run the command from the RUN line\n cmd=$(echo \"$line\" | sed 's/^RUN //g')\n echo \"Executing: $cmd\"\n eval \"$cmd\" # || echo ${cmd}\" encountered an error, but continuing...\"\n fi\ndone < /rocker_scripts/original.Dockerfile\n\n# Install extra tex packages that are not installed by default\nif command -v tlmgr &> /dev/null; then\n echo \"Installing texlive collection-latexrecommended...\"\n tlmgr install collection-latexrecommended\n tlmgr install pdfcol tcolorbox eurosym upquote adjustbox titling enumitem ulem soul rsfs\nfi\n\n1\n\nThe rocker-versioned2 repository for a particular R version is copied into {REPO_DIR} and unzipped. R_VERSION is defined in appendix.\n\n2\n\nThe unzipped directory will be named rocker-versioned2-R${R_VERSION}. We move the scripts directory to /rocker_scripts (base level) because the rocker scripts expect the scripts to be there.\n\n3\n\nR_DOCKERFILE is defined as verse_${R_VERSION}. The docker file we will process (find ENV and RUN lines) is called ROCKER_DOCKERFILE_NAME in the rocker files. We move this to /rocker_scripts/original.Dockerfile so we can refer to it later.\n\n4\n\nClean up the rocker directories that we no longer need.\n\n5\n\ncd to the base level where /rocker_scripts is.\n\n6\n\nThe big while loop is processing /rocker_scripts/original.Dockerfile. The code is using piping > and the input file and pipe is specified at the end of the while loop code.\n\n7\n\nThis looks if the line starts with ENV and if it does, it strips off ENV and stores the variable assigment statement to $var_assignment.\n\n8\n\nThe rocker docker files do not use the NB_USER environmental variable (defined in appendix). If the ENV line is defining the default user, we need to change that assignment to the variable NB_USER. This part is specific to the rocker docker files.\n\n9\n\nWe need to export any variables (ENV) found in the docker file so it is available to the scripts that will run in the RUN statements. We need to export the variables as done here (with eval and export) otherwise they don’t make it to the child scripts about to be run. Getting variables to be exported to child scripts being called by a parent script is tricky and this line required a lot of testing and debugging to get variables exported properly.\n\n10\n\nThe export line will only make the variable available to the child scripts. We also want them available in the final image. To do that, we write them to a file that we will source from the docker file. Scripts are run in an ephemeral subshell during docker builds so we cannot define the variable here.\n\n11\n\nIf the docker file line starts with RUN then run the command. This command should be a rocker script because that is how rocker docker files are organized. See an example rocker docker file.\n\n12\n\nHere the input file for the while loop is specified.\n\n13\n\nThe rocker install_texlive.sh script (which is part of verse) will provide a basic texlive installation. Here a few more packages are added so that the user is able to run vanilla Quarto to PDF and Myst to PDF. See the chapter on texlive.", + "section": "7.10 rocker.sh", + "text": "7.10 rocker.sh\nThis script will copy in the rocker scripts from rocker-versioned2 into ${REPO_DIR} to install things. It will read in one of the rocker docker files using R_DOCKERFILE defined in the appendix file (which is inserted into the main docker file). Variables defined here will only be available in this script. Click on the numbers in the script to learn what each section is doing.\n#!/bin/bash\nset -e\n\n# Copy in the rocker files. Work in ${REPO_DIR} to make sure I don't clobber anything\ncd ${REPO_DIR}\nwget https://github.com/rocker-org/rocker-versioned2/archive/refs/tags/R${R_VERSION}.tar.gz\ntar zxvf R${R_VERSION}.tar.gz && \\\nmv rocker-versioned2-R${R_VERSION}/scripts /rocker_scripts && \\\nROCKER_DOCKERFILE_NAME=\"${R_DOCKERFILE}.Dockerfile\"\nmv rocker-versioned2-R${R_VERSION}/dockerfiles/${ROCKER_DOCKERFILE_NAME} /rocker_scripts/original.Dockerfile && \\\nrm R${R_VERSION}.tar.gz && \\\nrm -rf rocker-versioned2-R${R_VERSION}\n\ncd /\n# Read the Dockerfile and process each line\nwhile IFS= read -r line; do\n # Check if the line starts with ENV or RUN\n if [[ \"$line\" == ENV* ]]; then\n # Assign variable\n var_assignment=$(echo \"$line\" | sed 's/^ENV //g')\n # Replace ENV DEFAULT_USER=\"jovyan\"\n if [[ \"$var_assignment\" == DEFAULT_USER* ]]; then\n var_assignment=\"DEFAULT_USER=${NB_USER}\"\n fi\n # Run this way eval \"export ...\" otherwise the \" will get turned to %22\n eval \"export $var_assignment\"\n # Write the exported variable to env.txt\n echo \"export $var_assignment\" >> ${REPO_DIR}/env.txt\n elif [[ \"$line\" == RUN* ]]; then\n # Run the command from the RUN line\n cmd=$(echo \"$line\" | sed 's/^RUN //g')\n echo \"Executing: $cmd\"\n eval \"$cmd\" # || echo ${cmd}\" encountered an error, but continuing...\"\n fi\ndone < /rocker_scripts/original.Dockerfile\n\n# Install extra tex packages that are not installed by default\nif command -v tlmgr &> /dev/null; then\n echo \"Installing texlive collection-latexrecommended...\"\n tlmgr install collection-latexrecommended\n tlmgr install pdfcol tcolorbox eurosym upquote adjustbox titling enumitem ulem soul rsfs\nfi\n\n1\n\nThe rocker-versioned2 repository for a particular R version is copied into {REPO_DIR} and unzipped. R_VERSION is defined in appendix.\n\n2\n\nThe unzipped directory will be named rocker-versioned2-R${R_VERSION}. We move the scripts directory to /rocker_scripts (base level) because the rocker scripts expect the scripts to be there.\n\n3\n\nR_DOCKERFILE is defined as verse_${R_VERSION}. The docker file we will process (find ENV and RUN lines) is called ROCKER_DOCKERFILE_NAME in the rocker files. We move this to /rocker_scripts/original.Dockerfile so we can refer to it later.\n\n4\n\nClean up the rocker directories that we no longer need.\n\n5\n\ncd to the base level where /rocker_scripts is.\n\n6\n\nThe big while loop is processing /rocker_scripts/original.Dockerfile. The code is using piping > and the input file and pipe is specified at the end of the while loop code.\n\n7\n\nThis looks if the line starts with ENV and if it does, it strips off ENV and stores the variable assigment statement to $var_assignment.\n\n8\n\nThe rocker docker files do not use the NB_USER environmental variable (defined in appendix). If the ENV line is defining the default user, we need to change that assignment to the variable NB_USER. This part is specific to the rocker docker files.\n\n9\n\nWe need to export any variables (ENV) found in the docker file so it is available to the scripts that will run in the RUN statements. We need to export the variables as done here (with eval and export) otherwise they don’t make it to the child scripts about to be run. Getting variables to be exported to child scripts being called by a parent script is tricky and this line required a lot of testing and debugging to get variables exported properly.\n\n10\n\nThe export line will only make the variable available to the child scripts. We also want them available in the final image. To do that, we write them to a file that we will source from the docker file. Scripts are run in an ephemeral subshell during docker builds so we cannot define the variable here.\n\n11\n\nIf the docker file line starts with RUN then run the command. This command should be a rocker script because that is how rocker docker files are organized. See an example rocker docker file.\n\n12\n\nHere the input file for the while loop is specified.\n\n13\n\nThe rocker install_texlive.sh script (which is part of verse) will provide a basic texlive installation. Here a few more packages are added so that the user is able to run vanilla Quarto to PDF and Myst to PDF. See the chapter on texlive.", "crumbs": [ "7  Developer notes" ] @@ -313,8 +343,18 @@ "objectID": "developers.html#start", "href": "developers.html#start", "title": "7  Developer notes", - "section": "7.8 start", - "text": "7.8 start\nWithin a JupyterHub, the user home directory $HOME is typically re-mapped to the user persistent home directory. That means that the image build process cannot put things into $HOME, they would just be lost when $HOME is re-mapped. If a process needs to have something in the home directory, e.g. in some local user configuration, this must be done in the start script. The repo2docker docker image specifies that the start script is ${REPO_DIR}/start.\nFor py-rocket-base, the start script is used to move the Desktop files (*.desktop) to where the Desktop server expects them, which is ${HOME}/.local/share/applications/ and ${HOME}/Desktop. Any *.desktop files found there will appear as clickable icons in \\desktop. This must be done in start, since the files need to be put in ${HOME} which not available until after the server starts.\n#!/bin/bash\nset -euo pipefail\n\n# Start - Set any environment variables here\n# These are inherited by all processes, *except* RStudio\n# USE export <parname>=value\n# Tell applications where to open desktop apps - this allows notebooks to pop open GUIs\nexport DISPLAY=\":1.0\"\n# source this file to get the variables defined in the rocker Dockerfile\nsource ${REPO_DIR}/env.txt\n# End - Set any environment variables here\n\n# The for loops will fail if they return null (no files). Set shell option nullglob\nshopt -s nullglob\n\n# Add any .desktop files to the application database and desktop. This is done\n# at startup-time because it's expected that a remote filesystem will be\n# mounted at $HOME, which would overwrite the data if it was created at\n# build-time.\nAPPLICATIONS_DIR=\"${HOME}/.local/share/applications\"\nDESKTOP_DIR=\"${HOME}/Desktop\"\n# Remove DESKTOP_DIR if it exists to avoid leftover files\nif [ -d \"${DESKTOP_DIR}\" ]; then\n rm -rf \"${DESKTOP_DIR}\"\nfi\nmkdir -p \"${APPLICATIONS_DIR}\"\nmkdir -p \"${DESKTOP_DIR}\"\nfor desktop_file_path in ${REPO_DIR}/Desktop/*.desktop; do\n cp \"${desktop_file_path}\" \"${APPLICATIONS_DIR}/.\"\n\n # Symlink application to desktop and set execute permission so xfce (desktop) doesn't complain\n desktop_file_name=\"$(basename ${desktop_file_path})\"\n # Set execute permissions on the copied .desktop file\n chmod +x \"${APPLICATIONS_DIR}/${desktop_file_name}\"\n ln -sf \"${APPLICATIONS_DIR}/${desktop_file_name}\" \"${DESKTOP_DIR}/${desktop_file_name}\"\ndone\nupdate-desktop-database \"${APPLICATIONS_DIR}\"\n\n# Add MIME Type data from XML files to the MIME database.\nMIME_DIR=\"${HOME}/.local/share/mime\"\nMIME_PACKAGES_DIR=\"${MIME_DIR}/packages\"\nmkdir -p \"${MIME_PACKAGES_DIR}\"\nfor mime_file_path in ${REPO_DIR}/Desktop/*.xml; do\n cp \"${mime_file_path}\" \"${MIME_PACKAGES_DIR}/.\"\ndone\nupdate-mime-database \"${MIME_DIR}\"\n\n# Run child start in a subshell to contain its environment\n[ -f ${REPO_DIR}/childimage/start ] && ( source ${REPO_DIR}/childimage/start )\n\nexec \"$@\"", + "section": "7.11 start", + "text": "7.11 start\nWithin a JupyterHub, the user home directory $HOME is typically re-mapped to the user persistent home directory. That means that the image build process cannot put things into $HOME, they would just be lost when $HOME is re-mapped. If a process needs to have something in the home directory, e.g. in some local user configuration, this must be done in the start script. The repo2docker docker image specifies that the start script is ${REPO_DIR}/start. In py-rocket-base, the start scripts in a child docker file is souces in a subshell from the py-rocket-base start script.\n#!/bin/bash\nset -euo pipefail\n\n# Start - Set any environment variables here\n# These are inherited by all processes, *except* RStudio\n# USE export <parname>=value\n# source this file to get the variables defined in the rocker Dockerfile\nsource ${REPO_DIR}/env.txt\n# End - Set any environment variables here\n\n# Run child start in a subshell to contain its environment\n[ -f ${REPO_DIR}/childimage/start ] && ( source ${REPO_DIR}/childimage/start )\n\nexec \"$@\"\n\n1\n\nIn a Docker file so no way to dynamically set environmental variables, so the env.txt file with the export <var>=<value> are source at start up.\n\n2\n\nRun any child start script in a subshell. Run in a subshell to contain any set statements or similar.", + "crumbs": [ + "7  Developer notes" + ] + }, + { + "objectID": "developers.html#desktop.sh", + "href": "developers.html#desktop.sh", + "title": "7  Developer notes", + "section": "7.12 desktop.sh", + "text": "7.12 desktop.sh\nThe default for XDG and xfce4 is for Desktop files to be in ~/Desktop but this leads to a variety of problems. First we are altering the user directiory which seems rude, second orphan desktop files might be in ~/Desktop so who knows what the user Desktop experience with be, here the Desktop dir is set to /usr/share/Desktop so is part of the image. Users that really want to customize Desktop can change ~/.config/user-dirs.dirs. Though py-rocket-base might not respect that. Not sure why you’d do that instead of just using a different image that doesn’t have the py-rocket-base behavior.\n#!/bin/bash\nset -e\n\n# Copy in the Desktop files\nAPPLICATIONS_DIR=/usr/share/applications\nDESKTOP_DIR=/usr/share/Desktop\nmkdir -p \"${DESKTOP_DIR}\"\nchown :staff /usr/share/Desktop\nchmod 775 /usr/share/Desktop\n# set the Desktop dir default for XDG\necho 'XDG_DESKTOP_DIR=\"${DESKTOP_DIR}\"' > /etc/xdg/user-dirs.defaults\n\n# The for loops will fail if they return null (no files). Set shell option nullglob\nshopt -s nullglob\n\nfor desktop_file_path in ${REPO_DIR}/Desktop/*.desktop; do\n cp \"${desktop_file_path}\" \"${APPLICATIONS_DIR}/.\"\n # Symlink application to desktop and set execute permission so xfce (desktop) doesn't complain\n desktop_file_name=\"$(basename ${desktop_file_path})\"\n # Set execute permissions on the copied .desktop file\n chmod +x \"${APPLICATIONS_DIR}/${desktop_file_name}\"\n ln -sf \"${APPLICATIONS_DIR}/${desktop_file_name}\" \"${DESKTOP_DIR}/${desktop_file_name}\"\ndone\nupdate-desktop-database \"${APPLICATIONS_DIR}\"\n\n# Add MIME Type data from XML files to the MIME database.\nMIME_DIR=\"/usr/share/mime\"\nMIME_PACKAGES_DIR=\"${MIME_DIR}/packages\"\nmkdir -p \"${MIME_PACKAGES_DIR}\"\nfor mime_file_path in ${REPO_DIR}/Desktop/*.xml; do\n cp \"${mime_file_path}\" \"${MIME_PACKAGES_DIR}/.\"\ndone\nupdate-mime-database \"${MIME_DIR}\"\n\n# Add icons\nICON_DIR=\"/usr/share/icons\"\nICON_PACKAGES_DIR=\"${ICON_DIR}/packages\"\nmkdir -p \"${ICON_PACKAGES_DIR}\"\nfor icon_file_path in \"${REPO_DIR}\"/Desktop/*.png; do\n cp \"${icon_file_path}\" \"${ICON_PACKAGES_DIR}/\" || echo \"Failed to copy ${icon_file_path}\"\ndone\nfor icon_file_path in \"${REPO_DIR}\"/Desktop/*.svg; do\n cp \"${icon_file_path}\" \"${ICON_PACKAGES_DIR}/\" || echo \"Failed to copy ${icon_file_path}\"\ndone\ngtk-update-icon-cache \"${ICON_DIR}\"\n\n1\n\nThis is the default local for system applications.\n\n2\n\nCreate the Desktop directory and make sure jovyan can put files there. This is mainly for debugging.\n\n3\n\nSet up the default XDG_DESKTOP_DIR value. This will be copied to the ~.config (by xinitrc).\n\n4\n\nCopy the .desktop file in the Desktop directory into the applications directory and make a symlink to the Desktop directory. The former means that the applications will appear in the menu in xfce4 desktop and the latter means there will be a desktop icon.\n\n5\n\nAdd any mime xml files to the mime folder and update the mime database.\n\n6\n\nAdd any png or svg icon files to the icon folder and update the icon database.", "crumbs": [ "7  Developer notes" ] diff --git a/docs/sitemap.xml b/docs/sitemap.xml index 9314fb5..d4739bc 100644 --- a/docs/sitemap.xml +++ b/docs/sitemap.xml @@ -18,7 +18,7 @@ https://nmfs-opensci.github.io/py-rocket-base/desktop.html - 2024-10-30T04:18:08.624Z + 2024-10-30T21:15:26.975Z https://nmfs-opensci.github.io/py-rocket-base/tex.html @@ -30,6 +30,6 @@ https://nmfs-opensci.github.io/py-rocket-base/developers.html - 2024-10-30T04:52:15.736Z + 2024-10-30T22:08:13.913Z