rstudio / rsconnect

Publish Shiny Applications, RMarkdown Documents, Jupyter Notebooks, Plumber APIs, and more
http://rstudio.github.io/rsconnect/
134 stars 83 forks source link

`writeManifest()` gives confusing error if no `app.R` #1098

Open hadley opened 3 months ago

hadley commented 3 months ago

i.e. create a new project with file foo.R containing library(shiny), then run writeManfiest():

> rsconnect::writeManifest()
Error in `quartoInspect()`:
! Failed to run `quarto inspect` against your content:
ERROR: /Users/hadleywickham/Desktop/madlibs2/madlibs.R is not a valid
Quarto input document

Presumably because it's using the wrong primary document, but why is it passing an R file to quarto? Should also log about the primary file.

aronatkins commented 3 months ago

Background:

It is assumed that if you have an R script, nothing else indicates a different content type, and it is not a well-known filename (app.R, plumber.R), then it must be a standalone R script that you want to render with Quarto.

See inferAppMode(): https://github.com/rstudio/rsconnect/blob/9999fdb11dde6d5efe596677f9a6be6bd779e31d/R/appMetadata.R#L192-L199

In general, we want Shiny applications in app.R files. Renaming of foo.R to app.R occurs very narrowly, mostly to support RStudio. https://github.com/rstudio/rsconnect/blob/9999fdb11dde6d5efe596677f9a6be6bd779e31d/R/bundle.R#L25-L36

That renaming workflow doesn't work when you're trying to produce a manifest.json, since the produced manifest needs to reference the original filename.

Connect uses shiny::runApp() to ask Shiny to run the application contained within the current directory. It does not supply a specific endpoint R file, but relies on the default Shiny behavior. When you try to use shiny::runApp() in a directory with foo.R, it complains:

R -s -e 'shiny::runApp()'
#> Loading required package: shiny
#> Error in `shinyAppDir()`:
#> ! App dir must contain either app.R or server.R.
#> Backtrace:
#>     ▆
#>  1. └─shiny::runApp()
#>  2.   ├─shiny::as.shiny.appobj(appDir)
#>  3.   └─shiny:::as.shiny.appobj.character(appDir)
#>  4.     └─shiny::shinyAppDir(x)
#>  5.       └─rlang::abort(...)
aronatkins commented 3 months ago

We don't log the inferred app-mode nor why we made that choice.

thomasp85 commented 1 month ago

Slightly related - the logic captured in

https://github.com/rstudio/rsconnect/blob/2fe8fdef45f843cfb1316fecb443ac30a0ed8a42/R/appMetadata.R#L36-L38

caught me quite off-guard. I was trying to be explicit about what the primary R script was but doing so makes writeManifest insist that it is a shiny app