RStudio Connect、Packrat 和本地存储库中的自定义包

T.H*_*lme 5 r local package shiny-server packrat

我们最近在我的办公室安装了 RStudio Connect。对于我们的工作,我们制作了自定义包,我们通过打开项目并构建+重新加载在我们之间进行了更新。

据我所知,让我们的自定义包在具有 RSConnect 的应用程序中工作的唯一方法是获取本地存储库并将其设置options(repos)为包含此存储库。

目前我有以下内容:

library(drat)

RepoAddress <- "C:/<RepoPath>" # High level path

drat::insertPackage(<sourcePackagePath>, repodir = RepoAddress)

# Add this new repo to Rs knowledge of repos.
options(repos = c(options("repos")$repos,LocalCurrent = paste0("file:",RepoAddress)))

# Install <PackageName> from the local repo :)
install.packages("<PackageName>")
Run Code Online (Sandbox Code Playgroud)

目前这工作得很好,我可以从本地存储库安装我的自定义包。这向我表明本地存储库设置正确。

另外,我已更改DESCRIPTION文件以添加一行额外的内容repository:LocalCurrent

但是,当我尝试部署引用的 Shiny 应用程序或 Rmd 时,我在部署时收到以下错误:

Error in findLocalRepoForPkg(pkg, repos, fatal = fatal) : 
  No package '<PackageName> 'found in local repositories specified
Run Code Online (Sandbox Code Playgroud)

我知道这是在部署过程中无法找到我的本地存储库的问题packrat(我相信在它使用的阶段packrat::snapshot())。这很令人困惑,因为我packrat本以为会使用option("repos")类似于 的存储库install.packages。如果我跟踪这些函数,我可以看到特定的失败点是packrat:::findLocalRepoForPkg("<PackageName", repos = packrat::get_opts("local.repos")),即使在我定义之后它也会失败packrat::set_opts("local.repos" = c(CurrentRepo2 = paste0("file:",RepoAddress)))

如果我钻取packrat:::findLocalRepoForPkg,它会失败,因为它找不到名为“C://”的文件/文件夹。我本以为这肯定会失败,因为存储库遵循 C://bin/windows/contrib/3.3/ 结构。回购协议在任何时候都不会具有它正在寻找的结构吗?

我认为最后一部分表明我严重误解了某些事情。任何有关配置我的存储库以便packrat能够理解的指导都会很棒。

Sor*_*ing 5

人们应该始终检查 RStudio connect 目前支持哪些选项: https://docs.rstudio.com/connect/admin/r/package-management/#private-packages

就我个人而言,我不喜欢包含本地/私有包的所有选项,因为它违背了部署闪亮应用程序的简单目标的目的。在许多情况下,我不能只在组织中建立本地存储库,因为我没有权限。我还必须向 IT 支持人员发送电子邮件,让他们手动安装新软件包,这也很不方便。总的来说,我认为 RS connect 是很棒的产品,因为它很简单,但当涉及到本地软件包时,事实并非如此。

我找到了一个不错的替代方案/Hack to Rstudio 官方推荐。我想这也可以与shinyapps.io一起使用,但我还没有尝试过。解决方案如下:

  1. 添加到 global.R if(!require(local_package) ) devtools::load_all("./local_package")

  2. 编写一个复制所有源文件的脚本,这样您就可以获得一个闪亮的应用程序,其中包含本地包的源目录,您可以调用该目录./inst/shinyconnect/或任何本地包将被复制到./inst/shinyconnect/local_package 清单。

  3. 将脚本添加./shinyconnect/packrat_sees_these_dependencies.R到闪亮的文件夹中,这将被 packrat-m​​anifest 拾取

  4. 破解 rsconnet/packrat 以在构建时忽略特定命名的包

(1)

#start of global.R...
#load more packages for shiny
library(devtools) #need for load_all
library(shiny)
library(htmltools) #or what ever you need


#load already built local_package or for shiny connection pseudo-build on-the-fly and load
if(!require(local_package)) {
  #if local_package here, just build on 2 sec with devtools::load_all()
  if(file.exists("./DESCRIPTION")) load_all(".") #for local test on PC/Mac, where the shinyapp is inside the local_package
  if(file.exists("./local_package/DESCRIPTION")) load_all("./local_package/") #for shiny conenct where local_package is inside shinyapp
}
library(local_package) #now local_package must load
Run Code Online (Sandbox Code Playgroud)

(3) 让脚本加载本地包的所有依赖项。Packrat 会看到这一点。该脚本永远不会被实际执行。将其放置在./shinyconnect/packrat_sees_these_dependencies.R

#these codelines will be recognized by packrat and package will be added to manifest
library(randomForest)
library(MASS)
library(whateverpackageyouneed)
Run Code Online (Sandbox Code Playgroud)

(4)在部署期间,清单生成器 (packrat) 将忽略任何命名的 local_package 的存在。这是 packrat 中的一个选项,但 rsconnect 不公开此选项。一种破解方法是将 rsconnect 加载到内存并修改 sub-sub-sub 函数performPackratSnapshot()以允许此操作。在下面的脚本中,我这样做并部署了一个闪亮的应用程序。

library(rsconnect)


orig_fun = getFromNamespace("performPackratSnapshot", pos="package:rsconnect")

#packages you want include manually, and packrat to ignore
ignored_packages = c("local_package")

#highjack rsconnect
local({
  assignInNamespace("performPackratSnapshot",value =  function (bundleDir, verbose = FALSE) {
      owd <- getwd()
      on.exit(setwd(owd), add = TRUE)
      setwd(bundleDir)
      srp <- packrat::opts$snapshot.recommended.packages()
      packrat::opts$snapshot.recommended.packages(TRUE, persist = FALSE)
      packrat::opts$ignored.packages(get("ignored_packages",envir = .GlobalEnv)) #ignoreing packages mentioned here
      print("ignoring following packages")
      print(get("ignored_packages",envir = .GlobalEnv))
      on.exit(packrat::opts$snapshot.recommended.packages(srp,persist = FALSE), add = TRUE)
      packages <- c("BiocManager", "BiocInstaller")
      for (package in packages) {
        if (length(find.package(package, quiet = TRUE))) {
          requireNamespace(package, quietly = TRUE)
          break
        }
      }
      suppressMessages(packrat::.snapshotImpl(project = bundleDir,
                                              snapshot.sources = FALSE, fallback.ok = TRUE, verbose = FALSE,
                                              implicit.packrat.dependency = FALSE))
      TRUE
    },
    pos = "package:rsconnect"
  )},
envir = as.environment("package:rsconnect")
)


new_fun = getFromNamespace("performPackratSnapshot", pos="package:rsconnect")
rsconnect::deployApp(appDir="./inst/shinyconnect/",appName ="shinyapp_name",logLevel = "verbose",forceUpdate = TRUE)
Run Code Online (Sandbox Code Playgroud)


T.H*_*lme 0

问题之一是命名法。

我已经建立了一个 CRAN 意义上的仓库。它工作正常并且没问题。当packrat引用 a时local repo,它指的是本地 git 风格的存储库。

这解决了为什么 findlocalrepoforpkg 看起来不起作用 - 它被设计为与不同类型的存储库一起使用。