R-Package GitHub Actions via {usethis} and r-lib

The Actions tab of the a GitHub repository showing successful tests with tick marks next to them.

A GitHub Action in action on GitHub.

tl;dr

You can trigger GitHub Actions to build and test your R package after a push or pull request. Create .github/workflows/ in your repo and add pre-prepared actions by the r-lib team with usethis::use_github_action().

Shortcut

I refer back to this post a lot, so here’s some jump-links to the sections with the code I need:

  1. Build check
  2. Test coverage
  3. Build {pkgdown} site

Otherwise read on for a more thorough explanation of GitHub Actions in the context of R packages.

Lights, camera…

GitHub Actions is a service that can be triggered to run workflows that build, test and deploy your code on GitHub. In other words, a continuous integration platform baked right into GitHub.

Before you start, I recommend checking out Jim Hester’s talk from rstudio::conf 2020 and reading the GitHub Actions with R book.

GitHub Actions can be really helpful for developing R packages.1 For example, you can trigger actions with a push or pull request (PR) that:

Checking the build and coverage are standard practices for package development. They help ensure that your package works and is stable. GitHub Actions provides the icing on the cake by doing these things automatically.

These are all important for users of your package too. Build and coverage results show the robustness of the code and a website makes your documentation much easier to access.

I wrote this post to remind me how to do it.

…Actions

How are actions stored, recognised and triggered?

Actions are expressed in YAML script files that read like a recipe for what to run and when to run it. You put these files in your repo at the path .github/workflows/, where GitHub recognises them. The information is interpreted and the actions are run remotely given the specified trigger.

You can learn more about the content of these YAML files from the GitHub actions with R book.

You could set these up manually, but actually you can shortcut the process with the {usethis} package and some pre-written examples.

{usethis} and r-lib

{usethis} helps to shortcut the tedious setup steps of R packages and projects. It also includes functions to add GitHub Actions to your R package for you.

In particular, usethis::use_github_action() will add a YAML file to .github/workflows/ where GitHub Actions will find it; you just supply the name of a pre-written action.

Where do these pre-written actions come from? Well, the kind folks at r-lib have made a repo of R-focused examples that you can use.

Example: {r2eng} package

I recently used this method to set up GitHub Actions for the in-development {r2eng} package.

{r2eng} has three actions in the workflow folder:

  1. R-CMD-check.yaml (see the YAML file) to run a build check
  2. test-coverage.yaml (YAML) to assess how much of the code is protected by testing
  3. pkgdown.yaml (YAML) to build the package’s website with {pkgdown}

This is a typical, minimal set of actions that suit me when developing R packages. Let’s talk them through.

1. Build check

An R CMD check2 runs a bunch of tests on your package (including your own unit tests) and returns errors, notes and warnings. You’re aiming for a passing build to prove the package is up to scratch.

{usethis} has three actions-related functions specifically for setting up the build check. The standard one will run the R CMD check on macOS, Linux and Windows to make sure it passes across all these platforms.3

Run this line to add the R-CMD-check.yaml action to the .github/workflows/ folder:

usethis::use_github_action_check_standard()

Note that this function will create .github/workflows/ if it doesn’t already exist.

Folllowing a push or PR, GitHub Actions will now automatically set up and run a build check on each OS to make sure the package meets the requirements.

2. Test coverage

The R CMD check runs your unit tests, but it doesn’t calculate how much of your code is actually covered by testing. Ideally you want this to be 100%, but also bear in mind that the metric doesn’t take account of the volume or quality of tests.

I use another r-lib package, {covr}, to interactively check how much of my code is tested. (In particular, the covr::report() function provides an interactive HTML report showing the total percentage and a line-by-line breakdown of where tests are missing.)

You can set up the free services Codecov or Coveralls to make your results public. You’ll need to have signed up for these services and granted their access to the repo you want to report on.

{usethis} makes it easy to set up these services for your repo: it adds the relevant YAML files, a line to the ‘Suggests’ section of your DESCRIPTION, and a badge to your README.

usethis::use_coverage("codecov")

You can see an example in action on the Codecov page for {r2eng}, which shows the percentage of coverage, a breakdown of the lines ‘hit’ and ‘missed’, and the commits that led to checks.

Of course, you can automate this. Run this line to add the test-coverage.yaml action to the .github/workflows/ folder

usethis::use_github_action("test-coverage")

The ‘test-coverage’ GitHub Action will recheck coverage when you next push to the repo, with the results being updated on your coverage service of choice.

3. Build {pkgdown} site

{pkgdown}, also from r-lib, can automatically and painlessly generate a simple website from your package’s documentation, which you are free to customise. You can serve the site on the web via GitHub Pages so users can access the docs easily online.

For example, here’s the {pkgdown} website for the {r2eng} package, which uses default settings at time of writing. You can see that the README has become the home page and there are ‘Reference’ and ‘Changelog’ tabs that autopoulate with the function documentation and NEWS file. Additional tabs are added here depending on the contents of your repo; for example, vignettes are added to an ‘Articles’ tab if they exist.

The GitHub Actions with R book has a section on {pkgdown}. In short, the steps are:

  1. Set-up an empty ‘gh-pages’ branch in your repo (the book has some code to do this from the command line)4
  2. Back in the main branch, run the {usethis} usethis::use_pkgdown() to activate {pkgdown} for your package repo
  3. Run usethis::use_github_action("pkgdown") to add the YAML file that tells GitHub Actions to build the website on push
  4. Push to your repo and GitHub Actions will generate the website files in the gh-pages branch
  5. From your repo settings, set GitHub Pages to serve from the root of the gh-pages branch
  6. Wait a few minutes and navigate to your site (in the form ‘username.github.io/reponame’)

