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:
- Tell a user to copy the command and paste it into a terminal window.
- Relying on the presence of the curl program by default on most Linux distributions, the command should download an installation script from the Internet.
- After downloading successfully, the script is piped to a shell interpreter, which is usually Bash.
- 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 | bashto a Dockerfile is equal to injecting potentially malicious code into a container. rvis 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.shprovided 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:
- curl: command line tool and library for transferring data with URLs. It’s a foundational piece of software for the Internet.
- The R Project for Statistical Computing
- R the software engineering way by Iris Meredith
- rv: a declarative R package manager