From 2bdb36e1632e6c9954983e36f50ea24bb4e7259d Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 17 May 2024 16:36:11 -0400 Subject: [PATCH] Install things as necessary from shinycoreci r-universe (#260) --- .Rbuildignore | 1 + .Rprofile | 0 .../internal/install-shinyverse/action.yaml | 46 +-- .github/shiny-workflows/package-install.R | 9 +- .github/workflows/apps-config.yml | 2 +- .github/workflows/apps-deploy.yml | 7 +- .github/workflows/apps-deps.yml | 5 +- .github/workflows/apps-docker.yml | 2 +- .github/workflows/apps-test-matrix.yml | 107 ++---- .github/workflows/apps-test-os.yml | 64 +--- .github/workflows/apps-test-precheck.yml | 4 +- .github/workflows/build-results.yml | 4 +- .github/workflows/install-debug.yaml | 2 +- .github/workflows/trim-old-branches.yml | 2 +- DESCRIPTION | 6 +- NAMESPACE | 4 +- R/data-apps-deps.R | 250 +++++++++---- R/data-shinyverse.R | 64 ++-- R/deploy-apps.R | 59 +--- R/install-path.R | 25 +- R/install.R | 329 ++++++++++-------- R/sysinfo.R | 2 +- R/test-in-browser.R | 35 +- R/test-in-external.R | 116 +++--- R/test-in-ide.R | 101 +++--- R/test-in-local.R | 83 ++--- R/view-test-images.R | 2 + README.Rmd | 16 +- README.md | 72 ++-- inst/Docker/centos/Dockerfile | 22 +- inst/Docker/ubuntu/Dockerfile | 23 +- inst/apps/.renvignore | 26 ++ inst/apps/000-pkg-versions/app.R | 117 +++++++ inst/apps/000-pkg-versions/tests/testthat.R | 1 + .../000-pkg-versions/tests/testthat/setup.R | 2 + .../tests/testthat/test-true.R | 5 + inst/apps/094-image-interaction-basic/app.R | 5 +- inst/gha/data-apps-deps-update.R | 31 +- inst/gha/gha-adjust-packages-to-install.R | 36 -- inst/gha/gha-shinyverse-packages.R | 42 --- man/deploy_apps.Rd | 4 +- man/resolve_libpath.Rd | 15 + man/shinycoreci_libpath.Rd | 21 ++ man/shinyverse_libpath.Rd | 21 -- man/test_in_browser.Rd | 4 +- man/test_in_ide.Rd | 4 +- man/test_in_local.Rd | 8 +- man/write_sysinfo.Rd | 2 +- 48 files changed, 961 insertions(+), 847 deletions(-) delete mode 100644 .Rprofile create mode 100644 inst/apps/.renvignore create mode 100644 inst/apps/000-pkg-versions/app.R create mode 100644 inst/apps/000-pkg-versions/tests/testthat.R create mode 100644 inst/apps/000-pkg-versions/tests/testthat/setup.R create mode 100644 inst/apps/000-pkg-versions/tests/testthat/test-true.R delete mode 100644 inst/gha/gha-adjust-packages-to-install.R delete mode 100644 inst/gha/gha-shinyverse-packages.R create mode 100644 man/resolve_libpath.Rd create mode 100644 man/shinycoreci_libpath.Rd delete mode 100644 man/shinyverse_libpath.Rd diff --git a/.Rbuildignore b/.Rbuildignore index 5e8ac7cb1f..3f86e672e7 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -11,6 +11,7 @@ ^todo\.md$ ^inst/apps/.*/tests/.* +^inst/apps/\.renvignore$ \.jshintrc$ www/nvd3/deprecated ^_pkgdown\.yml$ diff --git a/.Rprofile b/.Rprofile deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/.github/internal/install-shinyverse/action.yaml b/.github/internal/install-shinyverse/action.yaml index 0d47fd344b..71957fb4d0 100644 --- a/.github/internal/install-shinyverse/action.yaml +++ b/.github/internal/install-shinyverse/action.yaml @@ -20,7 +20,7 @@ inputs: required: false pandoc-version: description: Pandoc version to pass through - default: "2.7.3" + default: "3.x" required: false runs: using: "composite" @@ -40,54 +40,12 @@ runs: sudo apt-get update sudo apt-get install -y libglpk-dev - - name: Gather remotes and app deps - id: shinyverse - shell: Rscript {0} - run: | - source("inst/gha/gha-shinyverse-packages.R") - pkgs <- shinyverse_pkgs() - message("Shinyverse packages:\n", gsub(",", ",\n", pkgs)) - cat("packages=", pkgs, "\n", file = Sys.getenv("GITHUB_OUTPUT"), sep = "", append = TRUE) - - - name: Rtools version - if: runner.os == 'Windows' - id: rtools-version - shell: Rscript {0} - run: | - # Get R version like `"4.2"` - short_r_version <- sub("\\.\\d$", "", "${{ inputs.r-version }}") - # For R versions >= 4.2, use Rtools v42 - # Otherwise leave blank - if ( - package_version("${{ inputs.r-version }}") >= package_version("4.2") - ) { - message("Using Rtools v42") - cat("value=42\n", file = Sys.getenv("GITHUB_OUTPUT"), sep = "", append = TRUE) - } else { - message("Not setting Rtools version!") - } - - - name: Adjust packages to install - id: pkgs - shell: Rscript {0} - run: | - source("inst/gha/gha-adjust-packages-to-install.R") - pkgs_to_install <- adjust_pkgs( - "${{ steps.shinyverse.outputs.packages }}", - r_version = "${{ inputs.r-version }}" - ) - message("Final packages:\n", gsub(",", ",\n", pkgs_to_install)) - cat("to-install=", pkgs_to_install, "\n", file = Sys.getenv("GITHUB_OUTPUT"), sep = "", append = TRUE) - - - name: Install R, shinycoreci, and shinyverse + - name: Install R and shinycoreci uses: rstudio/shiny-workflows/setup-r-package@v1 with: r-version: ${{ inputs.r-version }} cache-version: ${{ inputs.cache-version }} http-user-agent: ${{ inputs.http-user-agent }} pandoc-version: ${{ inputs.pandoc-version }} - rtools-version: ${{ steps.rtools-version.outputs.value }} extra-packages: - local::. - ${{ steps.pkgs.outputs.to-install }} ${{ inputs.extra-packages }} diff --git a/.github/shiny-workflows/package-install.R b/.github/shiny-workflows/package-install.R index 6a2ccafb16..00f7ddacd9 100644 --- a/.github/shiny-workflows/package-install.R +++ b/.github/shiny-workflows/package-install.R @@ -1,5 +1,4 @@ - -if (!requireNamespace("digest", quietly = TRUE)) { - # Need for url remotes. See https://github.com/r-lib/actions/issues/562#issuecomment-1129088041 - install.packages("digest", repos = "http://cran.us.r-project.org") -} +# if (!requireNamespace("digest", quietly = TRUE)) { +# # Need for url remotes. See https://github.com/r-lib/actions/issues/562#issuecomment-1129088041 +# install.packages("digest", repos = "http://cran.us.r-project.org") +# } diff --git a/.github/workflows/apps-config.yml b/.github/workflows/apps-config.yml index 6008ba8d27..4ab793ce16 100644 --- a/.github/workflows/apps-config.yml +++ b/.github/workflows/apps-config.yml @@ -18,7 +18,7 @@ on: value: "ubuntu-20.04" cache-version: description: cache-version to be used when pulling library cache - value: "3" + value: "5" devel: description: Resolved r devel version # When R 4.3 was released, R version 4.4.0 was not recognized. diff --git a/.github/workflows/apps-deploy.yml b/.github/workflows/apps-deploy.yml index 00468c5566..d65841f3ae 100644 --- a/.github/workflows/apps-deploy.yml +++ b/.github/workflows/apps-deploy.yml @@ -38,14 +38,12 @@ jobs: retry: 3 # extra_app_text: ", apps = c('000-all', '000-manual', '300-bs-themer')" - ## Deployment server has been disabled - os: "${{ needs.config.outputs.ubuntu }}" r: "${{ needs.config.outputs.oldrel1 }}" type: "connect" account: "barret" server_name: "rsc.radixu.com" server_url: "https://rsc.radixu.com/__api__" - # rspm: "https://demo.rstudiopm.com/all/__linux__/focal/latest" cores: 1 retry: 3 # extra_app_text: ", apps = c('000-all', '000-manual', '300-bs-themer')" @@ -56,7 +54,7 @@ jobs: # RSPM: ${{ matrix.config.rspm }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: GitHub Pull - name: R Options @@ -65,12 +63,11 @@ jobs: echo 'MAKEFLAGS=-j2' >> .Renviron - name: Install R, shinycoreci, and shinyverse - timeout-minutes: 45 + timeout-minutes: 30 # Should be < 10 mins uses: ./.github/internal/install-shinyverse with: r-version: ${{ matrix.config.r }} cache-version: deploy-2-${{ needs.config.outputs.cache-version }} - extra-packages: rstudio/rsconnect # Perform as second step to make sure this version is installed - name: Install shinycoreci from GitHub diff --git a/.github/workflows/apps-deps.yml b/.github/workflows/apps-deps.yml index 9a9eec082c..106448224f 100644 --- a/.github/workflows/apps-deps.yml +++ b/.github/workflows/apps-deps.yml @@ -32,14 +32,14 @@ jobs: steps: - name: GitHub Pull (PR) if: github.event_name == 'pull_request' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # check out the commit sha, not the merged sha. Avoids the "Merge SHA_A into SHA_B" into commits ref: ${{ github.event.pull_request.head.sha }} # # Ref: https://github.com/actions/checkout/pull/115/files#diff-04c6e90faac2675aa89e2176d2eec7d8R203-R209 fetch-depth: 0 - name: GitHub Pull (Branch) if: github.event_name != 'pull_request' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -53,6 +53,7 @@ jobs: options(repos = c(CRAN="https://cran.rstudio.com/")) source("inst/gha/data-apps-deps-update.R") update_apps_deps() + update_shinyverse() - name: Display file contents run: | diff --git a/.github/workflows/apps-docker.yml b/.github/workflows/apps-docker.yml index 0fab9283af..8cab406050 100644 --- a/.github/workflows/apps-docker.yml +++ b/.github/workflows/apps-docker.yml @@ -65,7 +65,7 @@ jobs: echo "sha: $SHORT_SHA" echo "sha=$SHORT_SHA" >> $GITHUB_OUTPUT - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 if: steps.can-build.conclusion != 'skipped' - name: Log in to the Container registry diff --git a/.github/workflows/apps-test-matrix.yml b/.github/workflows/apps-test-matrix.yml index 092bfbfda8..9253117eb8 100644 --- a/.github/workflows/apps-test-matrix.yml +++ b/.github/workflows/apps-test-matrix.yml @@ -25,184 +25,127 @@ jobs: uses: ./.github/workflows/apps-config.yml macos-release: - if: ${{ ! cancelled() }} - needs: [config, ubuntu-release] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.release }} os: ${{ needs.config.outputs.macos }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} macos-oldrel-1: - if: ${{ ! cancelled() }} - needs: [config, ubuntu-oldrel-1] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.oldrel1 }} os: ${{ needs.config.outputs.macos }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} macos-oldrel-2: - if: ${{ ! cancelled() }} - needs: [config, ubuntu-oldrel-2] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.oldrel2 }} os: ${{ needs.config.outputs.macos }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} macos-oldrel-3: - if: ${{ ! cancelled() }} - needs: [config, ubuntu-oldrel-3] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.oldrel3 }} os: ${{ needs.config.outputs.macos }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} macos-oldrel-4: - if: ${{ ! cancelled() }} - needs: [config, ubuntu-oldrel-4] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.oldrel4 }} os: ${{ needs.config.outputs.macos }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} ubuntu-release: - needs: [precheck, config] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.release }} os: ${{ needs.config.outputs.ubuntu }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} ubuntu-oldrel-1: - needs: [precheck, config] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.oldrel1 }} os: ${{ needs.config.outputs.ubuntu }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} ubuntu-oldrel-2: - needs: [precheck, config] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.oldrel2 }} os: ${{ needs.config.outputs.ubuntu }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} ubuntu-oldrel-3: - needs: [precheck, config] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.oldrel3 }} os: ${{ needs.config.outputs.ubuntu }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} ubuntu-oldrel-4: - needs: [precheck, config] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.oldrel4 }} os: ${{ needs.config.outputs.ubuntu }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} windows-release: - if: ${{ ! cancelled() }} - needs: [config, macos-release] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.release }} os: ${{ needs.config.outputs.windows }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} windows-oldrel-1: - if: ${{ ! cancelled() }} - needs: [config, macos-oldrel-1] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.oldrel1 }} os: ${{ needs.config.outputs.windows }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} windows-oldrel-2: - if: ${{ ! cancelled() }} - needs: [config, macos-oldrel-2] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.oldrel2 }} os: ${{ needs.config.outputs.windows }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} windows-oldrel-3: - if: ${{ ! cancelled() }} - needs: [config, macos-oldrel-3] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.oldrel3 }} os: ${{ needs.config.outputs.windows }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} windows-oldrel-4: - if: ${{ ! cancelled() }} - needs: [config, macos-oldrel-4] + needs: [config] uses: ./.github/workflows/apps-test-os.yml with: r-version: ${{ needs.config.outputs.oldrel4 }} os: ${{ needs.config.outputs.windows }} cache-version: ${{ needs.config.outputs.cache-version }} - secrets: - SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }} - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} build-results: uses: ./.github/workflows/build-results.yml - if: ${{ (! cancelled()) && (github.event_name != 'pull_request') }} needs: - ## Comment for now to reduce requirements as we know the last round is windows - # - macos-release - # - macos-oldrel-1 - # - macos-oldrel-2 - # - macos-oldrel-3 - # - macos-oldrel-4 + - macos-release + - macos-oldrel-1 + - macos-oldrel-2 + - macos-oldrel-3 + - macos-oldrel-4 - # - ubuntu-release - # - ubuntu-oldrel-1 - # - ubuntu-oldrel-2 - # - ubuntu-oldrel-3 - # - ubuntu-oldrel-4 + - ubuntu-release + - ubuntu-oldrel-1 + - ubuntu-oldrel-2 + - ubuntu-oldrel-3 + - ubuntu-oldrel-4 - windows-release - windows-oldrel-1 diff --git a/.github/workflows/apps-test-os.yml b/.github/workflows/apps-test-os.yml index 565e99b6af..cc6664573b 100644 --- a/.github/workflows/apps-test-os.yml +++ b/.github/workflows/apps-test-os.yml @@ -27,17 +27,12 @@ on: required: false pandoc-version: type: string - default: "2.7.3" + default: "3.x" required: false # rtools-35: # type: boolean # default: true # required: false - secrets: - SLACK_CHANNEL_ID: - required: true - SLACK_BOT_TOKEN: - required: true name: Test apps - single @@ -95,14 +90,14 @@ jobs: - name: GitHub Pull (PR) if: github.event_name == 'pull_request' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # check out the commit sha, not the merged sha. Avoids the "Merge SHA_A into SHA_B" into commits ref: ${{ github.event.pull_request.head.sha }} # # Ref: https://github.com/actions/checkout/pull/115/files#diff-04c6e90faac2675aa89e2176d2eec7d8R203-R209 fetch-depth: 0 - name: GitHub Pull (Branch) if: github.event_name != 'pull_request' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -116,7 +111,7 @@ jobs: - name: Collect changed file names if: github.event_name == 'pull_request' id: files_changed - uses: Ana06/get-changed-files@v2.2.0 + uses: Ana06/get-changed-files@v2.3.0 with: format: "json" filter: "*" @@ -126,19 +121,8 @@ jobs: run: | echo 'MAKEFLAGS=-j2' >> .Renviron - - name: Notify slack INSTALL - uses: voxmedia/github-action-slack-notify-build@v1 - if: success() - id: slack - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} - with: - channel_id: ${{ secrets.SLACK_CHANNEL_ID }} - status: INSTALL (${{ steps.short.outputs.sha }} ${{ matrix.config.os }} - ${{ steps.short.outputs.r-version }}) - color: warning - - name: Install R, shinycoreci, and shinyverse - timeout-minutes: 100 # windows may take a while + timeout-minutes: 30 # Should be < 10 mins uses: ./.github/internal/install-shinyverse with: r-version: ${{ inputs.r-version }} @@ -147,18 +131,7 @@ jobs: pandoc-version: ${{ inputs.pandoc-version }} extra-packages: ${{ inputs.extra-packages }} - - name: Notify slack TEST - uses: voxmedia/github-action-slack-notify-build@v1 - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} - with: - message_id: ${{ steps.slack.outputs.message_id }} - channel_id: ${{ secrets.SLACK_CHANNEL_ID }} - status: TEST (${{ steps.short.outputs.sha }} ${{ inputs.os }} - ${{ steps.short.outputs.r-version }}) - color: warning - - # do not update pkgs, as they were installed above - # do update app pkgs, as they have NOT been installed + # Install packages as necessary! - name: Run tests timeout-minutes: 180 # 3 hrs env: @@ -194,8 +167,7 @@ jobs: test_output <- shinycoreci::test_in_local( apps = apps, retries = 1, - assert = FALSE, - install = FALSE + assert = FALSE ) shinycoreci::save_test_results( test_output, @@ -295,25 +267,3 @@ jobs: done git checkout ${{ steps.current_branch.outputs.name }} - - - name: Notify slack SUCCESS - if: success() - uses: voxmedia/github-action-slack-notify-build@v1 - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} - with: - message_id: ${{ steps.slack.outputs.message_id }} - channel_id: ${{ secrets.SLACK_CHANNEL_ID }} - status: SUCCESS (${{ steps.short.outputs.sha }} ${{ inputs.os }} - ${{ steps.short.outputs.r-version }}) - color: good - - - name: Notify slack FAILED - if: failure() - uses: voxmedia/github-action-slack-notify-build@v1 - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} - with: - message_id: ${{ steps.slack.outputs.message_id }} - channel_id: ${{ secrets.SLACK_CHANNEL_ID }} - status: FAILED (${{ steps.short.outputs.sha }} ${{ inputs.os }} - ${{ steps.short.outputs.r-version }}) - color: danger diff --git a/.github/workflows/apps-test-precheck.yml b/.github/workflows/apps-test-precheck.yml index 06cb849af8..f47e318bc5 100644 --- a/.github/workflows/apps-test-precheck.yml +++ b/.github/workflows/apps-test-precheck.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: GitHub Pull - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Validate testing files shell: Rscript {0} @@ -19,7 +19,7 @@ jobs: - name: Collect changed file names if: github.event_name == 'pull_request' id: files_changed - uses: Ana06/get-changed-files@v2.2.0 + uses: Ana06/get-changed-files@v2.3.0 with: format: "json" filter: "*" diff --git a/.github/workflows/build-results.yml b/.github/workflows/build-results.yml index 209d113e58..6cf00562fd 100644 --- a/.github/workflows/build-results.yml +++ b/.github/workflows/build-results.yml @@ -29,13 +29,13 @@ jobs: with: all_but_latest: true - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: _test_results # Only need current files fetch-depth: 0 - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: gh-pages path: _gh-pages diff --git a/.github/workflows/install-debug.yaml b/.github/workflows/install-debug.yaml index 06d45f91ec..305af52e08 100644 --- a/.github/workflows/install-debug.yaml +++ b/.github/workflows/install-debug.yaml @@ -24,7 +24,7 @@ jobs: R_KEEP_PKG_SOURCE: yes steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: r-lib/actions/setup-pandoc@v2 diff --git a/.github/workflows/trim-old-branches.yml b/.github/workflows/trim-old-branches.yml index b16d43e444..2fe642fde5 100644 --- a/.github/workflows/trim-old-branches.yml +++ b/.github/workflows/trim-old-branches.yml @@ -18,7 +18,7 @@ jobs: name: Trim Old Branches steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: GitHub Pull # https://github.com/actions/checkout/pull/112/files#diff-04c6e90faac2675aa89e2176d2eec7d8R194-R200 diff --git a/DESCRIPTION b/DESCRIPTION index 987366102d..a1e0c373a3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: shinycoreci Title: CI Testing for shiny -Version: 0.0.0.9008 +Version: 0.0.0.9009 Authors@R: c( person("Barret", "Schloerke", role = c("aut", "cre"), email = "barret@rstudio.com"), person("Winston", "Chang", role = "aut", email = "winston@rstudio.com"), @@ -18,11 +18,12 @@ Imports: sessioninfo, rprojroot, rstudioapi (>= 0.11), + withr, + rappdirs, cli Suggests: renv, httpuv, - rappdirs, rmarkdown (>= 2.9), rsconnect (>= 1.0.1), testthat, @@ -33,7 +34,6 @@ Suggests: dplyr, tidyr, later, - withr, shinytest Config/Needs/website: tidyverse/tidytemplate URL: https://github.com/rstudio/shinycoreci diff --git a/NAMESPACE b/NAMESPACE index 5ec57a978e..6563d00bae 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -12,8 +12,8 @@ export(platform) export(platform_rversion) export(r_version_short) export(save_test_results) -export(shinyverse_clean_libpath) -export(shinyverse_libpath) +export(shinycoreci_clean_libpaths) +export(shinycoreci_libpath) export(test_in_browser) export(test_in_connect) export(test_in_ide) diff --git a/R/data-apps-deps.R b/R/data-apps-deps.R index b0c810f039..07613aed34 100644 --- a/R/data-apps-deps.R +++ b/R/data-apps-deps.R @@ -1,69 +1,191 @@ # Do not edit by hand! # This file is automatically generated by `./inst/gha/data-apps-deps-update.R` -apps_deps <- c("base64enc", "BH", "Cairo", "clipr", "curl", "DBI", "dbplyr", -"DiagrammeR", "dplyr", "evaluate", "future", "ggplot2", "ggridges", -"ggvis", "hexbin", "highcharter", "jsonlite", "knitr", "lorem", -"magrittr", "maps", "markdown", "memoise", "networkD3", "plogr", -"png", "progress", "pryr", "radiant", "ragg", "RColorBrewer", -"reactable", "reactR", "reshape2", "rlang", "rmarkdown", "rprojroot", -"rsconnect", "RSQLite", "rversions", "scales", "sf", "shinyAce", -"shinydashboard", "shinyjs", "showtext", "sysfonts", "systemfonts", -"testthat", "tidyr", "tm", "waldo", "withr", "wordcloud") -apps_deps_map <- list(`001-hello` = "rsconnect", `012-datatables` = "ggplot2", - `016-knitr-pdf` = "rmarkdown", `020-knit-html` = c("evaluate", - "knitr", "rmarkdown"), `021-selectize-plot` = "maps", `022-unicode-chinese` = c("curl", - "showtext", "sysfonts"), `026-shiny-inline` = "rmarkdown", - `027-absolutely-positioned-panels` = "markdown", `047-image-output` = "png", - `048-including-html-text-and-markdown-files` = "markdown", - `051-movie-explorer` = c("DBI", "dbplyr", "dplyr", "ggvis", - "plogr", "RSQLite"), `063-superzip-example` = c("dplyr", - "RColorBrewer", "scales"), `082-word-cloud` = c("BH", "memoise", - "tm", "wordcloud"), `093-plot-interaction-basic` = "ggplot2", - `094-image-interaction-basic` = "png", `095-plot-interaction-advanced` = c("ggplot2", - "scales"), `099-plot-interaction-article-4` = "ggplot2", - `101-plot-interaction-article-6` = "ggplot2", `102-plot-interaction-article-7` = "ggplot2", - `103-plot-interaction-article-8` = "ggplot2", `104-plot-interaction-select` = "ggplot2", - `105-plot-interaction-zoom` = "ggplot2", `106-plot-interaction-exclude` = "ggplot2", - `108-module-output` = c("dplyr", "ggplot2"), `112-generate-report` = "rmarkdown", - `118-highcharter-births` = c("dplyr", "highcharter", "tidyr" - ), `121-async-timer` = c("future", "magrittr"), `122-async-outputs` = "future", - `123-async-renderprint` = "future", `124-async-download` = "future", - `125-async-req` = "future", `126-async-ticks` = "future", - `129-async-perf` = c("future", "ggplot2"), `140-selectize-inputs` = "jsonlite", - `141-radiant` = "radiant", `143-async-plot-caching` = "ggplot2", - `147-websocket` = "shinyjs", `150-networkD3-sankey` = c("networkD3", - "shinydashboard"), `151-reactr-input` = "reactR", `153-connection-header` = c("curl", - "future"), `156-subapps` = "rmarkdown", `161-discrete-limits` = c("dplyr", - "ggplot2"), `168-supporting-r-dir` = "withr", `169-prerender` = "rmarkdown", - `173-invalidatelater-leak` = "pryr", `174-throttle-debounce` = "magrittr", - `181-report-image` = c("Cairo", "jsonlite", "ragg", "showtext", +apps_deps <- c("base64enc", "BH", "bsicons", "bslib", "Cairo", "clipr", "crosstalk", +"curl", "DBI", "dbplyr", "DiagrammeR", "dplyr", "DT", "evaluate", +"flexdashboard", "future", "ggplot2", "ggridges", "ggvis", "gt", +"hexbin", "highcharter", "htmltools", "htmlwidgets", "httpuv", +"jsonlite", "knitr", "later", "leaflet", "lorem", "magrittr", +"maps", "markdown", "memoise", "networkD3", "plogr", "plotly", +"png", "progress", "promises", "pryr", "radiant", "ragg", "RColorBrewer", +"reactable", "reactlog", "reactR", "reshape2", "rlang", "rmarkdown", +"rprojroot", "rsconnect", "RSQLite", "rversions", "scales", "sessioninfo", +"sf", "shiny", "shinyAce", "shinydashboard", "shinyjs", "shinyjster", +"shinymeta", "shinytest2", "shinythemes", "shinyvalidate", "showtext", +"sysfonts", "systemfonts", "testthat", "thematic", "tibble", +"tidyr", "tm", "waldo", "websocket", "withr", "wordcloud") +apps_deps_map <- list(`000-all` = "shinytest2", `000-manual` = "shinytest2", `000-pkg-versions` = c("dplyr", +"jsonlite", "sessioninfo", "shiny", "shinytest2", "tibble"), + `001-hello` = c("rsconnect", "shiny", "shinyjster", "shinytest2" + ), `002-text` = c("shiny", "shinytest2"), `003-reactivity` = c("shiny", + "shinytest2"), `004-mpg` = c("shiny", "shinytest2"), `005-sliders` = c("shiny", + "shinytest2"), `006-tabsets` = c("shiny", "shinytest2"), + `007-widgets` = c("shiny", "shinytest2"), `008-html` = c("shiny", + "shinytest2"), `009-upload` = c("shiny", "shinytest2"), `010-download` = c("shiny", + "shinytest2"), `012-datatables` = c("DT", "ggplot2", "shiny", + "shinytest2"), `013-selectize` = c("shiny", "shinytest2"), + `014-onflushed` = c("later", "shinytest2"), `016-knitr-pdf` = c("rmarkdown", + "shinytest2"), `017-select-vs-selectize` = "shinytest2", + `018-datatable-options` = c("DT", "shinytest2"), `019-mathjax` = "shinytest2", + `020-knit-html` = c("evaluate", "knitr", "rmarkdown", "shinytest2" + ), `021-selectize-plot` = c("DT", "maps", "shiny", "shinytest2" + ), `022-unicode-chinese` = c("curl", "shinyjster", "shinytest2", + "showtext", "sysfonts"), `023-optgroup-server` = "shinytest2", + `024-optgroup-selectize` = "shinytest2", `025-loop-ui` = c("shinyjster", + "shinytest2"), `026-shiny-inline` = c("rmarkdown", "shiny", + "shinytest2"), `027-absolutely-positioned-panels` = c("markdown", + "shinytest2"), `032-client-data-and-query-string` = c("shinyjster", + "shinytest2"), `033-conditionalpanel-demo` = "shinytest2", + `037-date-and-date-range` = "shinytest2", `039-download-file` = "shinytest2", + `041-dynamic-ui` = "shinytest2", `047-image-output` = c("png", + "shinytest2"), `048-including-html-text-and-markdown-files` = c("markdown", + "shinytest2"), `050-kmeans-example` = "shinytest2", `051-movie-explorer` = c("DBI", + "dbplyr", "dplyr", "ggvis", "plogr", "RSQLite", "shinytest2" + ), `054-nvd3-line-chart-output` = c("shiny", "shinytest2" + ), `060-retirement-simulation` = c("shiny", "shinytest2", + "shinythemes"), `061-server-to-client-custom-messages` = "shinytest2", + `062-submitbutton-demo` = "shinytest2", `063-superzip-example` = c("dplyr", + "DT", "leaflet", "RColorBrewer", "scales", "shinytest2"), + `065-update-input-demo` = "shinytest2", `066-upload-file` = "shinytest2", + `081-widgets-gallery` = "shinytest2", `082-word-cloud` = c("BH", + "memoise", "shinytest2", "tm", "wordcloud"), `093-plot-interaction-basic` = "ggplot2", + `094-image-interaction-basic` = "png", `095-plot-interaction-advanced` = c("DT", + "ggplot2", "scales"), `096-plot-interaction-article-1` = "shiny", + `099-plot-interaction-article-4` = "ggplot2", `101-plot-interaction-article-6` = "ggplot2", + `102-plot-interaction-article-7` = "ggplot2", `103-plot-interaction-article-8` = "ggplot2", + `104-plot-interaction-select` = "ggplot2", `105-plot-interaction-zoom` = "ggplot2", + `106-plot-interaction-exclude` = "ggplot2", `108-module-output` = c("dplyr", + "ggplot2", "shiny", "shinytest2"), `109-render-table` = "shinytest2", + `110-error-sanitization` = "shinytest2", `111-insert-ui` = c("shiny", + "shinytest2"), `112-generate-report` = c("rmarkdown", "shiny", + "shinytest2"), `114-modal-dialog` = "shinytest2", `115-remove-modal` = c("bslib", + "shiny", "shinytest2"), `117-shinythemes` = c("shinytest2", + "shinythemes"), `118-highcharter-births` = c("dplyr", "highcharter", + "shiny", "shinytest2", "tidyr"), `119-namespaced-conditionalpanel-demo` = "shinytest2", + `121-async-timer` = c("future", "magrittr", "promises", "shiny", + "shinyjster", "shinytest2"), `122-async-outputs` = c("future", + "htmltools", "promises", "shiny", "shinyjster", "shinytest2" + ), `123-async-renderprint` = c("future", "promises", "shiny", + "shinyjster", "shinytest2"), `124-async-download` = c("future", + "promises", "shiny", "shinyjster", "shinytest2"), `125-async-req` = c("future", + "promises", "shiny", "shinyjster", "shinytest2"), `126-async-ticks` = c("future", + "later", "promises", "shiny", "shinyjster", "shinytest2"), + `128-plot-dim-error` = c("shiny", "shinyjster", "shinytest2" + ), `129-async-perf` = c("future", "ggplot2", "promises", + "shiny", "shinyjster", "shinytest2"), `130-output-null` = c("shiny", + "shinyjster", "shinytest2"), `131-renderplot-args` = c("shiny", + "shinyjster", "shinytest2"), `132-async-events` = c("later", + "promises", "shiny", "shinyjster", "shinytest2"), `133-async-hold-inputs` = c("later", + "promises", "shiny", "shinyjster", "shinytest2"), `134-async-hold-timers` = c("later", + "promises", "shiny", "shinyjster", "shinytest2"), `135-bookmark-uioutput` = c("htmltools", + "promises", "shiny", "shinytest2"), `136-plot-cache` = "shinytest2", + `137-plot-cache-key` = c("shiny", "shinytest2"), `138-icon-fontawesome` = c("shiny", + "shinytest2"), `140-selectize-inputs` = c("jsonlite", "shiny", + "shinyjster", "shinytest2"), `141-radiant` = c("radiant", + "shiny"), `142-reactive-timer` = c("later", "promises", "shiny", + "shinyjster", "shinytest2"), `143-async-plot-caching` = c("ggplot2", + "promises", "shiny", "shinyjster", "shinytest2"), `145-dt-replacedata` = c("DT", + "shiny", "shinyjster", "shinytest2"), `147-websocket` = c("httpuv", + "shiny", "shinyjs", "shinyjster", "shinytest2", "websocket" + ), `148-addresourcepath-deleted` = c("shiny", "shinyjster", + "shinytest2"), `149-onRender` = c("htmlwidgets", "plotly", + "shiny", "shinyjster", "shinytest2"), `150-networkD3-sankey` = c("networkD3", + "shiny", "shinydashboard", "shinytest2"), `151-reactr-input` = c("htmltools", + "reactR", "shiny", "shinyjster", "shinytest2"), `152-set-reactivevalue` = c("shiny", + "shinyjster", "shinytest2"), `153-connection-header` = c("curl", + "future", "shiny", "shinyjster", "shinytest2"), `154-index-html-server-r` = c("shinyjster", + "shinytest2"), `155-index-html-app-r` = c("shinyjster", "shinytest2" + ), `156-subapps` = c("leaflet", "rmarkdown", "shiny", "shinyjster", + "shinytest2"), `157-date-format` = c("shiny", "shinyjster", + "shinytest2"), `158-input-labels` = c("shiny", "shinyjster", + "shinytest2"), `159-rate-policy` = c("shiny", "shinytest2" + ), `160-select-input` = c("shiny", "shinyjster", "shinytest2" + ), `161-discrete-limits` = c("dplyr", "ggplot2", "shiny"), + `163-select-factor` = c("shiny", "shinyjster", "shinytest2" + ), `164-no-whitespace` = c("shiny", "shinyjster", "shinytest2" + ), `165-trailing-comma` = c("shiny", "shinyjster", "shinytest2" + ), `166-dynamic-hosted-tab` = c("shinyjster", "shinytest2" + ), `167-resource-warnings` = "shiny", `168-supporting-r-dir` = c("shinyjster", + "shinytest2", "withr"), `169-prerender` = c("rmarkdown", + "shiny", "shinyjster", "shinytest2"), `170-date-range-max` = c("shiny", + "shinyjster", "shinytest2"), `171-path-traversal` = c("shinyjster", + "shinytest2"), `173-invalidatelater-leak` = c("pryr", "shiny", + "shinyjster", "shinytest2"), `174-throttle-debounce` = c("magrittr", + "shiny", "shinyjster", "shinytest2"), `175-setInputValue` = c("shiny", + "shinyjster", "shinytest2"), `178-delayed-widget` = c("htmlwidgets", + "leaflet", "shiny", "shinyjster", "shinytest2"), `179-nondelayed-widget` = c("htmlwidgets", + "leaflet", "shiny", "shinyjster", "shinytest2"), `180-delayed-staticwidget` = c("htmlwidgets", + "leaflet", "shiny", "shinyjster", "shinytest2"), `181-report-image` = c("Cairo", + "htmltools", "jsonlite", "ragg", "shiny", "shinytest2", "showtext", "sysfonts", "systemfonts"), `182-report-png` = c("Cairo", - "jsonlite", "ragg", "showtext", "sysfonts", "systemfonts" - ), `183-report-cairo` = c("Cairo", "jsonlite", "ragg", "showtext", + "htmltools", "jsonlite", "ragg", "shiny", "shinytest2", "showtext", + "sysfonts", "systemfonts"), `183-report-cairo` = c("Cairo", + "htmltools", "jsonlite", "ragg", "shiny", "shinytest2", "showtext", "sysfonts", "systemfonts"), `184-report-ragg` = c("Cairo", - "jsonlite", "ragg", "showtext", "sysfonts", "systemfonts" - ), `185-report-theme` = c("Cairo", "jsonlite", "knitr", "ragg", - "rmarkdown", "waldo"), `193-reactlog-dynamic-ui` = "rversions", - `200-flexdashboard-render-text` = c("knitr", "rmarkdown"), - `205-dynamic-tabs-compat` = "withr", `206-freeze-thaw` = "rlang", - `208-bind-cache-event` = "magrittr", `209-datepicker` = c("jsonlite", - "magrittr", "rlang"), `210-future_promise` = c("future", - "magrittr", "shinyjs"), `211-sv-custom-inputs` = "base64enc", - `212-daterangepicker` = "jsonlite", `216-quosures` = c("DiagrammeR", - "rlang"), `221-async-script-dynamic-ui` = "rlang", `224-shinymeta` = c("clipr", - "dplyr", "shinyAce"), `225-snapshot-info-option` = c("jsonlite", - "testthat"), `226-snapshot-info-url` = c("jsonlite", "testthat" - ), `300-bs-themer` = c("curl", "ggplot2", "ggridges", "hexbin", - "knitr", "reactable", "reshape2", "rlang", "rprojroot", "rsconnect" - ), `301-bs-themes` = c("ggplot2", "reshape2", "rversions", - "sf", "withr"), `302-bootswatch-themes` = c("ggplot2", "progress", - "rversions", "sf", "withr"), `304-bslib-card` = c("rlang", - "rversions"), `305-bslib-value-box` = c("rlang", "rversions" - ), `306-accordion-add-remove` = "magrittr", `308-sidebar-kitchen-sink` = c("jsonlite", - "lorem", "testthat"), `309-flexdashboard-tabs-navs` = "rmarkdown", - `310-bslib-sidebar-dynamic` = c("jsonlite", "testthat"), - `311-bslib-sidebar-toggle-methods` = c("rversions", "testthat" - ), `313-bslib-card-tab-focus` = c("rversions", "testthat", - "withr"), `314-bslib-tooltips` = "withr", `315-bslib-input-switch` = "withr", - `316-bslib-popovers` = c("rversions", "testthat", "withr" + "htmltools", "jsonlite", "ragg", "shiny", "shinytest2", "showtext", + "sysfonts", "systemfonts"), `185-report-theme` = c("Cairo", + "htmltools", "jsonlite", "knitr", "ragg", "rmarkdown", "shiny", + "shinyjster", "shinytest2", "waldo"), `186-rdir-sortc` = c("shinyjster", + "shinytest2"), `187-navbar-collapse` = c("bslib", "shiny", + "shinyjster", "shinytest2"), `188-encoding` = c("shiny", + "shinytest2"), `189-ui-http-response` = c("shiny", "shinyjster", + "shinytest2"), `190-reactlog-module-ex1` = c("reactlog", + "shiny", "shinytest2"), `191-reactlog-pythagoras` = c("reactlog", + "shiny", "shinytest2"), `192-reactlog-hello` = c("reactlog", + "shiny", "shinytest2"), `193-reactlog-dynamic-ui` = c("reactlog", + "rversions", "shiny", "shinytest2"), `194-reactlog-plot-cache-key` = c("reactlog", + "shiny", "shinytest2"), `195-radio-empty` = "shinytest2", + `200-flexdashboard-render-text` = c("flexdashboard", "knitr", + "plotly", "rmarkdown", "shiny", "shinyjster", "shinytest2" + ), `205-dynamic-tabs-compat` = c("bslib", "shiny", "shinytest2", + "withr"), `206-freeze-thaw` = c("rlang", "shiny", "shinyjster", + "shinytest2"), `207-freeze-invalidate` = c("shiny", "shinyjster", + "shinytest2"), `208-bind-cache-event` = c("magrittr", "shiny", + "shinyjster", "shinytest2"), `209-datepicker` = c("jsonlite", + "magrittr", "rlang", "shiny", "shinyjster", "shinytest2"), + `210-future_promise` = c("future", "later", "magrittr", "promises", + "shiny", "shinyjs", "shinyjster", "shinytest2"), `211-sv-custom-inputs` = c("base64enc", + "htmltools", "shiny", "shinyjster", "shinytest2", "shinyvalidate" + ), `212-daterangepicker` = c("jsonlite", "shiny", "shinyjster", + "shinytest2"), `213-tab-panels` = c("bslib", "shiny", "shinytest2" + ), `214-render-script-attrs` = c("htmltools", "shiny", "shinyjster", + "shinytest2"), `215-bslib-nav` = c("bslib", "shiny", "shinytest2" + ), `216-quosures` = c("DiagrammeR", "rlang", "shiny", "shinytest2" + ), `218-insert-ui-jquery` = c("shiny", "shinytest2"), `219-sliders-date` = c("shiny", + "shinytest2"), `220-execute-hidden-plot` = c("bslib", "shiny", + "shinyjster", "shinytest2"), `221-async-script-dynamic-ui` = c("htmltools", + "httpuv", "later", "promises", "rlang", "shiny", "shinyjster", + "shinytest2"), `222-shinyoptions` = c("shiny", "shinyjster", + "shinytest2"), `223-set-current-theme` = c("bslib", "htmltools", + "shiny", "shinyjster", "shinytest2"), `224-shinymeta` = c("clipr", + "dplyr", "DT", "plotly", "shiny", "shinyAce", "shinyjster", + "shinymeta", "shinytest2"), `225-snapshot-info-option` = c("jsonlite", + "shiny", "shinytest2", "testthat"), `226-snapshot-info-url` = c("jsonlite", + "shiny", "shinytest2", "testthat"), `227-persistent-progress` = c("bslib", + "shiny", "shinyjster", "shinytest2"), `300-bs-themer` = c("bsicons", + "bslib", "curl", "DT", "ggplot2", "ggridges", "hexbin", "htmltools", + "knitr", "reactable", "reshape2", "rlang", "rprojroot", "rsconnect", + "shiny", "thematic"), `301-bs-themes` = c("bslib", "DT", + "ggplot2", "reshape2", "rversions", "sf", "shiny", "shinytest2", + "thematic", "withr"), `302-bootswatch-themes` = c("bslib", + "DT", "ggplot2", "progress", "rversions", "sf", "shiny", + "shinytest2", "thematic", "withr"), `303-bslib-html-template` = c("bslib", + "shiny", "shinytest2"), `304-bslib-card` = c("bslib", "gt", + "leaflet", "plotly", "rlang", "rversions", "shiny", "shinytest2" + ), `305-bslib-value-box` = c("bsicons", "bslib", "plotly", + "rlang", "rversions", "shiny", "shinytest2"), `306-accordion-add-remove` = c("bslib", + "magrittr", "shiny", "shinytest2"), `307-accordion-replace` = c("bsicons", + "bslib", "shiny", "shinyjster", "shinytest2"), `308-sidebar-kitchen-sink` = c("bslib", + "crosstalk", "jsonlite", "lorem", "plotly", "shiny", "shinytest2", + "testthat"), `309-flexdashboard-tabs-navs` = c("flexdashboard", + "rmarkdown", "shiny", "shinytest2"), `310-bslib-sidebar-dynamic` = c("bslib", + "htmltools", "jsonlite", "shiny", "shinytest2", "testthat" + ), `311-bslib-sidebar-toggle-methods` = c("bslib", "rversions", + "shiny", "shinytest2", "testthat"), `313-bslib-card-tab-focus` = c("bslib", + "plotly", "rversions", "shiny", "shinytest2", "testthat", + "withr"), `314-bslib-tooltips` = c("bsicons", "bslib", "plotly", + "shiny", "shinytest2", "withr"), `315-bslib-input-switch` = c("bslib", + "shiny", "shinytest2", "withr"), `316-bslib-popovers` = c("bsicons", + "bslib", "plotly", "rversions", "shiny", "shinytest2", "testthat", + "withr"), `900-text-jster` = c("shiny", "shinyjster", "shinytest2" + ), `901-button-jster` = c("shiny", "shinyjster", "shinytest2" )) diff --git a/R/data-shinyverse.R b/R/data-shinyverse.R index 6df4e1fe1e..217a01d0dc 100644 --- a/R/data-shinyverse.R +++ b/R/data-shinyverse.R @@ -1,39 +1,29 @@ -# Written by hand! +# Do not edit by hand! +# This file is automatically generated by `./inst/gha/data-apps-deps-update.R` +# To add / update a package, make a PR here: https://github.com/posit-dev-shinycoreci/posit-dev-shinycoreci.r-universe.dev/blob/main/packages.json +shinyverse_cran_url <- "https://posit-dev-shinycoreci.r-universe.dev" -# Used by GHA script -shinyverse_remotes <- c( - "r-lib/cachem", - "r-lib/fastmap", - "r-lib/later", - "rstudio/bslib", - "rstudio/bsicons", - "ramnathv/htmlwidgets", - "rstudio/crosstalk", - "rstudio/gt", - "rstudio/DT", - "rstudio/dygraphs", - "rstudio/flexdashboard", - "rstudio/fontawesome", - "rstudio/htmltools", - "rstudio/httpuv", - "rstudio/leaflet", - "rstudio/pool", - "rstudio/promises", - "rstudio/reactlog", - "rstudio/sass", - "rstudio/shiny", - "rstudio/shinymeta", - "rstudio/shinytest", - "rstudio/chromote", - "rstudio/shinytest2", - "rstudio/shinythemes", - "rstudio/shinyvalidate", - "rstudio/thematic", - "rstudio/webdriver", - "rstudio/websocket", - "ropensci/plotly", - "schloerke/shinyjster", - NULL -) +shinyverse_pkgs <- c("bsicons", "bslib", "cachem", "chromote", "crosstalk", "DT", +"dygraphs", "fastmap", "flexdashboard", "fontawesome", "gt", +"htmltools", "htmlwidgets", "httpuv", "later", "leaflet", "plotly", +"pool", "promises", "reactlog", "sass", "shiny", "shinycoreci", +"shinyjster", "shinymeta", "shinytest", "shinytest2", "shinythemes", +"shinyvalidate", "thematic", "webdriver", "websocket") -shinyverse_pkgs <- vapply(strsplit(shinyverse_remotes, "/"), `[[`, character(1), 2) +shinyverse_urls <- c("https://github.com/rstudio/bsicons", "https://github.com/rstudio/bslib", +"https://github.com/r-lib/cachem", "https://github.com/rstudio/chromote", +"https://github.com/rstudio/crosstalk", "https://github.com/rstudio/DT", +"https://github.com/rstudio/dygraphs", "https://github.com/r-lib/fastmap", +"https://github.com/rstudio/flexdashboard", "https://github.com/rstudio/fontawesome", +"https://github.com/rstudio/gt", "https://github.com/rstudio/htmltools", +"https://github.com/ramnathv/htmlwidgets", "https://github.com/rstudio/httpuv", +"https://github.com/r-lib/later", "https://github.com/rstudio/leaflet", +"https://github.com/ropensci/plotly", "https://github.com/rstudio/pool", +"https://github.com/rstudio/promises", "https://github.com/rstudio/reactlog", +"https://github.com/rstudio/sass", "https://github.com/rstudio/shiny", +"https://github.com/rstudio/shinycoreci", "https://github.com/schloerke/shinyjster", +"https://github.com/rstudio/shinymeta", "https://github.com/rstudio/shinytest", +"https://github.com/rstudio/shinytest2", "https://github.com/rstudio/shinythemes", +"https://github.com/rstudio/shinyvalidate", "https://github.com/rstudio/thematic", +"https://github.com/rstudio/webdriver", "https://github.com/rstudio/websocket" +) diff --git a/R/deploy-apps.R b/R/deploy-apps.R index 52c81e6b10..56a8bedfc9 100644 --- a/R/deploy-apps.R +++ b/R/deploy-apps.R @@ -7,23 +7,22 @@ #' @param apps A character vector of fully defined shiny application folders #' @param account,server args supplied to `[rsconnect::deployApp]` #' @param ... ignored -#' @param install If TRUE, will install all of shinyverse into the default libpath #' @param extra_packages A character vector of extra packages to install #' @param cores number of cores to use when deploying #' @param retry If \code{TRUE}, try failure apps again. (Only happens once.) #' @param retrying_ For internal use only +#' @inheritParams resolve_libpath #' @export deploy_apps <- function( - apps = apps_deploy, - account = "testing-apps", - server = "shinyapps.io", - ..., - install = TRUE, - extra_packages = NULL, - cores = 1, - retry = 2, - retrying_ = FALSE -) { + apps = apps_deploy, + account = "testing-apps", + server = "shinyapps.io", + ..., + local_pkgs = FALSE, + extra_packages = NULL, + cores = 1, + retry = 2, + retrying_ = FALSE) { is_missing <- list( account = missing(account), server = missing(server), @@ -33,32 +32,11 @@ deploy_apps <- function( apps <- resolve_app_name(apps) - on_ci <- isTRUE(as.logical(Sys.getenv("CI"))) - - shinyverse_lib_path <- - if (on_ci) { - if (retrying_) { - # Use standard libpath location - install_shinyverse_local(install = FALSE, install_apps_deps = FALSE) - } else { - # Install on first pass - # Install everything. No need to validated if pkgs are loaded as deploying in background process - install_shinyverse_local(install = install, validate_loaded = FALSE, extra_packages = extra_packages, install_apps_deps = FALSE) - } - } else { - if (retrying_) { - # Get lib path only as still same pkgs as before - shinyverse_libpath() - } else { - # Install on first pass - # Install everything. No need to validated if pkgs are loaded as deploying in background process - install_shinyverse(install = install, validate_loaded = FALSE, extra_packages = extra_packages, install_apps_deps = FALSE) - } - } + libpath <- resolve_libpath(local_pkgs = local_pkgs) if (!retrying_) { # Always make sure the app dependencies are available - install_missing_app_deps(apps) + install_missing_app_deps(apps, libpath = libpath) } @@ -73,7 +51,7 @@ deploy_apps <- function( deploy_res <- callr::r( show = TRUE, spinner = TRUE, # helps with CI from timing out - libpath = shinyverse_lib_path, # use shinyverse library path + libpath = libpath, # use shinyverse library path args = list( apps_dirs = app_dirs, cores = cores, @@ -111,7 +89,7 @@ deploy_apps <- function( appFiles = app_files ) }) - if (inherits(deployment_worked, 'try-error')) { + if (inherits(deployment_worked, "try-error")) { return(1) } else { return(as.numeric(!isTRUE(deployment_worked))) @@ -154,7 +132,7 @@ deploy_apps <- function( fn_arg("retry", retry - 1) ) fn <- paste0( - "deploy_apps(", paste0(args, collapse = ", "),")" + "deploy_apps(", paste0(args, collapse = ", "), ")" ) if (is.numeric(retry) && length(retry) > 0 && retry > 0) { @@ -164,9 +142,9 @@ deploy_apps <- function( apps = error_apps, account = account, server = server, - cores = 1, # simplify it + cores = 1, # simplify it retrying_ = TRUE, # no need to update again, still in the original function exec - retry = retry - 1 # do not allow for infinite retries + retry = retry - 1 # do not allow for infinite retries ) ) } @@ -177,7 +155,6 @@ deploy_apps <- function( fn, "\n" ) - } @@ -185,7 +162,7 @@ validate_rsconnect_account <- function(account, server) { accts <- rsconnect::accounts() accts_found <- sum( (account %in% accts$name) & - (server %in% accts$server) + (server %in% accts$server) ) if (accts_found == 0) { print(accts) diff --git a/R/install-path.R b/R/install-path.R index ef07f5c6a3..26ad539713 100644 --- a/R/install-path.R +++ b/R/install-path.R @@ -1,10 +1,25 @@ +on_ci <- function() { + isTRUE(as.logical(Sys.getenv("CI"))) +} + +#' Resolve library path +#' @param local_pkgs If `TRUE`, local packages will be used instead of the isolated shinyverse installation. +#' @keywords internal +resolve_libpath <- function(..., local_pkgs = FALSE) { + stopifnot(length(list(...)) == 0) + # If using local_pkgs, use the standard libpath location + libpath <- if (isTRUE(local_pkgs)) .libPaths()[1] else shinycoreci_libpath() + libpath +} + + #' Shinyverse libpath #' #' Methods to get and reset the shinyverse `libpath`. #' #' @export -#' @describeIn shinyverse_libpath Library path that will persist across installations. But will have a different path for different R versions -shinyverse_libpath <- function() { +#' @describeIn shinycoreci_libpath Library path that will persist across installations. But will have a different path for different R versions +shinycoreci_libpath <- function() { # Dir location inspration from learnr: # https://github.com/rstudio/learnr/blob/1c01ac258230cbe217eee16c77cc71924faab1d3/R/storage.R#L275 dir <- file.path( @@ -22,7 +37,7 @@ shinyverse_libpath <- function() { dir } #' @export -#' @describeIn shinyverse_libpath Removes the cached R library -shinyverse_clean_libpath <- function() { - unlink(shinyverse_libpath(), recursive = TRUE) +#' @describeIn shinycoreci_libpath Removes the cached R library +shinycoreci_clean_libpaths <- function() { + unlink(dirname(shinycoreci_libpath()), recursive = TRUE) } diff --git a/R/install.R b/R/install.R index d2dc472e92..e741493674 100644 --- a/R/install.R +++ b/R/install.R @@ -18,200 +18,221 @@ shinycoreci_is_local <- function() { } -# Used in GHA workflow -install_shinyverse_local <- function( - ..., - # Install into normal libpath so caching is automatically handled - libpath = .libPaths()[1], - install_apps_deps = FALSE -) { - install_shinyverse(..., libpath = libpath, install_apps_deps = install_apps_deps) -} - -#' @noRd -#' @return lib path being used -install_shinyverse <- function( - install = TRUE, - validate_loaded = TRUE, - upgrade = TRUE, # pak::pkg_install(upgrade = FALSE) - dependencies = NA, # pak::pkg_install(dependencies = NA) - extra_packages = NULL, - install_apps_deps = TRUE, - libpath = shinyverse_libpath() -) { - if (!isTRUE(install)) return(.libPaths()[1]) - - # Make sure none of the shinyverse is loaded into namespace - if (isTRUE(validate_loaded)) { - shiny_search <- paste0("package:", shinyverse_pkgs) - if (any(shiny_search %in% search())) { - bad_namespaces <- shinyverse_pkgs[shiny_search %in% search()] - stop( - "The following packages are already loaded:\n", - paste0("* ", bad_namespaces, "\n", collapse = ""), - "Please restart and try again" - ) - } - } - # Remove shinyverse - pak_apps_deps <- - if (isTRUE(install_apps_deps)) { - paste0("any::", apps_deps[!(apps_deps %in% c(shinyverse_pkgs, "shinycoreci", "shinycoreciapps"))]) +# # Attempt to set up all the packages in the shinyverse, even if they are not directly depended upon. +# attempt_to_install_universe <- function( +# ..., +# libpath = .libPaths()[1], +# verbose = TRUE +# ) { +# return() +# stopifnot(length(list(...)) == 0) + +# # pkgs <- paste0(shinyverse_pkgs, "?source") + +# tryCatch( +# { +# install_missing_pkgs( +# pkgs, +# libpath = libpath, +# prompt = "Installing shinyverse packages: ", +# verbose = verbose +# ) +# }, +# error = function(e) { +# # Couldn't install all at once, Installing individually +# message("Failed to install shinyverse packages in a single attempt. Error: ", e) +# message("Installing shinyverse packages individually!") +# Map(seq_along(pkgs), pkgs, f = function(i, pkg) { +# tryCatch( +# { +# install_missing_pkgs( +# pkg, +# libpath = libpath, +# prompt = paste0("[", i, "/", length(pkgs), "] Installing shinyverse package: "), +# verbose = verbose +# ) +# }, +# error = function(e) { +# message("Failed to install ", pkg, " from universe") +# } +# ) +# }) +# } +# ) + +# } + + + +## Used in GHA workflow +# Install missing dependencies given an app name +# If more than one app name is provided, run through all of them individually +install_missing_app_deps <- function( + app_name = names(apps_deps_map), + ..., + libpath = .libPaths()[1], + upgrade = FALSE, + dependencies = NA, + verbose = TRUE + ) { + stopifnot(length(list(...)) == 0) + + app_deps <- + if (length(app_name) > 1) { + unique(unlist( + lapply(app_name, function(app_name_val) { + apps_deps_map[[resolve_app_name(app_name_val)]] + }) + )) } else { - NULL + apps_deps_map[[resolve_app_name(app_name)]] } - # Load pak into current namespace - pkgs <- c(shinyverse_remotes, pak_apps_deps, extra_packages) - message("Installing shinyverse and app deps: ", libpath) - if (!is.null(extra_packages)) { - message("Extra packages:\n", paste0("* ", extra_packages, collapse = "\n")) - } - install_pkgs_with_callr(pkgs, libpath = libpath, upgrade = upgrade, dependencies = dependencies) - return(libpath) + install_missing_pkgs( + app_deps, + libpath = libpath, + upgrade = upgrade, + dependencies = dependencies, + verbose = verbose + ) + + invisible() } -# Install missing dependencies given an app name -# If more than one app name is provided, run through all of them individually -install_missing_app_deps <- function(app_name = names(apps_deps_map), libpath = .libPaths()[1], upgrade = FALSE, dependencies = NA, ..., recursing = FALSE) { - if (!isTRUE(recursing)) { - install_troublesome_pkgs(libpath = libpath) - } - if (length(app_name) > 1) { - for (app_name_val in app_name) { - install_missing_app_deps(app_name_val, libpath = libpath, upgrade = upgrade, dependencies = dependencies, recursing = TRUE) - } - return(invisible()) - } - app_name <- resolve_app_name(app_name) +installed_pkgs <- new.env(parent = emptyenv()) + +pak_deps_map <- new.env(parent = emptyenv()) + + +get_extra_shinyverse_deps <- function(packages) { + + if (length(packages) == 0) return(NULL) - app_deps <- apps_deps_map[[app_name]] + # Recursively find all shinycoreci packages as dependencies from `packages` + ret <- c() + queue <- packages + while (TRUE) { + pkg <- queue[1] + queue <- queue[-1] - install_missing_pkgs(app_deps, libpath = libpath, upgrade = upgrade, dependencies = dependencies) - deps <- Filter(app_deps, f = function(dep) !is_installed(dep, libpath = libpath)) - if (length(deps) > 0) { - message("Installing missing packages: ", paste0(deps, collapse = ", ")) - install_pkgs_with_callr(deps, libpath = libpath, upgrade = upgrade, dependencies = dependencies) + if (is.null(pkg)) break + if (is.na(pkg) && length(queue) == 0) break + if (is.na(pkg)) next + if (pkg %in% ret) next + + pkg_dep_packages <- pak_deps_map[[pkg]] + if (is.null(pkg_dep_packages)) { + withr::with_options(list( + repos = c( + # Use the shinycoreci universe to avoid GH rate limits! + "AAA" = shinyverse_cran_url, + getOption("repos", c("CRAN" = "https://cloud.r-project.org")) + ) + ), { + stopifnot(utils::packageVersion("pak") >= "0.3.0") + pak__pkg_deps <- utils::getFromNamespace("pkg_deps", "pak") + pkg_dep_packages <- pak__pkg_deps(pkg)$package + # str(list(pkg = pkg, pkg_dep_packages = pkg_dep_packages)) + }) + # Store in env does not need `<<-` + pak_deps_map[[pkg]] <- pkg_dep_packages + } + + queue <- unique(c(queue, pkg_dep_packages[pkg_dep_packages %in% shinyverse_pkgs])) + + if (pkg %in% shinyverse_pkgs) { + ret <- c(ret, pkg) + } } - invisible() + ret } -# packages_to_install is what is really installed given the value of packages -install_missing_pkgs <- function(packages, libpath = .libPaths()[1], upgrade = FALSE, dependencies = NA, packages_to_install = packages) { - packages_to_install <- unlist(Map( + +# packages is what is really installed given the value of packages +install_missing_pkgs <- function( packages, - packages_to_install, - f = function(package, value) { - if (!is_installed(package, libpath = libpath)) { - value - } else { - NULL - } + ..., + libpath = .libPaths()[1], + upgrade = FALSE, + dependencies = NA, + prompt = "Installing packages: ", + verbose = TRUE +) { + stopifnot(length(list(...)) == 0) + + # Make sure to get underlying dependencies + # Always add shiny as it is always needed + # Only install shinycoreci if the libpath is shinycoreci_libpath() + packages <- unique(c(packages, get_extra_shinyverse_deps(c(packages, "shiny", if (libpath == shinycoreci_libpath()) "shinycoreci" )))) + + pkgs_to_install <- packages[!(packages %in% names(installed_pkgs))] + + if (length(pkgs_to_install) > 0) { + message( + prompt, + paste0(pkgs_to_install, collapse = ", ") + ) + message("libpath: ", libpath) + + install_pkgs_with_callr( + pkgs_to_install, + libpath = libpath, + upgrade = upgrade, + dependencies = dependencies, + verbose = verbose + ) + # Update the installed status as an install error was not thrown + for (package in pkgs_to_install) { + # Set in environment + installed_pkgs[[package]] <- TRUE } - )) - if (length(packages_to_install) > 0) { - message("Installing missing packages: ", paste0(packages_to_install, collapse = ", ")) - install_pkgs_with_callr(packages_to_install, libpath = libpath, upgrade = upgrade, dependencies = dependencies) } invisible() } install_pkgs_with_callr <- function( - packages, - libpath = .libPaths()[1], - upgrade = TRUE, # pak::pkg_install(upgrade = FALSE) - dependencies = NA # pak::pkg_install(dependencies = NA) -) { + packages, + ..., + libpath = .libPaths()[1], + upgrade = TRUE, # pak::pkg_install(upgrade = FALSE) + dependencies = NA, # pak::pkg_install(dependencies = NA) + verbose = TRUE + ) { + stopifnot(length(list(...)) == 0) callr::r( - function(packages, lib, upgrade, dependencies) { + function(shinyverse_cran_url, packages, upgrade, dependencies) { + options(repos = c( + # Use the shinycoreci universe to avoid GH rate limits! + "AAA" = shinyverse_cran_url, + getOption("repos", c("CRAN" = "https://cloud.r-project.org")) + )) + # Performing a leap of faith that pak is installed. # Avoids weird installs when using pak to install shinycoreci stopifnot(utils::packageVersion("pak") >= "0.3.0") pak__pkg_install <- utils::getFromNamespace("pkg_install", "pak") pak__pkg_install( packages, - lib = lib, ask = FALSE, # Not interactive, so don't ask upgrade = upgrade, dependencies = dependencies ) }, list( + shinyverse_cran_url = shinyverse_cran_url, packages = packages, - lib = libpath, upgrade = upgrade, dependencies = dependencies ), - show = TRUE, + show = verbose, + libpath = libpath, + supervise = TRUE, spinner = TRUE # helps with CI from timing out ) } - - - - - -# This logic should mimic `./gihub/internal/install-shinyvers/action.yaml` logic -install_troublesome_pkgs <- function(libpath = .libPaths()[1]) { - - # Get R version like `"4.2"` - short_r_version <- sub("\\.\\d$", "", as.character(getRversion())) - - if (is_mac()) { - switch(short_r_version, - "4.2" = { - install_missing_pkgs( - packages = "XML", - packages_to_install = "XML", - libpath = libpath - ) - } - ) - } - - if (is_linux()) { - switch(short_r_version, - "4.2" = { - install_missing_pkgs( - packages = "XML", - packages_to_install = "XML", - libpath = libpath - ) - }, - "3.6" = { - install_missing_pkgs( - packages = "rjson", - packages_to_install = "url::https://cran.r-project.org/src/contrib/Archive/rjson/rjson_0.2.20.tar.gz", - libpath = libpath - ) - } - ) - } - - if (is_windows()) { - switch(short_r_version, - "4.0" = { - install_missing_pkgs( - packages = "terra", - packages_to_install = "url::https://cloud.r-project.org/bin/windows/contrib/4.0/terra_1.5-21.zip", - libpath = libpath - ) - }, - "3.6" = { - install_missing_pkgs( - packages = "terra", - packages_to_install = "url::https://cloud.r-project.org/bin/windows/contrib/3.6/terra_1.2-5.zip", - libpath = libpath - ) - } - ) - } - - invisible() -} diff --git a/R/sysinfo.R b/R/sysinfo.R index 8ecc133682..1eeaa59cbd 100644 --- a/R/sysinfo.R +++ b/R/sysinfo.R @@ -3,7 +3,7 @@ #' @param file Name of file, or file object to write to (defaults to stdout). #' @param libpath Library path to find installed packages. #' @export -write_sysinfo <- function(file = stdout(), libpath = shinyverse_libpath()) { +write_sysinfo <- function(file = stdout(), libpath = resolve_libpath()) { withr::local_libpaths(libpath, action = "prefix") platform_info <- sessioninfo::platform_info() diff --git a/R/test-in-browser.R b/R/test-in-browser.R index bb0d521751..04c9e7f6a3 100644 --- a/R/test-in-browser.R +++ b/R/test-in-browser.R @@ -7,21 +7,23 @@ #' @param port `port` for the foreground app process #' @param port_background `port` for the background app process #' @param host `host` for the foreground and background app processes -#' @param local_pkgs If `TRUE`, local packages will be used instead of the isolated shinyverse installation. +#' @inheritParams resolve_libpath #' @param ... ignored #' @export #' @examples -#' \dontrun{test_in_browser()} +#' \dontrun{ +#' test_in_browser() +#' } test_in_browser <- function( - app_name = apps[1], - apps = apps_manual, - ..., - port = 8080, - port_background = NULL, - host = "127.0.0.1", - local_pkgs = FALSE -) { - libpath <- install_shinyverse(install = !isTRUE(local_pkgs)) + app_name = apps[1], + apps = apps_manual, + ..., + port = 8080, + port_background = NULL, + host = "127.0.0.1", + local_pkgs = FALSE) { + should_install <- !isTRUE(local_pkgs) + libpath <- resolve_libpath(local_pkgs = local_pkgs) app_infos <- lapply(apps, function(app_name) { app_proc <- NULL @@ -53,7 +55,7 @@ test_in_browser <- function( return() } - message("Killing background Shiny Session...", appendLF = FALSE) + message("Killing background ", app_name, " Shiny Session...", appendLF = FALSE) if (app_proc$is_alive()) { app_proc$kill() } @@ -103,6 +105,13 @@ test_in_browser <- function( message(" OK") } + if (should_install) { + install_missing_app_deps( + app_name, + libpath = libpath + ) + } + # start new app message("Launching background app process...", appendLF = FALSE) @@ -166,9 +175,9 @@ test_in_browser <- function( test_in_external( app_infos = app_infos, + default_output_lines = "(Loading next app...)", default_app_name = resolve_app_name(app_name), host = host, port = port ) - } diff --git a/R/test-in-external.R b/R/test-in-external.R index 366d7d2023..f71af49dbe 100644 --- a/R/test-in-external.R +++ b/R/test-in-external.R @@ -1,32 +1,38 @@ - - - test_in_external <- function( - app_infos, - default_app_name = 1, - host = "127.0.0.1", - port = 8080 -) { - + app_infos, + ..., + default_output_lines = "", + default_app_name = 1, + host = "127.0.0.1", + port = 8080) { + stopifnot(length(list(...)) == 0) # run shiny app in the browser if (rstudioapi::isAvailable()) { if (rstudioapi::isAvailable("1.3.387")) { # browser, window, pane shiny_viewer_type <- rstudioapi::readRStudioPreference("shiny_viewer_type", "not-correct") if (!identical(shiny_viewer_type, "browser")) { - on.exit({ - rstudioapi::writeRStudioPreference("shiny_viewer_type", shiny_viewer_type) - }, add = TRUE) + on.exit( + { + rstudioapi::writeRStudioPreference("shiny_viewer_type", shiny_viewer_type) + }, + add = TRUE + ) rstudioapi::writeRStudioPreference("shiny_viewer_type", "browser") } } else { # RStudio, but early version # This feels hacky, but is necessary - runExternal <- get(".rs.invokeShinyWindowExternal", envir = as.environment("tools:rstudio")) - old_option <- options(shiny.launch.browser = runExternal) - on.exit({ - options(old_option) - }) + try( + { + runExternal <- get(".rs.invokeShinyWindowExternal", envir = as.environment("tools:rstudio")) + old_option <- options(shiny.launch.browser = runExternal) + on.exit({ + options(old_option) + }) + }, + silent = TRUE + ) } } @@ -93,12 +99,10 @@ test_in_external <- function( # }) # ") ), - shiny::fixedPanel( class = "background_app", shiny::uiOutput("app_iframe", class = "iframe_container") ), - shiny::tags$head( shiny::tags$style(paste0(" .apps_dir { @@ -180,18 +184,22 @@ test_in_external <- function( ) server <- function(input, output, session) { + app_name <- shiny::eventReactive( + { + input$app_name + }, + { + if (identical(input$app_name, "")) { + shiny::req(FALSE) + } + if (!input$app_name %in% external_app_names) { + message("incorrect app name: '", input$app_name, "'") + shiny::req(FALSE) + } - app_name <- shiny::eventReactive({input$app_name}, { - if (identical(input$app_name, "")) { - shiny::req(FALSE) - } - if (! input$app_name %in% external_app_names) { - message("incorrect app name: '", input$app_name, "'") - shiny::req(FALSE) + input$app_name } - - input$app_name - }) + ) app_info <- shiny::reactive({ app_infos[[app_name()]] @@ -201,7 +209,7 @@ test_in_external <- function( app_info()$header() }) - output_lines <- shiny::reactiveVal() + output_lines <- shiny::reactiveVal(default_output_lines) # user_agent <- shiny::reactive({ # shiny::req(input$user_agent) @@ -215,11 +223,19 @@ test_in_external <- function( go_to_next_app <- function() { # get next app app_pos <- which(external_app_names == app_name()) + 1 + if (app_pos > length(external_app_names)) { + shiny::showModal(shiny::modalDialog( + title = "Testing complete!", + easyClose = TRUE, + )) + app_pos <- 1 + } shiny::updateSelectizeInput( session, "app_name", selected = external_app_names[app_pos] ) + output_lines(default_output_lines) } # shiny::observeEvent({input$accept}, { @@ -244,16 +260,21 @@ test_in_external <- function( # go_to_next_app() # }) - shiny::observeEvent({input[["next"]]}, { - # message("CLOSE APP: ", app_name(), "\n") - # app_status_save( - # app_dir = file.path(dir, app_name()), - # pass = FALSE, - # log = output_lines(), - # user_agent = user_agent() - # ) - go_to_next_app() - }) + shiny::observeEvent( + { + input[["next"]] + }, + { + # message("CLOSE APP: ", app_name(), "\n") + # app_status_save( + # app_dir = file.path(dir, app_name()), + # pass = FALSE, + # log = output_lines(), + # user_agent = user_agent() + # ) + go_to_next_app() + } + ) # Can not call app_info()$on_session_ended() directly as it requires a reactive context # That is not allowed in session$onSessionEnded @@ -268,7 +289,7 @@ test_in_external <- function( on_session_ended <<- app_info()$on_session_ended }) session$onSessionEnded(function() { - if (! is.function(on_session_ended)) { + if (!is.function(on_session_ended)) { return() } on_session_ended() @@ -295,10 +316,15 @@ test_in_external <- function( output_lines(ret) }) # reset the output on refresh - shiny::observeEvent({input$refresh}, { - app_info()$output_lines(reset = TRUE) - output_lines("") - }) + shiny::observeEvent( + { + input$refresh + }, + { + app_info()$output_lines(reset = TRUE) + output_lines("") + } + ) output$server_output <- shiny::renderText({ app_has_started() output_lines() diff --git a/R/test-in-ide.R b/R/test-in-ide.R index f0e7c8e156..f46342e386 100644 --- a/R/test-in-ide.R +++ b/R/test-in-ide.R @@ -12,36 +12,32 @@ #' @param refresh_ For internal use. If TRUE, packages will not be reinstalled. #' @export #' @examples -#' \dontrun{test_in_ide(dir = "apps")} +#' \dontrun{ +#' test_in_ide(dir = "apps") +#' } test_in_ide <- function( - app_name = apps[1], - apps = apps_manual, - ..., - port = 8000, - host = "127.0.0.1", - delay = 1, - local_pkgs = FALSE, - viewer = NULL, - refresh_ = FALSE -) { + app_name = apps[1], + apps = apps_manual, + ..., + port = 8000, + host = "127.0.0.1", + delay = 1, + local_pkgs = FALSE, + viewer = NULL, + refresh_ = FALSE) { sys_call <- match.call() apps <- resolve_app_name(apps) - local_libpath <- - if (isTRUE(refresh_)) { - if (isTRUE(local_pkgs)) { - install_shinyverse(install = FALSE) - } else { - shinyverse_libpath() - } - } else { - # First time though - install_shinyverse(install = !isTRUE(local_pkgs), validate_loaded = TRUE) - } - withr::local_libpaths(local_libpath, action = "prefix") + should_install <- !isTRUE(local_pkgs) + libpath <- resolve_libpath(local_pkgs = local_pkgs) + withr::local_libpaths(libpath, action = "prefix") app_name <- resolve_app_name(app_name) + if (should_install) { + install_missing_app_deps(app_name, libpath = libpath) + } + if (rstudioapi::isAvailable()) { # stop("This function should only be run within the RStudio IDE") @@ -52,9 +48,12 @@ test_in_ide <- function( viewer <- rstudioapi::readRStudioPreference("shiny_viewer_type", "pane") } shiny_viewer_type <- force(viewer) - on.exit({ - rstudioapi::writeRStudioPreference("shiny_viewer_type", shiny_viewer_type) - }, add = TRUE) + on.exit( + { + rstudioapi::writeRStudioPreference("shiny_viewer_type", shiny_viewer_type) + }, + add = TRUE + ) if (is.null(viewer)) { message("!! Setting `shiny_viewer_type` to `'pane'` !!") @@ -66,7 +65,6 @@ test_in_ide <- function( } # viewer supplied rstudioapi::writeRStudioPreference("shiny_viewer_type", viewer) - } else { # RStudio, but early version # This feels hacky, but is necessary @@ -77,18 +75,18 @@ test_in_ide <- function( message("!! Setting `shiny_viewer_type` to `'pane'` !!") viewer <- "pane" } - runPane <- get(".rs.invokeShinyPaneViewer", envir = as.environment("tools:rstudio")) - runWindow <- get(".rs.invokeShinyWindowViewer", envir = as.environment("tools:rstudio")) - runFn <- switch( - match.arg(viewer, c("pane", "window")), - "pane" = runPane, - "window" = runWindow - ) - old_option <- options(shiny.launch.browser = runFn) - on.exit({ - options(old_option) + try({ + runPane <- get(".rs.invokeShinyPaneViewer", envir = as.environment("tools:rstudio")) + runWindow <- get(".rs.invokeShinyWindowViewer", envir = as.environment("tools:rstudio")) + runFn <- switch(match.arg(viewer, c("pane", "window")), + "pane" = runPane, + "window" = runWindow + ) + old_option <- options(shiny.launch.browser = runFn) + on.exit({ + options(old_option) + }) }) - } } @@ -99,15 +97,21 @@ test_in_ide <- function( # app_status_init(dir, user_agent = app_status_user_agent_ide()) old_ops <- options(width = 100) - on.exit({ - options(old_ops) - }, add = TRUE) + on.exit( + { + options(old_ops) + }, + add = TRUE + ) increment_app_and_wait <- function() { - next_app_name <- next_app_name(app_name) - if (is.null(next_app_name)) { + restart_with_app(next_app_name) + } + + restart_with_app <- function(app_name) { + if (is.null(app_name)) { message("All done testing!") return(invisible(NULL)) } @@ -115,12 +119,13 @@ test_in_ide <- function( # do not try to update apps again. Set the next app. Keep all existing arguments. next_sys_call_list <- as.list(sys_call) next_sys_call_list[[1]] <- substitute(shinycoreci::test_in_ide) - next_sys_call_list$app_name <- next_app_name + next_sys_call_list$app_name <- app_name next_sys_call_list$refresh_ <- TRUE next_sys_call <- as.call(next_sys_call_list) message("Restarting RStudio and launching next app in ", delay, " second... (interrupt again to stop)") Sys.sleep(delay) + message("Restarting RStudio with next app!") # # if this section of code is reached, it is considered a pass! # app_status_save( @@ -144,7 +149,13 @@ test_in_ide <- function( eval(parse(text = next_sys_call_txt)) }) } - return(invisible(next_app_name)) + return(invisible(app_name)) + } + + # If not in a fresh session, restart! + if (!isTRUE(refresh_)) { + restart_with_app(app_name) + return() } diff --git a/R/test-in-local.R b/R/test-in-local.R index f8662be20e..5094aa268e 100644 --- a/R/test-in-local.R +++ b/R/test-in-local.R @@ -14,25 +14,25 @@ ci_status <- list( #' @param retries number of attempts to retry before declaring the test a failure #' @param repo_dir Location of local shinycoreci repo #' @param ... ignored -#' @param install If \code{TRUE}, installs shinyverse in the default libpath before running tests. App dependencies will always be installed if missing. +#' @inheritParams resolve_libpath #' @export test_in_local <- function( - apps = apps_with_tests(repo_dir), - ..., - install = TRUE, - assert = TRUE, - timeout = 10 * 60, - retries = 2, - repo_dir = rprojroot::find_package_root_file() -) { + apps = apps_with_tests(repo_dir), + ..., + assert = TRUE, + timeout = 10 * 60, + retries = 2, + repo_dir = rprojroot::find_package_root_file(), + local_pkgs = FALSE) { retries <- as.numeric(retries) repo_dir <- normalizePath(repo_dir, mustWork = TRUE) + should_install <- !isTRUE(local_pkgs) + libpath <- resolve_libpath(local_pkgs = local_pkgs) + stopifnot(length(apps_with_tests(repo_dir)) > 0) apps <- resolve_app_name(apps, known_apps = apps_with_tests(repo_dir)) - libpath <- install_shinyverse_local(install = install, install_apps_deps = FALSE) - withr::defer({ # Record platform info and package versions (after everything has been installed) write_sysinfo( @@ -49,24 +49,25 @@ test_in_local <- function( ) run_test <- function(app_name, show_output = TRUE) { - - install_result <- try({ - install_missing_app_deps(app_name, libpath = libpath) - }) - # Check for installation results - if (inherits(install_result, "try-error")) { - tmpfile <- tempfile() - app_deps <- apps_deps_map[[app_name]] - cat( - file = tmpfile, - "Failed to install:\n", paste0("* ", app_deps, "\n"), - "\nError:\n", as.character(install_result), "\n" - ) - return(list( - status = ci_status$no_install, - result = as.character(install_result), - log_file = tmpfile - )) + if (should_install) { + install_result <- try({ + install_missing_app_deps(app_name, libpath = libpath, verbose = show_output) + }) + # Check for installation results + if (inherits(install_result, "try-error")) { + tmpfile <- tempfile() + app_deps <- apps_deps_map[[app_name]] + cat( + file = tmpfile, + "Failed to install:\n", paste0("* ", app_deps, "\n"), + "\nError:\n", as.character(install_result), "\n" + ) + return(list( + status = ci_status$no_install, + result = as.character(install_result), + log_file = tmpfile + )) + } } log_file <- tempfile("coreci-log-", fileext = ".log") @@ -79,9 +80,12 @@ test_in_local <- function( list(NOT_CRAN = "true"), { message("\n###\nRunning tests for app: ", basename(app_path_), "\n") - on.exit({ - message("\nStopping tests for app: ", basename(app_path_), "\n###") - }, add = TRUE) + on.exit( + { + message("\nStopping tests for app: ", basename(app_path_), "\n###") + }, + add = TRUE + ) shiny::runTests( appDir = app_path_, @@ -97,7 +101,8 @@ test_in_local <- function( timeout = timeout, stdout = log_file, stderr = "2>&1", - show = show_output + show = show_output, + supervise = TRUE ) result <- test_result$result[[1]] status <- @@ -130,7 +135,6 @@ test_in_local <- function( # (break statements at beginning and end of while loop) show_output <- FALSE while (TRUE) { - # get all positions that should be tested to_test_positions <- which(test_dt$status %in% c(ci_status$fail, ci_status$default)) if (length(to_test_positions) == 0) { @@ -140,11 +144,12 @@ test_in_local <- function( pb <- progress_bar( total = length(to_test_positions), - format = "[:current/:total;:elapsed;:eta] :app" + format = "[:current/:total; :elapsed; :eta] :app\n", + clear = FALSE, + show_after = 0 ) # for each file position... for (to_test_position in to_test_positions) { - # get the failure test file app_name <- test_dt$app_name[to_test_position] @@ -184,6 +189,7 @@ test_in_local <- function( # can not retry anymore; stop testing break } + message("\n\nRetrying failed tests... (Displaying test output from now on)") retries <- retries - 1 show_output <- TRUE } @@ -211,7 +217,6 @@ assert_test_output <- function(output) { test_dt <- output concat_info <- function(title, statuses, include_result = TRUE) { - sub_rows <- test_dt$status %in% statuses sub_test_dt <- test_dt[sub_rows, ] sub_app_name <- sub_test_dt$app_name @@ -247,8 +252,8 @@ assert_test_output <- function(output) { } # display_message("App test successes", ci_status$pass, include_result = FALSE) display_message("Apps which did not return a result", ci_status$did_not_return_result, include_result = FALSE) - display_message("App test failures", ci_status$fail, include_result = TRUE) - display_message("Apps which could NOT be tested", ci_status$no_install, include_result = TRUE) + display_message("App test failures", ci_status$fail, include_result = TRUE) + display_message("Apps which could NOT be tested", ci_status$no_install, include_result = TRUE) if (any(test_dt$status %in% ci_status$fail)) { stop( diff --git a/R/view-test-images.R b/R/view-test-images.R index 348ead28f1..0bcf2f7e14 100644 --- a/R/view-test-images.R +++ b/R/view-test-images.R @@ -1,3 +1,5 @@ +utils::globalVariables(c("png_name", "test_name", "variant")) + #' View Shinytest Images #' #' @param repo_dir directory to the shinycoreci repo diff --git a/README.Rmd b/README.Rmd index f9447e3ef6..87c1095a4b 100644 --- a/README.Rmd +++ b/README.Rmd @@ -46,34 +46,34 @@ shinycoreci [uses the following GitHub Runnner Images](https://github.com/rstudi [windows-2022]: https://github.com/actions/runner-images/blob/main/images/windows/Windows2022-Readme.md [macOS-12]: https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md + ## Installation Install the development version from [GitHub](https://github.com/) with: ``` r -# install.packages("pak", repos = sprintf("https://r-lib.github.io/p/pak/stable/%s/%s/%s", .Platform$pkgType, R.Version()$os, R.Version()$arch)) -pak::pkg_install("rstudio/shinycoreci") +pak::pak("rstudio/shinycoreci") ``` These GitHub packages will be installed to make sure the latest package development is working as expected: ```{r, echo = FALSE, results = "asis"} -pkgs <- shinycoreci:::shinyverse_remotes - -# remove branches, sha, and commas -github_pkgs <- sub("[, (@#].*$", "", pkgs) +shinyverse_urls <- shinycoreci:::shinyverse_urls # print as links to repos cat( paste0( - "* [", pkgs, "](http://github.com/", github_pkgs, ")", + "* [", sub("https://github.com/", "", shinyverse_urls), "](", shinyverse_urls, ")", collapse = "\n" ) ) ``` +#### R-Universe + +`{shinycoreci}` testing leverages rOpenSci [`r-universe`](https://r-universe.dev/search/), specifically the [`posit-dev-shinycoreci`](https://posit-dev-shinycoreci.r-universe.dev/builds) universe. This universe is used to install the latest development versions of the Shiny related packages (updated hourly) used in the testing apps without the need for a GitHub token. This last detail is important, as it allows GitHub Actions to install packages freely without the worry of being rate limited. This gives us the ability to attempt to install each app's dependencies independently, leading to higher test coverage as a single dependencies does not block the entire test execution. + -Tools for manual and automated testing of shiny apps. ## Running manual tests diff --git a/README.md b/README.md index b98a9993c3..97dcbe6758 100644 --- a/README.md +++ b/README.md @@ -30,45 +30,47 @@ shinycoreci [uses the following GitHub Runnner Images](https://github.com/rstudi Install the development version from [GitHub](https://github.com/) with: ``` r -# install.packages("pak", repos = sprintf("https://r-lib.github.io/p/pak/stable/%s/%s/%s", .Platform$pkgType, R.Version()$os, R.Version()$arch)) -pak::pkg_install("rstudio/shinycoreci") +pak::pak("rstudio/shinycoreci") ``` These GitHub packages will be installed to make sure the latest package development is working as expected: -- [r-lib/cachem](http://github.com/r-lib/cachem) -- [r-lib/fastmap](http://github.com/r-lib/fastmap) -- [r-lib/later](http://github.com/r-lib/later) -- [rstudio/bslib](http://github.com/rstudio/bslib) -- [rstudio/bsicons](http://github.com/rstudio/bsicons) -- [ramnathv/htmlwidgets](http://github.com/ramnathv/htmlwidgets) -- [rstudio/crosstalk](http://github.com/rstudio/crosstalk) -- [rstudio/gt](http://github.com/rstudio/gt) -- [rstudio/DT](http://github.com/rstudio/DT) -- [rstudio/dygraphs](http://github.com/rstudio/dygraphs) -- [rstudio/flexdashboard](http://github.com/rstudio/flexdashboard) -- [rstudio/fontawesome](http://github.com/rstudio/fontawesome) -- [rstudio/htmltools](http://github.com/rstudio/htmltools) -- [rstudio/httpuv](http://github.com/rstudio/httpuv) -- [rstudio/leaflet](http://github.com/rstudio/leaflet) -- [rstudio/pool](http://github.com/rstudio/pool) -- [rstudio/promises](http://github.com/rstudio/promises) -- [rstudio/reactlog](http://github.com/rstudio/reactlog) -- [rstudio/sass](http://github.com/rstudio/sass) -- [rstudio/shiny](http://github.com/rstudio/shiny) -- [rstudio/shinymeta](http://github.com/rstudio/shinymeta) -- [rstudio/shinytest](http://github.com/rstudio/shinytest) -- [rstudio/chromote](http://github.com/rstudio/chromote) -- [rstudio/shinytest2](http://github.com/rstudio/shinytest2) -- [rstudio/shinythemes](http://github.com/rstudio/shinythemes) -- [rstudio/shinyvalidate](http://github.com/rstudio/shinyvalidate) -- [rstudio/thematic](http://github.com/rstudio/thematic) -- [rstudio/webdriver](http://github.com/rstudio/webdriver) -- [rstudio/websocket](http://github.com/rstudio/websocket) -- [ropensci/plotly](http://github.com/ropensci/plotly) -- [schloerke/shinyjster](http://github.com/schloerke/shinyjster) - -Tools for manual and automated testing of shiny apps. +- [rstudio/bsicons](https://github.com/rstudio/bsicons) +- [rstudio/bslib](https://github.com/rstudio/bslib) +- [r-lib/cachem](https://github.com/r-lib/cachem) +- [rstudio/chromote](https://github.com/rstudio/chromote) +- [rstudio/crosstalk](https://github.com/rstudio/crosstalk) +- [rstudio/DT](https://github.com/rstudio/DT) +- [rstudio/dygraphs](https://github.com/rstudio/dygraphs) +- [r-lib/fastmap](https://github.com/r-lib/fastmap) +- [rstudio/flexdashboard](https://github.com/rstudio/flexdashboard) +- [rstudio/fontawesome](https://github.com/rstudio/fontawesome) +- [rstudio/gt](https://github.com/rstudio/gt) +- [rstudio/htmltools](https://github.com/rstudio/htmltools) +- [ramnathv/htmlwidgets](https://github.com/ramnathv/htmlwidgets) +- [rstudio/httpuv](https://github.com/rstudio/httpuv) +- [r-lib/later](https://github.com/r-lib/later) +- [rstudio/leaflet](https://github.com/rstudio/leaflet) +- [ropensci/plotly](https://github.com/ropensci/plotly) +- [rstudio/pool](https://github.com/rstudio/pool) +- [rstudio/promises](https://github.com/rstudio/promises) +- [rstudio/reactlog](https://github.com/rstudio/reactlog) +- [rstudio/sass](https://github.com/rstudio/sass) +- [rstudio/shiny](https://github.com/rstudio/shiny) +- [rstudio/shinycoreci](https://github.com/rstudio/shinycoreci) +- [schloerke/shinyjster](https://github.com/schloerke/shinyjster) +- [rstudio/shinymeta](https://github.com/rstudio/shinymeta) +- [rstudio/shinytest](https://github.com/rstudio/shinytest) +- [rstudio/shinytest2](https://github.com/rstudio/shinytest2) +- [rstudio/shinythemes](https://github.com/rstudio/shinythemes) +- [rstudio/shinyvalidate](https://github.com/rstudio/shinyvalidate) +- [rstudio/thematic](https://github.com/rstudio/thematic) +- [rstudio/webdriver](https://github.com/rstudio/webdriver) +- [rstudio/websocket](https://github.com/rstudio/websocket) + +#### R-Universe + +`{shinycoreci}` testing leverages rOpenSci [`r-universe`](https://r-universe.dev/search/), specifically the [`posit-dev-shinycoreci`](https://posit-dev-shinycoreci.r-universe.dev/builds) universe. This universe is used to install the latest development versions of the Shiny related packages (updated hourly) used in the testing apps without the need for a GitHub token. This last detail is important, as it allows GitHub Actions to install packages freely without the worry of being rate limited. This gives us the ability to attempt to install each app’s dependencies independently, leading to higher test coverage as a single dependencies does not block the entire test execution. ## Running manual tests diff --git a/inst/Docker/centos/Dockerfile b/inst/Docker/centos/Dockerfile index a39dfebc7b..12764a6acc 100644 --- a/inst/Docker/centos/Dockerfile +++ b/inst/Docker/centos/Dockerfile @@ -137,26 +137,8 @@ RUN R --quiet -e 'install.packages("pak", repos = sprintf("https://r-lib.github. # Install shinycoreci then install shinyverse; Do not install apps deps as they have been installed via binary in prior step RUN R --quiet \ -e " \ - pkgs <- c('base64enc', 'bslib', 'Cairo', 'clipr', 'curl', 'dbplyr', 'DiagrammeR', \ - 'dplyr', 'DT', 'evaluate', 'flexdashboard', 'future', 'ggplot2', \ - 'ggvis', 'hexbin', 'htmltools', 'htmlwidgets', \ - 'httpuv', 'jsonlite', 'knitr', 'later', 'leaflet', 'magrittr', \ - 'maps', 'markdown', 'memoise', 'networkD3', 'plotly', 'png', \ - 'progress', 'promises', 'pryr', 'radiant', 'ragg', 'RColorBrewer', \ - 'reactable', 'reactlog', 'reactR', 'rlang', 'rmarkdown', 'rprojroot', \ - 'rsconnect', 'RSQLite', 'rversions', 'scales', 'sf', 'shiny', \ - 'shinyAce', 'shinydashboard', 'shinyjs', 'shinymeta', \ - 'shinytest2', 'shinythemes', 'shinyvalidate', 'showtext', 'sysfonts', \ - 'systemfonts', 'testthat', 'thematic', 'tidyr', 'tm', 'websocket', \ - 'withr', 'wordcloud', \ - 'sessioninfo', \ - 'debugme', 'highcharter', 'parsedate', 'quantmod', 'rjson', 'rlist', 'showimage', 'TTR', 'XML', 'xts' \ - ); \ - # NOTE! System requirements must be updated by hand. :-( - # pak::pkg_system_requirements(pkgs, execute = TRUE); \ - install.packages(pkgs); \ - pak::pkg_install('rstudio/shinycoreci@${SHINYCORECI_SHA}');\ - shinycoreci:::install_shinyverse_local(upgrade = FALSE, install_apps_deps = FALSE);\ + pak::pkg_install('rstudio/shinycoreci@${SHINYCORECI_SHA}'); \ + shinycoreci:::install_missing_app_deps();\ " diff --git a/inst/Docker/ubuntu/Dockerfile b/inst/Docker/ubuntu/Dockerfile index e2e12ace82..200e50890e 100644 --- a/inst/Docker/ubuntu/Dockerfile +++ b/inst/Docker/ubuntu/Dockerfile @@ -5,7 +5,7 @@ # -ARG R_VERSION=4.2 +ARG R_VERSION=4.3 # Not `xenial` because it is EOL # bionic, focal @@ -122,25 +122,8 @@ RUN R --quiet -e 'install.packages("pak", repos = sprintf("https://r-lib.github. # Install shinycoreci then install shinyverse; Do not install apps deps as they have been installed via binary in prior step RUN R --quiet \ -e " \ - pkgs <- c('base64enc', 'bslib', 'Cairo', 'clipr', 'curl', 'dbplyr', 'DiagrammeR', \ - 'dplyr', 'DT', 'evaluate', 'flexdashboard', 'future', 'ggplot2', \ - 'ggvis', 'hexbin', 'htmltools', 'htmlwidgets', \ - 'httpuv', 'jsonlite', 'knitr', 'later', 'leaflet', 'magrittr', \ - 'maps', 'markdown', 'memoise', 'networkD3', 'plotly', 'png', \ - 'progress', 'promises', 'pryr', 'radiant', 'ragg', 'RColorBrewer', \ - 'reactable', 'reactlog', 'reactR', 'rlang', 'rmarkdown', 'rprojroot', \ - 'rsconnect', 'RSQLite', 'rversions', 'scales', 'sf', 'shiny', \ - 'shinyAce', 'shinydashboard', 'shinyjs', 'shinymeta', \ - 'shinytest2', 'shinythemes', 'shinyvalidate', 'showtext', 'sysfonts', \ - 'systemfonts', 'testthat', 'thematic', 'tidyr', 'tm', 'websocket', \ - 'withr', 'wordcloud', \ - 'sessioninfo', \ - 'debugme', 'highcharter', 'parsedate', 'quantmod', 'rjson', 'rlist', 'showimage', 'TTR', 'XML', 'xts' \ - ); \ - pak::pkg_system_requirements(pkgs, execute = TRUE); \ - install.packages(pkgs); \ - pak::pkg_install('rstudio/shinycoreci@${SHINYCORECI_SHA}');\ - shinycoreci:::install_shinyverse_local(upgrade = FALSE, install_apps_deps = FALSE);\ + pak::pkg_install('rstudio/shinycoreci@${SHINYCORECI_SHA}'); \ + shinycoreci:::install_missing_app_deps();\ " diff --git a/inst/apps/.renvignore b/inst/apps/.renvignore new file mode 100644 index 0000000000..19ffe16ac7 --- /dev/null +++ b/inst/apps/.renvignore @@ -0,0 +1,26 @@ +# File used by renv::dependencies() to ignore files to inspect +_snaps/ +fonts/ +www/ +data/ +rsconnect/ +*.db +*.ttc +*.js +*.png +*.jpg +*.json +*.md +*.txt +.DS_Store +.gitignore +.Renviron +*.html +*.css +*.csv +*.tsv +*.gz +*.Rproj +*.rds +*.xls +*.sh diff --git a/inst/apps/000-pkg-versions/app.R b/inst/apps/000-pkg-versions/app.R new file mode 100644 index 0000000000..815a7c2dc0 --- /dev/null +++ b/inst/apps/000-pkg-versions/app.R @@ -0,0 +1,117 @@ +### Keep this line to manually test this shiny application. Do not edit this line; shinycoreci::::is_manual_app + +# TODO-barret; Attempt to preinstall the shinyverse so that all packages are attempted to be installed from the R-Universe + +library(shiny) +library(sessioninfo) +library(dplyr) + + + + +ui <- fluidPage( + + titlePanel("Installed package information"), + + tags$hr(), + tags$h2("Packages from the shinycoreci universe: "), + uiOutput("pkg_from_universe"), + + tags$h2("shinyverse packages"), + + verbatimTextOutput("all_pkg_info"), + + # # Do not test with shinyjster as there is no way to verify all pkgs have been installed successfuly + # # Leave as manual app only + # shinyjster::shinyjster_js(" + # var jst = jster(); + # jst.add(Jster.shiny.waitUntilIdle); + # jst.add(function() { Jster.assert.isEqual($('#pkg_from_universe').text().trim(), 'Pass!') }); + # jst.test(); + # ") + +) + +server <- function(input, output, session) { + + # # Do not test on CI; See note above + # shinyjster::shinyjster_server(input, output, session) + + pkg_infos <- jsonlite::read_json("https://posit-dev-shinycoreci.r-universe.dev/api/packages/") + pkgs <- vapply(pkg_infos, `[[`, character(1), "Package") + universe_url <- "https://posit-dev-shinycoreci.r-universe.dev" + + get_pkg_info <- function(pkg) { + desc <- packageDescription(pkg) + + get_field <- function(field) { + if (!is.list(desc)) { + return(NA_character_) + } + val <- desc[[field]] + if (is.null(val)) { + return(NA_character_) + } + val + } + + tibble::tibble( + package = pkg, + version = get_field("Version"), + repository = get_field("Repository"), + packaged = get_field("Packaged"), + built = get_field("Built"), + remoteUrl = get_field("RemoteUrl"), + remoteRef = get_field("RemoteRef"), + remoteSha = get_field("RemoteSha"), + remoteType = get_field("RemoteType"), + remotePkgRef = get_field("RemotePkgRef"), + remoteRepos = get_field("RemoteRepos"), + remotePkgPlatform = get_field("RemotePkgPlatform"), + ) + } + + dt <- bind_rows(lapply(pkgs, get_pkg_info)) + + print(dt, n = Inf, width = 1000) + + # ---------------------------- + + # Packages inside `dt` are in the shinyverse so if there remote location is + # not the universe url, then they're not properly installed. + bad_dt <- dt %>% filter(!is.na(remoteRepos)) %>% filter(remoteRepos != universe_url) + + output$pkg_from_universe <- renderUI({ + if (nrow(bad_dt) == 0) { + tags$h4(tags$span("Pass!", style = "background-color: #7be092;")) + } else { + tagList( + tags$h4(tags$span("Fail!", style = "background-color: #e68a8a;")), + tags$ul( + Map( + bad_dt$package, + bad_dt$repository, + f = function(package, repository) { + tags$li(tags$code(package), " is from ", repository) + } + ) + ), + tags$h4("Bad packages"), + verbatimTextOutput("bad_pkg_info") + ) + } + }) + + output$bad_pkg_info <- renderPrint({ + bad_dt %>% + print(n = Inf, width = 1000) + }) + + output$all_pkg_info <- renderPrint({ + dt %>% + print(n = Inf, width = 1000) + }) +} + +# Create Shiny app ---- +shinyApp(ui = ui, server = server) diff --git a/inst/apps/000-pkg-versions/tests/testthat.R b/inst/apps/000-pkg-versions/tests/testthat.R new file mode 100644 index 0000000000..7d25b5b9e4 --- /dev/null +++ b/inst/apps/000-pkg-versions/tests/testthat.R @@ -0,0 +1 @@ +shinytest2::test_app() diff --git a/inst/apps/000-pkg-versions/tests/testthat/setup.R b/inst/apps/000-pkg-versions/tests/testthat/setup.R new file mode 100644 index 0000000000..be65b4f035 --- /dev/null +++ b/inst/apps/000-pkg-versions/tests/testthat/setup.R @@ -0,0 +1,2 @@ +# Load application support files into testing environment +shinytest2::load_app_env() diff --git a/inst/apps/000-pkg-versions/tests/testthat/test-true.R b/inst/apps/000-pkg-versions/tests/testthat/test-true.R new file mode 100644 index 0000000000..a295642d0f --- /dev/null +++ b/inst/apps/000-pkg-versions/tests/testthat/test-true.R @@ -0,0 +1,5 @@ +# Do not test app as we can not guarantee all pkgs have been installed successfuly +# Only leave app for manual testing +test_that("tautology", { + expect_true(TRUE) +}) diff --git a/inst/apps/094-image-interaction-basic/app.R b/inst/apps/094-image-interaction-basic/app.R index dd7a364742..69c2dc8853 100644 --- a/inst/apps/094-image-interaction-basic/app.R +++ b/inst/apps/094-image-interaction-basic/app.R @@ -56,10 +56,11 @@ server <- function(input, output, session) { # Generate an image with black lines every 10 pixels output$image1 <- renderImage({ # Get width and height of image output - width <- session$clientData$output_image1_width - height <- session$clientData$output_image1_height + width <- round(session$clientData$output_image1_width) + height <- round(session$clientData$output_image1_height) npixels <- width * height + # Fill the pixels for R, G, B m <- matrix(1, nrow = height, ncol = width) # Add gray vertical and horizontal lines every 10 pixels diff --git a/inst/gha/data-apps-deps-update.R b/inst/gha/data-apps-deps-update.R index d60eface10..c9a55abfc4 100644 --- a/inst/gha/data-apps-deps-update.R +++ b/inst/gha/data-apps-deps-update.R @@ -1,11 +1,11 @@ if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv") if (!requireNamespace("yaml", quietly = TRUE)) install.packages("yaml") if (!requireNamespace("withr", quietly = TRUE)) install.packages("withr") +if (!requireNamespace("jsonlite", quietly = TRUE)) install.packages("jsonlite") update_apps_deps <- function(repo_dir = ".") { withr::local_dir(repo_dir) - source("R/data-shinyverse.R") revdeps <- renv::dependencies( "inst/apps", progress = TRUE @@ -14,7 +14,7 @@ update_apps_deps <- function(repo_dir = ".") { subset( revdeps, !( - Package %in% c(base_packages(), "shinycoreci", shinyverse_pkgs) + Package %in% c(base_packages(), "shinycoreci") ) ) apps_deps <- sort(unique(revdeps$Package)) @@ -42,3 +42,30 @@ base_packages <- function() { pkg_df <- as.data.frame(utils::installed.packages(), stringsAsFactors = FALSE) pkg_df$Package[pkg_df$Priority %in% c("base", "recommended")] } + +update_shinyverse <- function() { + pkg_infos <- jsonlite::read_json("https://posit-dev-shinycoreci.r-universe.dev/api/packages/") + + pkgs <- vapply(pkg_infos, `[[`, character(1), "Package") + urls <- vapply(pkg_infos, `[[`, character(1), "RemoteUrl") + + # Sort according to pkg name + urls <- urls[order(pkgs)] + pkgs <- pkgs[order(pkgs)] + + universe_txt <- '"https://posit-dev-shinycoreci.r-universe.dev"' + pkgs_txt <- utils::capture.output(dput(pkgs)) + urls_txt <- utils::capture.output(dput(urls)) + cat( + file = "R/data-shinyverse.R", + sep = "", + "# Do not edit by hand!\n", + "# This file is automatically generated by `./inst/gha/data-apps-deps-update.R`\n", + "# To add / update a package, make a PR here: https://github.com/posit-dev-shinycoreci/posit-dev-shinycoreci.r-universe.dev/blob/main/packages.json\n", + "shinyverse_cran_url <- ", paste0(universe_txt, collapse = "\n"), "\n", + "\n", + "shinyverse_pkgs <- ", paste0(pkgs_txt, collapse = "\n"), "\n", + "\n", + "shinyverse_urls <- ", paste0(urls_txt, collapse = "\n"), "\n" + ) +} diff --git a/inst/gha/gha-adjust-packages-to-install.R b/inst/gha/gha-adjust-packages-to-install.R deleted file mode 100644 index a61d6838de..0000000000 --- a/inst/gha/gha-adjust-packages-to-install.R +++ /dev/null @@ -1,36 +0,0 @@ -adjust_pkgs <- function(pkgs_to_install = "rstudio/shiny,rstudio/bslib", r_version = "4.2.1") { - is_windows <- .Platform$OS.type == "windows" - is_linux <- Sys.info()[["sysname"]] == "Linux" - is_mac <- Sys.info()[["sysname"]] == "Darwin" - # Get R version like `"4.2"` - short_r_version <- sub("\\.\\d$", "", r_version) - - replace_or_add <- function(find_val, replace_val) { - pkgs_to_install <<- - if (grepl(find_val, pkgs_to_install, fixed = TRUE)) { - sub(find_val, replace_val, pkgs_to_install, fixed = TRUE) - } else { - paste0(pkgs_to_install, ",", replace_val) - } - } - - # Current dev version of terra has a fix that makes it installable on R<4.1 - if (as.package_version(r_version) < as.package_version("4.1")) { - replace_or_add("any::terra", "rspatial/terra") - } - - if (is_linux) { - switch(short_r_version, - "4.2" = { - replace_or_add("any::XML", "XML") - }, - "3.6" = { - replace_or_add( - "any::rjson", - "url::https://cran.r-project.org/src/contrib/Archive/rjson/rjson_0.2.20.tar.gz" - ) - } - ) - } - pkgs_to_install -} diff --git a/inst/gha/gha-shinyverse-packages.R b/inst/gha/gha-shinyverse-packages.R deleted file mode 100644 index 6189c3ba2b..0000000000 --- a/inst/gha/gha-shinyverse-packages.R +++ /dev/null @@ -1,42 +0,0 @@ -shinyverse_pkgs <- function() { - # Set an output variable for GHA to use in installations - - shinyverse_remotes_file <- "R/data-shinyverse.R" - stopifnot(file.exists(shinyverse_remotes_file)) - - apps_deps_file <- "R/data-apps-deps.R" - stopifnot(file.exists(apps_deps_file)) - - source(shinyverse_remotes_file) - stopifnot(length(shinyverse_remotes) > 0) - - source(apps_deps_file) - stopifnot(length(apps_deps) > 0) - - # Very difficult to have both urls and remotes mixed. Does not really work in practice - # Ex: - # Error: Cannot install packages: - # * url::https://github.com/rstudio/shinytest2/archive/HEAD.zip: - # * Can't install dependency rstudio/shiny - # * Can't install dependency rstudio/shinyvalidate - # * rstudio/shinyvalidate: Conflicts with url::https://github.com/rstudio/shinyvalidate/archive/HEAD.zip - # * rstudio/shiny: Conflicts with url::https://github.com/rstudio/shiny/archive/HEAD.zip - - # # Use URL type to download shinyverse. - # # Speed is not as important as minimizing GHA requests - # pak_shinyverse_urls <- paste0( - # # url::https://github.com/tidyverse/stringr/archive/HEAD.zip - # # "url::https://github.com/", shinyverse_remotes, "/archive/HEAD.zip" - # shinyverse_remotes - # ) - - paste0( - c( - shinyverse_remotes, - ## Don't install apps_deps here. Let the methods install them if they're missing - # paste0("any::", apps_deps), - NULL - ), - collapse = "," - ) -} diff --git a/man/deploy_apps.Rd b/man/deploy_apps.Rd index 0ab408ec26..9b2bcb78fa 100644 --- a/man/deploy_apps.Rd +++ b/man/deploy_apps.Rd @@ -9,7 +9,7 @@ deploy_apps( account = "testing-apps", server = "shinyapps.io", ..., - install = TRUE, + local_pkgs = FALSE, extra_packages = NULL, cores = 1, retry = 2, @@ -23,7 +23,7 @@ deploy_apps( \item{...}{ignored} -\item{install}{If TRUE, will install all of shinyverse into the default libpath} +\item{local_pkgs}{If \code{TRUE}, local packages will be used instead of the isolated shinyverse installation.} \item{extra_packages}{A character vector of extra packages to install} diff --git a/man/resolve_libpath.Rd b/man/resolve_libpath.Rd new file mode 100644 index 0000000000..dc87193662 --- /dev/null +++ b/man/resolve_libpath.Rd @@ -0,0 +1,15 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/install-path.R +\name{resolve_libpath} +\alias{resolve_libpath} +\title{Resolve library path} +\usage{ +resolve_libpath(..., local_pkgs = FALSE) +} +\arguments{ +\item{local_pkgs}{If \code{TRUE}, local packages will be used instead of the isolated shinyverse installation.} +} +\description{ +Resolve library path +} +\keyword{internal} diff --git a/man/shinycoreci_libpath.Rd b/man/shinycoreci_libpath.Rd new file mode 100644 index 0000000000..4e583a860b --- /dev/null +++ b/man/shinycoreci_libpath.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/install-path.R +\name{shinycoreci_libpath} +\alias{shinycoreci_libpath} +\alias{shinycoreci_clean_libpaths} +\title{Shinyverse libpath} +\usage{ +shinycoreci_libpath() + +shinycoreci_clean_libpaths() +} +\description{ +Methods to get and reset the shinyverse \code{libpath}. +} +\section{Functions}{ +\itemize{ +\item \code{shinycoreci_libpath()}: Library path that will persist across installations. But will have a different path for different R versions + +\item \code{shinycoreci_clean_libpaths()}: Removes the cached R library + +}} diff --git a/man/shinyverse_libpath.Rd b/man/shinyverse_libpath.Rd deleted file mode 100644 index 3eda8945ce..0000000000 --- a/man/shinyverse_libpath.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/install-path.R -\name{shinyverse_libpath} -\alias{shinyverse_libpath} -\alias{shinyverse_clean_libpath} -\title{Shinyverse libpath} -\usage{ -shinyverse_libpath() - -shinyverse_clean_libpath() -} -\description{ -Methods to get and reset the shinyverse \code{libpath}. -} -\section{Functions}{ -\itemize{ -\item \code{shinyverse_libpath()}: Library path that will persist across installations. But will have a different path for different R versions - -\item \code{shinyverse_clean_libpath()}: Removes the cached R library - -}} diff --git a/man/test_in_browser.Rd b/man/test_in_browser.Rd index a00d4559e7..24008fac15 100644 --- a/man/test_in_browser.Rd +++ b/man/test_in_browser.Rd @@ -33,5 +33,7 @@ test_in_browser( Automatically runs the next app in a fresh callr::r_bg session. To stop, close the shiny application window. } \examples{ -\dontrun{test_in_browser()} +\dontrun{ +test_in_browser() +} } diff --git a/man/test_in_ide.Rd b/man/test_in_ide.Rd index f40b2f2478..2b97c8c67e 100644 --- a/man/test_in_ide.Rd +++ b/man/test_in_ide.Rd @@ -44,5 +44,7 @@ Kill testing by hitting \verb{esc} in RStudio. If \code{options()} need to be set, set them in your \preformatted{.Rprofile} file. See \code{usethis::edit_r_profile()} } \examples{ -\dontrun{test_in_ide(dir = "apps")} +\dontrun{ +test_in_ide(dir = "apps") +} } diff --git a/man/test_in_local.Rd b/man/test_in_local.Rd index 204f9c2f44..d2d663a15e 100644 --- a/man/test_in_local.Rd +++ b/man/test_in_local.Rd @@ -7,11 +7,11 @@ test_in_local( apps = apps_with_tests(repo_dir), ..., - install = TRUE, assert = TRUE, timeout = 10 * 60, retries = 2, - repo_dir = rprojroot::find_package_root_file() + repo_dir = rprojroot::find_package_root_file(), + local_pkgs = FALSE ) } \arguments{ @@ -19,8 +19,6 @@ test_in_local( \item{...}{ignored} -\item{install}{If \code{TRUE}, installs shinyverse in the default libpath before running tests. App dependencies will always be installed if missing.} - \item{assert}{logical value which will determine if \code{\link[=assert_test_output]{assert_test_output()}} will be called on the result} \item{timeout}{Length of time allowed for an application's full test suit can run before determining it is a failure} @@ -28,6 +26,8 @@ test_in_local( \item{retries}{number of attempts to retry before declaring the test a failure} \item{repo_dir}{Location of local shinycoreci repo} + +\item{local_pkgs}{If \code{TRUE}, local packages will be used instead of the isolated shinyverse installation.} } \description{ Test apps using \code{shiny::runTests()} using local libpath diff --git a/man/write_sysinfo.Rd b/man/write_sysinfo.Rd index 0bb1fc3072..f5e5aa6a40 100644 --- a/man/write_sysinfo.Rd +++ b/man/write_sysinfo.Rd @@ -4,7 +4,7 @@ \alias{write_sysinfo} \title{Write system information to a file} \usage{ -write_sysinfo(file = stdout(), libpath = shinyverse_libpath()) +write_sysinfo(file = stdout(), libpath = resolve_libpath()) } \arguments{ \item{file}{Name of file, or file object to write to (defaults to stdout).}