Skip to content

Commit

Permalink
add vscode dev files (revised)
Browse files Browse the repository at this point in the history
  • Loading branch information
rpodcast committed Apr 29, 2022
1 parent 2e793ef commit deb107f
Show file tree
Hide file tree
Showing 16 changed files with 1,045 additions and 3 deletions.
16 changes: 16 additions & 0 deletions .devcontainer/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
INSTALL_ZSH=false
INSTALL_FISH=true
UPGRADE_PACKAGES=true
RENV_PATHS_CACHE_HOST=/opt/local/renv/cache
RENV_PATHS_CACHE_CONTAINER=/renv/cache
RENV_PATHS_CACHE=/renv/cache
USER=eric
USERID=1000
GROUPID=1000
PASSWORD=1rstudio
VOLUME_PATH=/home/eric/r_projects
LOCAL_PORT=3955
QUARTO_VERSION=latest
RSTUDIO_VERSION=2022.02.1-461
S6_VERSION=v1.21.7.0
COMPOSE_PROJECT_NAME=rweekly
86 changes: 86 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
###################################################################################################################
# Adapted from https://github.com/microsoft/vscode-dev-containers/blob/master/containers/r/.devcontainer/Dockerfile
# licence: MIT
###################################################################################################################

FROM rocker/r-ver:4.1.3

# Options for setup script
ARG USERNAME
ARG INSTALL_ZSH="false"
ARG USER_UID="1000"
ARG USER_GID="1000"
ARG UPGRADE_PACKAGES="false"
ARG INSTALL_OHMYZSH="false"
ARG INSTALL_NONFREE="false"
ARG QUARTO_VERSION=latest

