|
| 1 | +name: 'setup-r-dependencies' |
| 2 | +description: 'Action to setup installation tools and install R dependencies' |
| 3 | +author: 'Jim Hester' |
| 4 | +inputs: |
| 5 | + cache: |
| 6 | + description: 'Whether packages should be cached across runs or not. If `"always"` is provided, the package cache will be saved even if the workflow fails.' |
| 7 | + required: true |
| 8 | + default: true |
| 9 | + cache-version: |
| 10 | + description: 'The version of the cache, change this from the default (1) to start over with a fresh cache. Ignored if cache: false' |
| 11 | + required: true |
| 12 | + default: 1 |
| 13 | + extra-packages: |
| 14 | + description: 'Any extra packages to install outside of the packages listed in the dependencies' |
| 15 | + needs: |
| 16 | + description: 'Any extra Config/Needs fields which need to be included when installing dependencies' |
| 17 | + packages: |
| 18 | + description: 'Which package(s) to install.' |
| 19 | + default: 'deps::., any::sessioninfo' |
| 20 | + pak-version: |
| 21 | + description: | |
| 22 | + Which pak version to use. Possible values are "stable", "rc", |
| 23 | + "devel", "none", "repo". The first three install pak from our |
| 24 | + repository at GitHub Pages. |
| 25 | + "none" will skip pak installation. Use this if you want to install |
| 26 | + pak yourself. Set the `R_LIB_FOR_PAK` environment variable to point |
| 27 | + to the library where pak is installed. |
| 28 | + `repo` means that the action will install pak from the configured |
| 29 | + repositories, using `install.packages()`. `repo` is appropriate on |
| 30 | + systems that do not have access to our pak reporitory on GitHUb. |
| 31 | + default: 'stable' |
| 32 | + working-directory: |
| 33 | + description: 'Using the working-directory keyword, you can specify the working directory of where "pkg_deps" command searches for dependencies in the "DESCRIPTION" file.' |
| 34 | + default: '.' |
| 35 | + dependencies: |
| 36 | + description: 'Types of dependencies to install. Must be an R expression. Note that it often needs to be quoted in YAML, see the README for details.' |
| 37 | + default: '"all"' |
| 38 | + upgrade: |
| 39 | + description: 'Whether to install the latest available versions of the dependencies. Must be an R expression. See the README for details if you need quoting.' |
| 40 | + default: 'FALSE' |
| 41 | + lockfile-create-lib: |
| 42 | + description: 'The package library to consider when creating the pak lockfile. This is passed to the `lib` argument of `pak::lockfile_create()`. Defaults to an empty library, for reproducibility. Must be an R expression. Note that it often needs to be quoted in YAML, see the README for detail\ |
| 43 | +s.' |
| 44 | + default: 'NULL' |
| 45 | + install-pandoc: |
| 46 | + description: 'Whether to install pandoc. By default it is installed if it is not on the PATH and the local package suggests or depends on the rmarkdown package.' |
| 47 | + pandoc-version: |
| 48 | + description: 'Pandoc version to install.' |
| 49 | + default: '3.1.11' |
| 50 | + install-quarto: |
| 51 | + description: | |
| 52 | + Whether to install quarto. If it is 'auto' (the default), it is |
| 53 | + installed if there is at least one `.qmd` file in the repository, |
| 54 | + inside `working-directory`. Set to 'true' to always install it. |
| 55 | + Set to 'false' to never install it. |
| 56 | + default: 'auto' |
| 57 | + quarto-version: |
| 58 | + description: | |
| 59 | + Version of quarto to install, if quarto is installed. It is passed |
| 60 | + to the `quarto-dev/quarto-actions/setup@v2` action. The default is |
| 61 | + 'release' to install the latest release. Other possible values are |
| 62 | + a version number number (without the `v` prefix), and 'pre-release'. |
| 63 | + default: 'release' |
| 64 | +runs: |
| 65 | + using: "composite" |
| 66 | + steps: |
| 67 | + - name: Set site library path |
| 68 | + run: | |
| 69 | + # Set site library path |
| 70 | + cat("::group::Set site library path\n") |
| 71 | + libpak <- Sys.getenv("R_LIB_FOR_PAK") |
| 72 | + if (libpak != "") { |
| 73 | + message("R_LIB_FOR_PAK is already set to ", libpak) |
| 74 | + } |
| 75 | + if (Sys.getenv("RENV_PROJECT") != "") { |
| 76 | + message("renv project detected, no need to set R_LIBS_SITE") |
| 77 | + if (libpak == "") { |
| 78 | + cat(sprintf("R_LIB_FOR_PAK=%s\n", .libPaths()[1]), file = Sys.getenv("GITHUB_ENV"), append = TRUE) |
| 79 | + message("Setting R_LIB_FOR_PAK to ", .libPaths()[1]) |
| 80 | + } |
| 81 | + q("no") |
| 82 | + } |
| 83 | + lib <- Sys.getenv("R_LIBS_SITE") |
| 84 | + if (lib == "") { |
| 85 | + lib <- file.path(dirname(.Library), "site-library") |
| 86 | + cat(sprintf("R_LIBS_SITE=%s\n", lib), file = Sys.getenv("GITHUB_ENV"), append = TRUE) |
| 87 | + message("Setting R_LIBS_SITE to ", lib) |
| 88 | + if (libpak == "") { |
| 89 | + cat(sprintf("R_LIB_FOR_PAK=%s\n", lib), file = Sys.getenv("GITHUB_ENV"), append = TRUE) |
| 90 | + message("Setting R_LIB_FOR_PAK to ", lib) |
| 91 | + } |
| 92 | + } else { |
| 93 | + message("R_LIBS_SITE is already set to ", lib) |
| 94 | + if (libpak == "") { |
| 95 | + plib <- strsplit(lib, .Platform$path.sep)[[1]][[1]] |
| 96 | + cat(sprintf("R_LIB_FOR_PAK=%s\n", plib), file = Sys.getenv("GITHUB_ENV"), append = TRUE) |
| 97 | + message("Setting R_LIB_FOR_PAK to ", plib) |
| 98 | + } |
| 99 | + } |
| 100 | + if (nchar("${{ env.R_LIBS_USER && 'ok' || '' }}") == 0) { |
| 101 | + message("R_LIBS_USER GH env var is unset, setting now: ", Sys.getenv("R_LIBS_USER")) |
| 102 | + cat(sprintf("R_LIBS_USER=%s\n", Sys.getenv("R_LIBS_USER")), file = Sys.getenv("GITHUB_ENV"), append = TRUE) |
| 103 | + } else { |
| 104 | + message("R_LIBS_USER GH env var is already set: ", Sys.getenv("R_LIBS_USER")) |
| 105 | + } |
| 106 | + cat("::endgroup::\n") |
| 107 | + shell: Rscript {0} |
| 108 | + |
| 109 | + - name: Install pak (Windows) |
| 110 | + if: ${{ runner.os == 'Windows' && inputs.pak-version != 'none' }} |
| 111 | + run: | |
| 112 | + # Install pak |
| 113 | + cat("::group::Install pak\n") |
| 114 | + lib <- Sys.getenv("R_LIB_FOR_PAK") |
| 115 | + dir.create(lib, showWarnings = FALSE, recursive = TRUE) |
| 116 | + if ("${{ inputs.pak-version }}" == "repo") { |
| 117 | + install.packages("pak", lib = lib) |
| 118 | + } else { |
| 119 | + install.packages("pak", lib = lib, repos = sprintf( |
| 120 | + "https://r-lib.github.io/p/pak/%s/%s/%s/%s", |
| 121 | + "${{ inputs.pak-version }}", |
| 122 | + .Platform$pkgType, |
| 123 | + R.Version()$os, |
| 124 | + R.Version()$arch |
| 125 | + )) |
| 126 | + } |
| 127 | + cat("::endgroup::\n") |
| 128 | + shell: Rscript {0} |
| 129 | + |
| 130 | + - name: Install pak (Unix) |
| 131 | + if: ${{ runner.os != 'Windows' && inputs.pak-version != 'none' }} |
| 132 | + run: | |
| 133 | + # Install pak |
| 134 | + echo "::group::Install pak" |
| 135 | + if which sudo >/dev/null; then SUDO="sudo -E --preserve-env=PATH env"; else SUDO=""; fi |
| 136 | + $SUDO R -q -e 'dir.create(Sys.getenv("R_LIB_FOR_PAK"), recursive = TRUE, showWarnings = FALSE)' |
| 137 | + $SUDO R -q -e 'if ("${{ inputs.pak-version }}" == "repo") { install.packages("pak", lib = Sys.getenv("R_LIB_FOR_PAK")) } else { install.packages("pak", lib = Sys.getenv("R_LIB_FOR_PAK"), repos = sprintf("https://r-lib.github.io/p/pak/%s/%s/%s/%s", "${{ inputs.pak-version }}", .Platform$pkgType, R.Version()$os, R.Version()$arch)) }' |
| 138 | + echo "::endgroup::" |
| 139 | + shell: bash |
| 140 | + |
| 141 | + - name: Query dependencies |
| 142 | + id: install |
| 143 | + run: | |
| 144 | + # Dependency resolution |
| 145 | + cat("::group::Dependency resolution\n") |
| 146 | + cat("os-version=", sessionInfo()$running, "\n", file = Sys.getenv("GITHUB_OUTPUT"), sep = "", append = TRUE) |
| 147 | + r_version <- |
| 148 | + if (grepl("development", R.version.string)) { |
| 149 | + pdf(tempfile()) |
| 150 | + ge_ver <- attr(recordPlot(), "engineVersion") |
| 151 | + dev.off() |
| 152 | + paste0("R version ", getRversion(), " (ge:", ge_ver, "; iid:", .Internal(internalsID()), ")") |
| 153 | + } else { |
| 154 | + R.version.string |
| 155 | + } |
| 156 | + cat("r-version=", r_version, "\n", file = Sys.getenv("GITHUB_OUTPUT"), sep = "", append = TRUE) |
| 157 | + needs <- sprintf("Config/Needs/%s", strsplit("${{ inputs.needs }}", "[[:space:],]+")[[1]]) |
| 158 | + if (length(needs) == 0L) needs <- NULL |
| 159 | + deps <- strsplit("${{ inputs.packages }}", "[[:space:],]+")[[1]] |
| 160 | + extra_deps <- strsplit("${{ inputs.extra-packages }}", "[[:space:],]+")[[1]] |
| 161 | + dir.create(".github", showWarnings=FALSE) |
| 162 | + Sys.setenv("PKGCACHE_HTTP_VERSION" = "2") |
| 163 | + library(pak, lib.loc = Sys.getenv("R_LIB_FOR_PAK")) |
| 164 | + pak::lockfile_create( |
| 165 | + c(deps, extra_deps), |
| 166 | + lockfile = ".github/pkg.lock", |
| 167 | + upgrade = (${{ inputs.upgrade }}), |
| 168 | + dependencies = c(needs, (${{ inputs.dependencies }})), |
| 169 | + lib = ${{ inputs.lockfile-create-lib }} |
| 170 | + ) |
| 171 | + cat("::endgroup::\n") |
| 172 | + cat("::group::Show Lockfile\n") |
| 173 | + writeLines(readLines(".github/pkg.lock")) |
| 174 | + cat("::endgroup::\n") |
| 175 | + shell: Rscript {0} |
| 176 | + working-directory: ${{ inputs.working-directory }} |
| 177 | + |
| 178 | + - name: Adjust PATH to avoid using zstd from Rtools |
| 179 | + if: runner.os == 'Windows' |
| 180 | + shell: bash |
| 181 | + run: | |
| 182 | + echo 'C:/tools/zstd' >> $GITHUB_PATH |
| 183 | +
|
| 184 | + - name: R package cache |
| 185 | + if: inputs.cache == 'true' |
| 186 | + uses: actions/cache@v4 |
| 187 | + with: |
| 188 | + path: | |
| 189 | + ${{ env.R_LIBS_USER }}/* |
| 190 | + renv/library |
| 191 | + !${{ env.R_LIBS_USER }}/pak |
| 192 | + !${{ env.R_LIBS_USER }}/_cache |
| 193 | + key: ${{ format('{0}-{1}-{2}-{3}', steps.install.outputs.os-version, steps.install.outputs.r-version, inputs.cache-version, hashFiles(format('{0}/.github/pkg.lock', inputs.working-directory ))) }} |
| 194 | + restore-keys: ${{ format('{0}-{1}-{2}-', steps.install.outputs.os-version, steps.install.outputs.r-version, inputs.cache-version) }} |
| 195 | + |
| 196 | + - name: R package cache restore |
| 197 | + id: cache-packages-restore |
| 198 | + if: inputs.cache == 'always' |
| 199 | + uses: actions/cache/restore@v4 |
| 200 | + with: |
| 201 | + path: | |
| 202 | + ${{ env.R_LIBS_USER }}/* |
| 203 | + renv/library |
| 204 | + !${{ env.R_LIBS_USER }}/pak |
| 205 | + !${{ env.R_LIBS_USER }}/_cache |
| 206 | + key: ${{ format('{0}-{1}-{2}-{3}', steps.install.outputs.os-version, steps.install.outputs.r-version, inputs.cache-version, hashFiles(format('{0}/.github/pkg.lock', inputs.working-directory ))) }} |
| 207 | + restore-keys: ${{ format('{0}-{1}-{2}-', steps.install.outputs.os-version, steps.install.outputs.r-version, inputs.cache-version) }} |
| 208 | + |
| 209 | + - name: Install Seurat |
| 210 | + run: | |
| 211 | + # Install/Update Seurat to fix SeuratData missing ‘vctrs’ |
| 212 | + cat("::group::Install/update packages\n") |
| 213 | + Sys.setenv("PKGCACHE_HTTP_VERSION" = "2") |
| 214 | + library(pak, lib.loc = Sys.getenv("R_LIB_FOR_PAK")) |
| 215 | + pak::pkg_install("Seurat") |
| 216 | + shell: Rscript {0} |
| 217 | + working-directory: ${{ inputs.working-directory }} |
| 218 | + |
| 219 | + - name: Install dependencies |
| 220 | + run: | |
| 221 | + # Install/Update packages |
| 222 | + cat("::group::Install/update packages\n") |
| 223 | + Sys.setenv("PKGCACHE_HTTP_VERSION" = "2") |
| 224 | + library(pak, lib.loc = Sys.getenv("R_LIB_FOR_PAK")) |
| 225 | + pak::lockfile_install(".github/pkg.lock") |
| 226 | + ## Clean up lock file |
| 227 | + unlink(".github/pkg.lock") |
| 228 | + cat("::endgroup::\n") |
| 229 | + shell: Rscript {0} |
| 230 | + working-directory: ${{ inputs.working-directory }} |
| 231 | + |
| 232 | + - name: Install SeuratWrappers |
| 233 | + run: | |
| 234 | + # Install/Update ‘R.utils’ to fix SeuratWrappers missing ‘R.utils’ |
| 235 | + # then install SeuratWrappers |
| 236 | + cat("::group::Install/update packages\n") |
| 237 | + Sys.setenv("PKGCACHE_HTTP_VERSION" = "2") |
| 238 | + library(pak, lib.loc = Sys.getenv("R_LIB_FOR_PAK")) |
| 239 | + pak::pkg_install("R.utils") |
| 240 | + pak::pkg_install("satijalab/seurat-wrappers") |
| 241 | + shell: Rscript {0} |
| 242 | + working-directory: ${{ inputs.working-directory }} |
| 243 | + |
| 244 | + |
| 245 | + - name: Install additional packages |
| 246 | + run: | |
| 247 | + options(timeout=500) |
| 248 | + SeuratData::InstallData("pbmcsca") |
| 249 | + pak::pkg_install("glmGamPoi") |
| 250 | + shell: Rscript {0} |
| 251 | + working-directory: ${{ inputs.working-directory }} |
| 252 | + |
| 253 | + |
| 254 | + - name: Check whether pandoc needs to be installed |
| 255 | + id: check-pandoc |
| 256 | + run: | |
| 257 | + # Pandoc check |
| 258 | + cat("::group::Check if package needs pandoc\n") |
| 259 | + o <- '${{ inputs.install-pandoc }}' |
| 260 | + if (! o %in% c('true', 'false')) { |
| 261 | + if (Sys.which("pandoc") != "") { |
| 262 | + cat("Pandoc is already installed at", Sys.which("pandoc"), "\n") |
| 263 | + o <- 'false' |
| 264 | + } else if (file.exists("DESCRIPTION")) { |
| 265 | + deptypes <- list(direct = "all", indirect = character()) |
| 266 | + deps <- pak::pkg_deps(".", dependencies = deptypes) |
| 267 | + if ("rmarkdown" %in% deps$package) { |
| 268 | + cat("Pandoc is needed for rmarkdown\n") |
| 269 | + o <- 'true' |
| 270 | + } else { |
| 271 | + cat("Pandoc is not needed\n") |
| 272 | + o <- 'false' |
| 273 | + } |
| 274 | + } else { |
| 275 | + cat("Pandoc is not needed, no R package found\n") |
| 276 | + o <- 'false' |
| 277 | + } |
| 278 | + } |
| 279 | + cat("install=", o, "\n", file = Sys.getenv("GITHUB_OUTPUT"), sep = "", append = TRUE) |
| 280 | + cat("::endgroup::\n") |
| 281 | + shell: Rscript {0} |
| 282 | + working-directory: ${{ inputs.working-directory }} |
| 283 | + |
| 284 | + - name: Install pandoc if needed |
| 285 | + if: ${{ steps.check-pandoc.outputs.install == 'true' }} |
| 286 | + uses: r-lib/actions/setup-pandoc@v2 |
| 287 | + with: |
| 288 | + pandoc-version: ${{ inputs.pandoc-version }} |
| 289 | + |
| 290 | + - name: Check whether quarto if needed |
| 291 | + id: check-quarto |
| 292 | + run: | |
| 293 | + # Quarto check |
| 294 | + cat("::group::Check if package needs quarto\n") |
| 295 | + o <- '${{ inputs.install-quarto }}' |
| 296 | + if (! o %in% c('true', 'false')) { |
| 297 | + if (Sys.which("quarto") != "") { |
| 298 | + cat("Quarto is already installed at", Sys.which("quarto"), "\n") |
| 299 | + o <- "false" |
| 300 | + } else { |
| 301 | + qmd <- dir(recursive = TRUE, pattern = "[.]qmd$") |
| 302 | + if (length(qmd) > 0) { |
| 303 | + cat("Quarto is needed for qmd file(s):", qmd[1], "...\n") |
| 304 | + o <- "true" |
| 305 | + } else { |
| 306 | + cat("No qmd files found, Quarto is not needed.\n") |
| 307 | + o <- "false" |
| 308 | + } |
| 309 | + } |
| 310 | + } |
| 311 | + cat("install=", o, "\n", file = Sys.getenv("GITHUB_OUTPUT"), sep = "", append = TRUE) |
| 312 | + cat("::endgroup::\n") |
| 313 | + shell: Rscript {0} |
| 314 | + working-directory: ${{ inputs.working-directory }} |
| 315 | + |
| 316 | + - name: Install quarto if needed |
| 317 | + if: ${{ steps.check-quarto.outputs.install == 'true' }} |
| 318 | + uses: quarto-dev/quarto-actions/setup@87b35bb88b36317fa36b5189e9553b4164a5c5a3 |
| 319 | + with: |
| 320 | + version: ${{ inputs.quarto-version }} |
| 321 | + |
| 322 | + - name: Session info |
| 323 | + run: | |
| 324 | + # Session info |
| 325 | + cat("::group::Session info\n") |
| 326 | + if (requireNamespace("sessioninfo", quietly = TRUE)) { |
| 327 | + if (packageVersion("sessioninfo") >= "1.2.1") { |
| 328 | + sessioninfo::session_info(pkgs = "installed", include_base = TRUE) |
| 329 | + } else { |
| 330 | + options(width = 200) |
| 331 | + sessioninfo::session_info(rownames(installed.packages()), include_base=TRUE) |
| 332 | + } |
| 333 | + } else { |
| 334 | + sessionInfo() |
| 335 | + } |
| 336 | + cat("::endgroup::\n") |
| 337 | + shell: Rscript {0} |
| 338 | + working-directory: ${{ inputs.working-directory }} |
| 339 | + |
| 340 | + - name: Don't use tar 1.30 from Rtools35 to store the cache |
| 341 | + if: runner.os == 'Windows' |
| 342 | + shell: bash |
| 343 | + run: | |
| 344 | + if command -v /c/Rtools/bin/tar && /c/Rtools/bin/tar --version | grep -q 'tar (GNU tar) 1.30' |
| 345 | + then echo 'C:/Program Files/Git/usr/bin' >> $GITHUB_PATH |
| 346 | + fi |
| 347 | +
|
| 348 | + - name: R package cache save |
| 349 | + if: ${{ always() && steps.cache-packages-restore.outputs.cache-hit != 'true' && inputs.cache == 'always' }} |
| 350 | + uses: actions/cache/save@v4 |
| 351 | + with: |
| 352 | + path: | |
| 353 | + ${{ env.R_LIBS_USER }}/* |
| 354 | + renv/library |
| 355 | + !${{ env.R_LIBS_USER }}/pak |
| 356 | + !${{ env.R_LIBS_USER }}/_cache |
| 357 | + key: ${{ steps.cache-packages-restore.outputs.cache-primary-key }} |
0 commit comments