├── .github ├── dependabot.yml ├── examples.py └── workflows │ ├── docker-build.yml │ ├── examples.yml │ ├── r-devel-daily.yml │ └── test.yml ├── Dockerfile ├── README.Rmd ├── README.md ├── actions └── readme-build │ └── action.yaml ├── examples ├── RPostgres │ └── Dockerfile ├── V8 │ └── Dockerfile ├── arrow-s3 │ └── Dockerfile ├── arrow │ └── Dockerfile ├── data.table │ └── Dockerfile ├── dplyr │ └── Dockerfile ├── ggplot2 │ └── Dockerfile ├── golem │ └── Dockerfile ├── h2o │ └── Dockerfile ├── knitr │ └── Dockerfile ├── odbc │ └── Dockerfile ├── pagedown │ ├── Dockerfile │ ├── README.md │ ├── card.Rmd │ └── pagedown.sh ├── pingr │ └── Dockerfile ├── plumber │ ├── Dockerfile │ └── plumber.R ├── praise │ └── Dockerfile ├── prophet │ └── Dockerfile ├── rJava │ └── Dockerfile ├── renv-shiny │ ├── .Rprofile │ ├── Dockerfile │ ├── README.md │ ├── app.R │ ├── renv.lock │ └── renv │ │ ├── .gitignore │ │ ├── activate.R │ │ └── settings.json ├── rmarkdown │ └── Dockerfile ├── rstan │ └── Dockerfile ├── sf │ └── Dockerfile ├── shiny │ └── Dockerfile ├── ssh-curl │ └── Dockerfile ├── tidyverse-minimal │ └── Dockerfile ├── tidyverse │ └── Dockerfile └── xgboost │ └── Dockerfile ├── installr ├── patches ├── R-4.0.0.patch ├── R-4.0.1.patch ├── R-4.0.2.patch ├── R-4.0.3.patch ├── R-4.0.4.patch ├── R-4.0.5.patch ├── R-4.1.0.patch ├── R-4.1.1.patch ├── R-4.1.2.patch ├── R-4.1.3.patch ├── R-4.2.0.patch ├── R-4.2.1.patch ├── R-4.2.2.patch ├── R-4.2.3.patch ├── R-4.3.0.patch ├── R-4.3.1.patch ├── R-4.3.2.patch └── R-4.3.3.patch ├── test.bats └── tools ├── add-date-tags.R ├── calculate_tags.R ├── calculate_tags.sh └── readme_tags.sh /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/examples.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | import glob 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description="Print examples to test as JSON.") 8 | parser.add_argument( 9 | 'examples', 10 | type=str, 11 | nargs='?', 12 | default='all', 13 | help='Comma-separated list of examples. Specify "all" to build all (the default).' 14 | ) 15 | args = parser.parse_args() 16 | if args.examples == 'all': 17 | os.chdir('examples') 18 | examples = glob.glob('*') 19 | else: 20 | examples = args.examples.split(',') 21 | examples = [ e.strip() for e in examples ] 22 | print(json.dumps(examples)) 23 | 24 | if __name__ == '__main__': 25 | main() 26 | -------------------------------------------------------------------------------- /.github/workflows/docker-build.yml: -------------------------------------------------------------------------------- 1 | name: Docker build, all supported versions 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | if: "!contains(github.event.head_commit.message, '[ci skip]')" 12 | name: ${{ matrix.config.r }} 13 | 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | config: 18 | # - { r: 'devel' } 19 | # - { r: 'next' } 20 | - { r: '4.5.0' } 21 | # - { r: '4.3.3' } 22 | # - { r: '4.2.3' } 23 | # - { r: '4.1.3' } 24 | # - { r: '4.0.5' } 25 | # - { r: '3.6.3' } 26 | 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | with: 31 | fetch-depth: 10 32 | 33 | - name: Login to GitHub Container Registry 34 | uses: docker/login-action@v3 35 | with: 36 | registry: ghcr.io 37 | username: ${{ github.repository_owner }} 38 | password: ${{ secrets.GITHUB_TOKEN }} 39 | 40 | - name: Login to DockerHub 41 | uses: docker/login-action@v3 42 | with: 43 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 44 | password: ${{ secrets.DOCKER_HUB_TOKEN }} 45 | 46 | - name: Set up QEMU 47 | uses: docker/setup-qemu-action@v3 48 | with: 49 | image: tonistiigi/binfmt:qemu-v8.1.5 50 | 51 | - name: Set up Docker Buildx 52 | uses: docker/setup-buildx-action@v3 53 | 54 | - name: Build 55 | uses: docker/build-push-action@v6 56 | with: 57 | platforms: linux/amd64 58 | push: false 59 | load: true 60 | build-args: | 61 | R_VERSION=${{ matrix.config.r }} 62 | tags: rhub/r-minimal:test 63 | 64 | - name: Calculate tags 65 | id: tags 66 | run: | 67 | cp -r tools /tmp 68 | docker run -v /tmp/tools:/tools \ 69 | rhub/r-minimal:test bash -c "/tools/calculate_tags.sh ${{ matrix.config.r }}" 70 | 71 | - name: Push 72 | uses: docker/build-push-action@v6 73 | with: 74 | platforms: linux/amd64,linux/arm64 75 | push: true 76 | build-args: | 77 | R_VERSION=${{ matrix.config.r }} 78 | tags: ${{ steps.tags.outputs.tags }} 79 | 80 | readme: 81 | if: ${{ always() }} 82 | name: Update README 83 | runs-on: ubuntu-latest 84 | needs: build 85 | 86 | steps: 87 | - name: Checkout 88 | uses: actions/checkout@v4 89 | with: 90 | fetch-depth: 10 91 | 92 | - name: Update README 93 | uses: ./actions/readme-build 94 | -------------------------------------------------------------------------------- /.github/workflows/examples.yml: -------------------------------------------------------------------------------- 1 | name: Examples 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | examples: 7 | description: | 8 | Comma-separated list of example directories. Specify "all" to use build all. 9 | required: false 10 | default: 'all' 11 | type: string 12 | schedule: 13 | - cron: '55 6 * * 6' 14 | 15 | jobs: 16 | setup-matrix: 17 | runs-on: ubuntu-latest 18 | outputs: 19 | examples: ${{ steps.setup-matrix.outputs.examples }} 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - name: Set up matrix of examples 25 | id: setup-matrix 26 | run: | 27 | examples=$(python .github/examples.py ${{ github.event.inputs.examples }}) 28 | echo "examples=$examples" >> $GITHUB_OUTPUT 29 | 30 | examples: 31 | needs: setup-matrix 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | example: ${{ fromJson(needs.setup-matrix.outputs.examples) }} 36 | runs-on: ubuntu-latest 37 | name: "examples/${{ matrix.example }}" 38 | 39 | steps: 40 | - uses: actions/checkout@v4 41 | 42 | - name: Set up Docker Buildx 43 | uses: docker/setup-buildx-action@v3 44 | 45 | - name: Build 46 | uses: docker/build-push-action@v6 47 | with: 48 | push: false 49 | platforms: linux/amd64 50 | context: examples/${{ matrix.example }} 51 | -------------------------------------------------------------------------------- /.github/workflows/r-devel-daily.yml: -------------------------------------------------------------------------------- 1 | name: Daily R devel and R patched build 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | build: 7 | description: 'Build R-devel and R-next' 8 | required: true 9 | type: choice 10 | options: 11 | - 'yes' 12 | - 'no' 13 | default: 'yes' 14 | readme: 15 | description: 'Update README' 16 | required: true 17 | type: choice 18 | options: 19 | - 'yes' 20 | - 'no' 21 | default: 'yes' 22 | schedule: 23 | - cron: '55 6 * * *' 24 | 25 | jobs: 26 | 27 | daily-builds: 28 | if: ${{ github.event.inputs.build == '' || github.event.inputs.build == 'yes' }} 29 | name: ${{ matrix.config.r }} 30 | runs-on: ubuntu-latest 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | config: 36 | - { r: 'devel' } 37 | - { r: 'next' } 38 | 39 | steps: 40 | - name: Checkout 41 | uses: actions/checkout@v4 42 | with: 43 | fetch-depth: 10 44 | 45 | - name: Login to GitHub Container Registry 46 | uses: docker/login-action@v3 47 | with: 48 | registry: ghcr.io 49 | username: ${{ github.repository_owner }} 50 | password: ${{ secrets.GITHUB_TOKEN }} 51 | 52 | - name: Login to DockerHub 53 | uses: docker/login-action@v3 54 | with: 55 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 56 | password: ${{ secrets.DOCKER_HUB_TOKEN }} 57 | 58 | - name: Set up QEMU 59 | uses: docker/setup-qemu-action@v3 60 | with: 61 | image: tonistiigi/binfmt:qemu-v8.1.5 62 | 63 | - name: Set up Docker Buildx 64 | uses: docker/setup-buildx-action@v3 65 | 66 | - name: Build 67 | uses: docker/build-push-action@v6 68 | with: 69 | platforms: linux/amd64 70 | push: false 71 | load: true 72 | build-args: | 73 | R_VERSION=${{ matrix.config.r }} 74 | tags: rhub/r-minimal:test 75 | 76 | - name: Calculate tags 77 | id: tags 78 | run: | 79 | cp -r tools /tmp 80 | docker run -v /tmp/tools:/tools \ 81 | rhub/r-minimal:test bash -c "/tools/calculate_tags.sh ${{ matrix.config.r }}" 82 | 83 | - name: Push 84 | uses: docker/build-push-action@v6 85 | with: 86 | platforms: linux/amd64,linux/arm64 87 | push: true 88 | build-args: | 89 | R_VERSION=${{ matrix.config.r }} 90 | tags: ${{ steps.tags.outputs.tags }} 91 | 92 | debug: 93 | runs-on: ubuntu-latest 94 | if: always() 95 | name: Parameters 96 | steps: 97 | - name: Input parameters 98 | run: | 99 | echo "build: ${{ github.event.inputs.build }}" 100 | echo "readme: ${{ github.event.inputs.readme }}" 101 | 102 | readme: 103 | if: ${{ (github.event.inputs.readme == '' || github.event.inputs.readme == 'yes') && always() }} 104 | name: Update README 105 | runs-on: ubuntu-latest 106 | needs: daily-builds 107 | 108 | steps: 109 | - name: Checkout 110 | uses: actions/checkout@v4 111 | with: 112 | fetch-depth: 10 113 | 114 | - name: Update README 115 | uses: ./actions/readme-build 116 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | 7 | jobs: 8 | 9 | test: 10 | 11 | runs-on: ubuntu-latest 12 | if: "!contains(github.event.head_commit.message, '[ci skip]')" 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 10 19 | 20 | - name: Install bats 21 | run: | 22 | sudo apt-get update && sudo apt-get install bats 23 | 24 | - name: Run tests 25 | run: | 26 | bats test.bats 27 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG ALPINE_VERSION=3.19 2 | 3 | FROM alpine:${ALPINE_VERSION} as build 4 | 5 | LABEL org.opencontainers.image.licenses="MIT" \ 6 | org.opencontainers.image.source="https://github.com/r-hub/r-minimal" \ 7 | org.opencontainers.image.vendor="r-hub" \ 8 | org.opencontainers.image.authors="r-hub admin " 9 | 10 | ENV _R_SHLIB_STRIP_=true 11 | ENV TZ=UTC 12 | 13 | ARG R_VERSION=4.5.0 14 | 15 | WORKDIR /root 16 | 17 | RUN apk add --no-cache gcc musl-dev gfortran g++ zlib-dev bzip2-dev xz-dev pcre-dev \ 18 | pcre2-dev curl-dev make perl readline-dev patch 19 | 20 | RUN if [[ "$R_VERSION" == "devel" ]]; then \ 21 | wget https://cran.r-project.org/src/base-prerelease/R-devel.tar.gz; \ 22 | elif [[ "$R_VERSION" == "next" ]]; then \ 23 | wget https://cran.rstudio.com/src/base-prerelease/R-patched.tar.gz -O R-next.tar.gz; \ 24 | else \ 25 | wget https://cran.rstudio.com/src/base/R-${R_VERSION%%.*}/R-${R_VERSION}.tar.gz; \ 26 | fi 27 | RUN tar xzf R-${R_VERSION}.tar.gz 28 | 29 | # The directory inside the tarball sometimes has different names 30 | 31 | RUN if [[ -e R-patched ]]; then mv R-patched R-next; fi 32 | RUN if [[ -e R-beta ]]; then mv R-beta R-next; fi 33 | RUN if [[ -e R-alpha ]]; then mv R-alpha R-next; fi 34 | RUN if [[ -e R-rc ]]; then mv R-rc R-next; fi 35 | RUN if [[ -e R-prerelease ]]; then mv R-prerelease R-next; fi 36 | RUN if [[ -e R-pre ]]; then mv R-pre R-next; fi 37 | 38 | RUN if echo ${R_VERSION} | grep -q "^3[.][45][.]"; then \ 39 | echo "export CFLAGS='-D__MUSL__ -fcommon'" >> R-${R_VERSION}/FLAGS; \ 40 | echo "export FFLAGS=-fallow-argument-mismatch" >> R-${R_VERSION}/FLAGS; \ 41 | else \ 42 | echo "export CFLAGS=-D__MUSL__" >> R-${R_VERSION}/FLAGS; \ 43 | fi 44 | 45 | # Patch older R to allow libcurl 8.x.x, which is API compatible 46 | RUN if [[ "${R_VERSION}" != "next" ]] && [[ "${R_VERSION}" != "devel" ]] \ 47 | && [[ "`apk version -t ${R_VERSION} 4.3.0`" == "<" ]]; then \ 48 | cd R-${R_VERSION}; \ 49 | perl -i -0pe 's/#if LIBCURL_VERSION_MAJOR > 7\n exit[(]1[)]/#if LIBCURL_VERSION_MAJOR > 7\n exit(0)/gms' configure; \ 50 | fi 51 | 52 | COPY patches patches 53 | RUN cd R-${R_VERSION} && ls -l .. && if [ -f "../patches/R-${R_VERSION}.patch" ]; then \ 54 | echo Patching R; \ 55 | patch -p1 < "../patches/R-${R_VERSION}.patch"; \ 56 | fi 57 | 58 | 59 | RUN cd R-${R_VERSION} && \ 60 | . FLAGS && \ 61 | CXXFLAGS=-D__MUSL__ ./configure \ 62 | --with-recommended-packages=no \ 63 | --with-readline=yes --with-x=no --enable-java=no \ 64 | --enable-R-shlib \ 65 | --disable-openmp --with-internal-tzcode 66 | RUN cd R-${R_VERSION} && make -j 4 67 | RUN cd R-${R_VERSION} && make install 68 | 69 | RUN strip -x /usr/local/lib/R/bin/exec/R 70 | RUN strip -x /usr/local/lib/R/lib/* 71 | RUN find /usr/local/lib/R -name "*.so" -exec strip -x \{\} \; 72 | 73 | RUN rm -rf /usr/local/lib/R/library/translations 74 | RUN cp /usr/local/lib/R/doc/NEWS.rds /tmp 75 | RUN rm -rf /usr/local/lib/R/doc 76 | RUN mkdir -p /usr/local/lib/R/doc/html 77 | RUN cp /tmp/NEWS.rds /usr/local/lib/R/doc/ 78 | RUN find /usr/local/lib/R/library -name help | xargs rm -rf 79 | 80 | RUN find /usr/local/lib/R/share/zoneinfo/America/ -mindepth 1 -maxdepth 1 \ 81 | '!' -name New_York -exec rm -r '{}' ';' 82 | RUN find /usr/local/lib/R/share/zoneinfo/ -mindepth 1 -maxdepth 1 \ 83 | '!' -name UTC '!' -name America '!' -name GMT -exec rm -r '{}' ';' 84 | 85 | RUN sed -i 's/,//g' /usr/local/lib/R/library/utils/iconvlist 86 | 87 | RUN touch /usr/local/lib/R/doc/html/R.css 88 | 89 | # ---------------------------------------------------------------------------- 90 | 91 | FROM alpine:${ALPINE_VERSION} as final 92 | ARG R_VERSION=4.4.0 93 | 94 | ENV _R_SHLIB_STRIP_=true 95 | ENV TZ=UTC 96 | 97 | COPY --from=build /usr/local /usr/local 98 | 99 | COPY installr /usr/local/bin/ 100 | 101 | RUN apk add --no-cache libgfortran xz-libs libcurl libpcrecpp libbz2 \ 102 | pcre2 make readline 103 | 104 | RUN apk add --no-cache bash && \ 105 | ln -sf /bin/bash /bin/sh; 106 | 107 | WORKDIR /root 108 | 109 | ENV DOWNLOAD_STATIC_LIBV8=1 110 | 111 | CMD ["R"] 112 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | editor_options: 4 | markdown: 5 | wrap: 72 6 | --- 7 | 8 | 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>", 14 | out.width = "100%" 15 | ) 16 | ``` 17 | 18 | # Minimal Docker images for R 19 | 20 | ## What is R? 21 | 22 | R is a free software environment for statistical computing and graphics. 23 | It compiles and runs on a wide variety of UNIX platforms, Windows and 24 | MacOS. See more at 25 | 26 | ## Goals and features 27 | 28 | The main goal of these images is to keep them minimal, so they can be used 29 | as part of a bigger (web) application, or as a base image. Currently the 30 | (R 4.4.1) `r-minimal` image is 23.74MB compressed, and 45.88MB 31 | uncompressed. 32 | 33 | All images use Alpine Linux. 34 | 35 | The images include the `installr` tools that can install R packages from 36 | CRAN or GitHub: 37 | 38 | ``` 39 | ❯ installr -h 40 | Usage: ./installr [ -c | -d ] [ -e ] [ -a pkgs ] [ -t pkgs ] [ -r ] [ -p ] REMOTES ... 41 | 42 | Options: 43 | -c install C and C++ compilers and keep them 44 | -d install C and C++ compilers, temporarily 45 | -a install Alpine packages and keep them 46 | -t install Alpine packages, temporarily 47 | -p do not remove pak after the installation (ignored if -r is given). 48 | -e use renv to restore the renv.lock file if present. 49 | 50 | REMOTES may be: 51 | * package names from CRAN/Bioconductor, e.g. ggplot2 52 | * slugs of GitHub repos, e.g. tidyverse/ggplot2 53 | * GitHub branch, tag or commit, e.g tidyverse/ggplot2@v1.0.0 54 | * URLs to package .tar.gz files, e.g. url::https://x.com/pkg.tar.gz 55 | * path to a local directory, e.g. local::. 56 | ``` 57 | 58 | Recent r-minimal containers use [pak](https://github.com/r-lib/pak) for R 59 | packages installation. If you have problems with pak, or need to install 60 | a package from a source that pak does not support, but the remotes package 61 | does, then install the remotes package first. 62 | 63 | ## Limitations 64 | 65 | To keep the images minimal, they do not include a number of parts and 66 | features that most users would prefer to have for interactive R 67 | development: 68 | 69 | * Recommended R packages are not installed. 70 | * Documentation is not included. 71 | * No X11 support. 72 | * No OpenMP support. (But you can configure it for a package, see [examples/data.table](examples/data.table).) 73 | * No JPEG, PNG or TIFF support. 74 | * No Cairo support. 75 | * No Tcl/Tk support. 76 | * No translations, only English. 77 | * The image does not have C, C++ or Fortran compilers. 78 | * Limited time zone data: `GMT`, `UTC` and `America/New_York`, see 79 | below if you need better time zone data. 80 | 81 | ## Usage 82 | 83 | Get the image from 84 | [Docker Hub](https://hub.docker.com/repository/docker/rhub/r-minimal): 85 | 86 | ``` 87 | docker pull docker.io/rhub/r-minimal:latest 88 | ``` 89 | 90 | or from [GitHub Packages](https://github.com/r-hub/r-minimal/packages/92808?version=latest): 91 | 92 | ``` 93 | docker pull ghcr.io/r-hub/r-minimal/r-minimal:latest 94 | ``` 95 | 96 | ## Platforms 97 | 98 | All images are available on `linux/amd64` and `linux/arm64` platforms. 99 | 100 | ## Supported R versions 101 | 102 | Currently we support the last patch version of the last five minor R 103 | versions. The `latest` tag always uses the last R release. 104 | 105 | ```{r, include = FALSE} 106 | source("tools/calculate_tags.R") 107 | ``` 108 | 109 | | image | R version | tags | note | 110 | |-----------|-------------------|------------------------|-------------| 111 | | R devel | `r rv("devel")` | `r rv_tags("devel")` | Built daily | 112 | | R next | `r rv("next")` | `r rv_tags("next")` | Built daily | 113 | | R release | `r rv("release")` | `r rv_tags("release")` | | 114 | | R 4.3.x | `r rv("4.3")` | `r rv_tags("4.3")` | | 115 | | R 4.2.x | `r rv("4.2")` | `r rv_tags("4.2")` | | 116 | | R 4.1.x | `r rv("4.1")` | `r rv_tags("4.1")` | | 117 | | R 4.0.x | `r rv("4.0")` | `r rv_tags("4.0")` | | 118 | | R 3.6.x | `r rv("3.6")` | `r rv_tags("3.6")` | | 119 | 120 | ## Daily R-devel builds 121 | 122 | We tag our daily R-devel builds with the build date. 123 | These images can be useful for tracking down bugs and 124 | regressions in R. E.g.: 125 | ```sh 126 | docker run -ti ghcr.io/r-hub/r-minimal/r-minimal:2022-11-25 127 | ``` 128 | The tags start on 2021-07-25. `linux/arm64` images are available 129 | from 2022-01-13. 130 | 131 | ## Dockerfile examples 132 | 133 | One of our main goals is to be able to use `rhub/r-minimal` as a base 134 | image, and easily add R packages from CRAN or GitHub to it, to create a 135 | new image. Run `installr` from a `Dockerfile` to add R packages to 136 | the `r-minimal` image: 137 | 138 | ```dockerfile 139 | FROM rhub/r-minimal 140 | RUN installr praise 141 | CMD [ "R", "--slave", "-e", "cat(praise::praise())" ] 142 | ``` 143 | 144 | Package with compiled code: 145 | 146 | ```dockerfile 147 | FROM rhub/r-minimal 148 | RUN installr -d glue 149 | ``` 150 | 151 | After the package(s) have been installed, `installr` removed the compilers, 152 | as these are typically not needed on the final image. If you want to keep 153 | them use `installr -c` instead of `installr -d`. 154 | 155 | Package with system requirements: 156 | 157 | ```dockerfile 158 | FROM rhub/r-minimal 159 | RUN installr -d -t linux-headers pingr 160 | CMD [ "R", "-q", "-e", "pingr::is_online() || stop('offline')" ] 161 | ``` 162 | 163 | Similarly to compilers, system packages are removed after the R packages 164 | have been installed. If you want to keep (some of) them, use `installr -a` 165 | instead of `installr -t`. (You can also mix the two.) 166 | 167 | Using with renv: 168 | 169 | To use `renv` to restore the `renv.lock` file, use the `-e` option: 170 | 171 | ```dockerfile 172 | FROM rhub/r-minimal 173 | COPY .Rprofile .Rprofile 174 | COPY renv renv 175 | COPY renv.lock . 176 | RUN installr -d -e 177 | ``` 178 | 179 | If you copy the entire folder with renv, including the `activate.R` and `.Rprofile`, renv will bootstrap itself with the same version as the lock and restore the packages with the proper versions. 180 | All the necessary compilers and libraries needed at runtime need to be installed with the `-a` and `-t` options. 181 | Please refer to [examples/renv-shiny](examples/renv-shiny) for an example that install shiny and rmarkdown in a container. 182 | 183 | 184 | ## Popular packages: 185 | 186 | Hints on installing some popular R packages: 187 | 188 | | package | installr command | ~ image size (uncompressed) | 189 | |------------|--------------------------------------------------------------------|-----------------------------| 190 | | data.table | See [examples/data.table](examples/data.table) for OpenMP support | 26.2 MB (50.0 MB) | 191 | | dplyr | `installr -d dplyr` | 31.9 MB (59.7 MB) | 192 | | ggplot2 | `installr -d -t gfortran ggplot2` | 56.1 MB (93.5 MB) | 193 | | h2o | See [examples/h2o](examples/h2o). | 354.0 MB (511.0 MB) | 194 | | knitr | `installr -d knitr` | 25.3 MB (48.3 MB) | 195 | | shiny | See [examples/shiny](examples/shiny). | 49.8 MB (103.5 MB) | 196 | | sf | See [examples/sf](examples/sf). | 79.7 MB (194.8 MB) | 197 | | plumber | See [examples/plumber](examples/plumber). | 56.3 MB (127.7 MB) | 198 | | rmarkdown | `installr -d rmarkdown` | 58.1 MB (153.5 MB) (with pandoc) | 199 | | tidyverse | See [examples/tidyverse](examples/tidyverse). | 113.7 MB (208.6 MB) | 200 | | tidyverse w/o reprex | See [examples/tidyverse-minimal](examples/tidyverse-minimal). | 66.5 MB (115.4 MB) | 201 | | rstan | See [examples/rstan](examples/rstan). | 92.0 MB (298.5 MB) | 202 | | xgboost | `installr -d -t "gfortran libexecinfo-dev" -a libexecinfo xgboost` | 35.5 MB (71.1 MB) | 203 | 204 | See also the `Dockerfile`s in the `examples` directory. 205 | 206 | > Note that package and system dependencies change over time, so if any 207 | > of these commands do not work any more, please 208 | > [let us know](https://github.com/r-hub/r-minimal). 209 | 210 | > See the [Dockerfile](examples/rmarkdown/Dockerfile) for installing pandoc. 211 | 212 | ## Time zones 213 | 214 | The image uses R's internal time zone database, but most time zones are 215 | removed from, to save space. The only supported ones are `GMT`, `UTC` and 216 | `America/New_York`. If you need more time zones, then install Alpine's 217 | time zone package and point R to it: 218 | 219 | ``` 220 | apk add --no-cache tzdata 221 | export TZDIR=/usr/share/zoneinfo 222 | ``` 223 | 224 | See also the discussion at https://github.com/r-hub/r-minimal/issues/24 225 | 226 | ## Known failures and workarounds 227 | 228 | * The ps package needs the `linux-headers` Alpine package at compile time. 229 | Many tidyverse packages depend on ps, so they'll need it as well: 230 | 231 | ``` 232 | installr -d -t linux-headers ps 233 | ``` 234 | 235 | * The arrow package needs a `Makevars` file to add a link flag. See the 236 | example `Dockerfile` in the [examples/arrow](examples/arrow) directory. 237 | 238 | * The V8 packagees do not compile on aarch64 machines by default. 239 | On x86_64 it installs fine: 240 | 241 | ``` 242 | installr -d -t curl-dev V8 243 | ``` 244 | 245 | This means that other packages that need V8 (e.g. rstan and prophet) 246 | do not work on aarch64, either. 247 | 248 | * To install the magick package, you need both the `imagemagick` and 249 | `imagemagick-dev` Alpine packages, both at install time and run time: 250 | 251 | ``` 252 | installr -d -a "imagemagick imagemagick-dev" -t "curl-dev" magick 253 | ``` 254 | 255 | * pingr 2.0.4 does not build on Alpine, use the dev version: 256 | 257 | ``` 258 | installr -d -t linux-headers r-lib/pingr 259 | ``` 260 | 261 | * Rcpp 1.0.13 [does not compile with R 4.4.2]( 262 | https://github.com/RcppCore/Rcpp/issues/1341). Use the dev version of 263 | Rcpp until a new version is released on CRAN. This affects all packages 264 | that depend on Rcpp, naturally. E.g. shiny, V8, rstan, odbc, golem, 265 | prophet, pagedown, plumber, sf, etc. See the updated [examples]. 266 | 267 | ``` 268 | installr -d Rcppcore/Rcpp 269 | ``` 270 | 271 | ## License 272 | 273 | See for the R licenses 274 | 275 | These Dockerfiles are licensed under the MIT License. 276 | 277 | © [R Consortium](https://github.com/rconsortium) 278 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Minimal Docker images for R 5 | 6 | ## What is R? 7 | 8 | R is a free software environment for statistical computing and graphics. 9 | It compiles and runs on a wide variety of UNIX platforms, Windows and 10 | MacOS. See more at 11 | 12 | ## Goals and features 13 | 14 | The main goal of these images is to keep them minimal, so they can be 15 | used as part of a bigger (web) application, or as a base image. 16 | Currently the (R 4.4.1) `r-minimal` image is 23.74MB compressed, and 17 | 45.88MB uncompressed. 18 | 19 | All images use Alpine Linux. 20 | 21 | The images include the `installr` tools that can install R packages from 22 | CRAN or GitHub: 23 | 24 | ❯ installr -h 25 | Usage: ./installr [ -c | -d ] [ -e ] [ -a pkgs ] [ -t pkgs ] [ -r ] [ -p ] REMOTES ... 26 | 27 | Options: 28 | -c install C and C++ compilers and keep them 29 | -d install C and C++ compilers, temporarily 30 | -a install Alpine packages and keep them 31 | -t install Alpine packages, temporarily 32 | -p do not remove pak after the installation (ignored if -r is given). 33 | -e use renv to restore the renv.lock file if present. 34 | 35 | REMOTES may be: 36 | * package names from CRAN/Bioconductor, e.g. ggplot2 37 | * slugs of GitHub repos, e.g. tidyverse/ggplot2 38 | * GitHub branch, tag or commit, e.g tidyverse/ggplot2@v1.0.0 39 | * URLs to package .tar.gz files, e.g. url::https://x.com/pkg.tar.gz 40 | * path to a local directory, e.g. local::. 41 | 42 | Recent r-minimal containers use [pak](https://github.com/r-lib/pak) for 43 | R packages installation. If you have problems with pak, or need to 44 | install a package from a source that pak does not support, but the 45 | remotes package does, then install the remotes package first. 46 | 47 | ## Limitations 48 | 49 | To keep the images minimal, they do not include a number of parts and 50 | features that most users would prefer to have for interactive R 51 | development: 52 | 53 | - Recommended R packages are not installed. 54 | - Documentation is not included. 55 | - No X11 support. 56 | - No OpenMP support. (But you can configure it for a package, see 57 | [examples/data.table](examples/data.table).) 58 | - No JPEG, PNG or TIFF support. 59 | - No Cairo support. 60 | - No Tcl/Tk support. 61 | - No translations, only English. 62 | - The image does not have C, C++ or Fortran compilers. 63 | - Limited time zone data: `GMT`, `UTC` and `America/New_York`, see below 64 | if you need better time zone data. 65 | 66 | ## Usage 67 | 68 | Get the image from [Docker 69 | Hub](https://hub.docker.com/repository/docker/rhub/r-minimal): 70 | 71 | docker pull docker.io/rhub/r-minimal:latest 72 | 73 | or from [GitHub 74 | Packages](https://github.com/r-hub/r-minimal/packages/92808?version=latest): 75 | 76 | docker pull ghcr.io/r-hub/r-minimal/r-minimal:latest 77 | 78 | ## Platforms 79 | 80 | All images are available on `linux/amd64` and `linux/arm64` platforms. 81 | 82 | ## Supported R versions 83 | 84 | Currently we support the last patch version of the last five minor R 85 | versions. The `latest` tag always uses the last R release. 86 | 87 | | image | R version | tags | note | 88 | |-----------|-------------|-------------------------------------------------------------------|-------------| 89 | | R devel | 4.6.0-devel | `devel`, `4.6.0`, `4.6`, `4.6.0-devel`, `4.6-devel`, `2025-06-08` | Built daily | 90 | | R next | 4.5.1-RC | `next`, `4.5.1`, `4.5`, `rc`, `4.5.1-rc`, `4.5-rc` | Built daily | 91 | | R release | 4.5.0 | `4.5.0`, `4.5`, `release`, `latest` | | 92 | | R 4.3.x | 4.3.3 | `4.3.3`, `4.3` | | 93 | | R 4.2.x | 4.2.3 | `4.2.3`, `4.2` | | 94 | | R 4.1.x | 4.1.3 | `4.1.3`, `4.1` | | 95 | | R 4.0.x | 4.0.5 | `4.0.5`, `4.0` | | 96 | | R 3.6.x | 3.6.3 | `3.6.3`, `3.6` | | 97 | 98 | ## Daily R-devel builds 99 | 100 | We tag our daily R-devel builds with the build date. These images can be 101 | useful for tracking down bugs and regressions in R. E.g.: 102 | 103 | ``` sh 104 | docker run -ti ghcr.io/r-hub/r-minimal/r-minimal:2022-11-25 105 | ``` 106 | 107 | The tags start on 2021-07-25. `linux/arm64` images are available from 108 | 2022-01-13. 109 | 110 | ## Dockerfile examples 111 | 112 | One of our main goals is to be able to use `rhub/r-minimal` as a base 113 | image, and easily add R packages from CRAN or GitHub to it, to create a 114 | new image. Run `installr` from a `Dockerfile` to add R packages to the 115 | `r-minimal` image: 116 | 117 | ``` dockerfile 118 | FROM rhub/r-minimal 119 | RUN installr praise 120 | CMD [ "R", "--slave", "-e", "cat(praise::praise())" ] 121 | ``` 122 | 123 | Package with compiled code: 124 | 125 | ``` dockerfile 126 | FROM rhub/r-minimal 127 | RUN installr -d glue 128 | ``` 129 | 130 | After the package(s) have been installed, `installr` removed the 131 | compilers, as these are typically not needed on the final image. If you 132 | want to keep them use `installr -c` instead of `installr -d`. 133 | 134 | Package with system requirements: 135 | 136 | ``` dockerfile 137 | FROM rhub/r-minimal 138 | RUN installr -d -t linux-headers pingr 139 | CMD [ "R", "-q", "-e", "pingr::is_online() || stop('offline')" ] 140 | ``` 141 | 142 | Similarly to compilers, system packages are removed after the R packages 143 | have been installed. If you want to keep (some of) them, use 144 | `installr -a` instead of `installr -t`. (You can also mix the two.) 145 | 146 | Using with renv: 147 | 148 | To use `renv` to restore the `renv.lock` file, use the `-e` option: 149 | 150 | ``` dockerfile 151 | FROM rhub/r-minimal 152 | COPY .Rprofile .Rprofile 153 | COPY renv renv 154 | COPY renv.lock . 155 | RUN installr -d -e 156 | ``` 157 | 158 | If you copy the entire folder with renv, including the `activate.R` and 159 | `.Rprofile`, renv will bootstrap itself with the same version as the 160 | lock and restore the packages with the proper versions. All the 161 | necessary compilers and libraries needed at runtime need to be installed 162 | with the `-a` and `-t` options. Please refer to 163 | [examples/renv-shiny](examples/renv-shiny) for an example that install 164 | shiny and rmarkdown in a container. 165 | 166 | ## Popular packages: 167 | 168 | Hints on installing some popular R packages: 169 | 170 | | package | installr command | ~ image size (uncompressed) | 171 | |----------------------|--------------------------------------------------------------------|----------------------------------| 172 | | data.table | See [examples/data.table](examples/data.table) for OpenMP support | 26.2 MB (50.0 MB) | 173 | | dplyr | `installr -d dplyr` | 31.9 MB (59.7 MB) | 174 | | ggplot2 | `installr -d -t gfortran ggplot2` | 56.1 MB (93.5 MB) | 175 | | h2o | See [examples/h2o](examples/h2o). | 354.0 MB (511.0 MB) | 176 | | knitr | `installr -d knitr` | 25.3 MB (48.3 MB) | 177 | | shiny | See [examples/shiny](examples/shiny). | 49.8 MB (103.5 MB) | 178 | | sf | See [examples/sf](examples/sf). | 79.7 MB (194.8 MB) | 179 | | plumber | See [examples/plumber](examples/plumber). | 56.3 MB (127.7 MB) | 180 | | rmarkdown | `installr -d rmarkdown` | 58.1 MB (153.5 MB) (with pandoc) | 181 | | tidyverse | See [examples/tidyverse](examples/tidyverse). | 113.7 MB (208.6 MB) | 182 | | tidyverse w/o reprex | See [examples/tidyverse-minimal](examples/tidyverse-minimal). | 66.5 MB (115.4 MB) | 183 | | rstan | See [examples/rstan](examples/rstan). | 92.0 MB (298.5 MB) | 184 | | xgboost | `installr -d -t "gfortran libexecinfo-dev" -a libexecinfo xgboost` | 35.5 MB (71.1 MB) | 185 | 186 | See also the `Dockerfile`s in the `examples` directory. 187 | 188 | > Note that package and system dependencies change over time, so if any 189 | > of these commands do not work any more, please [let us 190 | > know](https://github.com/r-hub/r-minimal). 191 | 192 | > See the [Dockerfile](examples/rmarkdown/Dockerfile) for installing 193 | > pandoc. 194 | 195 | ## Time zones 196 | 197 | The image uses R’s internal time zone database, but most time zones are 198 | removed from, to save space. The only supported ones are `GMT`, `UTC` 199 | and `America/New_York`. If you need more time zones, then install 200 | Alpine’s time zone package and point R to it: 201 | 202 | apk add --no-cache tzdata 203 | export TZDIR=/usr/share/zoneinfo 204 | 205 | See also the discussion at 206 | 207 | 208 | ## Known failures and workarounds 209 | 210 | - The ps package needs the `linux-headers` Alpine package at compile 211 | time. Many tidyverse packages depend on ps, so they’ll need it as 212 | well: 213 | 214 | installr -d -t linux-headers ps 215 | 216 | - The arrow package needs a `Makevars` file to add a link flag. See the 217 | example `Dockerfile` in the [examples/arrow](examples/arrow) 218 | directory. 219 | 220 | - The V8 packagees do not compile on aarch64 machines by default. On 221 | x86_64 it installs fine: 222 | 223 | installr -d -t curl-dev V8 224 | 225 | This means that other packages that need V8 (e.g. rstan and prophet) 226 | do not work on aarch64, either. 227 | 228 | - To install the magick package, you need both the `imagemagick` and 229 | `imagemagick-dev` Alpine packages, both at install time and run time: 230 | 231 | installr -d -a "imagemagick imagemagick-dev" -t "curl-dev" magick 232 | 233 | - pingr 2.0.4 does not build on Alpine, use the dev version: 234 | 235 | installr -d -t linux-headers r-lib/pingr 236 | 237 | - Rcpp 1.0.13 [does not compile with R 238 | 4.4.2](https://github.com/RcppCore/Rcpp/issues/1341). Use the dev 239 | version of Rcpp until a new version is released on CRAN. This affects 240 | all packages that depend on Rcpp, naturally. E.g. shiny, V8, rstan, 241 | odbc, golem, prophet, pagedown, plumber, sf, etc. See the updated 242 | \[examples\]. 243 | 244 | installr -d Rcppcore/Rcpp 245 | 246 | ## License 247 | 248 | See for the R licenses 249 | 250 | These Dockerfiles are licensed under the MIT License. 251 | 252 | © [R Consortium](https://github.com/rconsortium) 253 | -------------------------------------------------------------------------------- /actions/readme-build/action.yaml: -------------------------------------------------------------------------------- 1 | name: readme-build 2 | description: | 3 | Build `README.md` from `README.Rmd`. 4 | inputs: 5 | packages: 6 | description: | 7 | Which package(s) to install. Defaults to `any::rcmdcheck`. 8 | default: 'any::rmarkdown' 9 | runs: 10 | using: composite 11 | steps: 12 | - name: Install R 13 | uses: r-lib/actions/setup-r@v2 14 | 15 | - name: Install R packages 16 | uses: r-lib/actions/setup-r-dependencies@v2 17 | with: 18 | packages: ${{ inputs.packages }} 19 | 20 | - name: Render README 21 | run: | 22 | rmarkdown::render("README.Rmd") 23 | shell: Rscript {0} 24 | 25 | - name: Publish 26 | run: | 27 | git config --local user.name "$GITHUB_ACTOR" 28 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 29 | git commit -a -m 'Re-build README' || echo "No changes to commit" 30 | git push origin || echo "No changes to commit" 31 | shell: bash 32 | -------------------------------------------------------------------------------- /examples/RPostgres/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rhub/r-minimal 2 | 3 | RUN installr -d -t "libpq-dev" -a "libpq" RPostgres 4 | -------------------------------------------------------------------------------- /examples/V8/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | ENV DOWNLOAD_STATIC_LIBV8=1 5 | 6 | RUN installr -d -t curl-dev V8 Rcppcore/Rcpp 7 | -------------------------------------------------------------------------------- /examples/arrow-s3/Dockerfile: -------------------------------------------------------------------------------- 1 | # arrow with S3 support 2 | # We cannot use Alpine's libarrow for this, as it is built without 3 | # S3 support. 4 | # We also need to use an older arrow version. 5 | # Plus need to build this on an older R devel version. 6 | # See also https://github.com/r-hub/r-minimal/issues/84 7 | 8 | FROM ghcr.io/r-hub/r-minimal/r-minimal:2024-03-19 9 | 10 | ENV ARROW_S3=ON 11 | 12 | RUN mkdir -p ~/.R && echo "LDFLAGS+=-fPIC" >> ~/.R/Makevars 13 | RUN installr -d \ 14 | -t "make cmake linux-headers openssl-dev curl-dev" \ 15 | -a "openssl libcurl" arrow@15.0.1 16 | RUN R -q -e 'library(arrow)' 17 | RUN R -q -e 'stopifnot(isTRUE(arrow::arrow_info()$capabilities[["s3"]]))' 18 | -------------------------------------------------------------------------------- /examples/arrow/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN mkdir -p ~/.R && echo "LDFLAGS+=-fPIC" >> ~/.R/Makevars 5 | 6 | # The most recent 15.0.1 version of the arrow package does not compile 7 | # on Alpine, so we install the previous version. If you had success with 8 | # newer versions, please let us know! Thanks! 9 | 10 | RUN installr -d \ 11 | -t "make openssl-dev cmake linux-headers apache-arrow-dev" \ 12 | -a "openssl libarrow_dataset libarrow" arrow@14.0.2.1 13 | 14 | RUN R -q -e 'library(arrow)' 15 | -------------------------------------------------------------------------------- /examples/data.table/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM ghcr.io/r-hub/r-minimal/r-minimal 3 | 4 | RUN echo -e 'PKG_CFLAGS+=-fopenmp\nPKG_LIBS+=-fopenmp\n' >> /tmp/Makevars && \ 5 | R_MAKEVARS_USER=/tmp/Makevars \ 6 | installr -a "libgomp zlib" -t "openmp-dev zlib-dev" -d data.table && \ 7 | rm /tmp/Makevars 8 | 9 | RUN R -q -e 'data.table::getDTthreads()' 10 | 11 | RUN ldd /usr/local/lib/R/library/data.table/libs/data_table.so 2> /dev/null || true 12 | -------------------------------------------------------------------------------- /examples/dplyr/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN installr -d dplyr 5 | -------------------------------------------------------------------------------- /examples/ggplot2/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN installr -d -t gfortran ggplot2 5 | -------------------------------------------------------------------------------- /examples/golem/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | # we have to install `shiny` first, to be able to install `golem` 5 | # so I leave the example of `shiny` installation here 6 | RUN apk add --no-cache --update-cache \ 7 | --repository http://nl.alpinelinux.org/alpine/v3.11/main \ 8 | autoconf=2.69-r2 \ 9 | automake=1.16.1-r0 && \ 10 | # repeat autoconf and automake (under `-t`) 11 | # to (auto)remove them after installation 12 | installr -d \ 13 | -t "libsodium-dev curl-dev linux-headers autoconf automake" \ 14 | -a libsodium \ 15 | shiny Rcppcore/Rcpp 16 | 17 | # installation of `golem` 18 | RUN installr -d \ 19 | -t "curl-dev openssl-dev libxml2-dev gfortran libgit2-dev" \ 20 | -a "libgit2" golem 21 | -------------------------------------------------------------------------------- /examples/h2o/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN R_DEFAULT_INTERNET_TIMEOUT=10000 installr -d -a openjdk11-jre -t "curl-dev musl-dev" h2o 5 | -------------------------------------------------------------------------------- /examples/knitr/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN installr -d knitr 5 | -------------------------------------------------------------------------------- /examples/odbc/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN installr -d -t unixodbc-dev -a unixodbc odbc Rcppcore/Rcpp 5 | -------------------------------------------------------------------------------- /examples/pagedown/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN apk add --no-cache --update-cache \ 5 | --repository http://nl.alpinelinux.org/alpine/v3.11/main \ 6 | autoconf=2.69-r2 \ 7 | automake=1.16.1-r0 && \ 8 | # repeat autoconf and automake (under `-t`) 9 | # to (auto)remove them after installation 10 | installr -d \ 11 | -t "openssl-dev linux-headers autoconf automake zlib-dev" \ 12 | -a "openssl chromium chromium-chromedriver" \ 13 | pagedown Rcppcore/Rcpp 14 | 15 | RUN wget https://github.com/jgm/pandoc/releases/download/2.13/pandoc-2.13-linux-amd64.tar.gz && \ 16 | tar xzf pandoc-2.13-linux-amd64.tar.gz && \ 17 | mv pandoc-2.13/bin/* /usr/local/bin/ && \ 18 | rm -rf pandoc-2.13* 19 | 20 | COPY pagedown.sh /usr/local/bin/pagedown.sh 21 | 22 | RUN chmod +x /usr/local/bin/pagedown.sh 23 | -------------------------------------------------------------------------------- /examples/pagedown/README.md: -------------------------------------------------------------------------------- 1 | 2 | # pagedown 3 | 4 | This is an [`rhub/r-minimal`](https://github.com/r-hub/r-minimal) 5 | Docker image for [`rstudio/pagedown`](https://github.com/rstudio/pagedown). 6 | 7 | Currently it is about 560MB. It could be a bit smaller if the 8 | installed R packages were trimmed from documentation, etc., but probably 9 | not much smaller since it needs to include pandoc and chromium. 10 | 11 | The `pagedown.sh` script on the image converts an `.Rmd` file from 12 | the standard input to a `.pdf` file on the standard output. 13 | See this file to create your own workflow. 14 | 15 | Example: 16 | 17 | ``` 18 | docker build -t rhub/pagedown . 19 | cat card.Rmd | docker run -i rhub/pagedown pagedown.sh > card.pdf 20 | ``` 21 | 22 | ## License 23 | 24 | See https://www.r-project.org/Licenses/ for the R licenses 25 | 26 | These Dockerfiles are licensed under the MIT License. 27 | 28 | (c) [R Consortium](https://github.com/rconsortium) 29 | -------------------------------------------------------------------------------- /examples/pagedown/card.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | phone: "+1 844-448-1212" 3 | email: "info@rstudio.com" 4 | url: www.rstudio.com 5 | address: | 6 | @rstudio 7 | logo: "https://www.rstudio.com/wp-content/uploads/2016/09/RStudio-Logo-Blue-Gray-250.png" 8 | person: 9 | - name: Tareef Kawaf 10 | title: President of RStudio, PBC 11 | repeat: 12 12 | - name: Yihui Xie 13 | title: Responsible for `r head(grep('(? 26 | 27 | ::: {.wrapper data-repeat="12"} 28 | [Another Name]{slot="name"} 29 | [Another Title]{slot="title"} 30 | [someone@rstudio.com]{slot="email"} 31 | ::: 32 | 33 | 34 | 35 | 36 | ```{css, eval=FALSE} 37 | .wrapper { 38 | color: black; 39 | background-color: white; 40 | border: 1px dotted black; 41 | } 42 | .coordinates { 43 | color: #333; 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /examples/pagedown/pagedown.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env Rscript 2 | 3 | sin <- file("stdin") 4 | writeLines(readLines(sin), "/tmp/card.Rmd") 5 | close(sin) 6 | 7 | rmarkdown::render("/tmp/card.Rmd") 8 | 9 | pagedown::chrome_print("/tmp/card.html", extra_args = "--no-sandbox") 10 | 11 | pdf <- readBin("/tmp/card.pdf", "raw", file.size("/tmp/card.pdf")) 12 | 13 | con <- pipe("cat", "wb") 14 | writeBin(pdf, con) 15 | flush(con) 16 | close(con) 17 | -------------------------------------------------------------------------------- /examples/pingr/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN installr -d -t linux-headers r-lib/pingr 5 | 6 | CMD [ "R", "-q", "-e", "pingr::is_online() || stop('offline')" ] 7 | -------------------------------------------------------------------------------- /examples/plumber/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rhub/r-minimal 2 | 3 | RUN installr -d \ 4 | -t "libsodium-dev curl-dev linux-headers" \ 5 | -a libsodium \ 6 | plumber Rcppcore/Rcpp 7 | 8 | WORKDIR /app 9 | 10 | COPY . . 11 | 12 | EXPOSE 8000 13 | 14 | ENTRYPOINT ["R", "-e", "pr <- plumber::plumb('plumber.R'); pr$run(host = '0.0.0.0', port = 8000)"] 15 | -------------------------------------------------------------------------------- /examples/plumber/plumber.R: -------------------------------------------------------------------------------- 1 | #* @get /mean 2 | function(samples = 10) { 3 | data <- rnorm(samples) 4 | mean(data) 5 | } 6 | 7 | #* @post /sum 8 | function(a, b) { 9 | as.numeric(a) + as.numeric(b) 10 | } 11 | -------------------------------------------------------------------------------- /examples/praise/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN installr -d praise 5 | 6 | CMD [ "R", "--slave", "-e", "cat(praise::praise())" ] 7 | -------------------------------------------------------------------------------- /examples/prophet/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | ENV DOWNLOAD_STATIC_LIBV8=1 5 | 6 | RUN installr -d -t "linux-headers gfortran curl-dev" prophet Rcppcore/Rcpp 7 | -------------------------------------------------------------------------------- /examples/rJava/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rhub/r-minimal 2 | 3 | RUN installr -c -p -a openjdk17 \ 4 | && R CMD javareconf \ 5 | && installr -d \ 6 | -t "make pcre2-dev bzip2-dev xz-dev" \ 7 | -a "pcre2 libbz2 xz-libs" rJava 8 | -------------------------------------------------------------------------------- /examples/renv-shiny/.Rprofile: -------------------------------------------------------------------------------- 1 | source("renv/activate.R") 2 | -------------------------------------------------------------------------------- /examples/renv-shiny/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rhub/r-minimal:4.3.2 2 | 3 | ARG TARGETARCH 4 | ARG PANDOC_VERSION=3.1.12 5 | 6 | RUN wget https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-linux-${TARGETARCH}.tar.gz && \ 7 | tar xzf pandoc-*-linux-${TARGETARCH}.tar.gz && \ 8 | mv pandoc-*/bin/* /usr/local/bin/ && \ 9 | rm -rf pandoc-* 10 | 11 | COPY . . 12 | 13 | RUN installr -d -e \ 14 | -t "zlib-dev cairo-dev" \ 15 | -a "cairo font-liberation" 16 | 17 | 18 | EXPOSE 3838 19 | 20 | CMD ["R", "-e", "shiny::runApp(host = '0.0.0.0', port = 3838)"] 21 | -------------------------------------------------------------------------------- /examples/renv-shiny/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Shiny app with renv 3 | 4 | To build run: 5 | ``` 6 | docker build -t r-minimal-shiny-example . 7 | ``` 8 | and then run the app with: 9 | ``` 10 | docker run -it -p3838:3838 r-minimal-shiny-example 11 | ``` 12 | 13 | Go to http://localhost:3838/ to see the app. 14 | -------------------------------------------------------------------------------- /examples/renv-shiny/app.R: -------------------------------------------------------------------------------- 1 | # 2 | # This is a Shiny web application. You can run the application by clicking 3 | # the 'Run App' button above. 4 | # 5 | # Find out more about building applications with Shiny here: 6 | # 7 | # https://shiny.posit.co/ 8 | # 9 | 10 | library(shiny) 11 | library(rmarkdown) 12 | 13 | # Define UI for application that draws a histogram 14 | ui <- fluidPage( 15 | 16 | # Application title 17 | titlePanel("Old Faithful Geyser Data"), 18 | 19 | # Sidebar with a slider input for number of bins 20 | sidebarLayout( 21 | sidebarPanel( 22 | sliderInput("bins", 23 | "Number of bins:", 24 | min = 1, 25 | max = 50, 26 | value = 30) 27 | ), 28 | 29 | # Show a plot of the generated distribution 30 | mainPanel( 31 | plotOutput("distPlot") 32 | ) 33 | ) 34 | ) 35 | 36 | # Define server logic required to draw a histogram 37 | server <- function(input, output) { 38 | 39 | output$distPlot <- renderPlot({ 40 | # generate bins based on input$bins from ui.R 41 | x <- faithful[, 2] 42 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 43 | 44 | # draw the histogram with the specified number of bins 45 | hist(x, breaks = bins, col = 'darkgray', border = 'white', 46 | xlab = 'Waiting time to next eruption (in mins)', 47 | main = 'Histogram of waiting times') 48 | }) 49 | } 50 | 51 | # Run the application 52 | shinyApp(ui = ui, server = server) 53 | -------------------------------------------------------------------------------- /examples/renv-shiny/renv.lock: -------------------------------------------------------------------------------- 1 | { 2 | "R": { 3 | "Version": "4.3.2", 4 | "Repositories": [ 5 | { 6 | "Name": "CRAN", 7 | "URL": "https://cran-r.c3sl.ufpr.br" 8 | } 9 | ] 10 | }, 11 | "Packages": { 12 | "Cairo": { 13 | "Package": "Cairo", 14 | "Version": "1.6-2", 15 | "Source": "Repository", 16 | "Repository": "CRAN", 17 | "Requirements": [ 18 | "R", 19 | "grDevices", 20 | "graphics" 21 | ], 22 | "Hash": "3918e6b40d27984ca4a99c73b92406c3" 23 | }, 24 | "R6": { 25 | "Package": "R6", 26 | "Version": "2.5.1", 27 | "Source": "Repository", 28 | "Repository": "CRAN", 29 | "Requirements": [ 30 | "R" 31 | ], 32 | "Hash": "470851b6d5d0ac559e9d01bb352b4021" 33 | }, 34 | "Rcpp": { 35 | "Package": "Rcpp", 36 | "Version": "1.0.12", 37 | "Source": "Repository", 38 | "Repository": "CRAN", 39 | "Requirements": [ 40 | "methods", 41 | "utils" 42 | ], 43 | "Hash": "5ea2700d21e038ace58269ecdbeb9ec0" 44 | }, 45 | "base64enc": { 46 | "Package": "base64enc", 47 | "Version": "0.1-3", 48 | "Source": "Repository", 49 | "Repository": "CRAN", 50 | "Requirements": [ 51 | "R" 52 | ], 53 | "Hash": "543776ae6848fde2f48ff3816d0628bc" 54 | }, 55 | "bslib": { 56 | "Package": "bslib", 57 | "Version": "0.6.1", 58 | "Source": "Repository", 59 | "Repository": "CRAN", 60 | "Requirements": [ 61 | "R", 62 | "base64enc", 63 | "cachem", 64 | "grDevices", 65 | "htmltools", 66 | "jquerylib", 67 | "jsonlite", 68 | "lifecycle", 69 | "memoise", 70 | "mime", 71 | "rlang", 72 | "sass" 73 | ], 74 | "Hash": "c0d8599494bc7fb408cd206bbdd9cab0" 75 | }, 76 | "cachem": { 77 | "Package": "cachem", 78 | "Version": "1.0.8", 79 | "Source": "Repository", 80 | "Repository": "CRAN", 81 | "Requirements": [ 82 | "fastmap", 83 | "rlang" 84 | ], 85 | "Hash": "c35768291560ce302c0a6589f92e837d" 86 | }, 87 | "cli": { 88 | "Package": "cli", 89 | "Version": "3.6.2", 90 | "Source": "Repository", 91 | "Repository": "CRAN", 92 | "Requirements": [ 93 | "R", 94 | "utils" 95 | ], 96 | "Hash": "1216ac65ac55ec0058a6f75d7ca0fd52" 97 | }, 98 | "commonmark": { 99 | "Package": "commonmark", 100 | "Version": "1.9.1", 101 | "Source": "Repository", 102 | "Repository": "CRAN", 103 | "Hash": "5d8225445acb167abf7797de48b2ee3c" 104 | }, 105 | "crayon": { 106 | "Package": "crayon", 107 | "Version": "1.5.2", 108 | "Source": "Repository", 109 | "Repository": "CRAN", 110 | "Requirements": [ 111 | "grDevices", 112 | "methods", 113 | "utils" 114 | ], 115 | "Hash": "e8a1e41acf02548751f45c718d55aa6a" 116 | }, 117 | "digest": { 118 | "Package": "digest", 119 | "Version": "0.6.34", 120 | "Source": "Repository", 121 | "Repository": "CRAN", 122 | "Requirements": [ 123 | "R", 124 | "utils" 125 | ], 126 | "Hash": "7ede2ee9ea8d3edbf1ca84c1e333ad1a" 127 | }, 128 | "ellipsis": { 129 | "Package": "ellipsis", 130 | "Version": "0.3.2", 131 | "Source": "Repository", 132 | "Repository": "CRAN", 133 | "Requirements": [ 134 | "R", 135 | "rlang" 136 | ], 137 | "Hash": "bb0eec2fe32e88d9e2836c2f73ea2077" 138 | }, 139 | "evaluate": { 140 | "Package": "evaluate", 141 | "Version": "0.23", 142 | "Source": "Repository", 143 | "Repository": "CRAN", 144 | "Requirements": [ 145 | "R", 146 | "methods" 147 | ], 148 | "Hash": "daf4a1246be12c1fa8c7705a0935c1a0" 149 | }, 150 | "fastmap": { 151 | "Package": "fastmap", 152 | "Version": "1.1.1", 153 | "Source": "Repository", 154 | "Repository": "CRAN", 155 | "Hash": "f7736a18de97dea803bde0a2daaafb27" 156 | }, 157 | "fontawesome": { 158 | "Package": "fontawesome", 159 | "Version": "0.5.2", 160 | "Source": "Repository", 161 | "Repository": "CRAN", 162 | "Requirements": [ 163 | "R", 164 | "htmltools", 165 | "rlang" 166 | ], 167 | "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" 168 | }, 169 | "fs": { 170 | "Package": "fs", 171 | "Version": "1.6.3", 172 | "Source": "Repository", 173 | "Repository": "CRAN", 174 | "Requirements": [ 175 | "R", 176 | "methods" 177 | ], 178 | "Hash": "47b5f30c720c23999b913a1a635cf0bb" 179 | }, 180 | "glue": { 181 | "Package": "glue", 182 | "Version": "1.7.0", 183 | "Source": "Repository", 184 | "Repository": "CRAN", 185 | "Requirements": [ 186 | "R", 187 | "methods" 188 | ], 189 | "Hash": "e0b3a53876554bd45879e596cdb10a52" 190 | }, 191 | "highr": { 192 | "Package": "highr", 193 | "Version": "0.10", 194 | "Source": "Repository", 195 | "Repository": "CRAN", 196 | "Requirements": [ 197 | "R", 198 | "xfun" 199 | ], 200 | "Hash": "06230136b2d2b9ba5805e1963fa6e890" 201 | }, 202 | "htmltools": { 203 | "Package": "htmltools", 204 | "Version": "0.5.7", 205 | "Source": "Repository", 206 | "Repository": "CRAN", 207 | "Requirements": [ 208 | "R", 209 | "base64enc", 210 | "digest", 211 | "ellipsis", 212 | "fastmap", 213 | "grDevices", 214 | "rlang", 215 | "utils" 216 | ], 217 | "Hash": "2d7b3857980e0e0d0a1fd6f11928ab0f" 218 | }, 219 | "httpuv": { 220 | "Package": "httpuv", 221 | "Version": "1.6.14", 222 | "Source": "Repository", 223 | "Repository": "CRAN", 224 | "Requirements": [ 225 | "R", 226 | "R6", 227 | "Rcpp", 228 | "later", 229 | "promises", 230 | "utils" 231 | ], 232 | "Hash": "16abeb167dbf511f8cc0552efaf05bab" 233 | }, 234 | "jquerylib": { 235 | "Package": "jquerylib", 236 | "Version": "0.1.4", 237 | "Source": "Repository", 238 | "Repository": "CRAN", 239 | "Requirements": [ 240 | "htmltools" 241 | ], 242 | "Hash": "5aab57a3bd297eee1c1d862735972182" 243 | }, 244 | "jsonlite": { 245 | "Package": "jsonlite", 246 | "Version": "1.8.8", 247 | "Source": "Repository", 248 | "Repository": "CRAN", 249 | "Requirements": [ 250 | "methods" 251 | ], 252 | "Hash": "e1b9c55281c5adc4dd113652d9e26768" 253 | }, 254 | "knitr": { 255 | "Package": "knitr", 256 | "Version": "1.45", 257 | "Source": "Repository", 258 | "Repository": "CRAN", 259 | "Requirements": [ 260 | "R", 261 | "evaluate", 262 | "highr", 263 | "methods", 264 | "tools", 265 | "xfun", 266 | "yaml" 267 | ], 268 | "Hash": "1ec462871063897135c1bcbe0fc8f07d" 269 | }, 270 | "later": { 271 | "Package": "later", 272 | "Version": "1.3.2", 273 | "Source": "Repository", 274 | "Repository": "CRAN", 275 | "Requirements": [ 276 | "Rcpp", 277 | "rlang" 278 | ], 279 | "Hash": "a3e051d405326b8b0012377434c62b37" 280 | }, 281 | "lifecycle": { 282 | "Package": "lifecycle", 283 | "Version": "1.0.4", 284 | "Source": "Repository", 285 | "Repository": "CRAN", 286 | "Requirements": [ 287 | "R", 288 | "cli", 289 | "glue", 290 | "rlang" 291 | ], 292 | "Hash": "b8552d117e1b808b09a832f589b79035" 293 | }, 294 | "magrittr": { 295 | "Package": "magrittr", 296 | "Version": "2.0.3", 297 | "Source": "Repository", 298 | "Repository": "CRAN", 299 | "Requirements": [ 300 | "R" 301 | ], 302 | "Hash": "7ce2733a9826b3aeb1775d56fd305472" 303 | }, 304 | "memoise": { 305 | "Package": "memoise", 306 | "Version": "2.0.1", 307 | "Source": "Repository", 308 | "Repository": "CRAN", 309 | "Requirements": [ 310 | "cachem", 311 | "rlang" 312 | ], 313 | "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" 314 | }, 315 | "mime": { 316 | "Package": "mime", 317 | "Version": "0.12", 318 | "Source": "Repository", 319 | "Repository": "CRAN", 320 | "Requirements": [ 321 | "tools" 322 | ], 323 | "Hash": "18e9c28c1d3ca1560ce30658b22ce104" 324 | }, 325 | "promises": { 326 | "Package": "promises", 327 | "Version": "1.2.1", 328 | "Source": "Repository", 329 | "Repository": "CRAN", 330 | "Requirements": [ 331 | "R6", 332 | "Rcpp", 333 | "fastmap", 334 | "later", 335 | "magrittr", 336 | "rlang", 337 | "stats" 338 | ], 339 | "Hash": "0d8a15c9d000970ada1ab21405387dee" 340 | }, 341 | "rappdirs": { 342 | "Package": "rappdirs", 343 | "Version": "0.3.3", 344 | "Source": "Repository", 345 | "Repository": "CRAN", 346 | "Requirements": [ 347 | "R" 348 | ], 349 | "Hash": "5e3c5dc0b071b21fa128676560dbe94d" 350 | }, 351 | "renv": { 352 | "Package": "renv", 353 | "Version": "1.0.4", 354 | "Source": "Repository", 355 | "Repository": "CRAN", 356 | "Requirements": [ 357 | "utils" 358 | ], 359 | "Hash": "11abaf7c540ff33f94514d50f929bfd1" 360 | }, 361 | "rlang": { 362 | "Package": "rlang", 363 | "Version": "1.1.3", 364 | "Source": "Repository", 365 | "Repository": "CRAN", 366 | "Requirements": [ 367 | "R", 368 | "utils" 369 | ], 370 | "Hash": "42548638fae05fd9a9b5f3f437fbbbe2" 371 | }, 372 | "rmarkdown": { 373 | "Package": "rmarkdown", 374 | "Version": "2.25", 375 | "Source": "Repository", 376 | "Repository": "CRAN", 377 | "Requirements": [ 378 | "R", 379 | "bslib", 380 | "evaluate", 381 | "fontawesome", 382 | "htmltools", 383 | "jquerylib", 384 | "jsonlite", 385 | "knitr", 386 | "methods", 387 | "stringr", 388 | "tinytex", 389 | "tools", 390 | "utils", 391 | "xfun", 392 | "yaml" 393 | ], 394 | "Hash": "d65e35823c817f09f4de424fcdfa812a" 395 | }, 396 | "sass": { 397 | "Package": "sass", 398 | "Version": "0.4.8", 399 | "Source": "Repository", 400 | "Repository": "CRAN", 401 | "Requirements": [ 402 | "R6", 403 | "fs", 404 | "htmltools", 405 | "rappdirs", 406 | "rlang" 407 | ], 408 | "Hash": "168f9353c76d4c4b0a0bbf72e2c2d035" 409 | }, 410 | "shiny": { 411 | "Package": "shiny", 412 | "Version": "1.8.0", 413 | "Source": "Repository", 414 | "Repository": "CRAN", 415 | "Requirements": [ 416 | "R", 417 | "R6", 418 | "bslib", 419 | "cachem", 420 | "commonmark", 421 | "crayon", 422 | "ellipsis", 423 | "fastmap", 424 | "fontawesome", 425 | "glue", 426 | "grDevices", 427 | "htmltools", 428 | "httpuv", 429 | "jsonlite", 430 | "later", 431 | "lifecycle", 432 | "methods", 433 | "mime", 434 | "promises", 435 | "rlang", 436 | "sourcetools", 437 | "tools", 438 | "utils", 439 | "withr", 440 | "xtable" 441 | ], 442 | "Hash": "3a1f41807d648a908e3c7f0334bf85e6" 443 | }, 444 | "sourcetools": { 445 | "Package": "sourcetools", 446 | "Version": "0.1.7-1", 447 | "Source": "Repository", 448 | "Repository": "CRAN", 449 | "Requirements": [ 450 | "R" 451 | ], 452 | "Hash": "5f5a7629f956619d519205ec475fe647" 453 | }, 454 | "stringi": { 455 | "Package": "stringi", 456 | "Version": "1.8.3", 457 | "Source": "Repository", 458 | "Repository": "CRAN", 459 | "Requirements": [ 460 | "R", 461 | "stats", 462 | "tools", 463 | "utils" 464 | ], 465 | "Hash": "058aebddea264f4c99401515182e656a" 466 | }, 467 | "stringr": { 468 | "Package": "stringr", 469 | "Version": "1.5.1", 470 | "Source": "Repository", 471 | "Repository": "CRAN", 472 | "Requirements": [ 473 | "R", 474 | "cli", 475 | "glue", 476 | "lifecycle", 477 | "magrittr", 478 | "rlang", 479 | "stringi", 480 | "vctrs" 481 | ], 482 | "Hash": "960e2ae9e09656611e0b8214ad543207" 483 | }, 484 | "tinytex": { 485 | "Package": "tinytex", 486 | "Version": "0.49", 487 | "Source": "Repository", 488 | "Repository": "CRAN", 489 | "Requirements": [ 490 | "xfun" 491 | ], 492 | "Hash": "5ac22900ae0f386e54f1c307eca7d843" 493 | }, 494 | "vctrs": { 495 | "Package": "vctrs", 496 | "Version": "0.6.5", 497 | "Source": "Repository", 498 | "Repository": "CRAN", 499 | "Requirements": [ 500 | "R", 501 | "cli", 502 | "glue", 503 | "lifecycle", 504 | "rlang" 505 | ], 506 | "Hash": "c03fa420630029418f7e6da3667aac4a" 507 | }, 508 | "withr": { 509 | "Package": "withr", 510 | "Version": "3.0.0", 511 | "Source": "Repository", 512 | "Repository": "CRAN", 513 | "Requirements": [ 514 | "R", 515 | "grDevices", 516 | "graphics" 517 | ], 518 | "Hash": "d31b6c62c10dcf11ec530ca6b0dd5d35" 519 | }, 520 | "xfun": { 521 | "Package": "xfun", 522 | "Version": "0.42", 523 | "Source": "Repository", 524 | "Repository": "CRAN", 525 | "Requirements": [ 526 | "grDevices", 527 | "stats", 528 | "tools" 529 | ], 530 | "Hash": "fd1349170df31f7a10bd98b0189e85af" 531 | }, 532 | "xtable": { 533 | "Package": "xtable", 534 | "Version": "1.8-4", 535 | "Source": "Repository", 536 | "Repository": "CRAN", 537 | "Requirements": [ 538 | "R", 539 | "stats", 540 | "utils" 541 | ], 542 | "Hash": "b8acdf8af494d9ec19ccb2481a9b11c2" 543 | }, 544 | "yaml": { 545 | "Package": "yaml", 546 | "Version": "2.3.8", 547 | "Source": "Repository", 548 | "Repository": "CRAN", 549 | "Hash": "29240487a071f535f5e5d5a323b7afbd" 550 | } 551 | } 552 | } 553 | -------------------------------------------------------------------------------- /examples/renv-shiny/renv/.gitignore: -------------------------------------------------------------------------------- 1 | library/ 2 | local/ 3 | cellar/ 4 | lock/ 5 | python/ 6 | sandbox/ 7 | staging/ 8 | -------------------------------------------------------------------------------- /examples/renv-shiny/renv/activate.R: -------------------------------------------------------------------------------- 1 | 2 | local({ 3 | 4 | # the requested version of renv 5 | version <- "1.0.4" 6 | attr(version, "sha") <- NULL 7 | 8 | # the project directory 9 | project <- getwd() 10 | 11 | # use start-up diagnostics if enabled 12 | diagnostics <- Sys.getenv("RENV_STARTUP_DIAGNOSTICS", unset = "FALSE") 13 | if (diagnostics) { 14 | start <- Sys.time() 15 | profile <- tempfile("renv-startup-", fileext = ".Rprof") 16 | utils::Rprof(profile) 17 | on.exit({ 18 | utils::Rprof(NULL) 19 | elapsed <- signif(difftime(Sys.time(), start, units = "auto"), digits = 2L) 20 | writeLines(sprintf("- renv took %s to run the autoloader.", format(elapsed))) 21 | writeLines(sprintf("- Profile: %s", profile)) 22 | print(utils::summaryRprof(profile)) 23 | }, add = TRUE) 24 | } 25 | 26 | # figure out whether the autoloader is enabled 27 | enabled <- local({ 28 | 29 | # first, check config option 30 | override <- getOption("renv.config.autoloader.enabled") 31 | if (!is.null(override)) 32 | return(override) 33 | 34 | # if we're being run in a context where R_LIBS is already set, 35 | # don't load -- presumably we're being run as a sub-process and 36 | # the parent process has already set up library paths for us 37 | rcmd <- Sys.getenv("R_CMD", unset = NA) 38 | rlibs <- Sys.getenv("R_LIBS", unset = NA) 39 | if (!is.na(rlibs) && !is.na(rcmd)) 40 | return(FALSE) 41 | 42 | # next, check environment variables 43 | # TODO: prefer using the configuration one in the future 44 | envvars <- c( 45 | "RENV_CONFIG_AUTOLOADER_ENABLED", 46 | "RENV_AUTOLOADER_ENABLED", 47 | "RENV_ACTIVATE_PROJECT" 48 | ) 49 | 50 | for (envvar in envvars) { 51 | envval <- Sys.getenv(envvar, unset = NA) 52 | if (!is.na(envval)) 53 | return(tolower(envval) %in% c("true", "t", "1")) 54 | } 55 | 56 | # enable by default 57 | TRUE 58 | 59 | }) 60 | 61 | # bail if we're not enabled 62 | if (!enabled) { 63 | 64 | # if we're not enabled, we might still need to manually load 65 | # the user profile here 66 | profile <- Sys.getenv("R_PROFILE_USER", unset = "~/.Rprofile") 67 | if (file.exists(profile)) { 68 | cfg <- Sys.getenv("RENV_CONFIG_USER_PROFILE", unset = "TRUE") 69 | if (tolower(cfg) %in% c("true", "t", "1")) 70 | sys.source(profile, envir = globalenv()) 71 | } 72 | 73 | return(FALSE) 74 | 75 | } 76 | 77 | # avoid recursion 78 | if (identical(getOption("renv.autoloader.running"), TRUE)) { 79 | warning("ignoring recursive attempt to run renv autoloader") 80 | return(invisible(TRUE)) 81 | } 82 | 83 | # signal that we're loading renv during R startup 84 | options(renv.autoloader.running = TRUE) 85 | on.exit(options(renv.autoloader.running = NULL), add = TRUE) 86 | 87 | # signal that we've consented to use renv 88 | options(renv.consent = TRUE) 89 | 90 | # load the 'utils' package eagerly -- this ensures that renv shims, which 91 | # mask 'utils' packages, will come first on the search path 92 | library(utils, lib.loc = .Library) 93 | 94 | # unload renv if it's already been loaded 95 | if ("renv" %in% loadedNamespaces()) 96 | unloadNamespace("renv") 97 | 98 | # load bootstrap tools 99 | `%||%` <- function(x, y) { 100 | if (is.null(x)) y else x 101 | } 102 | 103 | catf <- function(fmt, ..., appendLF = TRUE) { 104 | 105 | quiet <- getOption("renv.bootstrap.quiet", default = FALSE) 106 | if (quiet) 107 | return(invisible()) 108 | 109 | msg <- sprintf(fmt, ...) 110 | cat(msg, file = stdout(), sep = if (appendLF) "\n" else "") 111 | 112 | invisible(msg) 113 | 114 | } 115 | 116 | header <- function(label, 117 | ..., 118 | prefix = "#", 119 | suffix = "-", 120 | n = min(getOption("width"), 78)) 121 | { 122 | label <- sprintf(label, ...) 123 | n <- max(n - nchar(label) - nchar(prefix) - 2L, 8L) 124 | if (n <= 0) 125 | return(paste(prefix, label)) 126 | 127 | tail <- paste(rep.int(suffix, n), collapse = "") 128 | paste0(prefix, " ", label, " ", tail) 129 | 130 | } 131 | 132 | startswith <- function(string, prefix) { 133 | substring(string, 1, nchar(prefix)) == prefix 134 | } 135 | 136 | bootstrap <- function(version, library) { 137 | 138 | friendly <- renv_bootstrap_version_friendly(version) 139 | section <- header(sprintf("Bootstrapping renv %s", friendly)) 140 | catf(section) 141 | 142 | # attempt to download renv 143 | catf("- Downloading renv ... ", appendLF = FALSE) 144 | withCallingHandlers( 145 | tarball <- renv_bootstrap_download(version), 146 | error = function(err) { 147 | catf("FAILED") 148 | stop("failed to download:\n", conditionMessage(err)) 149 | } 150 | ) 151 | catf("OK") 152 | on.exit(unlink(tarball), add = TRUE) 153 | 154 | # now attempt to install 155 | catf("- Installing renv ... ", appendLF = FALSE) 156 | withCallingHandlers( 157 | status <- renv_bootstrap_install(version, tarball, library), 158 | error = function(err) { 159 | catf("FAILED") 160 | stop("failed to install:\n", conditionMessage(err)) 161 | } 162 | ) 163 | catf("OK") 164 | 165 | # add empty line to break up bootstrapping from normal output 166 | catf("") 167 | 168 | return(invisible()) 169 | } 170 | 171 | renv_bootstrap_tests_running <- function() { 172 | getOption("renv.tests.running", default = FALSE) 173 | } 174 | 175 | renv_bootstrap_repos <- function() { 176 | 177 | # get CRAN repository 178 | cran <- getOption("renv.repos.cran", "https://cloud.r-project.org") 179 | 180 | # check for repos override 181 | repos <- Sys.getenv("RENV_CONFIG_REPOS_OVERRIDE", unset = NA) 182 | if (!is.na(repos)) { 183 | 184 | # check for RSPM; if set, use a fallback repository for renv 185 | rspm <- Sys.getenv("RSPM", unset = NA) 186 | if (identical(rspm, repos)) 187 | repos <- c(RSPM = rspm, CRAN = cran) 188 | 189 | return(repos) 190 | 191 | } 192 | 193 | # check for lockfile repositories 194 | repos <- tryCatch(renv_bootstrap_repos_lockfile(), error = identity) 195 | if (!inherits(repos, "error") && length(repos)) 196 | return(repos) 197 | 198 | # retrieve current repos 199 | repos <- getOption("repos") 200 | 201 | # ensure @CRAN@ entries are resolved 202 | repos[repos == "@CRAN@"] <- cran 203 | 204 | # add in renv.bootstrap.repos if set 205 | default <- c(FALLBACK = "https://cloud.r-project.org") 206 | extra <- getOption("renv.bootstrap.repos", default = default) 207 | repos <- c(repos, extra) 208 | 209 | # remove duplicates that might've snuck in 210 | dupes <- duplicated(repos) | duplicated(names(repos)) 211 | repos[!dupes] 212 | 213 | } 214 | 215 | renv_bootstrap_repos_lockfile <- function() { 216 | 217 | lockpath <- Sys.getenv("RENV_PATHS_LOCKFILE", unset = "renv.lock") 218 | if (!file.exists(lockpath)) 219 | return(NULL) 220 | 221 | lockfile <- tryCatch(renv_json_read(lockpath), error = identity) 222 | if (inherits(lockfile, "error")) { 223 | warning(lockfile) 224 | return(NULL) 225 | } 226 | 227 | repos <- lockfile$R$Repositories 228 | if (length(repos) == 0) 229 | return(NULL) 230 | 231 | keys <- vapply(repos, `[[`, "Name", FUN.VALUE = character(1)) 232 | vals <- vapply(repos, `[[`, "URL", FUN.VALUE = character(1)) 233 | names(vals) <- keys 234 | 235 | return(vals) 236 | 237 | } 238 | 239 | renv_bootstrap_download <- function(version) { 240 | 241 | sha <- attr(version, "sha", exact = TRUE) 242 | 243 | methods <- if (!is.null(sha)) { 244 | 245 | # attempting to bootstrap a development version of renv 246 | c( 247 | function() renv_bootstrap_download_tarball(sha), 248 | function() renv_bootstrap_download_github(sha) 249 | ) 250 | 251 | } else { 252 | 253 | # attempting to bootstrap a release version of renv 254 | c( 255 | function() renv_bootstrap_download_tarball(version), 256 | function() renv_bootstrap_download_cran_latest(version), 257 | function() renv_bootstrap_download_cran_archive(version) 258 | ) 259 | 260 | } 261 | 262 | for (method in methods) { 263 | path <- tryCatch(method(), error = identity) 264 | if (is.character(path) && file.exists(path)) 265 | return(path) 266 | } 267 | 268 | stop("All download methods failed") 269 | 270 | } 271 | 272 | renv_bootstrap_download_impl <- function(url, destfile) { 273 | 274 | mode <- "wb" 275 | 276 | # https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17715 277 | fixup <- 278 | Sys.info()[["sysname"]] == "Windows" && 279 | substring(url, 1L, 5L) == "file:" 280 | 281 | if (fixup) 282 | mode <- "w+b" 283 | 284 | args <- list( 285 | url = url, 286 | destfile = destfile, 287 | mode = mode, 288 | quiet = TRUE 289 | ) 290 | 291 | if ("headers" %in% names(formals(utils::download.file))) 292 | args$headers <- renv_bootstrap_download_custom_headers(url) 293 | 294 | do.call(utils::download.file, args) 295 | 296 | } 297 | 298 | renv_bootstrap_download_custom_headers <- function(url) { 299 | 300 | headers <- getOption("renv.download.headers") 301 | if (is.null(headers)) 302 | return(character()) 303 | 304 | if (!is.function(headers)) 305 | stopf("'renv.download.headers' is not a function") 306 | 307 | headers <- headers(url) 308 | if (length(headers) == 0L) 309 | return(character()) 310 | 311 | if (is.list(headers)) 312 | headers <- unlist(headers, recursive = FALSE, use.names = TRUE) 313 | 314 | ok <- 315 | is.character(headers) && 316 | is.character(names(headers)) && 317 | all(nzchar(names(headers))) 318 | 319 | if (!ok) 320 | stop("invocation of 'renv.download.headers' did not return a named character vector") 321 | 322 | headers 323 | 324 | } 325 | 326 | renv_bootstrap_download_cran_latest <- function(version) { 327 | 328 | spec <- renv_bootstrap_download_cran_latest_find(version) 329 | type <- spec$type 330 | repos <- spec$repos 331 | 332 | baseurl <- utils::contrib.url(repos = repos, type = type) 333 | ext <- if (identical(type, "source")) 334 | ".tar.gz" 335 | else if (Sys.info()[["sysname"]] == "Windows") 336 | ".zip" 337 | else 338 | ".tgz" 339 | name <- sprintf("renv_%s%s", version, ext) 340 | url <- paste(baseurl, name, sep = "/") 341 | 342 | destfile <- file.path(tempdir(), name) 343 | status <- tryCatch( 344 | renv_bootstrap_download_impl(url, destfile), 345 | condition = identity 346 | ) 347 | 348 | if (inherits(status, "condition")) 349 | return(FALSE) 350 | 351 | # report success and return 352 | destfile 353 | 354 | } 355 | 356 | renv_bootstrap_download_cran_latest_find <- function(version) { 357 | 358 | # check whether binaries are supported on this system 359 | binary <- 360 | getOption("renv.bootstrap.binary", default = TRUE) && 361 | !identical(.Platform$pkgType, "source") && 362 | !identical(getOption("pkgType"), "source") && 363 | Sys.info()[["sysname"]] %in% c("Darwin", "Windows") 364 | 365 | types <- c(if (binary) "binary", "source") 366 | 367 | # iterate over types + repositories 368 | for (type in types) { 369 | for (repos in renv_bootstrap_repos()) { 370 | 371 | # retrieve package database 372 | db <- tryCatch( 373 | as.data.frame( 374 | utils::available.packages(type = type, repos = repos), 375 | stringsAsFactors = FALSE 376 | ), 377 | error = identity 378 | ) 379 | 380 | if (inherits(db, "error")) 381 | next 382 | 383 | # check for compatible entry 384 | entry <- db[db$Package %in% "renv" & db$Version %in% version, ] 385 | if (nrow(entry) == 0) 386 | next 387 | 388 | # found it; return spec to caller 389 | spec <- list(entry = entry, type = type, repos = repos) 390 | return(spec) 391 | 392 | } 393 | } 394 | 395 | # if we got here, we failed to find renv 396 | fmt <- "renv %s is not available from your declared package repositories" 397 | stop(sprintf(fmt, version)) 398 | 399 | } 400 | 401 | renv_bootstrap_download_cran_archive <- function(version) { 402 | 403 | name <- sprintf("renv_%s.tar.gz", version) 404 | repos <- renv_bootstrap_repos() 405 | urls <- file.path(repos, "src/contrib/Archive/renv", name) 406 | destfile <- file.path(tempdir(), name) 407 | 408 | for (url in urls) { 409 | 410 | status <- tryCatch( 411 | renv_bootstrap_download_impl(url, destfile), 412 | condition = identity 413 | ) 414 | 415 | if (identical(status, 0L)) 416 | return(destfile) 417 | 418 | } 419 | 420 | return(FALSE) 421 | 422 | } 423 | 424 | renv_bootstrap_download_tarball <- function(version) { 425 | 426 | # if the user has provided the path to a tarball via 427 | # an environment variable, then use it 428 | tarball <- Sys.getenv("RENV_BOOTSTRAP_TARBALL", unset = NA) 429 | if (is.na(tarball)) 430 | return() 431 | 432 | # allow directories 433 | if (dir.exists(tarball)) { 434 | name <- sprintf("renv_%s.tar.gz", version) 435 | tarball <- file.path(tarball, name) 436 | } 437 | 438 | # bail if it doesn't exist 439 | if (!file.exists(tarball)) { 440 | 441 | # let the user know we weren't able to honour their request 442 | fmt <- "- RENV_BOOTSTRAP_TARBALL is set (%s) but does not exist." 443 | msg <- sprintf(fmt, tarball) 444 | warning(msg) 445 | 446 | # bail 447 | return() 448 | 449 | } 450 | 451 | catf("- Using local tarball '%s'.", tarball) 452 | tarball 453 | 454 | } 455 | 456 | renv_bootstrap_download_github <- function(version) { 457 | 458 | enabled <- Sys.getenv("RENV_BOOTSTRAP_FROM_GITHUB", unset = "TRUE") 459 | if (!identical(enabled, "TRUE")) 460 | return(FALSE) 461 | 462 | # prepare download options 463 | pat <- Sys.getenv("GITHUB_PAT") 464 | if (nzchar(Sys.which("curl")) && nzchar(pat)) { 465 | fmt <- "--location --fail --header \"Authorization: token %s\"" 466 | extra <- sprintf(fmt, pat) 467 | saved <- options("download.file.method", "download.file.extra") 468 | options(download.file.method = "curl", download.file.extra = extra) 469 | on.exit(do.call(base::options, saved), add = TRUE) 470 | } else if (nzchar(Sys.which("wget")) && nzchar(pat)) { 471 | fmt <- "--header=\"Authorization: token %s\"" 472 | extra <- sprintf(fmt, pat) 473 | saved <- options("download.file.method", "download.file.extra") 474 | options(download.file.method = "wget", download.file.extra = extra) 475 | on.exit(do.call(base::options, saved), add = TRUE) 476 | } 477 | 478 | url <- file.path("https://api.github.com/repos/rstudio/renv/tarball", version) 479 | name <- sprintf("renv_%s.tar.gz", version) 480 | destfile <- file.path(tempdir(), name) 481 | 482 | status <- tryCatch( 483 | renv_bootstrap_download_impl(url, destfile), 484 | condition = identity 485 | ) 486 | 487 | if (!identical(status, 0L)) 488 | return(FALSE) 489 | 490 | renv_bootstrap_download_augment(destfile) 491 | 492 | return(destfile) 493 | 494 | } 495 | 496 | # Add Sha to DESCRIPTION. This is stop gap until #890, after which we 497 | # can use renv::install() to fully capture metadata. 498 | renv_bootstrap_download_augment <- function(destfile) { 499 | sha <- renv_bootstrap_git_extract_sha1_tar(destfile) 500 | if (is.null(sha)) { 501 | return() 502 | } 503 | 504 | # Untar 505 | tempdir <- tempfile("renv-github-") 506 | on.exit(unlink(tempdir, recursive = TRUE), add = TRUE) 507 | untar(destfile, exdir = tempdir) 508 | pkgdir <- dir(tempdir, full.names = TRUE)[[1]] 509 | 510 | # Modify description 511 | desc_path <- file.path(pkgdir, "DESCRIPTION") 512 | desc_lines <- readLines(desc_path) 513 | remotes_fields <- c( 514 | "RemoteType: github", 515 | "RemoteHost: api.github.com", 516 | "RemoteRepo: renv", 517 | "RemoteUsername: rstudio", 518 | "RemotePkgRef: rstudio/renv", 519 | paste("RemoteRef: ", sha), 520 | paste("RemoteSha: ", sha) 521 | ) 522 | writeLines(c(desc_lines[desc_lines != ""], remotes_fields), con = desc_path) 523 | 524 | # Re-tar 525 | local({ 526 | old <- setwd(tempdir) 527 | on.exit(setwd(old), add = TRUE) 528 | 529 | tar(destfile, compression = "gzip") 530 | }) 531 | invisible() 532 | } 533 | 534 | # Extract the commit hash from a git archive. Git archives include the SHA1 535 | # hash as the comment field of the tarball pax extended header 536 | # (see https://www.kernel.org/pub/software/scm/git/docs/git-archive.html) 537 | # For GitHub archives this should be the first header after the default one 538 | # (512 byte) header. 539 | renv_bootstrap_git_extract_sha1_tar <- function(bundle) { 540 | 541 | # open the bundle for reading 542 | # We use gzcon for everything because (from ?gzcon) 543 | # > Reading from a connection which does not supply a 'gzip' magic 544 | # > header is equivalent to reading from the original connection 545 | conn <- gzcon(file(bundle, open = "rb", raw = TRUE)) 546 | on.exit(close(conn)) 547 | 548 | # The default pax header is 512 bytes long and the first pax extended header 549 | # with the comment should be 51 bytes long 550 | # `52 comment=` (11 chars) + 40 byte SHA1 hash 551 | len <- 0x200 + 0x33 552 | res <- rawToChar(readBin(conn, "raw", n = len)[0x201:len]) 553 | 554 | if (grepl("^52 comment=", res)) { 555 | sub("52 comment=", "", res) 556 | } else { 557 | NULL 558 | } 559 | } 560 | 561 | renv_bootstrap_install <- function(version, tarball, library) { 562 | 563 | # attempt to install it into project library 564 | dir.create(library, showWarnings = FALSE, recursive = TRUE) 565 | output <- renv_bootstrap_install_impl(library, tarball) 566 | 567 | # check for successful install 568 | status <- attr(output, "status") 569 | if (is.null(status) || identical(status, 0L)) 570 | return(status) 571 | 572 | # an error occurred; report it 573 | header <- "installation of renv failed" 574 | lines <- paste(rep.int("=", nchar(header)), collapse = "") 575 | text <- paste(c(header, lines, output), collapse = "\n") 576 | stop(text) 577 | 578 | } 579 | 580 | renv_bootstrap_install_impl <- function(library, tarball) { 581 | 582 | # invoke using system2 so we can capture and report output 583 | bin <- R.home("bin") 584 | exe <- if (Sys.info()[["sysname"]] == "Windows") "R.exe" else "R" 585 | R <- file.path(bin, exe) 586 | 587 | args <- c( 588 | "--vanilla", "CMD", "INSTALL", "--no-multiarch", 589 | "-l", shQuote(path.expand(library)), 590 | shQuote(path.expand(tarball)) 591 | ) 592 | 593 | system2(R, args, stdout = TRUE, stderr = TRUE) 594 | 595 | } 596 | 597 | renv_bootstrap_platform_prefix <- function() { 598 | 599 | # construct version prefix 600 | version <- paste(R.version$major, R.version$minor, sep = ".") 601 | prefix <- paste("R", numeric_version(version)[1, 1:2], sep = "-") 602 | 603 | # include SVN revision for development versions of R 604 | # (to avoid sharing platform-specific artefacts with released versions of R) 605 | devel <- 606 | identical(R.version[["status"]], "Under development (unstable)") || 607 | identical(R.version[["nickname"]], "Unsuffered Consequences") 608 | 609 | if (devel) 610 | prefix <- paste(prefix, R.version[["svn rev"]], sep = "-r") 611 | 612 | # build list of path components 613 | components <- c(prefix, R.version$platform) 614 | 615 | # include prefix if provided by user 616 | prefix <- renv_bootstrap_platform_prefix_impl() 617 | if (!is.na(prefix) && nzchar(prefix)) 618 | components <- c(prefix, components) 619 | 620 | # build prefix 621 | paste(components, collapse = "/") 622 | 623 | } 624 | 625 | renv_bootstrap_platform_prefix_impl <- function() { 626 | 627 | # if an explicit prefix has been supplied, use it 628 | prefix <- Sys.getenv("RENV_PATHS_PREFIX", unset = NA) 629 | if (!is.na(prefix)) 630 | return(prefix) 631 | 632 | # if the user has requested an automatic prefix, generate it 633 | auto <- Sys.getenv("RENV_PATHS_PREFIX_AUTO", unset = NA) 634 | if (auto %in% c("TRUE", "True", "true", "1")) 635 | return(renv_bootstrap_platform_prefix_auto()) 636 | 637 | # empty string on failure 638 | "" 639 | 640 | } 641 | 642 | renv_bootstrap_platform_prefix_auto <- function() { 643 | 644 | prefix <- tryCatch(renv_bootstrap_platform_os(), error = identity) 645 | if (inherits(prefix, "error") || prefix %in% "unknown") { 646 | 647 | msg <- paste( 648 | "failed to infer current operating system", 649 | "please file a bug report at https://github.com/rstudio/renv/issues", 650 | sep = "; " 651 | ) 652 | 653 | warning(msg) 654 | 655 | } 656 | 657 | prefix 658 | 659 | } 660 | 661 | renv_bootstrap_platform_os <- function() { 662 | 663 | sysinfo <- Sys.info() 664 | sysname <- sysinfo[["sysname"]] 665 | 666 | # handle Windows + macOS up front 667 | if (sysname == "Windows") 668 | return("windows") 669 | else if (sysname == "Darwin") 670 | return("macos") 671 | 672 | # check for os-release files 673 | for (file in c("/etc/os-release", "/usr/lib/os-release")) 674 | if (file.exists(file)) 675 | return(renv_bootstrap_platform_os_via_os_release(file, sysinfo)) 676 | 677 | # check for redhat-release files 678 | if (file.exists("/etc/redhat-release")) 679 | return(renv_bootstrap_platform_os_via_redhat_release()) 680 | 681 | "unknown" 682 | 683 | } 684 | 685 | renv_bootstrap_platform_os_via_os_release <- function(file, sysinfo) { 686 | 687 | # read /etc/os-release 688 | release <- utils::read.table( 689 | file = file, 690 | sep = "=", 691 | quote = c("\"", "'"), 692 | col.names = c("Key", "Value"), 693 | comment.char = "#", 694 | stringsAsFactors = FALSE 695 | ) 696 | 697 | vars <- as.list(release$Value) 698 | names(vars) <- release$Key 699 | 700 | # get os name 701 | os <- tolower(sysinfo[["sysname"]]) 702 | 703 | # read id 704 | id <- "unknown" 705 | for (field in c("ID", "ID_LIKE")) { 706 | if (field %in% names(vars) && nzchar(vars[[field]])) { 707 | id <- vars[[field]] 708 | break 709 | } 710 | } 711 | 712 | # read version 713 | version <- "unknown" 714 | for (field in c("UBUNTU_CODENAME", "VERSION_CODENAME", "VERSION_ID", "BUILD_ID")) { 715 | if (field %in% names(vars) && nzchar(vars[[field]])) { 716 | version <- vars[[field]] 717 | break 718 | } 719 | } 720 | 721 | # join together 722 | paste(c(os, id, version), collapse = "-") 723 | 724 | } 725 | 726 | renv_bootstrap_platform_os_via_redhat_release <- function() { 727 | 728 | # read /etc/redhat-release 729 | contents <- readLines("/etc/redhat-release", warn = FALSE) 730 | 731 | # infer id 732 | id <- if (grepl("centos", contents, ignore.case = TRUE)) 733 | "centos" 734 | else if (grepl("redhat", contents, ignore.case = TRUE)) 735 | "redhat" 736 | else 737 | "unknown" 738 | 739 | # try to find a version component (very hacky) 740 | version <- "unknown" 741 | 742 | parts <- strsplit(contents, "[[:space:]]")[[1L]] 743 | for (part in parts) { 744 | 745 | nv <- tryCatch(numeric_version(part), error = identity) 746 | if (inherits(nv, "error")) 747 | next 748 | 749 | version <- nv[1, 1] 750 | break 751 | 752 | } 753 | 754 | paste(c("linux", id, version), collapse = "-") 755 | 756 | } 757 | 758 | renv_bootstrap_library_root_name <- function(project) { 759 | 760 | # use project name as-is if requested 761 | asis <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT_ASIS", unset = "FALSE") 762 | if (asis) 763 | return(basename(project)) 764 | 765 | # otherwise, disambiguate based on project's path 766 | id <- substring(renv_bootstrap_hash_text(project), 1L, 8L) 767 | paste(basename(project), id, sep = "-") 768 | 769 | } 770 | 771 | renv_bootstrap_library_root <- function(project) { 772 | 773 | prefix <- renv_bootstrap_profile_prefix() 774 | 775 | path <- Sys.getenv("RENV_PATHS_LIBRARY", unset = NA) 776 | if (!is.na(path)) 777 | return(paste(c(path, prefix), collapse = "/")) 778 | 779 | path <- renv_bootstrap_library_root_impl(project) 780 | if (!is.null(path)) { 781 | name <- renv_bootstrap_library_root_name(project) 782 | return(paste(c(path, prefix, name), collapse = "/")) 783 | } 784 | 785 | renv_bootstrap_paths_renv("library", project = project) 786 | 787 | } 788 | 789 | renv_bootstrap_library_root_impl <- function(project) { 790 | 791 | root <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT", unset = NA) 792 | if (!is.na(root)) 793 | return(root) 794 | 795 | type <- renv_bootstrap_project_type(project) 796 | if (identical(type, "package")) { 797 | userdir <- renv_bootstrap_user_dir() 798 | return(file.path(userdir, "library")) 799 | } 800 | 801 | } 802 | 803 | renv_bootstrap_validate_version <- function(version, description = NULL) { 804 | 805 | # resolve description file 806 | # 807 | # avoid passing lib.loc to `packageDescription()` below, since R will 808 | # use the loaded version of the package by default anyhow. note that 809 | # this function should only be called after 'renv' is loaded 810 | # https://github.com/rstudio/renv/issues/1625 811 | description <- description %||% packageDescription("renv") 812 | 813 | # check whether requested version 'version' matches loaded version of renv 814 | sha <- attr(version, "sha", exact = TRUE) 815 | valid <- if (!is.null(sha)) 816 | renv_bootstrap_validate_version_dev(sha, description) 817 | else 818 | renv_bootstrap_validate_version_release(version, description) 819 | 820 | if (valid) 821 | return(TRUE) 822 | 823 | # the loaded version of renv doesn't match the requested version; 824 | # give the user instructions on how to proceed 825 | remote <- if (!is.null(description[["RemoteSha"]])) { 826 | paste("rstudio/renv", description[["RemoteSha"]], sep = "@") 827 | } else { 828 | paste("renv", description[["Version"]], sep = "@") 829 | } 830 | 831 | # display both loaded version + sha if available 832 | friendly <- renv_bootstrap_version_friendly( 833 | version = description[["Version"]], 834 | sha = description[["RemoteSha"]] 835 | ) 836 | 837 | fmt <- paste( 838 | "renv %1$s was loaded from project library, but this project is configured to use renv %2$s.", 839 | "- Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile.", 840 | "- Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library.", 841 | sep = "\n" 842 | ) 843 | catf(fmt, friendly, renv_bootstrap_version_friendly(version), remote) 844 | 845 | FALSE 846 | 847 | } 848 | 849 | renv_bootstrap_validate_version_dev <- function(version, description) { 850 | expected <- description[["RemoteSha"]] 851 | is.character(expected) && startswith(expected, version) 852 | } 853 | 854 | renv_bootstrap_validate_version_release <- function(version, description) { 855 | expected <- description[["Version"]] 856 | is.character(expected) && identical(expected, version) 857 | } 858 | 859 | renv_bootstrap_hash_text <- function(text) { 860 | 861 | hashfile <- tempfile("renv-hash-") 862 | on.exit(unlink(hashfile), add = TRUE) 863 | 864 | writeLines(text, con = hashfile) 865 | tools::md5sum(hashfile) 866 | 867 | } 868 | 869 | renv_bootstrap_load <- function(project, libpath, version) { 870 | 871 | # try to load renv from the project library 872 | if (!requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) 873 | return(FALSE) 874 | 875 | # warn if the version of renv loaded does not match 876 | renv_bootstrap_validate_version(version) 877 | 878 | # execute renv load hooks, if any 879 | hooks <- getHook("renv::autoload") 880 | for (hook in hooks) 881 | if (is.function(hook)) 882 | tryCatch(hook(), error = warnify) 883 | 884 | # load the project 885 | renv::load(project) 886 | 887 | TRUE 888 | 889 | } 890 | 891 | renv_bootstrap_profile_load <- function(project) { 892 | 893 | # if RENV_PROFILE is already set, just use that 894 | profile <- Sys.getenv("RENV_PROFILE", unset = NA) 895 | if (!is.na(profile) && nzchar(profile)) 896 | return(profile) 897 | 898 | # check for a profile file (nothing to do if it doesn't exist) 899 | path <- renv_bootstrap_paths_renv("profile", profile = FALSE, project = project) 900 | if (!file.exists(path)) 901 | return(NULL) 902 | 903 | # read the profile, and set it if it exists 904 | contents <- readLines(path, warn = FALSE) 905 | if (length(contents) == 0L) 906 | return(NULL) 907 | 908 | # set RENV_PROFILE 909 | profile <- contents[[1L]] 910 | if (!profile %in% c("", "default")) 911 | Sys.setenv(RENV_PROFILE = profile) 912 | 913 | profile 914 | 915 | } 916 | 917 | renv_bootstrap_profile_prefix <- function() { 918 | profile <- renv_bootstrap_profile_get() 919 | if (!is.null(profile)) 920 | return(file.path("profiles", profile, "renv")) 921 | } 922 | 923 | renv_bootstrap_profile_get <- function() { 924 | profile <- Sys.getenv("RENV_PROFILE", unset = "") 925 | renv_bootstrap_profile_normalize(profile) 926 | } 927 | 928 | renv_bootstrap_profile_set <- function(profile) { 929 | profile <- renv_bootstrap_profile_normalize(profile) 930 | if (is.null(profile)) 931 | Sys.unsetenv("RENV_PROFILE") 932 | else 933 | Sys.setenv(RENV_PROFILE = profile) 934 | } 935 | 936 | renv_bootstrap_profile_normalize <- function(profile) { 937 | 938 | if (is.null(profile) || profile %in% c("", "default")) 939 | return(NULL) 940 | 941 | profile 942 | 943 | } 944 | 945 | renv_bootstrap_path_absolute <- function(path) { 946 | 947 | substr(path, 1L, 1L) %in% c("~", "/", "\\") || ( 948 | substr(path, 1L, 1L) %in% c(letters, LETTERS) && 949 | substr(path, 2L, 3L) %in% c(":/", ":\\") 950 | ) 951 | 952 | } 953 | 954 | renv_bootstrap_paths_renv <- function(..., profile = TRUE, project = NULL) { 955 | renv <- Sys.getenv("RENV_PATHS_RENV", unset = "renv") 956 | root <- if (renv_bootstrap_path_absolute(renv)) NULL else project 957 | prefix <- if (profile) renv_bootstrap_profile_prefix() 958 | components <- c(root, renv, prefix, ...) 959 | paste(components, collapse = "/") 960 | } 961 | 962 | renv_bootstrap_project_type <- function(path) { 963 | 964 | descpath <- file.path(path, "DESCRIPTION") 965 | if (!file.exists(descpath)) 966 | return("unknown") 967 | 968 | desc <- tryCatch( 969 | read.dcf(descpath, all = TRUE), 970 | error = identity 971 | ) 972 | 973 | if (inherits(desc, "error")) 974 | return("unknown") 975 | 976 | type <- desc$Type 977 | if (!is.null(type)) 978 | return(tolower(type)) 979 | 980 | package <- desc$Package 981 | if (!is.null(package)) 982 | return("package") 983 | 984 | "unknown" 985 | 986 | } 987 | 988 | renv_bootstrap_user_dir <- function() { 989 | dir <- renv_bootstrap_user_dir_impl() 990 | path.expand(chartr("\\", "/", dir)) 991 | } 992 | 993 | renv_bootstrap_user_dir_impl <- function() { 994 | 995 | # use local override if set 996 | override <- getOption("renv.userdir.override") 997 | if (!is.null(override)) 998 | return(override) 999 | 1000 | # use R_user_dir if available 1001 | tools <- asNamespace("tools") 1002 | if (is.function(tools$R_user_dir)) 1003 | return(tools$R_user_dir("renv", "cache")) 1004 | 1005 | # try using our own backfill for older versions of R 1006 | envvars <- c("R_USER_CACHE_DIR", "XDG_CACHE_HOME") 1007 | for (envvar in envvars) { 1008 | root <- Sys.getenv(envvar, unset = NA) 1009 | if (!is.na(root)) 1010 | return(file.path(root, "R/renv")) 1011 | } 1012 | 1013 | # use platform-specific default fallbacks 1014 | if (Sys.info()[["sysname"]] == "Windows") 1015 | file.path(Sys.getenv("LOCALAPPDATA"), "R/cache/R/renv") 1016 | else if (Sys.info()[["sysname"]] == "Darwin") 1017 | "~/Library/Caches/org.R-project.R/R/renv" 1018 | else 1019 | "~/.cache/R/renv" 1020 | 1021 | } 1022 | 1023 | renv_bootstrap_version_friendly <- function(version, shafmt = NULL, sha = NULL) { 1024 | sha <- sha %||% attr(version, "sha", exact = TRUE) 1025 | parts <- c(version, sprintf(shafmt %||% " [sha: %s]", substring(sha, 1L, 7L))) 1026 | paste(parts, collapse = "") 1027 | } 1028 | 1029 | renv_bootstrap_exec <- function(project, libpath, version) { 1030 | if (!renv_bootstrap_load(project, libpath, version)) 1031 | renv_bootstrap_run(version, libpath) 1032 | } 1033 | 1034 | renv_bootstrap_run <- function(version, libpath) { 1035 | 1036 | # perform bootstrap 1037 | bootstrap(version, libpath) 1038 | 1039 | # exit early if we're just testing bootstrap 1040 | if (!is.na(Sys.getenv("RENV_BOOTSTRAP_INSTALL_ONLY", unset = NA))) 1041 | return(TRUE) 1042 | 1043 | # try again to load 1044 | if (requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) { 1045 | return(renv::load(project = getwd())) 1046 | } 1047 | 1048 | # failed to download or load renv; warn the user 1049 | msg <- c( 1050 | "Failed to find an renv installation: the project will not be loaded.", 1051 | "Use `renv::activate()` to re-initialize the project." 1052 | ) 1053 | 1054 | warning(paste(msg, collapse = "\n"), call. = FALSE) 1055 | 1056 | } 1057 | 1058 | renv_json_read <- function(file = NULL, text = NULL) { 1059 | 1060 | jlerr <- NULL 1061 | 1062 | # if jsonlite is loaded, use that instead 1063 | if ("jsonlite" %in% loadedNamespaces()) { 1064 | 1065 | json <- tryCatch(renv_json_read_jsonlite(file, text), error = identity) 1066 | if (!inherits(json, "error")) 1067 | return(json) 1068 | 1069 | jlerr <- json 1070 | 1071 | } 1072 | 1073 | # otherwise, fall back to the default JSON reader 1074 | json <- tryCatch(renv_json_read_default(file, text), error = identity) 1075 | if (!inherits(json, "error")) 1076 | return(json) 1077 | 1078 | # report an error 1079 | if (!is.null(jlerr)) 1080 | stop(jlerr) 1081 | else 1082 | stop(json) 1083 | 1084 | } 1085 | 1086 | renv_json_read_jsonlite <- function(file = NULL, text = NULL) { 1087 | text <- paste(text %||% readLines(file, warn = FALSE), collapse = "\n") 1088 | jsonlite::fromJSON(txt = text, simplifyVector = FALSE) 1089 | } 1090 | 1091 | renv_json_read_default <- function(file = NULL, text = NULL) { 1092 | 1093 | # find strings in the JSON 1094 | text <- paste(text %||% readLines(file, warn = FALSE), collapse = "\n") 1095 | pattern <- '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]' 1096 | locs <- gregexpr(pattern, text, perl = TRUE)[[1]] 1097 | 1098 | # if any are found, replace them with placeholders 1099 | replaced <- text 1100 | strings <- character() 1101 | replacements <- character() 1102 | 1103 | if (!identical(c(locs), -1L)) { 1104 | 1105 | # get the string values 1106 | starts <- locs 1107 | ends <- locs + attr(locs, "match.length") - 1L 1108 | strings <- substring(text, starts, ends) 1109 | 1110 | # only keep those requiring escaping 1111 | strings <- grep("[[\\]{}:]", strings, perl = TRUE, value = TRUE) 1112 | 1113 | # compute replacements 1114 | replacements <- sprintf('"\032%i\032"', seq_along(strings)) 1115 | 1116 | # replace the strings 1117 | mapply(function(string, replacement) { 1118 | replaced <<- sub(string, replacement, replaced, fixed = TRUE) 1119 | }, strings, replacements) 1120 | 1121 | } 1122 | 1123 | # transform the JSON into something the R parser understands 1124 | transformed <- replaced 1125 | transformed <- gsub("{}", "`names<-`(list(), character())", transformed, fixed = TRUE) 1126 | transformed <- gsub("[[{]", "list(", transformed, perl = TRUE) 1127 | transformed <- gsub("[]}]", ")", transformed, perl = TRUE) 1128 | transformed <- gsub(":", "=", transformed, fixed = TRUE) 1129 | text <- paste(transformed, collapse = "\n") 1130 | 1131 | # parse it 1132 | json <- parse(text = text, keep.source = FALSE, srcfile = NULL)[[1L]] 1133 | 1134 | # construct map between source strings, replaced strings 1135 | map <- as.character(parse(text = strings)) 1136 | names(map) <- as.character(parse(text = replacements)) 1137 | 1138 | # convert to list 1139 | map <- as.list(map) 1140 | 1141 | # remap strings in object 1142 | remapped <- renv_json_read_remap(json, map) 1143 | 1144 | # evaluate 1145 | eval(remapped, envir = baseenv()) 1146 | 1147 | } 1148 | 1149 | renv_json_read_remap <- function(json, map) { 1150 | 1151 | # fix names 1152 | if (!is.null(names(json))) { 1153 | lhs <- match(names(json), names(map), nomatch = 0L) 1154 | rhs <- match(names(map), names(json), nomatch = 0L) 1155 | names(json)[rhs] <- map[lhs] 1156 | } 1157 | 1158 | # fix values 1159 | if (is.character(json)) 1160 | return(map[[json]] %||% json) 1161 | 1162 | # handle true, false, null 1163 | if (is.name(json)) { 1164 | text <- as.character(json) 1165 | if (text == "true") 1166 | return(TRUE) 1167 | else if (text == "false") 1168 | return(FALSE) 1169 | else if (text == "null") 1170 | return(NULL) 1171 | } 1172 | 1173 | # recurse 1174 | if (is.recursive(json)) { 1175 | for (i in seq_along(json)) { 1176 | json[i] <- list(renv_json_read_remap(json[[i]], map)) 1177 | } 1178 | } 1179 | 1180 | json 1181 | 1182 | } 1183 | 1184 | # load the renv profile, if any 1185 | renv_bootstrap_profile_load(project) 1186 | 1187 | # construct path to library root 1188 | root <- renv_bootstrap_library_root(project) 1189 | 1190 | # construct library prefix for platform 1191 | prefix <- renv_bootstrap_platform_prefix() 1192 | 1193 | # construct full libpath 1194 | libpath <- file.path(root, prefix) 1195 | 1196 | # run bootstrap code 1197 | renv_bootstrap_exec(project, libpath, version) 1198 | 1199 | invisible() 1200 | 1201 | }) 1202 | -------------------------------------------------------------------------------- /examples/renv-shiny/renv/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "bioconductor.version": null, 3 | "external.libraries": [], 4 | "ignored.packages": [], 5 | "package.dependency.fields": [ 6 | "Imports", 7 | "Depends", 8 | "LinkingTo" 9 | ], 10 | "ppm.enabled": null, 11 | "ppm.ignored.urls": [], 12 | "r.version": null, 13 | "snapshot.type": "implicit", 14 | "use.cache": true, 15 | "vcs.ignore.cellar": true, 16 | "vcs.ignore.library": true, 17 | "vcs.ignore.local": true, 18 | "vcs.manage.ignores": true 19 | } 20 | -------------------------------------------------------------------------------- /examples/rmarkdown/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN installr -d rmarkdown 5 | 6 | RUN wget https://github.com/jgm/pandoc/releases/download/2.13/pandoc-2.13-linux-amd64.tar.gz && \ 7 | tar xzf pandoc-2.13-linux-amd64.tar.gz && \ 8 | mv pandoc-2.13/bin/* /usr/local/bin/ && \ 9 | rm -rf pandoc-2.13* 10 | -------------------------------------------------------------------------------- /examples/rstan/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | ENV DOWNLOAD_STATIC_LIBV8=1 5 | 6 | RUN installr -d -t "curl-dev linux-headers gfortran" rstan Rcppcore/Rcpp 7 | -------------------------------------------------------------------------------- /examples/sf/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN installr -d \ 5 | -t "openssl-dev cmake linux-headers gfortran proj-dev gdal-dev sqlite-dev geos-dev udunits-dev" \ 6 | -a "libssl3 proj gdal geos expat udunits" \ 7 | sf Rcppcore/Rcpp 8 | 9 | RUN R -q -e 'library(sf)' 10 | -------------------------------------------------------------------------------- /examples/shiny/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN installr -d \ 5 | -t "zlib-dev" \ 6 | shiny Rcppcore/Rcpp 7 | -------------------------------------------------------------------------------- /examples/ssh-curl/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rhub/r-minimal 2 | 3 | RUN installr -c -p -a "openssl openssl-dev libssh2 libssh2-dev libpsl libpsl-dev perl" \ 4 | && wget -qO - https://curl.haxx.se/download/curl-8.8.0.tar.gz \ 5 | | tar xz \ 6 | && cd curl-* \ 7 | && ./configure --with-openssl --with-libssh2 \ 8 | && make \ 9 | && make install \ 10 | && cd .. \ 11 | && rm -rf curl-* \ 12 | && installr -d -t "openssl-dev libssh2-dev libpsl-dev perl" curl 13 | -------------------------------------------------------------------------------- /examples/tidyverse-minimal/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rhub/r-minimal AS build 2 | 3 | RUN installr -d \ 4 | -t "curl-dev libxml2-dev linux-headers gfortran fontconfig-dev fribidi-dev harfbuzz-dev freetype-dev libpng-dev tiff-dev" \ 5 | -a "libcurl libxml2 fontconfig fribidi harfbuzz freetype libpng tiff libjpeg icu-libs" \ 6 | tidyverse 7 | 8 | RUN rm -rf /usr/local/lib/R/library/*/help && \ 9 | rm -rf /usr/local/lib/R/library/*/NEWS* && \ 10 | rm -rf /usr/local/lib/R/library/*/html && \ 11 | rm -rf /usr/local/lib/R/library/*/doc && \ 12 | rm -rf /usr/local/lib/R/library/data.table/tests && \ 13 | rm -rf /usr/local/lib/R/library/knitr/examples && \ 14 | rm -rf /usr/local/lib/R/library/lubridate/pkgdown && \ 15 | rm -rf /usr/local/lib/R/library/googledrive/extdata && \ 16 | rm -rf /usr/local/lib/R/library/colorspace/*/www && \ 17 | rm -rf /usr/local/lib/R/library/vroom/bench 18 | 19 | RUN cd /usr/local/lib/R/library && \ 20 | rm -rf sass fontawesome tinytex reprex rmarkdown bslib callr processx ps \ 21 | knitr jquerylib evaluate highr xfun yaml base64enc htmltools digest 22 | 23 | FROM rhub/r-minimal 24 | 25 | COPY --from=build /usr/local/lib/R/library /usr/local/lib/R/library 26 | -------------------------------------------------------------------------------- /examples/tidyverse/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rhub/r-minimal 2 | 3 | RUN installr -d \ 4 | -t "curl-dev libxml2-dev linux-headers gfortran fontconfig-dev fribidi-dev harfbuzz-dev freetype-dev libpng-dev tiff-dev" \ 5 | -a "libcurl libxml2 fontconfig fribidi harfbuzz freetype libpng tiff libjpeg icu-libs" \ 6 | tidyverse 7 | -------------------------------------------------------------------------------- /examples/xgboost/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM rhub/r-minimal 3 | 4 | RUN mkdir -p ~/.R && echo 'CXX17FLAGS+=-DDMLC_LOG_STACK_TRACE=0 -DDMLC_USE_FOPEN64=0' >> ~/.R/Makevars 5 | 6 | RUN installr -d -t "gfortran" xgboost 7 | -------------------------------------------------------------------------------- /installr: -------------------------------------------------------------------------------- 1 | #! /bin/ash 2 | set -e 3 | 4 | usage() { 5 | echo "Usage: $0 [ -c | -d ] [ -e ] [ -a pkgs ] [ -t pkgs ] [ -r ] [ -p ] REMOTES ..." 6 | echo 7 | echo "Options:" 8 | echo " -c install C and C++ compilers and keep them" 9 | echo " -d install C and C++ compilers, temporarily" 10 | echo " -a install Alpine packages and keep them" 11 | echo " -t install Alpine packages, temporarily" 12 | echo " -p do not remove pak after the installation (ignored if -r is given)". 13 | echo " -e use renv to restore the renv.lock file if present." 14 | echo 15 | echo "REMOTES may be:" 16 | echo " * package names from CRAN/Bioconductor, e.g. ggplot2" 17 | echo " * slugs of GitHub repos, e.g. tidyverse/ggplot2" 18 | echo " * GitHub branch, tag or commit, e.g tidyverse/ggplot2@v1.0.0" 19 | echo " * URLs to package .tar.gz files, e.g. url::https://x.com/pkg.tar.gz" 20 | echo " * path to a local directory, e.g. local::." 21 | exit 2 22 | } 23 | 24 | error() { 25 | echo $1 >&2 26 | exit 2 27 | } 28 | 29 | unset KEEP_PAK 30 | 31 | 32 | while getopts 'pecda:t:?h' o 33 | do 34 | case $o in 35 | a) APKG_FIN=$OPTARG ; shift ;; 36 | t) APKG_TMP=$OPTARG ; shift ;; 37 | c) APKG_COMPILERS=true ;; 38 | d) APKG_COMPILERS_TMP=true ;; 39 | p) KEEP_PAK=true ;; 40 | e) USE_RENV=true ;; 41 | h|?) usage ;; 42 | esac 43 | shift 44 | done 45 | 46 | APKG_GCC="gcc musl-dev g++" 47 | 48 | if [[ -n "$APKG_COMPILERS" ]]; then 49 | APKG_FIN="$APKG_FIN $APKG_GCC" 50 | fi 51 | 52 | if [[ -n "$APKG_COMPILERS_TMP" ]]; then 53 | APKG_TMP="$APKG_TMP $APKG_GCC" 54 | fi 55 | 56 | echo 57 | echo -------------------------------------- 58 | [[ -n "$APKG_FIN" ]] && echo Adding "$APKG_FIN" 59 | [[ -n "$APKG_TMP" ]] && echo Temporarily adding "$APKG_TMP" 60 | apk add --no-cache $APKG_FIN $APKG_TMP 61 | 62 | echo 63 | echo -------------------------------------- 64 | echo "Installing pak (if needed)" 65 | Rscript -e 'if (! requireNamespace("pak", quietly=TRUE)) install.packages("pak", repos = sprintf("https://r-lib.github.io/p/pak/devel/%s/%s/%s", .Platform$pkgType, R.Version()$os, R.Version()$arch))' 66 | 67 | if [[ $# -eq 0 ]]; then 68 | echo 69 | echo -------------------------------------- 70 | echo No R packages to install 71 | else 72 | echo 73 | echo -------------------------------------- 74 | echo Installing "$@" 75 | Rscript -e 'pak::pkg_install(commandArgs(TRUE))' "$@" 76 | fi 77 | 78 | echo 79 | echo -------------------------------------- 80 | echo Cleaning up pak cache 81 | Rscript -e 'pak::cache_clean(); pak::meta_clean(TRUE)' 82 | rm -rf /tmp/Rtmp* 83 | if [[ -z "$KEEP_PAK" ]]; then 84 | echo 85 | echo -------------------------------------- 86 | echo Removing pak 87 | Rscript -e 'remove.packages("pak")' 88 | fi 89 | 90 | 91 | echo -------------------------------------- 92 | echo "Activating and Restoring renv.lock file if needed." 93 | if [[ "$USE_RENV" == true ]]; then 94 | if [[ -f "./renv.lock" ]]; then 95 | Rscript -e 'renv::consent(provided = TRUE)' 96 | Rscript -e 'renv::restore()' 97 | else 98 | echo "renv.lock file is not present in the current directory." 99 | fi 100 | fi 101 | rm -rf /tmp/Rtmp* 102 | 103 | echo 104 | echo -------------------------------------- 105 | [[ -n "$APKG_TMP" ]] && echo Removing "$APKG_TMP" 106 | apk del $APKG_TMP 107 | 108 | echo 109 | echo -------------------------------------- 110 | echo Cleaning up cache 111 | 112 | rm -rf /var/cache/apk/* 113 | -------------------------------------------------------------------------------- /patches/R-4.0.0.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 094396b..75fae89 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,12 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN 4.0.0}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | 63 | \subsection{SIGNIFICANT USER-VISIBLE CHANGES}{ 64 | \itemize{ 65 | -------------------------------------------------------------------------------- /patches/R-4.0.1.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 9417774..5a7fa23 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,12 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.0.1}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | 63 | \subsection{NEW FEATURES}{ 64 | \itemize{ 65 | -------------------------------------------------------------------------------- /patches/R-4.0.2.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 4c45f26..69285cd 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,12 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.0.2}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | 63 | \subsection{UTILITIES}{ 64 | \itemize{ 65 | -------------------------------------------------------------------------------- /patches/R-4.0.3.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 916961a..521a1fd 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,12 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.0.3}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | 63 | \subsection{NEW FEATURES}{ 64 | \itemize{ 65 | -------------------------------------------------------------------------------- /patches/R-4.0.4.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index ec44c62..510dadc 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,12 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.0.4}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | 63 | \subsection{NEW FEATURES}{ 64 | \itemize{ 65 | -------------------------------------------------------------------------------- /patches/R-4.0.5.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 5640a16..5bebf9e 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,12 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.0.5}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | \subsection{BUG FIXES}{ 63 | \itemize{ 64 | \item The change to the internal table in \R 4.0.4 for 65 | -------------------------------------------------------------------------------- /patches/R-4.1.0.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 2fcc2e7..f0ed95b 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,12 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.1.0}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | \subsection{FUTURE DIRECTIONS}{ 63 | \itemize{ 64 | \item It is planned that the 4.1.x series will be the last to 65 | -------------------------------------------------------------------------------- /patches/R-4.1.1.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 8515986..760dccb 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,13 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.1.1}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | + 63 | \subsection{NEW FEATURES}{ 64 | \itemize{ 65 | \item \code{require(\var{pkg}, quietly = TRUE)} is quieter and in 66 | -------------------------------------------------------------------------------- /patches/R-4.1.2.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 90c8408..d24571b 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,12 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.1.2}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | \subsection{C-LEVEL FACILITIES}{ 63 | \itemize{ 64 | \item The workaround in headers \file{R.h} and \file{Rmath.h} 65 | -------------------------------------------------------------------------------- /patches/R-4.1.3.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 8b4e587..b4f145e 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,13 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.1.3}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | + 63 | \subsection{NEW FEATURES}{ 64 | \itemize{ 65 | \item The default version of Bioconductor has been changed to 66 | -------------------------------------------------------------------------------- /patches/R-4.2.0.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 62f25ae..82dfac6 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,12 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.2.0}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | \subsection{SIGNIFICANT USER-VISIBLE CHANGES}{ 63 | \itemize{ 64 | \item The \code{formula} method of \code{aggregate()} now matches 65 | -------------------------------------------------------------------------------- /patches/R-4.2.1.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 630c88f..3504a87 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,13 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.2.1}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | + 63 | \subsection{NEW FEATURES}{ 64 | \itemize{ 65 | \item New function \code{utils::findCRANmirror()} to find out if a 66 | -------------------------------------------------------------------------------- /patches/R-4.2.2.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index b52627e..aee4475 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,13 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.2.2}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | + 63 | \subsection{NEW FEATURES}{ 64 | \itemize{ 65 | \item \code{tools::Rdiff(useDiff = TRUE)} checks for the presence 66 | -------------------------------------------------------------------------------- /patches/R-4.2.3.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index fe2a646..986e83d 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,13 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.2.3}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | + 63 | \subsection{C-LEVEL FACILITIES}{ 64 | \itemize{ 65 | \item The definition of \code{DL_FUNC} in \file{R_ext/Rdynload.h} 66 | -------------------------------------------------------------------------------- /patches/R-4.3.0.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 2fd50d5..6b24e71 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,13 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.3.0}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | + 63 | \subsection{SIGNIFICANT USER-VISIBLE CHANGES}{ 64 | \itemize{ 65 | \item Calling \code{&&} or \code{||} with LHS or (if evaluated) RHS of 66 | -------------------------------------------------------------------------------- /patches/R-4.3.1.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 5ca6489..62db476 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,13 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.3.1}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | + 63 | \subsection{C-LEVEL FACILITIES}{ 64 | \itemize{ 65 | \item The C-level API version of \R's \code{integrate()}, 66 | -------------------------------------------------------------------------------- /patches/R-4.3.2.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index dd59f87..f3abe02 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -6,6 +6,13 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.3.2}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | + 63 | \subsection{NEW FEATURES}{ 64 | \itemize{ 65 | \item The default initialization of the \code{"repos"} option 66 | -------------------------------------------------------------------------------- /patches/R-4.3.3.patch: -------------------------------------------------------------------------------- 1 | commit c06f7f2518673a75f9b36f2af9caf7b69ab4952e 2 | Author: luke 3 | Date: Sun Mar 31 19:35:58 2024 +0000 4 | 5 | readRDS() and unserialize() now signal an errorr instead of returning a PROMSXP. 6 | 7 | 8 | git-svn-id: https://svn.r-project.org/R/trunk@86235 00db46b3-68df-0310-9c12-caf00c1e9a41 9 | 10 | diff --git a/src/main/serialize.c b/src/main/serialize.c 11 | index a389f71311..a190fbf8f3 100644 12 | --- a/src/main/serialize.c 13 | +++ b/src/main/serialize.c 14 | @@ -2650,6 +2650,13 @@ do_serializeToConn(SEXP call, SEXP op, SEXP args, SEXP env) 15 | return R_NilValue; 16 | } 17 | 18 | +static SEXP checkNotPromise(SEXP val) 19 | +{ 20 | + if (TYPEOF(val) == PROMSXP) 21 | + error(_("cannot return a promise (PROMSXP) object")); 22 | + return val; 23 | +} 24 | + 25 | /* unserializeFromConn(conn, hook) used from readRDS(). 26 | It became public in R 2.13.0, and that version added support for 27 | connections internally */ 28 | @@ -2699,7 +2706,7 @@ do_unserializeFromConn(SEXP call, SEXP op, SEXP args, SEXP env) 29 | con->close(con); 30 | UNPROTECT(1); 31 | } 32 | - return ans; 33 | + return checkNotPromise(ans); 34 | } 35 | 36 | /* 37 | @@ -3330,8 +3337,8 @@ attribute_hidden SEXP 38 | do_serialize(SEXP call, SEXP op, SEXP args, SEXP env) 39 | { 40 | checkArity(op, args); 41 | - if (PRIMVAL(op) == 2) return R_unserialize(CAR(args), CADR(args)); 42 | - 43 | + if (PRIMVAL(op) == 2) //return R_unserialize(CAR(args), CADR(args)); 44 | + return checkNotPromise(R_unserialize(CAR(args), CADR(args))); 45 | SEXP object, icon, type, ver, fun; 46 | object = CAR(args); args = CDR(args); 47 | icon = CAR(args); args = CDR(args); 48 | diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd 49 | index 25bdb12..7456662 100644 50 | --- a/doc/NEWS.Rd 51 | +++ b/doc/NEWS.Rd 52 | @@ -7,6 +7,13 @@ 53 | \encoding{UTF-8} 54 | 55 | \section{\Rlogo CHANGES IN R 4.3.3}{ 56 | + \subsection{CHANGES IN R-MINIMAL FROM \url{https://github.com/r-hub/r-minimal}}{ 57 | + \itemize{ 58 | + \item readRDS() and unserialize() now signal an error instead of 59 | + returning a PROMSXP, to fix CVE-2024-27322. 60 | + } 61 | + } 62 | + 63 | \subsection{NEW FEATURES}{ 64 | \itemize{ 65 | \item \code{iconv()} now fixes up variant encoding names such as 66 | -------------------------------------------------------------------------------- /test.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | function setup() { 4 | true 5 | } 6 | 7 | function teardown() { 8 | true 9 | } 10 | 11 | @test "devel (arg)" { 12 | source tools/calculate_tags.sh 13 | run calculate devel 4.0.0 "R Under development (unstable) (2022-03-21 r81954)" 14 | echo "${lines[0]}" 15 | echo "${lines[0]}" | grep -q "Tags to add: devel 4.0.0 4.0 4.0.0-devel 4.0-devel `date -I`$" 16 | } 17 | 18 | @test "devel (detect)" { 19 | source tools/calculate_tags.sh 20 | function get_r_version_number() { echo 4.0.0; } 21 | export -f get_r_version_number 22 | run calculate devel 23 | echo "${lines[0]}" 24 | echo "${lines[0]}" | grep -q "Tags to add: devel 4.0.0 4.0 4.0.0-devel 4.0-devel `date -I`$" 25 | } 26 | 27 | @test "patched (arg)" { 28 | source tools/calculate_tags.sh 29 | run calculate next 4.1.3 "R version 4.1.3 Patched (2022-03-10 r82100)" 30 | echo "${lines[0]}" 31 | echo "${lines[0]}" | grep -q "Tags to add: next patched 4.1.3-patched 4.1-patched$" 32 | } 33 | 34 | @test "patched (detect)" { 35 | source tools/calculate_tags.sh 36 | function get_r_version_number() { 37 | echo 4.1.3; 38 | } 39 | function get_r_version_string() { 40 | echo "R version 4.1.3 Patched (2022-03-10 r82100)" 41 | } 42 | run calculate next 43 | echo "${lines[0]}" 44 | echo "${lines[0]}" | grep -q "Tags to add: next patched 4.1.3-patched 4.1-patched$" 45 | } 46 | 47 | @test "next (arg)" { 48 | source tools/calculate_tags.sh 49 | run calculate next 4.2.0 "R version 4.2.0 alpha (2022-04-03 r82074)" 50 | echo "${lines[0]}" 51 | echo "${lines[0]}" | grep -q "Tags to add: next 4.2.0 4.2 alpha 4.2.0-alpha 4.2-alpha$" 52 | } 53 | 54 | @test "next (detect)" { 55 | source tools/calculate_tags.sh 56 | function get_r_version_number() { 57 | echo 4.2.0; 58 | } 59 | function get_r_version_string() { 60 | echo "R version 4.2.0 alpha (2022-04-03 r82074)"; 61 | } 62 | export -f get_r_version_number 63 | export -f get_r_version_string 64 | run calculate next 65 | echo "${lines[0]}" 66 | echo "${lines[0]}" | grep -q "Tags to add: next 4.2.0 4.2 alpha 4.2.0-alpha 4.2-alpha$" 67 | } 68 | 69 | @test "release (arg)" { 70 | source tools/calculate_tags.sh 71 | function get_r_release_version() { 72 | echo 4.1.3; 73 | } 74 | export -f get_r_release_version 75 | run calculate 4.1.3 4.1.3 76 | echo "${lines[0]}" 77 | echo "${lines[0]}" | grep -q "Tags to add: 4.1.3 4.1 release latest$" 78 | } 79 | 80 | @test "release (detect)" { 81 | source tools/calculate_tags.sh 82 | function get_r_version_number() { 83 | echo 4.1.3; 84 | } 85 | function get_r_release_version() { 86 | echo 4.1.3; 87 | } 88 | export -f get_r_version_number 89 | export -f get_r_release_version 90 | run calculate 4.1.3 91 | echo "${lines[0]}" 92 | echo "${lines[0]}" | grep -q "Tags to add: 4.1.3 4.1 release latest$" 93 | } 94 | 95 | @test "old (arg)" { 96 | source tools/calculate_tags.sh 97 | function get_r_release_version() { 98 | echo 4.1.3; 99 | } 100 | export -f get_r_release_version 101 | run calculate 4.1.2 4.1.2 102 | echo "${lines[0]}" 103 | echo "${lines[0]}" | grep -q "Tags to add: 4.1.2 4.1$" 104 | } 105 | 106 | @test "old (detect)" { 107 | source tools/calculate_tags.sh 108 | function get_r_version_number() { 109 | echo 4.1.2; 110 | } 111 | function get_r_release_version() { 112 | echo 4.1.3; 113 | } 114 | export -f get_r_version_number 115 | export -f get_r_release_version 116 | run calculate 4.1.2 117 | echo "${lines[0]}" 118 | echo "${lines[0]}" | grep -q "Tags to add: 4.1.2 4.1$" 119 | 120 | } 121 | -------------------------------------------------------------------------------- /tools/add-date-tags.R: -------------------------------------------------------------------------------- 1 | 2 | last <- 1 3 | sleep <- 2 4 | 5 | get_builds_page <- function(page) { 6 | pkgs <- gh::gh( 7 | "/orgs/{org}/packages/{package_type}/{package_name}/versions", 8 | org = "r-hub", 9 | package_type = "container", 10 | package_name = URLencode("r-minimal/r-minimal", reserved=TRUE), 11 | per_page = 100, 12 | page = page 13 | ) 14 | rev(pkgs) 15 | } 16 | 17 | tag_pkg <- function(pkg) { 18 | out <- processx::run( 19 | "skopeo", 20 | c("inspect", "--no-tags", "--override-os", "linux", "--override-arch", "amd64", 21 | paste0("docker://ghcr.io/r-hub/r-minimal/r-minimal@", pkg$name) 22 | ) 23 | ) 24 | jout <- jsonlite::fromJSON(out$stdout) 25 | # not a real image, but metadata, nothing to do 26 | if (jout[["Architecture"]] == "unknown") return() 27 | 28 | message("PACKAGE ", pkg$name) 29 | verout <- processx::run( 30 | "docker", 31 | c("run", paste0("ghcr.io/r-hub/r-minimal/r-minimal@", pkg$name), 32 | "R", "-q", "--slave", "-e", "cat(R.Version()$version.string)" 33 | ) 34 | ) 35 | if (!grepl("development", verout$stdout)) return() 36 | 37 | date <- rematch2::re_match( 38 | verout$stdout, 39 | "[0-9][0-9][0-9][0-9][-][0-9][0-9][-][0-9][0-9]" 40 | )[[".match"]] 41 | 42 | if (is.na(date)) stop("Could not find date in version string") 43 | 44 | message(" -> ", date) 45 | processx::run( 46 | "regctl", 47 | c("image", "copy", 48 | paste0("ghcr.io/r-hub/r-minimal/r-minimal@", pkg$name), 49 | paste0("ghcr.io/r-hub/r-minimal/r-minimal:", date) 50 | ), 51 | echo = TRUE 52 | ) 53 | } 54 | 55 | tag_builds_page <- function(page) { 56 | message("PAGE ", page) 57 | pkgs <- get_builds_page(page) 58 | for (idx in seq_along(pkgs)) { 59 | pkg <- pkgs[[idx]] 60 | message("[", idx, "/", length(pkgs), "] ", appendLF = FALSE) 61 | tag_pkg(pkg) 62 | Sys.sleep(sleep) 63 | } 64 | } 65 | 66 | fill_in_gaps <- function() { 67 | begin <- as.Date("2021-07-25") 68 | end <- Sys.Date() 69 | dts <- rev(as.character(seq(begin, end, by = 1))) 70 | out <- processx::run( 71 | "regctl", 72 | c("tag", "ls", "ghcr.io/r-hub/r-minimal/r-minimal") 73 | )$stdout 74 | alltags <- rev(strsplit(out, "\n", fixed = TRUE)[[1]]) 75 | dtags <- grep( 76 | "^[0-9][0-9][0-9][0-9][-][0-9][0-9][-][0-9][0-9]$", 77 | alltags, 78 | value = TRUE 79 | ) 80 | last <- dtags[1] 81 | for (day in dts) { 82 | if (day %in% dtags) { 83 | last <- day 84 | } else { 85 | processx::run( 86 | "regctl", 87 | c("image", "copy", 88 | paste0("ghcr.io/r-hub/r-minimal/r-minimal:", last), 89 | paste0("ghcr.io/r-hub/r-minimal/r-minimal:", day) 90 | ), 91 | echo = TRUE 92 | ) 93 | } 94 | } 95 | } 96 | 97 | add_date_tags_main <- function() { 98 | for (page in last:1) { 99 | tag_builds_page(page) 100 | processx::run("docker", c("system", "prune", "-f")) 101 | } 102 | fill_in_gaps() 103 | } 104 | 105 | if (is.null(sys.calls())) { 106 | add_date_tags_main() 107 | } 108 | -------------------------------------------------------------------------------- /tools/calculate_tags.R: -------------------------------------------------------------------------------- 1 | 2 | docker <- function(cmd, args) { 3 | system2("docker", c(cmd, args), stdout = TRUE) 4 | } 5 | 6 | get_rver <- function(ver) { 7 | args <- c( 8 | "--rm", "-v", "`pwd`/tools:/tmp/tools", 9 | paste0("ghcr.io/r-hub/r-minimal/r-minimal:", ver), 10 | "R", "--slave", "-e", "\"cat(format(getRversion()))\"" 11 | ) 12 | docker("run", args) 13 | } 14 | 15 | get_rstr <- function(ver) { 16 | args <- c( 17 | "--rm", "-v", "`pwd`/tools:/tmp/tools", 18 | paste0("ghcr.io/r-hub/r-minimal/r-minimal:", ver), 19 | "R", "--slave", "-e", "\"cat(R.version.string)\"" 20 | ) 21 | docker("run", args) 22 | } 23 | 24 | get_tags <- function(ver) { 25 | args <- c( 26 | "--rm", "-v", "`pwd`/tools:/tmp/tools", 27 | paste0("ghcr.io/r-hub/r-minimal/r-minimal:", ver), 28 | "/tmp/tools/readme_tags.sh", ver 29 | ) 30 | strsplit(docker("run", args), " ", fixed = TRUE)[[1]] 31 | } 32 | 33 | rv <- function(ver) { 34 | rver <- get_rver(ver) 35 | if (ver == "devel") { 36 | paste0(rver, "-devel") 37 | } else if (ver == "next") { 38 | rstr <- get_rstr(ver) 39 | rstr <- sub("^R version ", "", rstr) 40 | rstr <- sub(" [(].*$", "", rstr) 41 | rstr <- sub(" ", "-", rstr) 42 | rstr 43 | } else { 44 | rver 45 | } 46 | } 47 | 48 | rv_tags <- function(ver) { 49 | tags <- get_tags(ver) 50 | if (ver == "release") tags <- unique(c(tags, "release", "latest")) 51 | paste(paste0("`", tags, "`"), collapse = ", ") 52 | } 53 | -------------------------------------------------------------------------------- /tools/calculate_tags.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | sourced=0 4 | if [ -n "$ZSH_EVAL_CONTEXT" ]; then 5 | case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac 6 | elif [ -n "$KSH_VERSION" ]; then 7 | [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1 8 | elif [ -n "$BASH_VERSION" ]; then 9 | (return 0 2>/dev/null) && sourced=1 10 | else 11 | # All other shells: examine $0 for known shell binary filenames 12 | # Detects `sh` and `dash`; add additional shell filenames as needed. 13 | case ${0##*/} in sh|dash) sourced=1;; esac 14 | fi 15 | 16 | # Potential tags: 17 | # - yyyy-mm-dd 18 | # - devel, x.y.z-devel x.y-devel 19 | # current `trunk` in the R repo 20 | # - alpha, x.y.z-alpha, x.y-alpha 21 | # current alpha 22 | # - beta, x.y.z-beta, x.y-beta 23 | # current beta 24 | # - rc, x.y.z-rc, x.y-rc 25 | # current rc 26 | # - next 27 | # the next version of R. This can be patched, alpha, beta, rc or prerelease 28 | # - patched, x.y.z-patched, x.y-patched 29 | # current patched branch 30 | # - release, latest 31 | # always the latest release 32 | # - x.y.z 33 | # - x.y 34 | 35 | # This is how we assign tags for the various versions: 36 | # 37 | # ## devel 38 | # - yyyy-mm-dd 39 | # - devel 40 | # - x.y.z-devel 41 | # - x.y.z 42 | # - x.y-devel 43 | # - x.y 44 | # 45 | # ## next 46 | # - next 47 | # - patched (if it is patched) 48 | # - x.y.z-patched (if it is) 49 | # - x.y-patched (if it is) 50 | # - alpha (if it is) 51 | # - x.y.z-alpha (if it is) 52 | # - x.y-alpha (if it is) 53 | # - beta (if it is) 54 | # - x.y.z-beta (if it is) 55 | # - x.y-beta (if it is) 56 | # - rc (if it is) 57 | # - x.y.z-rc (if it is) 58 | # - x.y-rc (if it is) 59 | # - x.y.z (unless it is patched) 60 | # - x.y (unless it is patched) 61 | # 62 | # ## x.y.z 63 | # - x.y.z 64 | # - x.y 65 | # - release (if it is) 66 | # - latest (if it is) 67 | 68 | function prefix_tags() { 69 | local tags=$1 70 | tags1=$(echo $tags | tr ' ' '\n' | sed -e 's|^|rhub/r-minimal:|g' | tr '\n' ',') 71 | tags2=$(echo $tags | tr ' ' '\n' | sed -e 's|^|ghcr.io/r-hub/r-minimal/r-minimal:|g' | tr '\n' ',') 72 | if [[ -n "$tags1" && -n "$tags2" ]]; then 73 | echo "$tags1,$tags2" 74 | elif [[ -n "$tags1" ]]; then 75 | echo "$tags1" 76 | else 77 | echo "$tags2" 78 | fi | sed 's/,,/,/g' | sed 's/,$//' 79 | } 80 | 81 | function get_r_version_number() { 82 | R --slave -e 'cat(format(getRversion()))' 83 | } 84 | 85 | function get_r_version_string() { 86 | R --slave -e 'cat(R.version$version.string)' 87 | } 88 | 89 | function get_r_release_version() { 90 | release=$(wget -q -O- https://api.r-hub.io/rversions/r-release) 91 | echo $release | 92 | sed 's/^.*"version"\s*:\s*"//' | 93 | sed 's/".*$//' 94 | } 95 | 96 | function calculate_raw() { 97 | r_version=${1} 98 | r_version_number=${2} 99 | vstr=${3} 100 | if [[ -z "$r_version" ]]; then 101 | >&2 echo "Version is not specified" 102 | exit 1; 103 | fi 104 | 105 | if [[ -z "$r_version_number" ]]; then 106 | r_version_number=$(get_r_version_number) 107 | fi 108 | r_minor=$(echo ${r_version_number} | sed 's/[.][0-9][0-9]*$//') 109 | 110 | if [[ -z "$vstr" ]]; then 111 | vstr=$(get_r_version_string) 112 | fi 113 | 114 | tags="" 115 | if [[ "${r_version}" = "devel" ]]; then 116 | tags="devel ${r_version_number} ${r_minor} ${r_version_number}-devel ${r_minor}-devel `date -I`" 117 | 118 | elif [[ "${r_version}" = "next" ]]; then 119 | tags="next ${r_version_number} ${r_minor}" 120 | if echo $vstr | grep -q '[Pp]atched'; then 121 | # drop the version number tags here, those are for the release 122 | tags="next patched ${r_version_number}-patched ${r_minor}-patched" 123 | elif echo $vstr | grep -q '[Aa]lpha'; then 124 | tags="$tags alpha ${r_version_number}-alpha ${r_minor}-alpha" 125 | elif echo $vstr | grep -q '[Bb]eta'; then 126 | tags="$tags beta ${r_version_number}-beta ${r_minor}-beta" 127 | elif echo $vstr | grep -q '[Rr][Cc]'; then 128 | tags="$tags rc ${r_version_number}-rc ${r_minor}-rc" 129 | fi 130 | else 131 | tags="${r_version_number} ${r_minor}" 132 | relver=$(get_r_release_version) 133 | if [[ "$relver" == "$r_version_number" ]]; then 134 | tags="$tags release latest" 135 | fi 136 | fi 137 | echo "$tags" 138 | } 139 | 140 | function calculate() { 141 | tags=$(calculate_raw "$@") 142 | echo "Tags to add: $tags" 143 | ftags="$(prefix_tags "$tags")" 144 | echo "::set-output name=tags::${ftags}" 145 | } 146 | 147 | function main() { 148 | calculate $@ 149 | } 150 | 151 | if [ "$sourced" = "0" ]; then 152 | main "$*" 153 | fi 154 | -------------------------------------------------------------------------------- /tools/readme_tags.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | SCRIPT_DIR=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) 4 | source ${SCRIPT_DIR}/calculate_tags.sh 5 | 6 | sourced=0 7 | if [ -n "$ZSH_EVAL_CONTEXT" ]; then 8 | case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac 9 | elif [ -n "$KSH_VERSION" ]; then 10 | [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1 11 | elif [ -n "$BASH_VERSION" ]; then 12 | (return 0 2>/dev/null) && sourced=1 13 | else 14 | # All other shells: examine $0 for known shell binary filenames 15 | # Detects `sh` and `dash`; add additional shell filenames as needed. 16 | case ${0##*/} in sh|dash) sourced=1;; esac 17 | fi 18 | 19 | function main() { 20 | tags=$(calculate_raw "$@") 21 | echo "$tags" 22 | } 23 | 24 | if [ "$sourced" = "0" ]; then 25 | main "$@" 26 | fi 27 | --------------------------------------------------------------------------------