# key dependencies for certain R packages
RUN apt-get update \
&& export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends software-properties-common curl wget libssl-dev libxml2-dev libsodium-dev imagemagick libmagick++-dev libgit2-dev libssh2-1-dev zlib1g-dev librsvg2-dev libudunits2-dev libcurl4-openssl-dev python3-pip pandoc libzip-dev libfreetype6-dev libfontconfig1-dev tk libpq5 libxt6 openssh-client openssh-server \
&& apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/*

COPY library-scripts/common-debian.sh /tmp/library-scripts/
RUN apt-get update && bash /tmp/library-scripts/common-debian.sh ${INSTALL_ZSH} ${USERNAME} ${USER_UID} ${USER_GID} ${UPGRADE_PACKAGES} ${INSTALL_OHMYZSH} ${INSTALL_NONFREE}

# install R packages needed for VSCode interaction and package management
RUN install2.r --error --skipinstalled --ncpus -4 languageserver renv remotes httpgd

# install radian via python and pip3
RUN apt-get update \
&& export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends python3-setuptools

RUN pip3 install radian

# install dot net core runtime for the R Tools plugin
RUN wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O /tmp/packages-microsoft-prod.deb \
&& dpkg -i /tmp/packages-microsoft-prod.deb

RUN apt-get update \
&& apt-get -y install --no-install-recommends apt-transport-https \
&& apt-get -y install --no-install-recommends dotnet-sdk-3.1

# ensure that the renv package cache env var is accessible in default R installation
RUN echo "RENV_PATHS_CACHE=/renv/cache" >> /usr/local/lib/R/etc/Renviron

# copy the modified .Rprofile template to the renv cache
#COPY library-scripts/.Rprofile-vscode /renv/.Rprofile-vscode

# [Optional] Uncomment this section to add Quarto to the container
RUN /rocker_scripts/install_quarto.sh

# [Optional] Uncomment this section to add Hugo to the container
# Customize version number as appropriate
#RUN curl -L https://github.com/gohugoio/hugo/releases/download/v0.87.0/hugo_extended_0.87.0_Linux-64bit.deb -o /tmp/hugo.deb
#RUN apt-get -y install ./tmp/hugo.deb

# [Optional] Uncomment this section to add addtional system dependencies needed for project
# RUN apt-get update \
# && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends ---packages list----

# [Optional] Uncomment this section for linking a local pusleaudio sound system on a linux host
# to the container.
#RUN apt-get update \
# && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends libpulse0 libasound2 libasound2-plugins pulseaudio-utils

# install obs-cli (Go version)
# https://github.com/muesli/obs-cli/releases
#ADD https://github.com/muesli/obs-cli/releases/download/v0.4.0/obs-cli_0.4.0_linux_x86_64.tar.gz /obs-cli-linux.tar.gz
#RUN tar -zxvf obs-cli-linux.tar.gz
#RUN cp /obs-cli /usr/local/bin/obs-cli
#RUN chmod 755 /usr/local/bin/obs-cli

# install obs-cli (Javascript version)
# https://github.com/leafac/obs-cli/releases
#ADD https://github.com/leafac/obs-cli/releases/download/v2.2.3/obs-cli-linux /usr/local/bin/obs-cli-js
#RUN chmod 755 /usr/local/bin/obs-cli-js

# [Optional] install additional R packages
RUN install2.r --error --skipinstalled --ncpus -4 tidyRSS pkgsearch lubridate dplyr rtweet

# [Optional] Set the default user. Omit if you want to keep the default as root.
USER $USERNAME
105 changes: 105 additions & 0 deletions .devcontainer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# R Projects Development Setup

This repo contains the various configurations, container settings, and other setup files that I will use in my new development setup for R projects on linux. Typically I would install R directly in my system along with the various IDEs and other tools that integrate with R. Now that I have systems with larger amounts of memory and faster CPUs, I am now experimenting with a development environment based on __container technology__, in particular Docker contaners. I have the following goals for my new development setup:

* Ability to use RStudio, Visual Studio Code, or other IDEs that can reference Docker containers or be fully contained inside one.
* Share a _central_ R package cache between containers using the [`{renv}`](https://rstudio.github.io/renv/) package management system.
* Leave no traces of the R installation or system dependencies on the host system. While that sounds a little brazen, I plan on using a custom Linux PC built for media production and I want to keep that system fairly clean and lean!

🎥 Check out this previous [Shiny Developer Series livestream](https://youtu.be/4wRiPG9LM3o) to see my detailed walkthrough!

## Pre-requisites

* Install Docker for your particular OS. For my development setup, it is Ubuntu 20.04. Instructions can be found at [docs.docker.com/get-docker](https://docs.docker.com/get-docker/)
+ If you are using a Linux-based operating system: Ensure that your user ID is added to the `docker` group.
* Install Visual Studio Code. Downloads and more information can be found at [code.visualstudio.com](https://code.visualstudio.com)
+ Install the [Remote Development Extension Pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack)
+ Install the [Docker Extension Pack](https://marketplace.visualstudio.com/items?itemName=formulahendry.docker-extension-pack) (while not necessarily required, I find it very useful for managing my containers inside the IDE)
+ If you use SSH keys with your online code repository account (for example GitHub or GitLab) you need to share your key with containers built. The process is documented at on the VSCode docs [here](https://code.visualstudio.com/docs/remote/containers#_sharing-git-credentials-with-your-container). For my setup, I have to run this once before launching VSCode: `ssh-add ~/.ssh/{name_of_key}`. I have a custom key but I would imagine most users will have a default key name of `id_rsa`.

## Visual Studio Code Setup

The `.devcontainer` directory in this repository contains the custom configuration files and build instructions for creating the development container. The `Dockerfile` and `devcontainer.json` were adapted from the VSCode dev containers GitHub repository [here](https://github.com/microsoft/vscode-dev-containers/tree/master/containers/r). Highlights of the customizations I made:

* Use the base Docker image of `rocker/r-ver:4.0.2` from the [Rocker Project](https://www.rocker-project.org/)
* Install the [Fish](https://fishshell.com/) shell instead of [ZSH](http://zsh.sourceforge.net/)
* Install additional OS libraries to make installation of R package binaries relatively painless
* Install the [radian](https://github.com/randy3k/radian) alternative R console
* Install `renv`
* Install certain dotnet core runtime packages (this may not be necessary in future)
* Configure environment variables for custom location of `renv` cache directory mounted into the container
* Set the default user as my user account and not root

With everything set up, I was able to mostly follow the official VS Code documenation page on [developing inside a container](https://code.visualstudio.com/docs/remote/containers) to get the development container launched. I am still learning the ropes so to speak with day-to-day usage of VS-Code with R coding. Here are some additional observations that I will update as I continue my journey:

+ There have been some random occurrances where I cannot view a Shiny app in a tab within VS Code when launching the app (the tab appears completely blank). However, when I click the notification in the lower-corner to open the link in an external browser, the app loads just fine. The only way I have found to at least manually fix the problem in the active session is to perform the workaround documented in [vscode-R/380](https://github.com/Ikuyadeu/vscode-R/issues/380#issuecomment-657890970) . Basically, run `options(vsc.browser=FALSE)` once in the R session, then run the application (which will open the app in an external window). Close the app, then run `options(vsc.browser="Active")` in the R session, and then run the app again. The app should open in the VS-Code tab like usual.
* The default linting is based on the [`{lintr}`](https://github.com/jimhester/lintr) package and might need some tweaking. Consult the [project configuration](https://github.com/jimhester/lintr#project-configuration) of the `lintr` repository for more infomation.

## RStudio Setup

In recent years, the open-source version of RStudio Server has been succesfully integrated into Docker containers in multiple projects including the excellent [Rocker Project](https://www.rocker-project.org/) led by Carl Boettiger, Dirk Eddelbuettel, and Noam Ross. Recently, they created version-stable R containers based on R version 4.0.0 or later at the [rocker-org/rocker-versioned2](https://github.com/rocker-org/rocker-versioned2) GitHub repository based on the recent Ubuntu 20.04 LTS release, as well as integrated the new [RStudio Package Manager (RSPM)](https://packagemanager.rstudio.com/) which hosts compiled binaries of R packages for installation on Linux. I decided to create a custom container for RStudio server following principles in their [new infrastructure](https://github.com/rocker-org/rocker-versioned2#modifying-and-extending-images-in-the-new-architecture) with a custom `Dockerfile` inspired by their [`Dockerfile_rstudio_4.0.2`](https://github.com/rocker-org/rocker-versioned2/blob/master/dockerfiles/Dockerfile_rstudio_4.0.2), and wrapping the execution within [Docker Compose](https://docs.docker.com/compose/). Highlights of the customizations I implemented:

* Install additional OS libraries to make installation of R package binaries relatively painless
* Install the [radian](https://github.com/randy3k/radian) alternative R console
* Configure environment variables for custom location of `renv` cache directory mounted into the container
* Install `renv`
* Store certain environment variables inside a custom text file called `rstudio.env`

## Configuring a central package cache

I have set up a local directory on my system that is mounted as a volume in each container to hold the R package cache. As long as your logged-in user has read and write priveleges to this directory on your host system, there should be no issues with each container reading and writing to the cache dir. In the containers, the directory is located at `/renv/cache` while on my host system it is located at `/opt/local/renv/cache`, but this could be any directory on your host system. In the Docker configuration files for each container, I set up the following environment variables:

```
RENV_PATHS_CACHE_HOST=/opt/local/renv/cache
RENV_PATHS_CACHE_CONTAINER=/renv/cache
RENV_PATHS_CACHE=/renv/cache
```

## Using Renv with VS-Code

One (intentional) effect of using `renv` is that out of the box the project's package library will not link to packages in the default library that are not included in the base installation of R. The interactions between an R session and VS-Code are largely driven by the [`{launguageserver}`](https://github.com/REditorSupport/languageserver) package available on CRAN. My solution to ensure any project with `renv` enabled can use all of the same integrations with VS-Code is to create a custom `.Rprofile` that contains certain triggers to bootstrap the installation of `languageserver` and perform necessary environment configurations if the session detects that the `TERM_PROGRAM` environment variable is set to `vscode`. The file [`.devcontainer/library-scripts/.Rprofile-vscode`](https://github.com/rpodcast/r_dev_projects/blob/master/.devcontainer/library-scripts/.Rprofile-vscode) is automatically copied to the container's `renv` cache directory, and can be manually copied to overwrite the current project's `.Rprofile` file. Much of this solution was adapted from ideas discussed in issue [vscode-R/259](https://github.com/Ikuyadeu/vscode-R/issues/259). The contents are below:

```r
# setup if using with vscode and R plugin
if (Sys.getenv("TERM_PROGRAM") == "vscode") {
source(file.path(Sys.getenv(if (.Platform$OS.type == "windows") "USERPROFILE" else "HOME"), ".vscode-R", "init.R"))
}
source("renv/activate.R")

if (Sys.getenv("TERM_PROGRAM") == "vscode") {
# obtain list of packages in renv library currently
project <- renv:::renv_project_resolve(NULL)
lib_packages <- names(unclass(renv:::renv_diagnostics_packages_library(project))$Packages)

# detect whether key packages are already installed
# was: !require("languageserver")
if (!"languageserver" %in% lib_packages) {
message("installing languageserver package")
renv::install("languageserver")
}

if (!"httpgd" %in% lib_packages) {
message("installing httpgd package")
renv::install("nx10/httpgd")
}

if (!"vscDebugger %in% lib_packages) {
message("installation vscDebugger package")
renv::install("ManuelHentschel/vscDebugger@v0.4.3")
}
# use the new httpgd plotting device
options(vsc.plot = FALSE)
options(device = function(...) {
httpgd::httpgd()
.vsc.browser(httpgd::httpgdURL(), viewer = "Beside")
})
}
```
## Next Steps
I would still consider this journey a work in progress, so here are some additonal tasks I hope to complete. Contributions and feedback are more than welcome!
* [ ] Figure out a way to seamlessly add this development setup inside a __specific__ project.
* [ ] Automatically override the default `.Rprofile` file created by `renv` so that I do not need to manually copy over the customized version.
75 changes: 75 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.134.1/containers/r
{
"name": "R",
"dockerComposeFile": "docker-compose.yml",
"workspaceFolder": "/workspace",
"service": "vscode",

"features": {
"fish": "latest"
},

// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.profiles.linux": {
"fish": {
"path": "/usr/bin/fish"
}
},
"terminal.integrated.defaultProfile.linux": "fish",
"r.alwaysUseActiveTerminal": true,
"r.bracketedPaste": true,
"r.sessionWatcher": true,
"r.rterm.linux": "/usr/local/bin/radian",
"r.rterm.option": [
""
],
"r.autoDetect": "false",
"r.terminalPath": "/usr/local/bin/radian",
"r.interpreterPath": "/usr/local/bin/R",
"r.debugger.timeouts.startup": 8000,
"editor.wordWrap": "on",
"editor.tabSize": 2,
"path-autocomplete.pathMappings": {
"/": "/",
"./": "${folder}"
},
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": "active"
},

// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"ikuyadeu.r",
//"/renv/cache/r-latest.vslx",
"ionutvmi.path-autocomplete",
"usernamehw.errorlens",
"mhutchie.git-graph",
"wayou.vscode-todo-highlight",
"tomoki1207.pdf",
"DavidAnson.vscode-markdownlint",
"Rubymaniac.vscode-paste-and-indent",
"GrapeCity.gc-excelviewer",
"IBM.output-colorizer",
//"Mohamed-El-Fodil-Ihaddaden.shinysnip",
"hediet.vscode-drawio",
//"MS-vsliveshare.vsliveshare-pack",
"ms-python.python",
"RDebugger.r-debugger",
//"GitHub.copilot",
"eamodio.gitlens",
"GitHub.vscode-pull-request-github",
"quarto.quarto"
//"hoovercj.vscode-power-mode"
]

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "R --version",

// Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
// "remoteUser": "docker"
}
38 changes: 38 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
version: '3.4'

services:
vscode:
build:
context: .
dockerfile: Dockerfile
args:
USERNAME: ${USER}
QUARTO_VERSION: ${QUARTO_VERSION}
user: ${USER}
volumes:
- ..:/workspace:cached
- ${RENV_PATHS_CACHE_HOST}:/renv/cache
cap_add:
- SYS_PTRACE
security_opt:
- seccomp:unconfined
command: sleep infinity
env_file: .env

rstudiodevcontainer:
build:
context: ./rstudio_docker
args:
S6_VERSION: ${S6_VERSION}
RSTUDIO_VERSION: ${RSTUDIO_VERSION}
QUARTO_VERSION: ${QUARTO_VERSION}
volumes:
- ${VOLUME_PATH}:${VOLUME_PATH}
- /home/${USER}/.ssh:/home/${USER}/.ssh
- ${RENV_PATHS_CACHE_HOST}:${RENV_PATHS_CACHE_CONTAINER}
- ${VOLUME_PATH}/.devcontainer/rstudio_config_dir:/home/${USER}/.config/rstudio
restart:
unless-stopped
ports:
- ${LOCAL_PORT}:8787
env_file: .env
17 changes: 17 additions & 0 deletions .devcontainer/keybindings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[
{
"key": "ctrl+v",
"command": "pasteAndIndent.action",
"when": "editorTextFocus && !editorReadonly"
},
{
"key": "ctrl+v",
"command": "editor.action.clipboardPasteAction",
"when": "!editorTextFocus"
},
{
"key": "ctrl+shift+v",
"command": "editor.action.clipboardPasteAction",
"when": "editorTextFocus && !editorReadonly"
}
]
8 changes: 8 additions & 0 deletions .devcontainer/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "R-Debugger",
"request": "launch",
"name": "Launch Workspace",
"debugMode": "workspace",
"workingDirectory": "${workspaceFolder}",
"allowGlobalDebugging": true
}
Loading

0 comments on commit deb107f

Please sign in to comment.