In this post I show how to use GitHub Actions for R packages on the four most popular BSD systems: FreeBSD, OpenBSD, NetBSD, and DragonFly BSD.
Infrastructure
The pieces:
- Standard Ubuntu GHA runner.
- I wrote setup actions for each OS, and included them in
r-hub/actions
. - I used the awesome VM Actions project, which uses virt-manager, libvirt & QEMU to create and manage BSD virtual machines.
- For each OS, I wrote a custom GHA
Rscript
shell. - The usual
r-lib/actions
actions for R packages and projects, to install package dependencies, runR CMD check
, etc.
I include the important details for each piece below.
Setup actions
I wrote four setup actions, one for each OS. They are at r-hub/actions
and they are called
r-hub/actions/setup-r-*bsd
, e.g. for FreeBSD it is
r-hub/actions/setup-r-freebsd
, etc. These actions are wrappers on the
vmactions/*bsd-vm
actions, and perform
some additional steps to be able to run R reliably. This is what they do:
- Install and configure R.
- Install pak. (pak has now builds for FreeBSD, OpenBSD, NetBSD and DragonFly BSD.)
- Set up pak for parallel package builds.
- Set up
R_LIBS_USER
to be “shared” between the runner and the BSD VM, such thatr-lib/actions/setup-r-dependencies
can cache the R packages. - Install system packages to be able to build the Tidyverse.
- Install a custom
Rscript
shell on the runner, that runs an R script on the BSD VM. See below.
Custom shells
To be able to run the r-lib/actions on
the BSD VM, I created a custom GHA shell called Rscript
, which is
automatically picked up by these actions. This lets me run the usual
setup-r-dependencies
and
check-r-package
actions on
*BSD, after some minor tweaks.
The four custom shells for the four BSD systems only have minor differences, it would probably make sense to unify them in a single script in the future. I include the one for FreeBSD here as an example.
1 |
|
I create files for $GITHUB_ENV
and $GITHUB_OUT
on the VM first, so that
actions running on the VM will be able to set environment variables and
produce output.
9 | # copy files to VM |
Then I copy the workspace to the VM. It is important to synchronize the
workspace files before and after and Rscript
command, otherwise caching
does not work properly.
The VM Actions also support a shared sshfs
file system, but I found that there are at least two issues with this. The
first is that I would need to map user and group ids between the runner and
the VM manually, and this seems cumbersome. The second is that on some OSes
it simply fails and I can’t access the mounted files on the BSD VM.
So it is simpler to use rsync to explicitly copy everything under
$GITHUB_WORKSPACE
back and forth. This is fast and works well.
Next I run the R script on the VM, after forwarding and setting some environment variables:
13 | # GITHUB_* is automatically forwarded, as is LC_* (!) |
Ideally, I would also forward every environment variable that was set via
$GITHUB_ENV
or directly from the workflow file. I am planning to do that
in the future.
ssh
does not have an option for setting the working directory, so I set
the working directory explicitly to $GITHUB_WORKSPACE
. I also set
R_PKG_CACHE_DIR
for pak and XDG_CACHE_HOME
for R packages that use a
persistent configuration or cache directory. R_LIB_FOR_PAK
is needed for
the r-lib/actions/setup-r-dependencies@v2
action.
21 | # copy files back from VM |
After the R script has finished I copy back $GITHUB_WORKSPACE
from the VM.
25 | scp freebsd:/tmp/${env_file} /tmp/${env_file} || true |
Finally, I also copy back $GITHUB_ENV
and $GITHUB_OUT
and append them
to the runner’s real $GITHUB_ENV
and $GITHUB_OUT
files.
At the end Rscript
must exit with the original exit status of the R
script on the BSD VM.
The Rscript
shells of the other BSD systems are very similar, they are
in the r-hub/actions
repo.
Example workflows
Using the setup actions, I can now create workflows to run R CMD check
on R packages on BSD systems. Again, I include the workflow for FreeBSD.
The others are very similar and they are part of the r-hub/actions
repo.
1 | on: |
It is handy to be able to select the FreeBSD release when triggering the workflow manually.
20 | name: freebsd.yaml |
I need to run this on ubuntu-latest
for the VM to work correctly.
After calling the r-hub/actions/setup-r-freebsd
action, I can use
r-hub/actions/platform-info
, which will pick up the special Rscript
shell to run its R code in the FreeBSD VM.
32 | - uses: r-lib/actions/setup-r-dependencies@v2 |
I can reuse the r-lib/actions/setup-r-dependencies
action as well, to
install dependencies, because that uses the Rscript
shell as well, and
the custom Rscript
shell will install the packages in the FreeBSD VM.
It’ll also make sure that the workspace (as in $GITHUB_WORKSPACE
) is
synchronized between the Ubuntu host and the FreeBSD VM, both before and
after running the R script. Synchronization ensures that caching the R
package library works seamlessly in r-lib/actions/setup-r-dependencies
.
I set some extra parameters:
pak-version: none
because pak is already installed, pak now has binary builds for FreeBSD (and the other three *BSD systems as well), so this is not strictly necessary,install-pandoc: false
andinstall-quarto: false
because the actionssetup-r-dependencies
uses to install Pandoc and Quarto do not work on *BSD.
40 | - uses: r-lib/actions/check-r-package@v2 |
I can also reuse r-lib/actions/check-r-package
to run the package check.
Uploading the snapshots and check failures as artifacts works because of
the $GITHUB_WORKSPACE
synchronization.
Interactive debugging
The r-hub/actions/platform-info@v1
action currently automatically uses
r-hub/actions/debug-shell@v1
, which uses
tmate to start a tmate session on the GHA VM, if two conditions are true:
- the workflow run is a re-run, and
- debug logging is turned on.
This means that if a workflow run fails, then I can request a re-run with debug logging from the web UI, and get an interactive shell to the VM. This works on Linux, macOS and Windows runners, but there is an extra step for *BSD because R is running in a QEMU VM. After connecting to the GHA VM, I need to further use ssh to log in to the FreeBSD VM:
1 | ssh freebsd |
The VM’s name matches the OS’s name, so on OpenBSD, this would be
ssh openbsd
, etc. When using ssh this was, $GITHUB_WORKSPACE
is
not synchronized! I can also call the custom Rscript
shell on the
runner, which will also synchronize $GITHUB_WORKSPACE
.
Future improvements
This system is still new, and I am sure that I’ll need many improvements.
Environment variables
An obvious improvement would be to pick up the environment variables from
the env
context, and forward them to the VM. All environment variables
that start with GITHUB_
are automatically forwarded to the VM via the
ssh configuration on the runner, so if I can choose the name of the env
var myself, then I can pick a name that’ll be forwarded.
Links and acknowledgements
- Almost all the hard work that made this possible was done in the VM Actions project.
- The setup actions are at
r-hub/actions
, with example workflows for each BSD OS. - See
r-lib/actions
for actions for R projects and packages.