From 74ab36333cd60707f8556449f251b8ec57af5d33 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Tue, 12 Mar 2024 10:06:03 -0400 Subject: [PATCH] First pass at installing less things and install apps deps on demand while testing --- .Rprofile | 0 .../internal/install-shinyverse/action.yaml | 49 ++-- .github/shiny-workflows/package-install.R | 9 +- .github/workflows/apps-config.yml | 2 +- .github/workflows/apps-deploy.yml | 3 +- .github/workflows/apps-deps.yml | 1 + .github/workflows/apps-test-os.yml | 3 +- DESCRIPTION | 2 +- NAMESPACE | 4 +- R/data-apps-deps.R | 246 +++++++++++++----- R/data-shinyverse.R | 61 ++--- R/deploy-apps.R | 55 ++-- R/install-path.R | 24 +- R/install.R | 217 ++++++++------- R/sysinfo.R | 2 +- R/test-in-browser.R | 33 ++- R/test-in-external.R | 116 +++++---- R/test-in-ide.R | 102 +++++--- R/test-in-local.R | 83 +++--- README.Rmd | 16 +- inst/Docker/centos/Dockerfile | 33 +-- inst/Docker/ubuntu/Dockerfile | 49 ++-- inst/apps/.renvignore | 26 ++ inst/apps/001-hello/app.R | 29 +-- inst/apps/063-superzip-example/ui.R | 63 +++-- inst/apps/094-image-interaction-basic/app.R | 5 +- inst/gha/data-apps-deps-update.R | 21 +- inst/gha/gha-adjust-packages-to-install.R | 2 +- inst/gha/gha-shinyverse-packages.R | 37 +-- 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 +- 35 files changed, 781 insertions(+), 572 deletions(-) delete mode 100644 .Rprofile create mode 100644 inst/apps/.renvignore create mode 100644 man/shinycoreci_libpath.Rd delete mode 100644 man/shinyverse_libpath.Rd 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..b58f7ab08a 100644 --- a/.github/internal/install-shinyverse/action.yaml +++ b/.github/internal/install-shinyverse/action.yaml @@ -40,32 +40,23 @@ 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: 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 @@ -73,20 +64,20 @@ runs: run: | source("inst/gha/gha-adjust-packages-to-install.R") pkgs_to_install <- adjust_pkgs( - "${{ steps.shinyverse.outputs.packages }}", + NULL, 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 }} + # rtools-version: ${{ steps.rtools-version.outputs.value }} extra-packages: local::. ${{ steps.pkgs.outputs.to-install }} 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..83fc622dbd 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: "4" 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..81cdd945e7 100644 --- a/.github/workflows/apps-deploy.yml +++ b/.github/workflows/apps-deploy.yml @@ -70,12 +70,13 @@ jobs: with: r-version: ${{ matrix.config.r }} cache-version: deploy-2-${{ needs.config.outputs.cache-version }} - extra-packages: rstudio/rsconnect + # extra-packages: rstudio/rsconnect # Perform as second step to make sure this version is installed - name: Install shinycoreci from GitHub shell: Rscript {0} run: | + pak::pkg_install("rstudio/shinycoreci@${{ github.sha }}") - name: Deploy Apps to Shinyapps.io diff --git a/.github/workflows/apps-deps.yml b/.github/workflows/apps-deps.yml index 9a9eec082c..b244caf1fa 100644 --- a/.github/workflows/apps-deps.yml +++ b/.github/workflows/apps-deps.yml @@ -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-test-os.yml b/.github/workflows/apps-test-os.yml index 565e99b6af..1699df44d6 100644 --- a/.github/workflows/apps-test-os.yml +++ b/.github/workflows/apps-test-os.yml @@ -157,8 +157,7 @@ jobs: 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: diff --git a/DESCRIPTION b/DESCRIPTION index 2426369b64..987366102d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -10,7 +10,7 @@ Description: Core shiny team tools to facilitate testing of the shiny-verse. License: MIT + file LICENSE Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 Imports: jsonlite, progress, 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..11fc92371e 100644 --- a/R/data-apps-deps.R +++ b/R/data-apps-deps.R @@ -1,69 +1,187 @@ # 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", "sf", +"shiny", "shinyAce", "shinydashboard", "shinyjs", "shinyjster", +"shinymeta", "shinytest2", "shinythemes", "shinyvalidate", "showtext", +"sysfonts", "systemfonts", "testthat", "thematic", "tidyr", "tm", +"waldo", "websocket", "withr", "wordcloud") +apps_deps_map <- list(`000-all` = "shinytest2", `000-manual` = "shinytest2", `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"), `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..a713096011 100644 --- a/R/data-shinyverse.R +++ b/R/data-shinyverse.R @@ -1,39 +1,26 @@ -# Written by hand! +# Do not edit by hand! +# This file is automatically generated by `./inst/gha/data-apps-deps-update.R` +shinyverse_pkgs <- c("shinycoreci", "cachem", "fastmap", "later", "bslib", "bsicons", +"htmlwidgets", "crosstalk", "gt", "DT", "dygraphs", "flexdashboard", +"fontawesome", "htmltools", "httpuv", "leaflet", "pool", "promises", +"reactlog", "sass", "shiny", "shinymeta", "shinytest", "chromote", +"shinytest2", "shinythemes", "shinyvalidate", "thematic", "webdriver", +"websocket", "plotly", "shinyjster") -# 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_urls <- c("https://github.com/rstudio/shinycoreci", "https://github.com/r-lib/cachem", +"https://github.com/r-lib/fastmap", "https://github.com/r-lib/later", +"https://github.com/rstudio/bslib", "https://github.com/rstudio/bsicons", +"https://github.com/ramnathv/htmlwidgets", "https://github.com/rstudio/crosstalk", +"https://github.com/rstudio/gt", "https://github.com/rstudio/DT", +"https://github.com/rstudio/dygraphs", "https://github.com/rstudio/flexdashboard", +"https://github.com/rstudio/fontawesome", "https://github.com/rstudio/htmltools", +"https://github.com/rstudio/httpuv", "https://github.com/rstudio/leaflet", +"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/shinymeta", +"https://github.com/rstudio/shinytest", "https://github.com/rstudio/chromote", +"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", +"https://github.com/ropensci/plotly", "https://github.com/schloerke/shinyjster" ) - -shinyverse_pkgs <- vapply(strsplit(shinyverse_remotes, "/"), `[[`, character(1), 2) diff --git a/R/deploy-apps.R b/R/deploy-apps.R index 52c81e6b10..e5034a74b1 100644 --- a/R/deploy-apps.R +++ b/R/deploy-apps.R @@ -14,16 +14,15 @@ #' @param retrying_ For internal use only #' @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", + ..., + install = TRUE, + extra_packages = NULL, + cores = 1, + retry = 2, + retrying_ = FALSE) { is_missing <- list( account = missing(account), server = missing(server), @@ -33,28 +32,7 @@ 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 <- shinycoreci_libpath() if (!retrying_) { # Always make sure the app dependencies are available @@ -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..8b70d8525e 100644 --- a/R/install-path.R +++ b/R/install-path.R @@ -1,10 +1,20 @@ +on_ci <- function() { + isTRUE(as.logical(Sys.getenv("CI"))) +} + + #' 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() { + if (on_ci()) { + # Use standard libpath location + return(.libPaths()[1]) + } + # Dir location inspration from learnr: # https://github.com/rstudio/learnr/blob/1c01ac258230cbe217eee16c77cc71924faab1d3/R/storage.R#L275 dir <- file.path( @@ -22,7 +32,11 @@ 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() { + if (on_ci()) { + stop("Cannot clean libpaths on CI") + } + + unlink(dirname(shinycoreci_libpath()), recursive = TRUE) } diff --git a/R/install.R b/R/install.R index d2dc472e92..2bf79985c3 100644 --- a/R/install.R +++ b/R/install.R @@ -18,118 +18,148 @@ 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"))]) - } else { - NULL - } - - # 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) -} - - +# # 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_old <- 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 = shinycoreci_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)) { +# apps_deps[!(apps_deps %in% c("shinycoreci"))] +# } else { +# NULL +# } + +# # Load pak into current namespace +# pkgs <- c(pak_apps_deps, extra_packages) +# message("Install libpath: ", libpath) +# message("Installing pkgs:\n", paste0("* ", pkgs, collapse = "\n")) +# # 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) +# } + +## 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, ..., 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) +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_old(libpath = libpath) + # } + 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 { + apps_deps_map[[resolve_app_name(app_name)]] } - return(invisible()) - } - app_name <- resolve_app_name(app_name) - app_deps <- apps_deps_map[[app_name]] - - 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) - } + install_missing_pkgs( + app_deps, + libpath = libpath, + upgrade = upgrade, + dependencies = dependencies + ) invisible() } + + +installed_pkgs <- base::list2env(list()) + # 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( +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, + packages_to_install = packages) { + + pkgs_to_install <- packages_to_install[!(packages_to_install %in% names(installed_pkgs))] + + if (length(pkgs_to_install) > 0) { + message( + "Installing missing packages: ", + paste0(pkgs_to_install, collapse = ", ") + ) + install_pkgs_with_callr( + pkgs_to_install, + libpath = libpath, + upgrade = upgrade, + dependencies = dependencies + ) + # Update the installed status + 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) + ) { callr::r( function(packages, lib, upgrade, dependencies) { + options(repos = c( + # Use the shinycoreci universe to avoid GH rate limits! + "AAA" = "https://posit-dev-shinycoreci.r-universe.dev", + 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") @@ -158,8 +188,7 @@ install_pkgs_with_callr <- function( # This logic should mimic `./gihub/internal/install-shinyvers/action.yaml` logic -install_troublesome_pkgs <- function(libpath = .libPaths()[1]) { - +install_troublesome_pkgs_old <- function(libpath = .libPaths()[1]) { # Get R version like `"4.2"` short_r_version <- sub("\\.\\d$", "", as.character(getRversion())) diff --git a/R/sysinfo.R b/R/sysinfo.R index 8ecc133682..b81ec89eda 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 = shinycoreci_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..853ecc1f5f 100644 --- a/R/test-in-browser.R +++ b/R/test-in-browser.R @@ -11,17 +11,19 @@ #' @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 <- if (should_install) shinycoreci_libpath() else .libPaths()[1] 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 = shinycoreci_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..b945726e25 100644 --- a/R/test-in-ide.R +++ b/R/test-in-ide.R @@ -12,36 +12,37 @@ #' @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() - } + should_install <- !isTRUE(local_pkgs) + libpath <- + if (should_install) { + shinycoreci_libpath() } else { - # First time though - install_shinyverse(install = !isTRUE(local_pkgs), validate_loaded = TRUE) + .libPaths()[1] } - withr::local_libpaths(local_libpath, action = "prefix") + 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 +53,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 +70,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 +80,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 +102,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 +124,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 +154,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..ec656e26de 100644 --- a/R/test-in-local.R +++ b/R/test-in-local.R @@ -17,22 +17,27 @@ ci_status <- list( #' @param install If \code{TRUE}, installs shinyverse in the default libpath before running tests. App dependencies will always be installed if missing. #' @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 <- + if (should_install) { + shinycoreci_libpath() + } else { + .libPaths()[1] + } + 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 +54,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) + }) + # 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 +85,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_, @@ -130,7 +139,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 +148,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 +193,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 +221,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 +256,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/README.Rmd b/README.Rmd index 1420bd36e8..0ac2005665 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) +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 rOpenScie [`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/inst/Docker/centos/Dockerfile b/inst/Docker/centos/Dockerfile index a39dfebc7b..7f454ef467 100644 --- a/inst/Docker/centos/Dockerfile +++ b/inst/Docker/centos/Dockerfile @@ -137,25 +137,26 @@ 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' \ - ); \ + # 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); \ + # install.packages(pkgs); \ pak::pkg_install('rstudio/shinycoreci@${SHINYCORECI_SHA}');\ + # TODO-barret; Shouldn't this handle it automatically? shinycoreci:::install_shinyverse_local(upgrade = FALSE, install_apps_deps = FALSE);\ " diff --git a/inst/Docker/ubuntu/Dockerfile b/inst/Docker/ubuntu/Dockerfile index e2e12ace82..b0fd9409f9 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,34 @@ 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);\ + # 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); \ + options(repos = c( \ + # Use the shinycoreci universe to avoid GH rate limits! \ + 'AAA' = 'https://posit-dev-shinycoreci.r-universe.dev', \ + getOption('repos', c('CRAN' = 'https://cloud.r-project.org')) \ + )) \ + if ('${SHINYCORECI_SHA}' != 'HEAD') { \ + pak::pkg_install('rstudio/shinycoreci@${SHINYCORECI_SHA}'); \ + } else { \ + pak::pkg_install('shinycoreci'); \ + } \ + 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/001-hello/app.R b/inst/apps/001-hello/app.R index 3a5988f2cb..d76c42eca9 100644 --- a/inst/apps/001-hello/app.R +++ b/inst/apps/001-hello/app.R @@ -15,12 +15,13 @@ ui <- fluidPage( sidebarPanel( # Input: Slider for the number of bins ---- - sliderInput(inputId = "bins", - label = "Number of bins:", - min = 1, - max = 50, - value = 30) - + sliderInput( + inputId = "bins", + label = "Number of bins:", + min = 1, + max = 50, + value = 30 + ) ), # Main panel for displaying outputs ---- @@ -28,7 +29,6 @@ ui <- fluidPage( # Output: Histogram ---- plotOutput(outputId = "distPlot") - ) ), @@ -62,7 +62,6 @@ ui <- fluidPage( # Define server logic required to draw a histogram ---- server <- function(input, output, session) { - # include shinyjster_server call at top of server definition shinyjster::shinyjster_server(input, output) @@ -73,15 +72,15 @@ server <- function(input, output, session) { }) output$distPlot <- renderPlot({ - - hist(x, breaks = bins(), col = "#75AADB", border = "white", - xlab = "Waiting time to next eruption (in mins)", - main = "Histogram of waiting times") - - }) - + hist(x, + breaks = bins(), col = "#75AADB", border = "white", + xlab = "Waiting time to next eruption (in mins)", + main = "Histogram of waiting times" + ) + }) } + # Create Shiny app ---- shinyApp(ui = ui, server = server) diff --git a/inst/apps/063-superzip-example/ui.R b/inst/apps/063-superzip-example/ui.R index 650d79fbbf..ec5da093ac 100644 --- a/inst/apps/063-superzip-example/ui.R +++ b/inst/apps/063-superzip-example/ui.R @@ -10,11 +10,12 @@ vars <- c( ) -navbarPage("Superzip", id="nav", - - tabPanel("Interactive map", - div(class="outer", - +navbarPage("Superzip", + id = "nav", + tabPanel( + "Interactive map", + div( + class = "outer", tags$head( # Include our custom CSS includeCSS("styles.css"), @@ -22,54 +23,60 @@ navbarPage("Superzip", id="nav", ), # If not using custom CSS, set height of leafletOutput to a number instead of percent - leafletOutput("map", width="100%", height="100%"), + leafletOutput("map", width = "100%", height = "100%"), # Shiny versions prior to 0.11 should use class = "modal" instead. - absolutePanel(id = "controls", class = "panel panel-default", fixed = TRUE, + absolutePanel( + id = "controls", class = "panel panel-default", fixed = TRUE, draggable = TRUE, top = 60, left = "auto", right = 20, bottom = "auto", width = 330, height = "auto", - h2(HTML("ZIP explorer
(no tiles)")), - selectInput("color", "Color", vars), selectInput("size", "Size", vars, selected = "adultpop"), - conditionalPanel("input.color == 'superzip' || input.size == 'superzip'", + conditionalPanel( + "input.color == 'superzip' || input.size == 'superzip'", # Only prompt for threshold when coloring or sizing by superzip numericInput("threshold", "SuperZIP threshold (top n percentile)", 5) ), - plotOutput("histCentile", height = 200), plotOutput("scatterCollegeIncome", height = 250) ), - - tags$div(id="cite", - 'Data compiled for ', tags$em('Coming Apart: The State of White America, 1960–2010'), ' by Charles Murray (Crown Forum, 2012).' + tags$div( + id = "cite", + "Data compiled for ", tags$em("Coming Apart: The State of White America, 1960–2010"), " by Charles Murray (Crown Forum, 2012)." ) ) ), - - tabPanel("Data explorer", + tabPanel( + "Data explorer", fluidRow( - column(3, - selectInput("states", "States", c("All states"="", structure(state.abb, names=state.name), "Washington, DC"="DC"), multiple=TRUE) + column( + 3, + selectInput("states", "States", c("All states" = "", structure(state.abb, names = state.name), "Washington, DC" = "DC"), multiple = TRUE) ), - column(3, - conditionalPanel("input.states", - selectInput("cities", "Cities", c("All cities"=""), multiple=TRUE) + column( + 3, + conditionalPanel( + "input.states", + selectInput("cities", "Cities", c("All cities" = ""), multiple = TRUE) ) ), - column(3, - conditionalPanel("input.states", - selectInput("zipcodes", "Zipcodes", c("All zipcodes"=""), multiple=TRUE) + column( + 3, + conditionalPanel( + "input.states", + selectInput("zipcodes", "Zipcodes", c("All zipcodes" = ""), multiple = TRUE) ) ) ), fluidRow( - column(1, - numericInput("minScore", "Min score", min=0, max=100, value=0) + column( + 1, + numericInput("minScore", "Min score", min = 0, max = 100, value = 0) ), - column(1, - numericInput("maxScore", "Max score", min=0, max=100, value=100) + column( + 1, + numericInput("maxScore", "Max score", min = 0, max = 100, value = 100) ) ), hr(), 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..de5ccaca37 100644 --- a/inst/gha/data-apps-deps-update.R +++ b/inst/gha/data-apps-deps-update.R @@ -5,7 +5,6 @@ if (!requireNamespace("withr", quietly = TRUE)) install.packages("withr") 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 +13,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 +41,21 @@ 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() { + infos <- jsonlite::read_json("https://raw.githubusercontent.com/posit-dev-shinycoreci/posit-dev-shinycoreci.r-universe.dev/main/packages.json") + + pkgs <- vapply(infos, `[[`, character(1), "package") + pkgs_txt <- utils::capture.output(dput(pkgs)) + urls <- vapply(infos, `[[`, character(1), "url") + 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", + "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 index a61d6838de..e99b5a6350 100644 --- a/inst/gha/gha-adjust-packages-to-install.R +++ b/inst/gha/gha-adjust-packages-to-install.R @@ -1,4 +1,4 @@ -adjust_pkgs <- function(pkgs_to_install = "rstudio/shiny,rstudio/bslib", r_version = "4.2.1") { +adjust_pkgs <- function(pkgs_to_install = "shiny,shinytest2", r_version = "4.2.1") { is_windows <- .Platform$OS.type == "windows" is_linux <- Sys.info()[["sysname"]] == "Linux" is_mac <- Sys.info()[["sysname"]] == "Darwin" diff --git a/inst/gha/gha-shinyverse-packages.R b/inst/gha/gha-shinyverse-packages.R index 6189c3ba2b..21c602a379 100644 --- a/inst/gha/gha-shinyverse-packages.R +++ b/inst/gha/gha-shinyverse-packages.R @@ -1,40 +1,9 @@ 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 - # ) - + ## Don't install apps_deps here. Let the methods install them if they're missing paste0( c( - shinyverse_remotes, - ## Don't install apps_deps here. Let the methods install them if they're missing - # paste0("any::", apps_deps), + # Minimum required packages to perform testing + "shiny", "shinytest2", NULL ), collapse = "," 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..ff9a184d57 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{install}{If \code{TRUE}, installs shinyverse in the default libpath before running tests. App dependencies will always be installed if missing.} } \description{ Test apps using \code{shiny::runTests()} using local libpath diff --git a/man/write_sysinfo.Rd b/man/write_sysinfo.Rd index 0bb1fc3072..521c6c7603 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 = shinycoreci_libpath()) } \arguments{ \item{file}{Name of file, or file object to write to (defaults to stdout).}