GitHub Actions will now rebuild the site automatically every time you make changes and push them.

Tickety-boo

You’ll get the full results of the actions in the ‘Actions’ tab of your repo on GitHub. A successful check gets a satisfying tick next it. A failing test gets a cross. You can select a result and expand the results to trace exactly what the error was.

The Actions tab of the a GitHub repository showing successful tests with tick marks next to them.

Successful builds in the ‘pkgdown’ workflow.

This is handy because you and your users can check the results of your checks from the ‘Actions’ tab of you repo without leaving GitHub.

It also means you can spot a failing PR and provide more commits to fix it before it gets merged.

A GitHub pull request showing the successful results of each test run with GitHub Actions.

Ticks! Ticks! Ticks!

You can also generate Markdown badges5 for your README that display the results of these actions and automatically update when they’re re-run. These are great for an at-a-glance understanding of a package’s development state. {usethis} adds these to your README automatically, but it’s useful to know that you can get these badges from GitHub itself.

GitHub's 'create status badge' tool showing a badge for the R CMD check and the Markdown needed to reproduce it.

More easily obtained than having to defeat a Pokémon gym leader.

For example, you can see the badges in the {r2eng} README, showing the check status and percentage of test coverage:

R build status codecov

Clicking them takes you to the relevant page for the full breakdown of results (the ‘Actions’ tab for the build check and codecov.io for the coverage).

Other platforms are available

So, I think a combo of {usethis} and r-lib’s pre-prepared YAML files is the simplest route to auto-checking your R package and rebuilding its site.

There are many other YAML examples from r-lib though, and you can write your own. There’s also an ‘awesome list’ of more general-purpose actions to explore.

It’s important to note that there are several other platforms for continuous integration, like Travis CI and Appveyor (see Roger Peng’s book for an overview), but this requires you to setup multiple accounts and configuration files. At time of writing, GitHub Actions has the benefit of testing across all the major operating systems and is easier to set up (learn more in Jim Hester’s talk).


Session info
## ─ Session info ───────────────────────────────────────────────────────────────
##  setting  value
##  version  R version 4.2.0 (2022-04-22)
##  os       macOS Big Sur/Monterey 10.16
##  system   x86_64, darwin17.0
##  ui       X11
##  language (EN)
##  collate  en_GB.UTF-8
##  ctype    en_GB.UTF-8
##  tz       Europe/London
##  date     2022-12-11
##  pandoc   2.18 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
## 
## ─ Packages ───────────────────────────────────────────────────────────────────
##  package     * version date (UTC) lib source
##  blogdown      1.9     2022-03-28 [1] CRAN (R 4.2.0)
##  bookdown      0.26    2022-04-15 [1] CRAN (R 4.2.0)
##  bslib         0.3.1   2021-10-06 [1] CRAN (R 4.2.0)
##  cli           3.3.0   2022-04-25 [1] CRAN (R 4.2.0)
##  digest        0.6.29  2021-12-01 [1] CRAN (R 4.2.0)
##  evaluate      0.15    2022-02-18 [1] CRAN (R 4.2.0)
##  fastmap       1.1.0   2021-01-25 [1] CRAN (R 4.2.0)
##  htmltools     0.5.2   2021-08-25 [1] CRAN (R 4.2.0)
##  jquerylib     0.1.4   2021-04-26 [1] CRAN (R 4.2.0)
##  jsonlite      1.8.0   2022-02-22 [1] CRAN (R 4.2.0)
##  knitr         1.39    2022-04-26 [1] CRAN (R 4.2.0)
##  magrittr      2.0.3   2022-03-30 [1] CRAN (R 4.2.0)
##  R6            2.5.1   2021-08-19 [1] CRAN (R 4.2.0)
##  rlang         1.0.2   2022-03-04 [1] CRAN (R 4.2.0)
##  rmarkdown     2.14    2022-04-25 [1] CRAN (R 4.2.0)
##  rstudioapi    0.14    2022-08-22 [1] CRAN (R 4.2.0)
##  sass          0.4.1   2022-03-23 [1] CRAN (R 4.2.0)
##  sessioninfo   1.2.2   2021-12-06 [1] CRAN (R 4.2.0)
##  stringi       1.7.6   2021-11-29 [1] CRAN (R 4.2.0)
##  stringr       1.4.0   2019-02-10 [1] CRAN (R 4.2.0)
##  xfun          0.30    2022-03-02 [1] CRAN (R 4.2.0)
##  yaml          2.3.5   2022-02-21 [1] CRAN (R 4.2.0)
## 
##  [1] /Library/Frameworks/R.framework/Versions/4.2/Resources/library
## 
## ──────────────────────────────────────────────────────────────────────────────

  1. You can do other interesting things with it, like run code on schedule. I used GitHub Actions to automate the posting of tweets to a Twitter bot account, @londonmapbot, for example.↩︎

  2. Learn more about checks from Hadley Wickham and Karl Broman.↩︎

  3. The other two functions test on macOS only (use_github_action_check_release()) and on all three operating systems and also on some minor R releases too (use_github_action_check_full()), though the latter is considered ‘overkill’ according to the documentation.↩︎

  4. For posterity, and in case the book ever disappears, the code that creates an empty ‘gh-pages’ branch from the command line is like:

    git checkout --orphan gh-pages
    git rm -rf .
    git commit --allow-empty -m 'Initial gh-pages commit'
    git push origin gh-pages
    git checkout main
    ↩︎
  5. I showed how to create these sorts of badges in an earlier blog post: ‘Make a README badge with {badgr}’.↩︎