Created
March 1, 2021 21:28
-
-
Save jimhester/3452d4bd9f7ecaf46cfcd47e872e3e8e to your computer and use it in GitHub Desktop.
Highly annotated R GitHub Actions workflow
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# We want to run this on all pushes on the 'main' branch, whether it is called main or master. | |
on: | |
push: | |
branches: | |
- main | |
- master | |
# We also want to run it on pull requests targetting the 'main' branch | |
pull_request: | |
branches: | |
- main | |
- master | |
# This is the overall workflow name, which shows up in the log | |
name: R-CMD-check | |
jobs: | |
# This can be called anything, but I think it is generally better practice to keep it the same as the overall workflow name | |
R-CMD-check: | |
# This specifies the operating system that the job will run on, here we look this up based on the matrix config below | |
runs-on: ${{ matrix.config.os }} | |
# We name the individual job with the operating system and the version of R we are running against | |
name: ${{ matrix.config.os }} (${{ matrix.config.r }}) | |
strategy: | |
# fail-fast: true is the default, but this means the whole workflow fails as soon as one job fails, which is often not what we want. | |
fail-fast: false | |
# Here we specify the build matrix, all the possibilities we want to consider | |
matrix: | |
# This name could be anything, config is just generic | |
config: | |
# We test macOS only on the release version, as it always has binaries there | |
- {os: macOS-latest, r: 'release'} | |
# We test windows only on release and 3.6, because 3.6 uses the 'old' RTools toolchain and R versions 4.0+ use the new one | |
- {os: windows-latest, r: 'release'} | |
- {os: windows-latest, r: '3.6'} | |
# We test R devel on linux using RStudio Package Manager. We need to set the user agent to R 4.0 so that RStudio Package Manager will serve the 4.0 binaries for R devel (currently 4.1). | |
# This will work as long as R-devel does not break API compatibility with the release version of R. | |
- {os: ubuntu-18.04, r: 'devel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/bionic/latest", http-user-agent: "R/4.0.0 (ubuntu-18.04) R (4.0.0 x86_64-pc-linux-gnu x86_64 linux-gnu) on GitHub Actions" } | |
# We set rspm here so we can have package binaries for all the linux builds. No there is not a way to inherit matrix values to avoid the repetition. | |
- {os: ubuntu-18.04, r: 'release', rspm: "https://packagemanager.rstudio.com/cran/__linux__/bionic/latest"} | |
- {os: ubuntu-18.04, r: 'oldrel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/bionic/latest"} | |
- {os: ubuntu-18.04, r: '3.5', rspm: "https://packagemanager.rstudio.com/cran/__linux__/bionic/latest"} | |
- {os: ubuntu-18.04, r: '3.4', rspm: "https://packagemanager.rstudio.com/cran/__linux__/bionic/latest"} | |
- {os: ubuntu-18.04, r: '3.3', rspm: "https://packagemanager.rstudio.com/cran/__linux__/bionic/latest"} | |
env: | |
# pak queries the RSPM environment when it retrieves the system requirements below | |
RSPM: ${{ matrix.config.rspm }} | |
# We set the GITHUB_PAT (which pak uses) to be the automatically available GITHUB_TOKEN, this increases the API limits when querying the GitHub API | |
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} | |
steps: | |
# The checkout action checks out a repository. v2 is the latest tag | |
- uses: actions/checkout@v2 | |
# This action installs and sets up R | |
- uses: r-lib/actions/setup-r@v1 | |
# We need to set the id for this step because we use an output for it in the cache step below | |
id: install-r | |
with: | |
# We pass the r-version and user agents we want to install and setup here, based on the matrix above | |
r-version: ${{ matrix.config.r }} | |
# The user agent is set by putting an option in `~/.Rprofile` in the setup code | |
http-user-agent: ${{ matrix.config.http-user-agent }} | |
# We install pandoc with this step | |
- uses: r-lib/actions/setup-pandoc@v1 | |
- name: Install pak and query dependencies | |
run: | | |
# We install the dev version of pak with this step. It contains all of its dependencies automatically | |
install.packages("pak", repos = "https://r-lib.github.io/p/pak/dev/") | |
# We need to query the package dependency versions and save them to a file, which we put in the `.github` folder, so it is automatically excluded from the built R package. | |
# We can then use this file as a key when caching, so if the dependencies have not changed we can just reuse the previously installed packages. | |
saveRDS(pak::pkg_deps("local::.", dependencies = TRUE), ".github/r-depends.rds") | |
shell: Rscript {0} | |
- name: Cache R packages | |
uses: actions/cache@v2 | |
with: | |
# Where the files we are caching live, e.g. the R library | |
path: ${{ env.R_LIBS_USER }} | |
# The key is the OS we are running on, the actually installed R version (rather than 'release', which could change when new R versions are released), | |
# -1-, which is just a version we can bump if we need to invalidate the cache, and our dependency versions we stored in the previous step. | |
# hashFiles() computes a hash based on the contents of the file, in this case the serialized R dependency information | |
key: ${{ matrix.config.os }}-${{ steps.install-r.outputs.installed-r-version }}-1-${{ hashFiles('.github/r-depends.rds') }} | |
# The restore key specifies where you want to load the cache from if a cache is _not_ found, in this case we load the most recent cache that matches all of the key parts _except_ the last part. | |
restore-keys: ${{ matrix.config.os }}-${{ steps.install-r.outputs.installed-r-version }}-1- | |
- name: Install system dependencies | |
# Install system dependencies, we only do this automatically on linux, Windows and macOS usually don't need to explicitly install system dependencies. | |
if: runner.os == 'Linux' | |
run: | | |
# local_system_requirements() queries RSPM for the commands needed to install the system requirements for the package and its dependenices and execute = TRUE runs them automatically. | |
pak::local_system_requirements(execute = TRUE) | |
# We also need to install the system requirements for the rcmdcheck package | |
pak::pkg_system_requirements("rcmdcheck", execute = TRUE) | |
shell: Rscript {0} | |
- name: Install dependencies | |
run: | | |
# Actually install the package dependencies, upgrading them if there are older versions in the cache. | |
# If you have _newer_ versions of packages in the cache they won't be downgraded, so you would have to bump the cache version in this case. | |
pak::local_install_dev_deps(upgrade = TRUE) | |
# install the rcmdcheck package, this won't do anything if the package is already in the cache. | |
pak::pkg_install("rcmdcheck") | |
shell: Rscript {0} | |
- name: Session info | |
run: | | |
# We use a little bit larger width for this, as the default of 80 makes the output wrap badly | |
options(width = 100) | |
# Get all the packages we installed in the last step | |
pkgs <- installed.packages()[, "Package"] | |
# Print their versions | |
sessioninfo::session_info(pkgs, include_base = TRUE) | |
shell: Rscript {0} | |
- name: Check | |
env: | |
_R_CHECK_CRAN_INCOMING_: false | |
run: | | |
# We explicitly turn on colors for this, as the auto-detection doesn't detect the actions log as color enabled. | |
options(crayon.enabled = TRUE) | |
# We don't build a manual (as that requires the full LaTeX stack, and use --as-cran to turn on CRAN's normal checks) | |
# We want to throw a full R error (and fail the GitHub Actions build) if there is a warning or an error from R CMD check | |
# And we want to output the checks into the 'check' directory | |
rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check") | |
shell: Rscript {0} | |
- name: Show testthat output | |
# Originally this was only in if: failure(), but we changed it to always output, this line could potentially be removed. | |
if: always() | |
# Find any testthat.Rout or testthat.Rout.fail files and output them | |
# Sometimes this is useful to see to make sure the tests you think are running have actually run. | |
run: find check -name 'testthat.Rout*' -exec cat '{}' \; || true | |
shell: bash | |
# If there are any failures, upload the results in the 'check' directory as an artifact so they can be inspected. | |
- name: Upload check results | |
if: failure() | |
uses: actions/upload-artifact@main | |
with: | |
name: ${{ matrix.config.os }}-r${{ matrix.config.r }}-results | |
path: check |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment