From 53da499d5b337ebd52a7d6ba5cb1bfc81550d11c Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Thu, 1 Feb 2024 08:57:59 +0100 Subject: [PATCH] Add Dev Container Configuration Files --- .Rbuildignore | 1 + .devcontainer/.gitignore | 23 ++++ .devcontainer/Dockerfile | 109 ++++++++++++++++++ .devcontainer/LICENSE | 22 ++++ .devcontainer/README.md | 17 +++ .devcontainer/conf/shell/etc/skel/.profile | 30 +++++ .../conf/shell/var/tmp/snippets/rc.sh | 13 +++ .devcontainer/devcontainer.json | 79 +++++++++++++ .../scripts/usr/local/bin/onCreateCommand.sh | 51 ++++++++ .../usr/local/bin/postCreateCommand.sh | 8 ++ .../scripts/usr/local/bin/postStartCommand.sh | 17 +++ 11 files changed, 370 insertions(+) create mode 100644 .devcontainer/.gitignore create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/LICENSE create mode 100644 .devcontainer/README.md create mode 100644 .devcontainer/conf/shell/etc/skel/.profile create mode 100644 .devcontainer/conf/shell/var/tmp/snippets/rc.sh create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/scripts/usr/local/bin/onCreateCommand.sh create mode 100755 .devcontainer/scripts/usr/local/bin/postCreateCommand.sh create mode 100755 .devcontainer/scripts/usr/local/bin/postStartCommand.sh diff --git a/.Rbuildignore b/.Rbuildignore index db3a9ba..6b87aa4 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -15,3 +15,4 @@ ^valgrind.dockerfile$ ^CRAN-SUBMISSION$ ^cran-comments\.md$ +^\.devcontainer$ diff --git a/.devcontainer/.gitignore b/.devcontainer/.gitignore new file mode 100644 index 0000000..7d673d4 --- /dev/null +++ b/.devcontainer/.gitignore @@ -0,0 +1,23 @@ +* + +!/conf/ +!/conf/shell/ +!/conf/shell/etc/ +!/conf/shell/etc/skel/ +!/conf/shell/var/ +!/conf/shell/var/tmp/ +!/conf/shell/var/tmp/snippets/ +!/scripts/ +!/scripts/usr/ +!/scripts/usr/local/ +!/scripts/usr/local/bin/ + +!/conf/shell/etc/skel/.profile +!/conf/shell/var/tmp/snippets/*.sh +!/scripts/usr/local/bin/*.sh + +!/.gitignore +!/devcontainer.json +!/Dockerfile +!/LICENSE +!/README.md diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..1a0a71b --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,109 @@ +ARG BUILD_ON_IMAGE=glcr.b-data.ch/r/tidyverse +ARG R_VERSION=latest + +FROM ${BUILD_ON_IMAGE}:${R_VERSION} as files + +RUN mkdir /files + +COPY conf/shell /files +COPY scripts /files + +## Ensure file modes are correct +RUN find /files -type d -exec chmod 755 {} \; \ + && find /files -type f -exec chmod 644 {} \; \ + && find /files/usr/local/bin -type f -exec chmod 755 {} \; \ + && cp -r /files/etc/skel/. /files/root \ + && bash -c 'rm -rf /files/root/{.bashrc,.profile}' \ + && chmod 700 /files/root + +FROM docker.io/koalaman/shellcheck:stable as sci + +FROM ${BUILD_ON_IMAGE}:${R_VERSION} + +ARG DEBIAN_FRONTEND=noninteractive + +ENV PARENT_IMAGE_CRAN=${CRAN} + +ARG BUILD_ON_IMAGE +ARG CRAN + +ARG CRAN_OVERRIDE=${CRAN} + +ENV PARENT_IMAGE=${BUILD_ON_IMAGE}:${R_VERSION} \ + CRAN=${CRAN_OVERRIDE:-$CRAN} \ + PARENT_IMAGE_BUILD_DATE=${BUILD_DATE} + +# hadolint ignore=DL3008,DL3015,SC2016 +RUN dpkgArch="$(dpkg --print-architecture)" \ + ## Ensure that common CA certificates + ## and OpenSSL libraries are up to date + && apt-get update \ + && apt-get -y install --only-upgrade \ + ca-certificates \ + openssl \ + ## Install pak + && pkgType="$(Rscript -e 'cat(.Platform$pkgType)')" \ + && os="$(Rscript -e 'cat(R.Version()$os)')" \ + && arch="$(Rscript -e 'cat(R.Version()$arch)')" \ + && install2.r -r "https://r-lib.github.io/p/pak/stable/$pkgType/$os/$arch" -e \ + pak \ + ## Install languageserver and decor + && install2.r -s -d TRUE -n "$(($(nproc)+1))" -e \ + languageserver \ + decor \ + ## Clean up + && rm -rf /tmp/* \ + /root/.cache \ + ## Install hadolint + && case "$dpkgArch" in \ + amd64) tarArch="x86_64" ;; \ + arm64) tarArch="arm64" ;; \ + *) echo "error: Architecture $dpkgArch unsupported"; exit 1 ;; \ + esac \ + && apiResponse="$(curl -sSL \ + https://api.github.com/repos/hadolint/hadolint/releases/latest)" \ + && downloadUrl="$(echo "$apiResponse" | grep -e \ + "browser_download_url.*Linux-$tarArch\"" | cut -d : -f 2,3 | tr -d \")" \ + && echo "$downloadUrl" | xargs curl -sSLo /usr/local/bin/hadolint \ + && chmod 755 /usr/local/bin/hadolint \ + ## Create backup of root directory + && cp -a /root /var/backups \ + ## Clean up + && rm -rf /var/lib/apt/lists/* + +## Update environment +ARG USE_ZSH_FOR_ROOT +ARG SET_LANG +ARG SET_TZ + +ENV LANG=${SET_LANG:-$LANG} \ + TZ=${SET_TZ:-$TZ} + + ## Change root's shell to ZSH +RUN if [ -n "$USE_ZSH_FOR_ROOT" ]; then \ + chsh -s /bin/zsh; \ + fi \ + ## Update timezone if needed + && if [ "$TZ" != "Etc/UTC" ]; then \ + echo "Setting TZ to $TZ"; \ + ln -snf "/usr/share/zoneinfo/$TZ" /etc/localtime \ + && echo "$TZ" > /etc/timezone; \ + fi \ + ## Add/Update locale if needed + && if [ "$LANG" != "en_US.UTF-8" ]; then \ + sed -i "s/# $LANG/$LANG/g" /etc/locale.gen; \ + locale-gen; \ + echo "Setting LANG to $LANG"; \ + update-locale --reset LANG="$LANG"; \ + fi \ + ## Update CRAN + && sed -i "s|$PARENT_IMAGE_CRAN|$CRAN|g" "$(R RHOME)/etc/Rprofile.site" + +## Unset environment variable BUILD_DATE +ENV BUILD_DATE= + +## Copy files as late as possible to avoid cache busting +COPY --from=files /files / + +## Copy shellcheck as late as possible to avoid cache busting +COPY --from=sci --chown=root:root /bin/shellcheck /usr/local/bin diff --git a/.devcontainer/LICENSE b/.devcontainer/LICENSE new file mode 100644 index 0000000..90a8259 --- /dev/null +++ b/.devcontainer/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2023 b-data GmbH + +The code in this directory is not part of unigd (the software) and is +distributed under the terms of the MIT License: + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 0000000..55c10b7 --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,17 @@ +# Dev Containers + +For further information, see [Development Containers](https://containers.dev). + +## Usage + +For local/remote usage with VS Code, please follow the instructions at +[Developing inside a Container](https://code.visualstudio.com/docs/devcontainers/containers). + + +For use with Github Codespaces: Open in GitHub Codespaces + + +## License + +The code in this directory is not part of unigd (the software) and is +distributed under the terms of the [MIT License](LICENSE). diff --git a/.devcontainer/conf/shell/etc/skel/.profile b/.devcontainer/conf/shell/etc/skel/.profile new file mode 100644 index 0000000..6628368 --- /dev/null +++ b/.devcontainer/conf/shell/etc/skel/.profile @@ -0,0 +1,30 @@ +# ~/.profile: executed by the command interpreter for login shells. +# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login +# exists. +# see /usr/share/doc/bash/examples/startup-files for examples. +# the files are located in the bash-doc package. + +# the default umask is set in /etc/profile; for setting the umask +# for ssh logins, install and configure the libpam-umask package. +#umask 022 + +# if running bash +if [ -n "$BASH_VERSION" ]; then + # include .bashrc if it exists + if [ -f "$HOME/.bashrc" ]; then + . "$HOME/.bashrc" + fi +else + # if not running zsh + if [ -z "$ZSH_VERSION" ]; then + # set PATH so it includes user's private bin if it exists + if [ -d "$HOME/bin" ] ; then + PATH="$HOME/bin:$PATH" + fi + + # set PATH so it includes user's private bin if it exists + if [ -d "$HOME/.local/bin" ] ; then + PATH="$HOME/.local/bin:$PATH" + fi + fi +fi diff --git a/.devcontainer/conf/shell/var/tmp/snippets/rc.sh b/.devcontainer/conf/shell/var/tmp/snippets/rc.sh new file mode 100644 index 0000000..49fe8b2 --- /dev/null +++ b/.devcontainer/conf/shell/var/tmp/snippets/rc.sh @@ -0,0 +1,13 @@ + +# remove potentially appended $HOME/.local/bin from PATH +PATH="${PATH%:$HOME/.local/bin}" + +# set PATH so it includes user's private bin if it exists +if [ -d "$HOME/bin" ] && [[ "$PATH" != *"$HOME/bin"* ]] ; then + PATH="$HOME/bin:$PATH" +fi + +# set PATH so it includes user's private bin if it exists +if [ -d "$HOME/.local/bin" ] && [[ "$PATH" != *"$HOME/.local/bin"* ]] ; then + PATH="$HOME/.local/bin:$PATH" +fi diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..a79d671 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,79 @@ +{ + "name": "R latest", + "build": { + "dockerfile": "Dockerfile", + "args": { + "BUILD_ON_IMAGE": "glcr.b-data.ch/r/tidyverse", + "CRAN": "https://cloud.r-project.org", + "R_VERSION": "latest", + "USE_ZSH_FOR_ROOT": "unset-to-use-bash", + "SET_LANG": "en_US.UTF-8", + "SET_TZ": "Etc/UTC" + } + }, + + "onCreateCommand": "onCreateCommand.sh", + "postCreateCommand": "postCreateCommand.sh", + "postStartCommand": "postStartCommand.sh", + + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "configureZshAsDefaultShell": true, + "upgradePackages": false, + "username": "vscode", + "userUid": "automatic", + "userGid": "automatic" + }, + "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { + "moby": false + }, + // A comma separated list of packages to install + "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": { + "packages": "qpdf" + } + }, + + "customizations": { + "vscode": { + "extensions": [ + "DavidAnson.vscode-markdownlint", + "eamodio.gitlens@11.7.0", + "editorconfig.editorconfig", + "GitHub.vscode-pull-request-github", + "grapecity.gc-excelviewer", + "mhutchie.git-graph", + "mutantdino.resourcemonitor", + "redhat.vscode-yaml", + "REditorSupport.r", + "timonwong.shellcheck", + "exiasr.hadolint", + "ms-azuretools.vscode-docker", + "ms-vscode.cpptools" + ], + "settings": { + "gitlens.showWelcomeOnInstall": false, + "gitlens.showWhatsNewAfterUpgrades": false, + "r.bracketedPaste": true, + "r.plot.useHttpgd": true, + "r.rterm.linux": "/usr/local/bin/radian", + "r.rterm.option": [ + "--no-save", + "--no-restore" + ], + "r.workspaceViewer.showObjectSize": true, + "resmon.show.battery": false, + "resmon.show.cpufreq": false + } + } + }, + + // Set 'remoteUser' to 'root' to connect as root instead. + "remoteUser": "vscode", + + "remoteEnv": { + // Pip: Install packages to the user site + "PIP_USER": "1", + // A comma separated list of packages to install + "R_PACKAGES": "fontquiver" + } +} diff --git a/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh b/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh new file mode 100755 index 0000000..e5230b9 --- /dev/null +++ b/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# Copyright (c) 2023 b-data GmbH. +# Distributed under the terms of the MIT License. + +set -e + +mkdir -p "$HOME/.local/bin" + +# Copy Bash-related files from root's backup directory +if [ "$(id -un)" == "root" ]; then + if [ ! -f /root/.bashrc ]; then + cp /var/backups/root/.bashrc /root; + fi + if [ ! -f /root/.profile ]; then + cp /var/backups/root/.profile /root; + fi +fi + +# Copy Zsh-related files and folders from the untouched home directory +if [ "$(id -un)" == "root" ]; then + if [ ! -d /root/.oh-my-zsh ]; then + cp -R /home/*/.oh-my-zsh /root; + fi + if [ ! -f /root/.zshrc ]; then + cp /home/*/.zshrc /root; + fi +else + if [ ! -d "$HOME/.oh-my-zsh" ]; then + sudo cp -R /root/.oh-my-zsh "$HOME"; + sudo chown -R "$(id -u)":"$(id -g)" "$HOME/.oh-my-zsh"; + fi + if [ ! -f "$HOME/.zshrc" ]; then + sudo cp /root/.zshrc "$HOME"; + sudo chown "$(id -u)":"$(id -g)" "$HOME/.zshrc"; + fi +fi + +# If existent, prepend the user's private bin to PATH +if ! grep -q "user's private bin" "$HOME/.bashrc"; then + cat "/var/tmp/snippets/rc.sh" >> "$HOME/.bashrc" +fi +if ! grep -q "user's private bin" "$HOME/.zshrc"; then + cat "/var/tmp/snippets/rc.sh" >> "$HOME/.zshrc" +fi + +# Enable Oh My Zsh plugins +sed -i "s/plugins=(git)/plugins=(docker docker-compose git git-lfs pip screen tmux vscode)/g" \ + "$HOME/.zshrc" + +# Remove old .zcompdump files +rm -f "$HOME"/.zcompdump* diff --git a/.devcontainer/scripts/usr/local/bin/postCreateCommand.sh b/.devcontainer/scripts/usr/local/bin/postCreateCommand.sh new file mode 100755 index 0000000..713bac1 --- /dev/null +++ b/.devcontainer/scripts/usr/local/bin/postCreateCommand.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Copyright (c) 2023 b-data GmbH. +# Distributed under the terms of the MIT License. + +set -e + +# Change ownership of the workspaces folder +sudo chown "$(id -u)":"$(id -g)" /workspaces diff --git a/.devcontainer/scripts/usr/local/bin/postStartCommand.sh b/.devcontainer/scripts/usr/local/bin/postStartCommand.sh new file mode 100755 index 0000000..4eec45a --- /dev/null +++ b/.devcontainer/scripts/usr/local/bin/postStartCommand.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Copyright (c) 2023 b-data GmbH. +# Distributed under the terms of the MIT License. + +set -e + +# Create R user library +mkdir -p "$(Rscript -e "cat(Sys.getenv('R_LIBS_USER'))")" + +# Install packages stated in environment variable R_PACKAGES +if [ -n "$R_PACKAGES" ]; then + R -q -e "pak::pak(trimws(unlist(strsplit('$R_PACKAGES', ',')))); \ + pak::cache_clean()" +fi + +# Install required (hard) dependencies for the current package +R -q -e "pak::pak(dependencies = NA); pak::cache_clean()"