Alexander Markov

Setting up a developer environment for the R language

While it’s not something new, instructing users to install software with shell commands like curl https://www.some.site/install.sh | bash still remains a controversial practice. Mainly it serves the purpose of bypassing the hard and often opaque ways of packaging software for the Linux ecosystem. The trick works like this:

  1. Tell a user to copy the command and paste it into a terminal window.
  2. Relying on the presence of the curl program by default on most Linux distributions, the command should download an installation script from the Internet.
  3. After downloading successfully, the script is piped to a shell interpreter, which is usually Bash.
  4. The script downloads software from the Internet and runs various commands to install it on the user’s system.

Must I remark that each of these steps demands a high level of trust in the provider of the software? I remember quite well that some time ago just opening a malicious email message on a Windows computer could have led to the infection of all connected hosts with viruses and trojans of various levels of nuisance. And now, as developers, we simply ask users to download and run a shell script in one click, right?

I don’t want to discuss all the security implications of blindly running unknown code. Unfortunately, I see the proliferation of all kinds of shortcuts in the name of improving developer experience and removing friction when using software.

So, what I suggest here is to do the opposite — add friction, make a user or developer think and understand the consequences of their actions. (Well, sometimes you have to write or say the words out loud to realize how futile they are. Nevertheless.)

Last week I was busy setting up a developer environment for the R language. From the helpful instructions by Iris Meredith, I’ve learnt about rv, a declarative package manager for R packages. On their Installation Instructions page, the developers provide two options for Linux systems: download the installation archive directly from GitHub or curl | bash a shell script.

As my aim is to set up a reproducible environment for R, I use Docker and therefore also have the same two options to add rv to the image. It’s tempting to just curl | bash in the Dockerfile, but I would argue against this with three points:

  • It might be insecure to run a shell script blindly. Adding RUN curl | bash to a Dockerfile is equal to injecting potentially malicious code into a container.
  • rv is a key dependency for my image and this must be stated clearly in the Readme file describing the building of the image
  • While there’s nothing wrong with the install.sh provided by the developers, it’s somewhat long and verbose. The script mostly gathers information about the system in order to download a suitable installation archive

My approach is to use a docker build wrapper script like this:

#!/usr/bin/env dash

# This image uses rv package manager
# https://github.com/A2-ai/rv/releases

# Download rv installation archive if it doesn't exist
# in the working directory

if [ ! -f "${PWD}/rv_latest.tar.gz" ]; then
    . "${PWD}"/download_rv.dash
fi

docker build -t "markov/r-lang" .

See this gist for download_rv.dash. The script exits with an error if it can’t download rv.

Finally, the Dockerfile for building an image with an R environment might look like this:

# syntax=docker/dockerfile:1

FROM rocker/r-ver:4.5 AS base

ENV RV_ARCHIVE=rv_latest.tar.gz

WORKDIR /my-r-package

COPY ${RV_ARCHIVE} ${RV_ARCHIVE}
COPY rproject.toml rproject.toml
COPY rv.lock rv.lock

RUN tar -xzf ${RV_ARCHIVE} \
    && rm ${RV_ARCHIVE} \
    && mv rv /usr/local/bin/rv

RUN <<EOF
apt-get update
apt-get install -y libcurl4-openssl-dev libuv1 --no-install-recommends
apt-get clean
rm -rf /var/lib/apt/lists/*
EOF

RUN rv sync && rv activate

List of useful links:

  1. curl: command line tool and library for transferring data with URLs. It’s a foundational piece of software for the Internet.
  2. The R Project for Statistical Computing
  3. R the software engineering way by Iris Meredith
  4. rv: a declarative R package manager