Skip to content

R

R #457

Workflow file for this run

---
# title: 'Github Actions workflow to check, build, and release R packages'
# author:
# - Hamada S. Badr:
# email: badr@jhu.edu
# institute: Johns Hopkins University
# correspondence: true
# date: "23 April 2024"
# yamllint disable rule:line-length
name: R
'on':
push:
branches:
- '*'
tags:
- '*'
pull_request:
branches:
- '*'
schedule:
- cron: '0 12 * * 1'
workflow_dispatch:
inputs:
release:
description: 'Create a new release?'
required: false
default: false
revdep:
description: 'Check reverse dependencies?'
required: false
default: false
jobs:
initiate:
name: Initiate R workflow
if: "! contains(github.event.head_commit.message, '[ci skip]')"
runs-on: Ubuntu-22.04
timeout-minutes: 360
outputs:
pkg_name: ${{ steps.pkg_info.outputs.pkg_name }}
pkg_version: ${{ steps.pkg_info.outputs.pkg_version }}
pkg_sha: ${{ steps.pkg_info.outputs.pkg_sha }}
r_devel: ${{ steps.r_info.outputs.r_devel }}
r_release: ${{ steps.r_info.outputs.r_release }}
r_oldrel: ${{ steps.r_info.outputs.r_oldrel }}
os_linux: ${{ steps.mat_info.outputs.os_linux }}
os_macos: ${{ steps.mat_info.outputs.os_macos }}
os_windows: ${{ steps.mat_info.outputs.os_windows }}
strategy:
fail-fast: true
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: true
- name: Set up package information
id: pkg_info
run: |
set -x
export PKG_NAME="$(grep '^Package' DESCRIPTION | sed 's/^[^:]*://g' | xargs)"
export PKG_VERSION="$(grep '^Version' DESCRIPTION | sed 's/^[^:]*://g' | xargs)"
export PKG_SHA="$(git rev-parse --short HEAD | sed 's/^[^:]*://g' | xargs)"
echo "pkg_name=$PKG_NAME" >> $GITHUB_OUTPUT
echo "pkg_version=$PKG_VERSION" >> $GITHUB_OUTPUT
echo "pkg_sha=$PKG_SHA" >> $GITHUB_OUTPUT
shell: bash
- name: Set up R information
id: r_info
run: |
set -x
echo "r_devel=devel" >> $GITHUB_OUTPUT
echo "r_release=release" >> $GITHUB_OUTPUT
echo "r_oldrel=oldrel" >> $GITHUB_OUTPUT
shell: bash
- name: Set up OS information
id: mat_info
run: |
set -x
echo "os_linux=Ubuntu-22.04" >> $GITHUB_OUTPUT
echo "os_macos=macOS-14" >> $GITHUB_OUTPUT
echo "os_windows=Windows-2022" >> $GITHUB_OUTPUT
shell: bash
check:
name: Check R-${{ matrix.r }} | ${{ matrix.os }}
if: "! contains(github.event.head_commit.message, '[ci skip]')"
runs-on: ${{ matrix.os }}
timeout-minutes: 360
needs: [initiate]
strategy:
fail-fast: true
matrix:
r:
- ${{ needs.initiate.outputs.r_release }}
os:
- ${{ needs.initiate.outputs.os_linux }}
- ${{ needs.initiate.outputs.os_macos }}
- ${{ needs.initiate.outputs.os_windows }}
include:
- r: ${{ needs.initiate.outputs.r_devel }}
os: ${{ needs.initiate.outputs.os_linux }}
- r: ${{ needs.initiate.outputs.r_oldrel }}
os: ${{ needs.initiate.outputs.os_linux }}
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: true
- name: Fetch pull request
if: github.event_name == 'pull_request' || github.event.issue.pull_request
uses: r-lib/actions/pr-fetch@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up R environment
uses: r-lib/actions/setup-r@v2
with:
r-version: ${{ matrix.r }}
rtools-version: ''
Ncpus: '1'
remove-openmp-macos: true
http-user-agent: 'default'
install-r: true
windows-path-include-rtools: true
windows-path-include-mingw: true
update-rtools: false
use-public-rspm: false
extra-repositories: ''
- name: Set up Pandoc
uses: r-lib/actions/setup-pandoc@v2
- name: Set up TinyTeX
uses: r-lib/actions/setup-tinytex@v2
- name: Set up system environment (Linux)
if: runner.os == 'Linux'
run: |
set -x
sudo apt-get install -y automake || true
sudo apt-get install -y libboost-all-dev || true
sudo apt-get install -y clang-format || true
sudo apt-get install -y cmake || true
sudo apt-get install -y git || true
sudo apt-get install -y libarchive-dev || true
sudo apt-get install -y libcurl4-openssl-dev || true
sudo apt-get install -y libgit2-dev || true
sudo apt-get install -y libglpk-dev || true
sudo apt-get install -y libgmp-dev || true
sudo apt-get install -y libharfbuzz-dev libfribidi-dev || true
sudo apt-get install -y libicu-dev || true
sudo apt-get install -y libnetcdf-dev libnetcdff-dev || true
sudo apt-get install -y libpng-dev || true
sudo apt-get install -y libtiff-dev || true
sudo apt-get install -y libxml2-dev || true
sudo apt-get install -y make || true
sudo apt-get install -y openmpi-bin openmpi-common libopenmpi-dev || true
sudo apt-get install -y pandoc || true
shell: bash
- name: Set up system environment (macOS)
if: runner.os == 'macOS'
run: |
set -x
rm -f /usr/local/bin/gfortran || true
brew reinstall --force --verbose automake || true
brew reinstall --force --verbose boost || true
brew reinstall --force --verbose cmake || true
brew reinstall --force --verbose curl || true
brew reinstall --force --verbose harfbuzz fribidi || true
brew reinstall --force --verbose gcc || true
brew reinstall --force --verbose git || true
brew reinstall --force --verbose glpk || true
brew reinstall --force --verbose gmp || true
brew reinstall --force --verbose icu4c || true
brew reinstall --force --verbose libarchive || true
brew reinstall --force --verbose libgit2 || true
brew reinstall --force --verbose libpng || true
brew reinstall --force --verbose libtiff || true
brew reinstall --force --verbose libxml2 || true
brew reinstall --force --verbose make || true
brew reinstall --force --verbose netcdf || true
brew reinstall --force --verbose open-mpi || true
brew reinstall --force --verbose --cask xquartz || true
shell: bash
- name: Set up system environment (Windows)
if: runner.os == 'Windows'
run: |
$ErrorActionPreference = 'SilentlyContinue'
echo "TMPDIR=${{ runner.temp }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
pacman -Syu --noconfirm mingw-w64-i686-boost
pacman -Syu --noconfirm mingw-w64-i686-cmake
pacman -Syu --noconfirm mingw-w64-i686-curl
pacman -Syu --noconfirm mingw-w64-i686-glpk
pacman -Syu --noconfirm mingw-w64-i686-gmp
pacman -Syu --noconfirm mingw-w64-i686-libxml2
pacman -Syu --noconfirm mingw-w64-i686-libpng
pacman -Syu --noconfirm mingw-w64-i686-libuv
pacman -Syu --noconfirm mingw-w64-i686-make
pacman -Syu --noconfirm mingw-w64-i686-netcdf
pacman -Syu --noconfirm mingw-w64-i686-nlopt
pacman -Syu --noconfirm mingw-w64-x86_64-boost
pacman -Syu --noconfirm mingw-w64-x86_64-cmake
pacman -Syu --noconfirm mingw-w64-x86_64-curl
pacman -Syu --noconfirm mingw-w64-x86_64-glpk
pacman -Syu --noconfirm mingw-w64-x86_64-gmp
pacman -Syu --noconfirm mingw-w64-x86_64-libxml2
pacman -Syu --noconfirm mingw-w64-x86_64-libpng
pacman -Syu --noconfirm mingw-w64-x86_64-libuv
pacman -Syu --noconfirm mingw-w64-x86_64-make
pacman -Syu --noconfirm mingw-w64-x86_64-netcdf
pacman -Syu --noconfirm mingw-w64-x86_64-nlopt
pacman -Syu --noconfirm mingw-w64-ucrt-x86_64-boost
pacman -Syu --noconfirm mingw-w64-ucrt-x86_64-cmake
pacman -Syu --noconfirm mingw-w64-ucrt-x86_64-curl
pacman -Syu --noconfirm mingw-w64-ucrt-x86_64-glpk
pacman -Syu --noconfirm mingw-w64-ucrt-x86_64-gmp
pacman -Syu --noconfirm mingw-w64-ucrt-x86_64-libxml2
pacman -Syu --noconfirm mingw-w64-ucrt-x86_64-libpng
pacman -Syu --noconfirm mingw-w64-ucrt-x86_64-libuv
pacman -Syu --noconfirm mingw-w64-ucrt-x86_64-make
pacman -Syu --noconfirm mingw-w64-ucrt-x86_64-netcdf
pacman -Syu --noconfirm mingw-w64-ucrt-x86_64-nlopt
shell: pwsh
- name: Set up R dependencies
id: r_dependencies
uses: r-lib/actions/setup-r-dependencies@v2
with:
cache: true
cache-version: 1
extra-packages: |
any::BH
any::covr
any::curl
any::devtools
any::htmltools
any::lintr
any::pkgdown
any::rcmdcheck
any::Rcpp
any::RcppEigen
any::RcppParallel
any::rmarkdown
any::roxygen2
any::styler
r-lib/revdepcheck
local::.
needs: |
check
coverage
website
pak-version: 'stable'
working-directory: '.'
continue-on-error: true
- name: Set up R dependencies (fallback)
if: steps.r_dependencies.outcome != 'success'
run: |
options(
crayon.enabled = TRUE,
install.packages.check.source = "no",
pkgType = ifelse(grepl("linux", R.version$os), "source", "binary")
)
install.packages("remotes")
remotes::install_cran("BH")
remotes::install_cran("covr")
remotes::install_cran("curl")
remotes::install_cran("devtools")
remotes::install_cran("htmltools")
remotes::install_cran("lintr")
remotes::install_cran("mgcv", type = "source")
remotes::install_cran("parsnip", type = "source")
remotes::install_cran("pkgdown")
remotes::install_cran("Rcpp")
remotes::install_cran("RcppEigen")
remotes::install_cran("RcppParallel")
remotes::install_cran("rcmdcheck")
remotes::install_cran("rmarkdown")
remotes::install_cran("roxygen2")
remotes::install_cran("sessioninfo")
remotes::install_cran("styler")
remotes::install_deps(dependencies = TRUE)
remotes::install_github("r-lib/revdepcheck")
remotes::install_local(".")
if (utils::packageVersion("sessioninfo") >= "1.2.1") {
sessioninfo::session_info(pkgs = "installed", include_base = TRUE)
} else {
options(width = 200)
sessioninfo::session_info(rownames(installed.packages()), include_base = TRUE)
}
shell: Rscript {0}
- name: Check R package
if: success()
uses: r-lib/actions/check-r-package@v2
with:
args: 'c("--no-manual", "--as-cran")'
build_args: 'c("--no-manual")'
error-on: '"warning"'
check-dir: '"check"'
working-directory: '.'
upload-snapshots: true
- name: Check R package reverse dependencies
if: matrix.os == needs.initiate.outputs.os_linux && matrix.r == needs.initiate.outputs.r_devel && (contains(github.event.head_commit.message, '[revdep]') || inputs.revdep == 'true')
run: |
options(crayon.enabled = TRUE)
revdepcheck::revdep_reset()
revdepcheck::revdep_check(timeout = as.difftime(60, units = "mins"), num_workers = 1, bioc = TRUE)
revdepcheck::revdep_reset()
unlink("revdep/library", recursive = TRUE)
shell: Rscript {0}
- name: Build packages
if: success()
run: |
options(crayon.enabled = TRUE)
src <- pkgbuild::build(".", dest_path = tempdir(), vignettes = TRUE, manual = FALSE, binary = FALSE)
bin <- pkgbuild::build(".", dest_path = tempdir(), vignettes = TRUE, manual = FALSE, binary = TRUE)
dir.create("build")
file.copy(c(src, bin), "build")
shell: Rscript {0}
- name: Upload packages
uses: actions/upload-artifact@v4
with:
name: ${{ needs.initiate.outputs.pkg_name }}_${{ needs.initiate.outputs.pkg_version }}_R-${{ matrix.r }}_${{ matrix.os }}
path: build
- name: Style and format R code
if: success() && matrix.os == needs.initiate.outputs.os_linux && matrix.r == needs.initiate.outputs.r_devel
run: |
set -x
Rscript -e 'styler::style_pkg(exclude_files = list.files("./R", pattern = "^RcppExports.*", full.names = TRUE))'
shell: bash
- name: Style and format C/C++ code
if: success() && matrix.os == needs.initiate.outputs.os_linux && matrix.r == needs.initiate.outputs.r_devel
run: |
set -x
find . -type f -regex '.*\.\(h\|hpp\|c\|cc\|cpp\|cxx\)' \
-not -path "./build/*" \
-not -path "./check/*" \
-not -path "./revdep/*" \
-not -path "*/RcppExports.*" \
-exec clang-format -style=Google --verbose -i {} \; || true
shell: bash
- name: Cleanup R/C/C++ code (Remove whitespaces)
if: success() && matrix.os == needs.initiate.outputs.os_linux && matrix.r == needs.initiate.outputs.r_devel
run: |
set -x
find . -type f \( -name 'DESCRIPTION' -o -regex '.*\.\(R\|stan\)' \) \
-not -path "./build/*" \
-not -path "./check/*" \
-not -path "./revdep/*" \
-not -path "*/RcppExports.*" \
-printf '%p\n' -exec sed -i -e 's/[ \t]*$//' {} \; || true
find . -type f -regex '.*\.\(h\|hpp\|c\|cc\|cpp\|cxx\)' \
-not -path "./build/*" \
-not -path "./check/*" \
-not -path "./revdep/*" \
-not -path "*/RcppExports.*" \
-printf '%p\n' -exec sed -i -e 's/[ \t]*$//' {} \; || true
shell: bash
- name: Render documents
if: success() && matrix.os == needs.initiate.outputs.os_linux && matrix.r == needs.initiate.outputs.r_devel
run: |
options(crayon.enabled = TRUE)
roxygen2::roxygenize()
if (file.exists("README.Rmd")) rmarkdown::render("README.Rmd", output_format = "github_document")
shell: Rscript {0}
- name: Generate CITATION.cff file
if: success() && matrix.os == needs.initiate.outputs.os_linux && matrix.r == needs.initiate.outputs.r_devel
run: |
options(crayon.enabled = TRUE)
try(remotes::install_cran("cffr"))
if (require("cffr")) cffr::cff_write(outfile = "CITATION.cff", keys = list())
shell: Rscript {0}
- name: Commit changes
if: success() && matrix.os == needs.initiate.outputs.os_linux && matrix.r == needs.initiate.outputs.r_devel
run: |
set -x
git config --local user.name "$GITHUB_ACTOR"
git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
find . -type f \( -name 'DESCRIPTION' -o -regex '.*\.\(R\|stan\)' \) \
-not -path "./build/*" \
-not -path "./check/*" \
-not -path "./revdep/*" \
-not -path "*/RcppExports.*" \
-printf '%p\n' -exec git add {} \; || true
git diff --stat || true
git commit -m 'Styling and formatting R code using `styler`' || echo 'No changes to commit'
find . -type f -regex '.*\.\(h\|hpp\|c\|cc\|cpp\|cxx\)' \
-not -path "./build/*" \
-not -path "./check/*" \
-not -path "*/RcppExports.*" \
-printf '%p\n' -exec git add {} \; || true
git diff --stat || true
git commit -m 'Styling and formatting C/C++ code using `clang-format`' || echo 'No changes to commit'
find . \( -name 'NAMESPACE' -o -name 'RcppExports.*' -o -regex '.*\.\(Rd\|Rmd|\md\)' \) \
-not -path "./build/*" \
-not -path "./check/*" \
-not -path "./revdep/*" \
-printf '%p\n' -exec git add {} \; || true
git diff --stat || true
git commit -m 'Roxygenize and render documents' || echo 'No changes to commit'
git add CITATION.cff
git commit -m 'Generate `CITATION.cff` file' || echo "No changes to commit"
git add revdep/\* || true
git diff --stat || true
git commit -m 'Update `revdep` results' || echo 'No changes to commit'
git pull --ff-only || true
git push origin || echo "No changes to commit"
shell: bash
- name: Push changes to pull request
if: ${{ success() && matrix.os == needs.initiate.outputs.os_linux && matrix.r == needs.initiate.outputs.r_devel &&
( github.event_name == 'pull_request' || github.event.issue.pull_request ) &&
( github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER' ) }}
uses: r-lib/actions/pr-push@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Analyze R code
if: success() && matrix.os == needs.initiate.outputs.os_linux && matrix.r == needs.initiate.outputs.r_devel
run: |
options(crayon.enabled = TRUE)
lintr::lint_package()
shell: Rscript {0}
- name: Analyze unit test coverage
if: success() && matrix.os == needs.initiate.outputs.os_linux && matrix.r == needs.initiate.outputs.r_devel
run: |
options(crayon.enabled = TRUE)
covr::codecov(quiet = FALSE)
shell: Rscript {0}
- name: Build package website
if: success() && matrix.os == needs.initiate.outputs.os_linux && matrix.r == needs.initiate.outputs.r_devel
run: |
pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
shell: Rscript {0}
- name: Deploy package website to GitHub pages 🚀
if: success() && matrix.os == needs.initiate.outputs.os_linux && matrix.r == needs.initiate.outputs.r_devel && github.event_name != 'pull_request'
uses: JamesIves/github-pages-deploy-action@v4
with:
clean: false
branch: gh-pages
folder: docs
release:
name: Create release
if: contains(github.event.head_commit.message, '[release]') || inputs.release == 'true'
runs-on: ${{ needs.initiate.outputs.os_linux }}
timeout-minutes: 360
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
needs: [initiate, check]
strategy:
fail-fast: true
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Create release
if: success()
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ needs.initiate.outputs.pkg_version }}
release_name: ${{ needs.initiate.outputs.pkg_name }} ${{ needs.initiate.outputs.pkg_version }}
body: |
Check changes in [NEWS.md](NEWS.md).
draft: false
prerelease: false
upload-src:
name: Upload source package
if: contains(github.event.head_commit.message, '[release]') || inputs.release == 'true'
runs-on: ${{ needs.initiate.outputs.os_linux }}
timeout-minutes: 360
needs: [initiate, check, release]
strategy:
fail-fast: true
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ./
- name: Set up system environment
id: artifacts
run: |
ls -R
export ARTIFACT_PATH="${{ needs.initiate.outputs.pkg_name }}_${{ needs.initiate.outputs.pkg_version }}_R-${{ needs.initiate.outputs.r_devel }}_${{ needs.initiate.outputs.os_linux }}"
echo "artifact_path=$ARTIFACT_PATH" >> $GITHUB_OUTPUT
shell: bash
- name: Upload source package
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: ${{ steps.artifacts.outputs.artifact_path }}/${{ needs.initiate.outputs.pkg_name }}_${{ needs.initiate.outputs.pkg_version }}.tar.gz
asset_name: ${{ needs.initiate.outputs.pkg_name }}_${{ needs.initiate.outputs.pkg_version }}.tar.gz
asset_content_type: application/gzip
upload-bin:
name: Upload R-${{ matrix.r }} | ${{ matrix.os }}
if: contains(github.event.head_commit.message, '[release]') || inputs.release == 'true'
runs-on: ${{ needs.initiate.outputs.os_linux }}
timeout-minutes: 360
needs: [initiate, check, release, upload-src]
strategy:
fail-fast: true
matrix:
r:
- ${{ needs.initiate.outputs.r_release }}
os:
- ${{ needs.initiate.outputs.os_linux }}
- ${{ needs.initiate.outputs.os_macos }}
- ${{ needs.initiate.outputs.os_windows }}
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ./
- name: Set up system environment
id: artifacts
run: |
ls -R
export ARTIFACT_PATH="${{ needs.initiate.outputs.pkg_name }}_${{ needs.initiate.outputs.pkg_version }}_R-${{ matrix.r }}_${{ matrix.os }}"
zip -rj $ARTIFACT_PATH.zip $ARTIFACT_PATH/*
echo "artifact_path=$ARTIFACT_PATH" >> $GITHUB_OUTPUT
shell: bash
- name: Upload release assets
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: ${{ steps.artifacts.outputs.artifact_path }}.zip
asset_name: ${{ steps.artifacts.outputs.artifact_path }}.zip
asset_content_type: application/zip