From 89ba593c6241d2df4e81b7976c02d81885e4ff68 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 18 Jul 2026 14:53:04 +0800 Subject: [PATCH 1/7] Add reusable R revdeps workflow --- .github/workflows/revdeps.yml | 373 ++++++++++++++++++++++++++++++++++ 1 file changed, 373 insertions(+) create mode 100644 .github/workflows/revdeps.yml diff --git a/.github/workflows/revdeps.yml b/.github/workflows/revdeps.yml new file mode 100644 index 0000000..a47dd56 --- /dev/null +++ b/.github/workflows/revdeps.yml @@ -0,0 +1,373 @@ +--- + +name: Reverse Dependencies +run-name: Reverse Dependencies (${{ inputs.package }}@${{ inputs.package == 'rstan' && inputs.rstan-source || (inputs.package == 'StanHeaders' && inputs.stanheaders-source || (inputs.package == 'rstantools' && inputs.rstantools-source || (inputs.package == 'loo' && inputs.loo-source || (inputs.package == 'posterior' && inputs.posterior-source || (inputs.package == 'bayesplot' && inputs.bayesplot-source || ''))))) }}) +on: + workflow_call: + inputs: + package: + description: 'Package to check (default is rstan)' + required: false + default: 'rstan' + type: string + batch-size: + description: 'Number of packages to check in each batch (default is 15)' + required: false + default: 15 + type: number + rstan-source: + description: 'rstan source (either a git ref or "CRAN", default is CRAN)' + required: false + default: 'CRAN' + type: string + stanheaders-source: + description: 'StanHeaders source (either a git ref or "CRAN", default is CRAN)' + required: false + default: 'CRAN' + type: string + rstantools-source: + description: rstantools source (either a git ref or "CRAN", default is CRAN)' + required: false + default: 'CRAN' + type: string + loo-source: + description: loo source (either a git ref or "CRAN", default is CRAN)' + required: false + default: 'CRAN' + type: string + posterior-source: + description: posterior source (either a git ref or "CRAN", default is CRAN)' + required: false + default: 'CRAN' + type: string + bayesplot-source: + description: bayesplot source (either a git ref or "CRAN", default is CRAN)' + required: false + default: 'CRAN' + type: string + run-donttest: + description: 'Run `donttest` examples for each package (increasing check time), default is false' + required: false + default: false + type: boolean + +jobs: + prepare-library: + runs-on: ubuntu-latest + name: Build & Cache Package Dependencies + outputs: + deps_matrix: ${{ steps.dep-batches.outputs.deps_matrix }} + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + - name: Clone rstan + if: inputs.rstan-source != 'CRAN' && inputs.rstan-source != 'cran' + uses: actions/checkout@v6 + with: + submodules: recursive + repository: stan-dev/rstan + ref: ${{ inputs.rstan-source }} + path: rstan-source + + - name: Clone StanHeaders + if: inputs.stanheaders-source != 'CRAN' && inputs.stanheaders-source != 'cran' + uses: actions/checkout@v6 + with: + submodules: recursive + repository: stan-dev/rstan + ref: ${{ inputs.stanheaders-source }} + path: StanHeaders-source + + - name: Clone rstantools + if: inputs.rstantools-source != 'CRAN' && inputs.rstantools-source != 'cran' + uses: actions/checkout@v6 + with: + submodules: recursive + repository: stan-dev/rstantools + ref: ${{ inputs.rstantools-source }} + path: rstantools-source + + - name: Clone loo + if: inputs.loo-source != 'CRAN' && inputs.loo-source != 'cran' + uses: actions/checkout@v6 + with: + submodules: recursive + repository: stan-dev/loo + ref: ${{ inputs.loo-source }} + path: loo-source + + - name: Clone posterior + if: inputs.posterior-source != 'CRAN' && inputs.posterior-source != 'cran' + uses: actions/checkout@v6 + with: + submodules: recursive + repository: stan-dev/posterior + ref: ${{ inputs.posterior-source }} + path: posterior-source + + - name: Clone bayesplot + if: inputs.bayesplot-source != 'CRAN' && inputs.bayesplot-source != 'cran' + uses: actions/checkout@v6 + with: + submodules: recursive + repository: stan-dev/bayesplot + ref: ${{ inputs.bayesplot-source }} + path: bayesplot-source + + - name: Restore cached INLA source + id: cache-inla + uses: actions/cache@v6 + with: + path: /tmp/INLA_25.10.19.tar.gz + key: inla-25.10.19 + + - name: Download INLA source if not cached + if: steps.cache-inla.outputs.cache-hit != 'true' + run: | + wget https://inla.r-inla-download.org/R/stable/src/contrib/INLA_25.10.19.tar.gz -O /tmp/INLA_25.10.19.tar.gz + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: 'release' + + - name: Download recursive dependencies, batch dependencies to check + id: dep-batches + run: | + batch_size <- ${{ inputs.batch-size }} + pkg <- "${{ inputs.package }}" + + # Parallelise building as much as possible + options(Ncpus = parallel::detectCores()) + Sys.setenv(MAKEFLAGS = paste0("-j", parallel::detectCores())) + + # Initialise a standalone library + dir.create("/tmp/PakLibrary") + .libPaths(c("/tmp/PakLibrary", .libPaths())) + + system("sudo apt-get install -y libcurl4-openssl-dev") + install.packages(c("pak", "rstantools")) + + # Lookup and (attempt to) install all dependencies for reverse-dependencies + pkg_deps <- tools::package_dependencies(pkg, reverse = TRUE)[[pkg]] + rec_deps <- unique(do.call(c, tools::package_dependencies(pkg_deps, which = 'all'))) + install.packages(rec_deps) + + # Remaining packages will be bioconductor or other non-CRAN packages, install manually + rec_deps <- rec_deps[!(rec_deps %in% installed.packages()[,"Package"])] + rec_deps <- rec_deps[!(rec_deps %in% c("blavsam", "eggCountsExtra", "BRugs", "cmdstanr", "INLA", "iwmm", "qs", "rkeops", "mcmcplots"))] + + install.packages('cmdstanr', repos = c('https://stan-dev.r-universe.dev', getOption("repos"))) + pak::pak(c( + paste0("any::", rec_deps), + "github::ecmerkle/blavsam", + "github::CraigWangStat/eggCountsExtra", + "local::/tmp/INLA_25.10.19.tar.gz", + "any::qgraph", + "topipa/iwmm" + )) + + install_dev <- function(pkg, pkg_src) { + if (tolower(pkg_src) == "cran") { + install.packages(pkg) + } else { + pkg <- ifelse(pkg == "rstan", "rstan/rstan/rstan", + ifelse(pkg == "StanHeaders", "rstan/StanHeaders", pkg)) + pak::pak(paste0("local::./", pkg, "-source")) + } + } + + # Install dev versions last so that they're not used for any dependencies + install_dev("StanHeaders", "${{ inputs.stanheaders-source }}") + install_dev("rstan", "${{ inputs.rstan-source }}") + install_dev("rstantools", "${{ inputs.rstantools-source }}") + install_dev("loo", "${{ inputs.loo-source }}") + install_dev("posterior", "${{ inputs.posterior-source }}") + install_dev("bayesplot", "${{ inputs.bayesplot-source }}") + + # Split dependencies into requested batch size and + # restructure into format for actions 'matrix' argument + curr_deps <- split(pkg_deps, ceiling(seq_along(pkg_deps) / batch_size)) + deps_matrix <- list() + for (i in seq_along(curr_deps)) { + deps_matrix[[i]] <- list(group = i, deps = jsonlite::toJSON(curr_deps[[i]])) + } + cat(paste0("deps_matrix=", jsonlite::toJSON(list(include = deps_matrix), auto_unbox = TRUE)), + file=Sys.getenv("GITHUB_OUTPUT"), append = TRUE) + shell: Rscript {0} + + - name: Cache built packages + uses: actions/cache/save@v6 + with: + path: /tmp/PakLibrary/ + key: ${{ github.run_id }} + + reverse-dependencies: + needs: prepare-library + runs-on: ubuntu-latest + name: Check Reverse Dependencies (Batch ${{ matrix.group }}) + strategy: + fail-fast: false + max-parallel: 10 + matrix: ${{ fromJson(needs.prepare-library.outputs.deps_matrix) }} + env: + R_REMOTES_NO_ERRORS_FROM_WARNINGS: true + _R_CHECK_FORCE_SUGGESTS_: false + R_LIBS_USER: /tmp/PakLibrary + NOT_CRAN: + _R_CHECK_CRAN_INCOMING_: false + _R_CHECK_CRAN_INCOMING_REMOTE_: false + _R_CHECK_DONTTEST_EXAMPLES_: ${{ inputs.run-donttest }} + _R_CHECK_ELAPSED_TIMEOUT_: 1h + _R_CHECK_SYSTEM_CLOCK_: 0 + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + - name: Restore built library + uses: actions/cache/restore@v6 + with: + path: /tmp/PakLibrary/ + key: ${{ github.run_id }} + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: 'release' + + - uses: r-lib/actions/setup-pandoc@v2 + - uses: quarto-dev/quarto-actions/setup@v2 + + - name: Configure Makevars + run: | + mkdir -p ~/.R + cat < ~/.R/Makevars + # Eigen warnings significantly bloat log size, so ignore + CXXFLAGS += -w + CXX17FLAGS += -w + EOF + + - name: Install system dependencies + run: | + sudo apt-get install -y pandoc libudunits2-dev libglpk-dev libgsl0-dev \ + texlive-latex-base texlive-latex-recommended texlive-fonts-extra \ + texlive-fonts-recommended texlive-luatex texlive-latex-extra \ + texlive-bibtex-extra libproj-dev + + - name: Run reverse-dependency checks + env: + R_LIBS_USER: /tmp/PakLibrary + NOT_CRAN: + _R_CHECK_CRAN_INCOMING_: false + _R_CHECK_CRAN_INCOMING_REMOTE_: false + _R_CHECK_DONTTEST_EXAMPLES_: ${{ inputs.run-donttest }} + _R_CHECK_ELAPSED_TIMEOUT_: 1h + _R_CHECK_SYSTEM_CLOCK_: 0 + run: | + curr_deps <- jsonlite::fromJSON('${{ matrix.deps }}') + + # Install any system dependencies needed for packages + rec_deps <- unique(do.call(c, tools::package_dependencies(curr_deps, which = 'all'))) + pak::sysreqs_fix_installed(c(curr_deps, rec_deps)) + cmdstanr::install_cmdstan(cores = parallel::detectCores()) + + dir.create("testdir") + download.packages(curr_deps, "testdir", repos = "https://cran.rstudio.com") + Sys.unsetenv("NOT_CRAN") + tools::check_packages_in_dir("testdir", check_args = "--as-cran --no-manual", Ncpus = parallel::detectCores() - 1) + shell: Rscript {0} + + - uses: actions/upload-artifact@v7 + with: + name: checkresults-${{ matrix.group }} + path: | + testdir/**/00check.log + testdir/**/00install.out + compression-level: 9 + retention-days: 1 + + summarise-results: + name: Summarise Results + needs: reverse-dependencies + runs-on: ubuntu-latest + + steps: + - name: Download all results + uses: actions/download-artifact@v7 + with: + path: checkresults + pattern: checkresults-* + merge-multiple: true + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: 'release' + + - name: Generate results summary output + run: | + setwd("checkresults") + install.packages("knitr") + res <- tools::check_packages_in_dir_details(".") + all_pkgs <- gsub("^\\./(.*)\\.Rcheck", "\\1", list.dirs(".", recursive = FALSE)) + install_failures <- unique(res$Package[res$Check == "whether package can be installed"]) + install_failures <- install_failures[order(install_failures)] + check_statuses <- c("tests", "re-building of vignette outputs", "examples", "examples with --run-donttest") + check_failures <- unique(res$Package[res$Check %in% check_statuses]) + check_failures <- check_failures[order(check_failures)] + summary_tbl <- knitr::kable( + data.frame( + "OK" = length(setdiff(all_pkgs, c(install_failures, check_failures))), + "Install Failed" = length(install_failures), + "Tests/Vignettes/Examples Failed" = length(check_failures), + check.names = FALSE + ) + ) + install_logs <- sapply(install_failures, \(pkg) { + paste0(c( + paste("###", pkg), + "
", + "Install Log", + "", + "```", + paste0(readLines(paste0(pkg, ".Rcheck/00install.out")), collapse = "\n"), + "```", + "", + "
\n"), + collapse = "\n" + ) + }) |> + paste0(collapse = "\n") + check_logs <- sapply(check_failures, \(pkg) { + paste0(c( + "", + paste("###", pkg), + "", + "
", + "Check Log", + "", + "```", + paste0(readLines(paste0(pkg, ".Rcheck/00check.log")), collapse = "\n"), + "```", + "", + "
\n"), + collapse = "\n" + ) + }) |> + paste0(collapse = "\n") + result_md <- paste0(c( + "# Reverse-Dependency Status\n", + "## Summary\n", + summary_tbl, + "## Install Failure\n", + install_logs, + "## Vignette/Test Failures\n", + check_logs), + collapse = "\n" + ) + cat(result_md, file = Sys.getenv("GITHUB_STEP_SUMMARY"), append = TRUE) + shell: Rscript {0} + + - name: Upload combined results + uses: actions/upload-artifact@v7 + with: + name: all-logs + path: checkresults + compression-level: 9 From 8bfae91f3b2406e29901c3df6139bc08a6fc35f8 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 20 Jul 2026 12:21:16 +0800 Subject: [PATCH 2/7] Fix defaults and collisions --- .github/workflows/revdeps.yml | 217 ++++++++++++++++++++-------------- 1 file changed, 130 insertions(+), 87 deletions(-) diff --git a/.github/workflows/revdeps.yml b/.github/workflows/revdeps.yml index a47dd56..fa6856f 100644 --- a/.github/workflows/revdeps.yml +++ b/.github/workflows/revdeps.yml @@ -1,7 +1,6 @@ --- name: Reverse Dependencies -run-name: Reverse Dependencies (${{ inputs.package }}@${{ inputs.package == 'rstan' && inputs.rstan-source || (inputs.package == 'StanHeaders' && inputs.stanheaders-source || (inputs.package == 'rstantools' && inputs.rstantools-source || (inputs.package == 'loo' && inputs.loo-source || (inputs.package == 'posterior' && inputs.posterior-source || (inputs.package == 'bayesplot' && inputs.bayesplot-source || ''))))) }}) on: workflow_call: inputs: @@ -13,7 +12,7 @@ on: batch-size: description: 'Number of packages to check in each batch (default is 15)' required: false - default: 15 + default: 20 type: number rstan-source: description: 'rstan source (either a git ref or "CRAN", default is CRAN)' @@ -57,21 +56,21 @@ jobs: name: Build & Cache Package Dependencies outputs: deps_matrix: ${{ steps.dep-batches.outputs.deps_matrix }} + cache_key: ${{ steps.calc-hash.outputs.cache_key }} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} steps: - name: Clone rstan - if: inputs.rstan-source != 'CRAN' && inputs.rstan-source != 'cran' + if: inputs.rstan-source != 'CRAN' uses: actions/checkout@v6 with: - submodules: recursive repository: stan-dev/rstan ref: ${{ inputs.rstan-source }} path: rstan-source - name: Clone StanHeaders - if: inputs.stanheaders-source != 'CRAN' && inputs.stanheaders-source != 'cran' + if: inputs.stanheaders-source != 'CRAN' uses: actions/checkout@v6 with: submodules: recursive @@ -80,37 +79,33 @@ jobs: path: StanHeaders-source - name: Clone rstantools - if: inputs.rstantools-source != 'CRAN' && inputs.rstantools-source != 'cran' + if: inputs.rstantools-source != 'CRAN' uses: actions/checkout@v6 with: - submodules: recursive repository: stan-dev/rstantools ref: ${{ inputs.rstantools-source }} path: rstantools-source - name: Clone loo - if: inputs.loo-source != 'CRAN' && inputs.loo-source != 'cran' + if: inputs.loo-source != 'CRAN' uses: actions/checkout@v6 with: - submodules: recursive repository: stan-dev/loo ref: ${{ inputs.loo-source }} path: loo-source - name: Clone posterior - if: inputs.posterior-source != 'CRAN' && inputs.posterior-source != 'cran' + if: inputs.posterior-source != 'CRAN' uses: actions/checkout@v6 with: - submodules: recursive repository: stan-dev/posterior ref: ${{ inputs.posterior-source }} path: posterior-source - name: Clone bayesplot - if: inputs.bayesplot-source != 'CRAN' && inputs.bayesplot-source != 'cran' + if: inputs.bayesplot-source != 'CRAN' uses: actions/checkout@v6 with: - submodules: recursive repository: stan-dev/bayesplot ref: ${{ inputs.bayesplot-source }} path: bayesplot-source @@ -145,45 +140,61 @@ jobs: dir.create("/tmp/PakLibrary") .libPaths(c("/tmp/PakLibrary", .libPaths())) - system("sudo apt-get install -y libcurl4-openssl-dev") - install.packages(c("pak", "rstantools")) + system("sudo apt-get install -y libcurl4-openssl-dev libglpk-dev") # Lookup and (attempt to) install all dependencies for reverse-dependencies pkg_deps <- tools::package_dependencies(pkg, reverse = TRUE)[[pkg]] rec_deps <- unique(do.call(c, tools::package_dependencies(pkg_deps, which = 'all'))) - install.packages(rec_deps) + + # Undeclared dependency for vignettes + if ("shinymrp" %in% pkg_deps) { + rec_deps <- c(rec_deps, "cmdstanr") + } + + install.packages(c(rec_deps, "pak")) + + if ("cmdstanr" %in% rec_deps) { + install.packages("cmdstanr", repos = c("https://stan-dev.r-universe.dev", getOption("repos"))) + } # Remaining packages will be bioconductor or other non-CRAN packages, install manually rec_deps <- rec_deps[!(rec_deps %in% installed.packages()[,"Package"])] - rec_deps <- rec_deps[!(rec_deps %in% c("blavsam", "eggCountsExtra", "BRugs", "cmdstanr", "INLA", "iwmm", "qs", "rkeops", "mcmcplots"))] - - install.packages('cmdstanr', repos = c('https://stan-dev.r-universe.dev', getOption("repos"))) - pak::pak(c( - paste0("any::", rec_deps), - "github::ecmerkle/blavsam", - "github::CraigWangStat/eggCountsExtra", - "local::/tmp/INLA_25.10.19.tar.gz", - "any::qgraph", - "topipa/iwmm" - )) - - install_dev <- function(pkg, pkg_src) { + + # Ignore archived packages which are still listed as 'Suggests' + rec_deps <- rec_deps[!(rec_deps %in% c("BRugs", "qs", "rkeops", "mcmcplots"))] + + if (length(rec_deps) > 0) { + # Update install locations for Github sources + rec_deps[rec_deps == "blavsam"] <- "github::ecmerkle/blavsam" + rec_deps[rec_deps == "eggCountsExtra"] <- "github::CraigWangStat/eggCountsExtra" + rec_deps[rec_deps == "INLA"] <- "local::/tmp/INLA_25.10.19.tar.gz" + rec_deps[rec_deps == "iwmm"] <- "github::topipa/iwmm" + + pak::pak(rec_deps) + } + + install_stan_pkg <- function(pkg, pkg_src) { if (tolower(pkg_src) == "cran") { install.packages(pkg) } else { - pkg <- ifelse(pkg == "rstan", "rstan/rstan/rstan", - ifelse(pkg == "StanHeaders", "rstan/StanHeaders", pkg)) - pak::pak(paste0("local::./", pkg, "-source")) + if (pkg == "rstan") { + pkg <- "rstan-source/rstan/rstan" + } else if (pkg == "StanHeaders") { + pkg <- "StanHeaders-source/StanHeaders" + } else { + pkg <- paste0(pkg, "-source") + } + pak::pak(paste0("local::./", pkg)) } } # Install dev versions last so that they're not used for any dependencies - install_dev("StanHeaders", "${{ inputs.stanheaders-source }}") - install_dev("rstan", "${{ inputs.rstan-source }}") - install_dev("rstantools", "${{ inputs.rstantools-source }}") - install_dev("loo", "${{ inputs.loo-source }}") - install_dev("posterior", "${{ inputs.posterior-source }}") - install_dev("bayesplot", "${{ inputs.bayesplot-source }}") + install_stan_pkg("StanHeaders", "${{ inputs.stanheaders-source }}") + install_stan_pkg("rstan", "${{ inputs.rstan-source }}") + install_stan_pkg("rstantools", "${{ inputs.rstantools-source }}") + install_stan_pkg("loo", "${{ inputs.loo-source }}") + install_stan_pkg("posterior", "${{ inputs.posterior-source }}") + install_stan_pkg("bayesplot", "${{ inputs.bayesplot-source }}") # Split dependencies into requested batch size and # restructure into format for actions 'matrix' argument @@ -196,11 +207,19 @@ jobs: file=Sys.getenv("GITHUB_OUTPUT"), append = TRUE) shell: Rscript {0} + - name: Generate unique hash key + id: calc-hash + run: | + # Ensure that the key is unique for each combination of inputs within each workflow run + # in case of multiple job runs within the same workflow + echo '${{ toJSON(inputs) }}' > /tmp/inputs.json + echo "cache_key=$(sha256sum /tmp/inputs.json | awk '{print "${{ github.run_id }}-" $1}')" >> "$GITHUB_OUTPUT" + - name: Cache built packages uses: actions/cache/save@v6 with: path: /tmp/PakLibrary/ - key: ${{ github.run_id }} + key: ${{ steps.calc-hash.outputs.cache_key }} reverse-dependencies: needs: prepare-library @@ -208,7 +227,7 @@ jobs: name: Check Reverse Dependencies (Batch ${{ matrix.group }}) strategy: fail-fast: false - max-parallel: 10 + max-parallel: 5 matrix: ${{ fromJson(needs.prepare-library.outputs.deps_matrix) }} env: R_REMOTES_NO_ERRORS_FROM_WARNINGS: true @@ -227,7 +246,7 @@ jobs: uses: actions/cache/restore@v6 with: path: /tmp/PakLibrary/ - key: ${{ github.run_id }} + key: ${{ needs.prepare-library.outputs.cache_key }} - uses: r-lib/actions/setup-r@v2 with: @@ -250,7 +269,13 @@ jobs: sudo apt-get install -y pandoc libudunits2-dev libglpk-dev libgsl0-dev \ texlive-latex-base texlive-latex-recommended texlive-fonts-extra \ texlive-fonts-recommended texlive-luatex texlive-latex-extra \ - texlive-bibtex-extra libproj-dev + texlive-bibtex-extra libproj-dev libglu1-mesa-dev libgdal-dev + + - name: Print Session Info + env: + R_LIBS_USER: /tmp/PakLibrary + run: installed.packages()[,c("Package", "Version")] + shell: Rscript {0} - name: Run reverse-dependency checks env: @@ -263,30 +288,38 @@ jobs: _R_CHECK_SYSTEM_CLOCK_: 0 run: | curr_deps <- jsonlite::fromJSON('${{ matrix.deps }}') + rec_deps <- unique(do.call(c, tools::package_dependencies(curr_deps, which = 'all'))) + if ("shinymrp" %in% curr_deps) { + rec_deps <- c(rec_deps, "cmdstanr") + } # Install any system dependencies needed for packages - rec_deps <- unique(do.call(c, tools::package_dependencies(curr_deps, which = 'all'))) pak::sysreqs_fix_installed(c(curr_deps, rec_deps)) - cmdstanr::install_cmdstan(cores = parallel::detectCores()) + + if ("cmdstanr" %in% c(curr_deps, rec_deps)) { + cmdstanr::install_cmdstan(cores = parallel::detectCores()) + } dir.create("testdir") download.packages(curr_deps, "testdir", repos = "https://cran.rstudio.com") Sys.unsetenv("NOT_CRAN") - tools::check_packages_in_dir("testdir", check_args = "--as-cran --no-manual", Ncpus = parallel::detectCores() - 1) + Ncpus <- ifelse(any(c("ctsem", "blavaan", "rstanarm") %in% curr_deps), 2, 3) + tools::check_packages_in_dir("testdir", check_args = "--as-cran --no-manual", Ncpus = Ncpus) shell: Rscript {0} - uses: actions/upload-artifact@v7 + id: upload-batch-results with: - name: checkresults-${{ matrix.group }} + name: ${{ inputs.package }}-checkresults-${{ needs.prepare-library.outputs.cache_key }}-${{ matrix.group }} path: | testdir/**/00check.log testdir/**/00install.out + testdir/**/*.Rout compression-level: 9 - retention-days: 1 - + summarise-results: name: Summarise Results - needs: reverse-dependencies + needs: [prepare-library, reverse-dependencies] runs-on: ubuntu-latest steps: @@ -294,7 +327,7 @@ jobs: uses: actions/download-artifact@v7 with: path: checkresults - pattern: checkresults-* + pattern: ${{ inputs.package }}-checkresults-${{ needs.prepare-library.outputs.cache_key }}-* merge-multiple: true - uses: r-lib/actions/setup-r@v2 @@ -303,15 +336,18 @@ jobs: - name: Generate results summary output run: | + install.packages(c("knitr", "gh")) + setwd("checkresults") - install.packages("knitr") - res <- tools::check_packages_in_dir_details(".") all_pkgs <- gsub("^\\./(.*)\\.Rcheck", "\\1", list.dirs(".", recursive = FALSE)) + res <- tools::check_packages_in_dir_details(".") + install_failures <- unique(res$Package[res$Check == "whether package can be installed"]) install_failures <- install_failures[order(install_failures)] check_statuses <- c("tests", "re-building of vignette outputs", "examples", "examples with --run-donttest") check_failures <- unique(res$Package[res$Check %in% check_statuses]) check_failures <- check_failures[order(check_failures)] + summary_tbl <- knitr::kable( data.frame( "OK" = length(setdiff(all_pkgs, c(install_failures, check_failures))), @@ -320,43 +356,50 @@ jobs: check.names = FALSE ) ) - install_logs <- sapply(install_failures, \(pkg) { - paste0(c( - paste("###", pkg), - "
", - "Install Log", - "", - "```", - paste0(readLines(paste0(pkg, ".Rcheck/00install.out")), collapse = "\n"), - "```", - "", - "
\n"), - collapse = "\n" - ) - }) |> - paste0(collapse = "\n") - check_logs <- sapply(check_failures, \(pkg) { - paste0(c( - "", - paste("###", pkg), - "", - "
", - "Check Log", - "", - "```", - paste0(readLines(paste0(pkg, ".Rcheck/00check.log")), collapse = "\n"), - "```", - "", - "
\n"), - collapse = "\n" - ) - }) |> - paste0(collapse = "\n") + + install_logs <- paste0( + sapply(install_failures, \(pkg) { + paste0(c( + paste("###", pkg), + "
", + "Install Log", + "", + "```", + paste0(readLines(paste0(pkg, ".Rcheck/00install.out")), collapse = "\n"), + "```", + "", + "
\n"), + collapse = "\n" + ) + }), + collapse = "\n" + ) + + check_logs <- paste0( + sapply(check_failures, \(pkg) { + paste0(c( + "", + paste("###", pkg), + "", + "
", + "Check Log", + "", + "```", + paste0(readLines(paste0(pkg, ".Rcheck/00check.log")), collapse = "\n"), + "```", + "", + "
\n"), + collapse = "\n" + ) + }), + collapse = "\n" + ) + result_md <- paste0(c( - "# Reverse-Dependency Status\n", + "# Reverse-Dependency Status (${{ inputs.package }})\n", "## Summary\n", summary_tbl, - "## Install Failure\n", + "## Install Failures\n", install_logs, "## Vignette/Test Failures\n", check_logs), @@ -368,6 +411,6 @@ jobs: - name: Upload combined results uses: actions/upload-artifact@v7 with: - name: all-logs + name: ${{ inputs.package }}-all-logs-${{ needs.prepare-library.outputs.cache_key }} path: checkresults compression-level: 9 From cc777a6e6084c823c6ee461fa6883a813057d2fb Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 20 Jul 2026 23:37:11 +0800 Subject: [PATCH 3/7] Also check reverse suggests --- .github/workflows/revdeps.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/revdeps.yml b/.github/workflows/revdeps.yml index fa6856f..e5122f3 100644 --- a/.github/workflows/revdeps.yml +++ b/.github/workflows/revdeps.yml @@ -143,7 +143,7 @@ jobs: system("sudo apt-get install -y libcurl4-openssl-dev libglpk-dev") # Lookup and (attempt to) install all dependencies for reverse-dependencies - pkg_deps <- tools::package_dependencies(pkg, reverse = TRUE)[[pkg]] + pkg_deps <- tools::package_dependencies(pkg, reverse = TRUE, which = "all")[[pkg]] rec_deps <- unique(do.call(c, tools::package_dependencies(pkg_deps, which = 'all'))) # Undeclared dependency for vignettes From 5b7d6e1692bdefe5b591509b18d2ae01e12f2976 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 20 Jul 2026 23:56:25 +0800 Subject: [PATCH 4/7] Additional dependencies for reverse suggests --- .github/workflows/revdeps.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/revdeps.yml b/.github/workflows/revdeps.yml index e5122f3..659d426 100644 --- a/.github/workflows/revdeps.yml +++ b/.github/workflows/revdeps.yml @@ -156,12 +156,15 @@ jobs: if ("cmdstanr" %in% rec_deps) { install.packages("cmdstanr", repos = c("https://stan-dev.r-universe.dev", getOption("repos"))) } + if ("survHEhmc" %in% rec_deps) { + install.packages("survHEhmc", repos = c("https://giabaio.r-universe.dev/bin/linux/noble-x86_64/4.6/", getOption("repos"))) + } # Remaining packages will be bioconductor or other non-CRAN packages, install manually rec_deps <- rec_deps[!(rec_deps %in% installed.packages()[,"Package"])] # Ignore archived packages which are still listed as 'Suggests' - rec_deps <- rec_deps[!(rec_deps %in% c("BRugs", "qs", "rkeops", "mcmcplots"))] + rec_deps <- rec_deps[!(rec_deps %in% c("BRugs", "qs", "rkeops", "mcmcplots", "pryr", "glmmADMB"))] if (length(rec_deps) > 0) { # Update install locations for Github sources @@ -169,6 +172,9 @@ jobs: rec_deps[rec_deps == "eggCountsExtra"] <- "github::CraigWangStat/eggCountsExtra" rec_deps[rec_deps == "INLA"] <- "local::/tmp/INLA_25.10.19.tar.gz" rec_deps[rec_deps == "iwmm"] <- "github::topipa/iwmm" + rec_deps[rec_deps == "bridgestan"] <- "github::roualdes/bridgestan/R" + rec_deps[rec_deps == "infinitefactor"] <- "github::poworoznek/infinitefactor" + rec_deps[rec_deps == "survHEinla"] <- "github::giabaio/survHEinla" pak::pak(rec_deps) } From 9172e3bc39049c32d5f6e2f89c89ae07bca85878 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 21 Jul 2026 00:10:38 +0800 Subject: [PATCH 5/7] Additional unavailable suggests --- .github/workflows/revdeps.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/revdeps.yml b/.github/workflows/revdeps.yml index 659d426..a47db5b 100644 --- a/.github/workflows/revdeps.yml +++ b/.github/workflows/revdeps.yml @@ -156,15 +156,12 @@ jobs: if ("cmdstanr" %in% rec_deps) { install.packages("cmdstanr", repos = c("https://stan-dev.r-universe.dev", getOption("repos"))) } - if ("survHEhmc" %in% rec_deps) { - install.packages("survHEhmc", repos = c("https://giabaio.r-universe.dev/bin/linux/noble-x86_64/4.6/", getOption("repos"))) - } # Remaining packages will be bioconductor or other non-CRAN packages, install manually rec_deps <- rec_deps[!(rec_deps %in% installed.packages()[,"Package"])] # Ignore archived packages which are still listed as 'Suggests' - rec_deps <- rec_deps[!(rec_deps %in% c("BRugs", "qs", "rkeops", "mcmcplots", "pryr", "glmmADMB"))] + rec_deps <- rec_deps[!(rec_deps %in% c("BRugs", "qs", "rkeops", "mcmcplots", "pryr", "glmmADMB", "survHEhmc"))] if (length(rec_deps) > 0) { # Update install locations for Github sources From 46f5981f7381c1a2d8e43215e7cca24c68eade4c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 21 Jul 2026 19:08:42 +0800 Subject: [PATCH 6/7] System dependencies --- .github/workflows/revdeps.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/revdeps.yml b/.github/workflows/revdeps.yml index a47db5b..5327596 100644 --- a/.github/workflows/revdeps.yml +++ b/.github/workflows/revdeps.yml @@ -126,6 +126,12 @@ jobs: with: r-version: 'release' + - name: Install system dependencies + run: | + sudo apt-get install -y pandoc libudunits2-dev libglpk-dev libgsl0-dev \ + libproj-dev libglu1-mesa-dev libgdal-dev libgl1-mesa-dev libmecab-dev \ + libcurl4-openssl-dev + - name: Download recursive dependencies, batch dependencies to check id: dep-batches run: | @@ -140,8 +146,6 @@ jobs: dir.create("/tmp/PakLibrary") .libPaths(c("/tmp/PakLibrary", .libPaths())) - system("sudo apt-get install -y libcurl4-openssl-dev libglpk-dev") - # Lookup and (attempt to) install all dependencies for reverse-dependencies pkg_deps <- tools::package_dependencies(pkg, reverse = TRUE, which = "all")[[pkg]] rec_deps <- unique(do.call(c, tools::package_dependencies(pkg_deps, which = 'all'))) @@ -272,7 +276,8 @@ jobs: sudo apt-get install -y pandoc libudunits2-dev libglpk-dev libgsl0-dev \ texlive-latex-base texlive-latex-recommended texlive-fonts-extra \ texlive-fonts-recommended texlive-luatex texlive-latex-extra \ - texlive-bibtex-extra libproj-dev libglu1-mesa-dev libgdal-dev + texlive-bibtex-extra libproj-dev libglu1-mesa-dev libgdal-dev \ + libgl1-mesa-dev libmecab-dev - name: Print Session Info env: From 667c13f9347173166ea6ffd0d59dd67d5407cc5f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 21 Jul 2026 20:35:40 +0800 Subject: [PATCH 7/7] Ignore spurious install warnings --- .github/workflows/revdeps.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/revdeps.yml b/.github/workflows/revdeps.yml index 5327596..df860f5 100644 --- a/.github/workflows/revdeps.yml +++ b/.github/workflows/revdeps.yml @@ -344,13 +344,13 @@ jobs: - name: Generate results summary output run: | - install.packages(c("knitr", "gh")) + install.packages("knitr") setwd("checkresults") all_pkgs <- gsub("^\\./(.*)\\.Rcheck", "\\1", list.dirs(".", recursive = FALSE)) res <- tools::check_packages_in_dir_details(".") - install_failures <- unique(res$Package[res$Check == "whether package can be installed"]) + install_failures <- unique(res$Package[res$Check == "whether package can be installed" & res$Status == "ERROR"]) install_failures <- install_failures[order(install_failures)] check_statuses <- c("tests", "re-building of vignette outputs", "examples", "examples with --run-donttest") check_failures <- unique(res$Package[res$Check %in% check_statuses])