From c69cd33d0be15dec4add6ed1ca5c3457f867a3d6 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Wed, 1 Apr 2026 05:24:46 -0500 Subject: [PATCH 1/5] infra: use pkgskills containers --- .github/workflows/R-CMD-check.yaml | 8 ++- .github/workflows/copilot-setup-steps.yml | 3 +- .github/workflows/install/action.yml | 80 ++++++++++++++++++++++- .github/workflows/pkgdown.yaml | 4 +- .github/workflows/pr-commands.yaml | 4 +- .github/workflows/qcthat.yaml | 4 +- .github/workflows/test-coverage.yaml | 4 +- 7 files changed, 97 insertions(+), 10 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 3f1cc98b..25cb2c5e 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -11,6 +11,7 @@ name: R-CMD-check jobs: R-CMD-check: runs-on: ${{ matrix.config.os }} + container: ${{ matrix.config.container }} name: ${{ matrix.config.os }} (${{ matrix.config.r }}) @@ -20,15 +21,16 @@ jobs: config: - {os: macos-latest, r: 'release'} - {os: windows-latest, r: 'release'} - - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - - {os: ubuntu-latest, r: 'release'} - - {os: ubuntu-latest, r: 'oldrel-1'} + - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release', container: 'ghcr.io/api2r/pkgskills-ci:devel'} + - {os: ubuntu-latest, r: 'release', container: 'ghcr.io/api2r/pkgskills-ci:release'} + - {os: ubuntu-latest, r: 'oldrel-1', container: 'ghcr.io/api2r/pkgskills-ci:oldrel-1'} steps: - uses: actions/checkout@v6 - uses: ./.github/workflows/install with: + use-container: "${{ matrix.config.container != '' }}" token: ${{ secrets.GITHUB_TOKEN }} r-version: ${{ matrix.config.r }} http-user-agent: ${{ matrix.config.http-user-agent }} diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 2fca7101..1e644a87 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -29,8 +29,8 @@ jobs: needs: build, check, website # Extra packages include things referenced in skills, to make sure the # agent has them available. + optional-packages: any::astgrepr extra-packages: >- - any::astgrepr any::cli any::covr any::devtools @@ -44,6 +44,7 @@ jobs: any::testthat any::usethis any::withr + gilead-biostats/qcthat local::. - name: Install air diff --git a/.github/workflows/install/action.yml b/.github/workflows/install/action.yml index 95c460b9..c64a16d7 100644 --- a/.github/workflows/install/action.yml +++ b/.github/workflows/install/action.yml @@ -20,26 +20,102 @@ inputs: description: "Extra packages, passed to r-lib/actions/setup-r-dependencies@v2" required: false default: any::rcmdcheck + optional-packages: + description: "Optional extra packages; installed individually, warn (not error) on failure" + required: false + default: "" cache-version: description: "Cache version for r-lib/actions/setup-r-dependencies@v2" required: false default: "1" + use-container: + description: "Set to 'true' when running inside a pre-warmed container (skips R and pandoc setup)" + required: false + default: "false" runs: using: "composite" steps: - - uses: r-lib/actions/setup-pandoc@v2 + - if: inputs.use-container != 'true' + uses: r-lib/actions/setup-pandoc@v2 - uses: r-lib/actions/setup-r@v2 with: + install-r: ${{ inputs.use-container != 'true' }} r-version: ${{ inputs.r-version }} http-user-agent: ${{ inputs.http-user-agent }} use-public-rspm: true - - uses: r-lib/actions/setup-r-dependencies@v2 + # In pre-warmed containers every package may already be in the system + # library, so nothing is ever written to R_LIBS_USER. Ensure the + # directory exists with a sentinel so the actions/cache glob matches + # and the "Path Validation Error" warning doesn't fire at cache-save. + - if: inputs.use-container == 'true' + name: Ensure R_LIBS_USER exists + shell: bash + run: | + lib="$(Rscript -e 'cat(Sys.getenv("R_LIBS_USER"))')" + mkdir -p "$lib" + touch "$lib/.keep" + + # Container path: call pak::pak() directly so it checks .libPaths() and + # only installs/updates packages that are genuinely missing or outdated. + # setup-r-dependencies always runs a full lockfile-create → lockfile-install + # cycle which reinstalls everything even when the container already has it. + - if: inputs.use-container == 'true' + name: Update R package dependencies (container) + shell: Rscript {0} + env: + GITHUB_PAT: ${{ inputs.token }} + run: | + # *Remove* this package from the container. Only relevant for stbl. + remove.packages("stbl") + + # Replicate needs → Config/Needs/ expansion from r-lib/actions + needs_parts <- strsplit("${{ inputs.needs }}", "[[:space:],]+")[[1]] + needs_parts <- needs_parts[nzchar(needs_parts)] + needs <- sprintf("Config/Needs/%s", needs_parts) + + extra_deps <- strsplit("${{ inputs.extra-packages }}", "[[:space:],]+")[[1]] + extra_deps <- extra_deps[nzchar(extra_deps)] + + # Over-protect for strangeness in the container + if (!requireNamespace("pak", quietly = TRUE)) { + install.packages("pak") + } + + pak::pak( + c("deps::.", extra_deps), + dependencies = c(needs, "all"), + upgrade = TRUE, + ask = FALSE + ) + + # Non-container path: standard setup-r-dependencies flow with caching. + - if: inputs.use-container != 'true' + uses: r-lib/actions/setup-r-dependencies@v2 env: GITHUB_PAT: ${{ inputs.token }} with: needs: ${{ inputs.needs }} extra-packages: ${{ inputs.extra-packages }} cache-version: ${{ inputs.cache-version }} + + - if: inputs.optional-packages != '' + name: Install optional packages + shell: Rscript {0} + env: + GITHUB_PAT: ${{ inputs.token }} + run: | + pkgs <- strsplit("${{ inputs.optional-packages }}", "\\s+")[[1]] + pkgs <- pkgs[nzchar(pkgs)] + for (pkg in pkgs) { + tryCatch( + pak::pak(pkg), + error = function(e) { + msg <- sprintf("Failed to install optional package %s: %s", pkg, conditionMessage(e)) + msg <- gsub("[\r\n]+", " ", msg) + cat("::warning::", msg, "\n", sep = "") + } + ) + } diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 5cfcf89c..441bbf70 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -14,6 +14,8 @@ name: pkgdown jobs: pkgdown: runs-on: ubuntu-latest + container: + image: ghcr.io/api2r/pkgskills-ci:release # Only restrict concurrency for non-PR jobs concurrency: group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} @@ -33,10 +35,10 @@ jobs: - uses: ./.github/workflows/install with: + use-container: "true" token: ${{ secrets.GITHUB_TOKEN }} needs: website extra-packages: any::pkgdown gilead-biostats/qcthat local::. - cache-version: "1" - name: Build site run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) diff --git a/.github/workflows/pr-commands.yaml b/.github/workflows/pr-commands.yaml index 1881e2a9..8c94262a 100644 --- a/.github/workflows/pr-commands.yaml +++ b/.github/workflows/pr-commands.yaml @@ -11,6 +11,8 @@ jobs: if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} name: document runs-on: ubuntu-latest + container: + image: ghcr.io/api2r/pkgskills-ci:release permissions: contents: write steps: @@ -22,10 +24,10 @@ jobs: - uses: ./.github/workflows/install with: + use-container: "true" token: ${{ secrets.GITHUB_TOKEN }} needs: pr-document extra-packages: any::roxygen2 - cache-version: "1" - name: Document run: roxygen2::roxygenise() diff --git a/.github/workflows/qcthat.yaml b/.github/workflows/qcthat.yaml index 16695bdd..a6ea54c7 100644 --- a/.github/workflows/qcthat.yaml +++ b/.github/workflows/qcthat.yaml @@ -48,6 +48,8 @@ env: jobs: qcthat: runs-on: ubuntu-latest + container: + image: ghcr.io/api2r/pkgskills-ci:release if: >- (github.event_name == 'issues' && contains(github.event.issue.labels.*.name, 'qcthat-uat')) || github.event_name != 'issues' @@ -58,9 +60,9 @@ jobs: - uses: ./.github/workflows/install with: + use-container: "true" token: ${{ secrets.GITHUB_TOKEN }} extra-packages: Gilead-BioStats/qcthat@main local::. - cache-version: "1" - name: Manage User Acceptance Testing if: >- diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index f4be0183..6878a499 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -11,15 +11,17 @@ name: test-coverage jobs: test-coverage: runs-on: ubuntu-latest + container: + image: ghcr.io/api2r/pkgskills-ci:release steps: - uses: actions/checkout@v6 - uses: ./.github/workflows/install with: + use-container: "true" token: ${{ secrets.GITHUB_TOKEN }} needs: coverage extra-packages: any::covr any::xml2 - cache-version: "1" - name: Test coverage run: | From 09e811b3f8e24e52d03520902bbefd7e495928c1 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Wed, 1 Apr 2026 05:41:17 -0500 Subject: [PATCH 2/5] infra: suggestion from copilot code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/install/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/install/action.yml b/.github/workflows/install/action.yml index c64a16d7..936be1fb 100644 --- a/.github/workflows/install/action.yml +++ b/.github/workflows/install/action.yml @@ -68,8 +68,8 @@ runs: env: GITHUB_PAT: ${{ inputs.token }} run: | - # *Remove* this package from the container. Only relevant for stbl. - remove.packages("stbl") + # *Remove* this package from the container if present. Only relevant for stbl. + try(remove.packages("stbl"), silent = TRUE) # Replicate needs → Config/Needs/ expansion from r-lib/actions needs_parts <- strsplit("${{ inputs.needs }}", "[[:space:],]+")[[1]] From a3b58e79443289c6c49e993768fc7afb4f0fd9c9 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Wed, 1 Apr 2026 05:43:40 -0500 Subject: [PATCH 3/5] infra: don't set dev mode for prs --- .github/workflows/pkgdown.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 441bbf70..fe42c2cf 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -27,12 +27,6 @@ jobs: steps: - uses: actions/checkout@v6 - - name: Set Development Mode Env Var - if: github.ref_name != 'main' - run: | - echo "Ref is '${{ github.ref_name }}', setting PKGDOWN_DEV_MODE=devel" - echo "PKGDOWN_DEV_MODE=devel" >> $GITHUB_ENV - - uses: ./.github/workflows/install with: use-container: "true" From 2e8c2709d654499bfc7076861353b52f2eb2fdab Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Wed, 1 Apr 2026 05:45:19 -0500 Subject: [PATCH 4/5] infra: correct url for pr site --- .github/workflows/pkgdown.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index fe42c2cf..79a6437a 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -54,7 +54,7 @@ jobs: strOwner <- tolower(qcthat::GetGHOwner()) strRepo <- qcthat::GetGHRepo() strURL <- glue::glue( - "https://{strOwner}.github.io/{strRepo}/pr/{intPRNumber}/dev" + "https://{strOwner}.github.io/{strRepo}/pr/{intPRNumber}" ) print(paste("🌐 URL:", strURL)) qcthat::CommentIssue( From f1176270f51d3b732365c6fcc567a727232de5d6 Mon Sep 17 00:00:00 2001 From: Jon Harmon Date: Wed, 1 Apr 2026 05:48:32 -0500 Subject: [PATCH 5/5] infra: url is still properly dev --- .github/workflows/pkgdown.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 79a6437a..fe42c2cf 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -54,7 +54,7 @@ jobs: strOwner <- tolower(qcthat::GetGHOwner()) strRepo <- qcthat::GetGHRepo() strURL <- glue::glue( - "https://{strOwner}.github.io/{strRepo}/pr/{intPRNumber}" + "https://{strOwner}.github.io/{strRepo}/pr/{intPRNumber}/dev" ) print(paste("🌐 URL:", strURL)) qcthat::CommentIssue(