posit-dev / rsconnect-python

Command line interface for publishing to Posit Connect
https://docs.posit.co/rsconnect-python/
GNU General Public License v2.0
28 stars 23 forks source link

quarto: write-manifest and deploy should recommend rsconnect when using knitr engine #565

Open JT-39 opened 7 months ago

JT-39 commented 7 months ago

Hi,

I am trying to use rsconnect from the command line to build a manifest.json file for my quarto (book) project.

When I run the following code in the root of my quarto project directory, in powershell:

rsconnect write-manifest quarto .

It creates a manifest.json file but is missing some crucial information within this file. There is no R package information and is missing:

  "locale": "en_GB",
  "platform": "4.3.1",
  "metadata": {                                    # (this is included except the discrepancies below)
        ............
        "primary_html": null,                  # missing
        "content_category": null,           # is 'site'
        "has_parameters": false             # missing

Any help?

Thanks, Jake

aronatkins commented 7 months ago

The rsconnect-python package does not understand R content or dependencies. It probably should complain when it is provided content using the knitr engine.

Have you seen the rsconnect R package? It is able to deploy Quarto content that uses R and (if needed) create manifests. https://rstudio.github.io/rsconnect/

From the R console, you should be able to:

rsconnect::writeManifest()

Assuming you are deploying to Posit Connect, the different deployment options are discussed in the Connect User Guide: https://docs.posit.co/connect/user/quarto/

Please let us know if this guidance doesn't work for your project.

aronatkins commented 7 months ago

@JT-39 - Could you share more about your project? I have just tried using write-manifest against a Quarto project that uses the knitr engine and receive an (expected) error:

rsconnect write-manifest quarto .
#=> Checking arguments...                            [OK]
#=> Inspecting Quarto project...                     [ERROR]
#=> Error: The following Quarto engine(s) are not supported: knitr

A minimal reproducible example would be really helpful.

JT-39 commented 7 months ago

Hi @aronatkins,

Sure. So this is a quarto book project. The manifest.json file doesn't works from using rsconnect write-manifest quarto .

rsconnect write-manifest quarto .
Checking arguments...                            [OK]
Inspecting Quarto project...                     [OK]
Creating manifest.json...                        [OK]

Produces a manifest.json file like this:

{
  "version": 1,
  "metadata": {
    "appmode": "quarto-static",
    "primary_rmd": "index.qmd",
    "content_category": "site"
  },
  "quarto": {
    "version": "1.4.552",
    "engines": [
      "markdown"
    ]
  },
  "files": {
    ".RData": {

The manifest.json file works from using rsconnect::writeManifest() and looks like this:

{
  "version": 1,
  "locale": "en_GB",
  "platform": "4.3.1",
  "metadata": {
    "appmode": "quarto-static",
    "primary_rmd": "index.qmd",
    "primary_html": null,
    "content_category": null,
    "has_parameters": false
  },
  "quarto": {
    "version": "1.4.552",
    "engines": ["markdown"]
  },
  "packages": null,
  "files": {
    ".RData": {

Let me know if you need any more info.

aronatkins commented 7 months ago

@JT-39 - Thanks for the additional context. What about your book project tells you that there are R package dependencies? Are you using executable code blocks?

The rsconnect R package should be correctly detecting any executable R code blocks and work from there. Alternatively, if you have an renv.lock, R packages and versions are extracted from there.

A couple of things to try:

First, update rsconnect and renv R packages to the latest CRAN releases. If you happen to be using an old version of either package, your project dependencies may not be detected.

install.packages(c("rsconnect","renv"))

Second, could you share what "engines" Quarto believes it is using? We care about the engines line from the inspect output.

quarto inspect
#=> {
#=>   "quarto": {
#=>     "version": "1.4.551"
#=>   },
#=>   "engines": [
#=>     "knitr"
#=>   ],
#=> ...
#=> }
JT-39 commented 7 months ago

Sorry @aronatkins, the R package dependencies were appearing in the manifest.json after running rsconnect::writeManifest(), however this was using an older version of {rsconnect} (pre 1.2.1 - but can't remember exact version). Since upgrading to newest version 1.2.1 these dependencies are no longer listed.

I think the "engines" are listed above, but it looks like its using "markdown".

I am happy with using rsconnect::writeManifest() to create the manifest.json file in R, but just wanted to see if it could be done on the command line as I am using vscode to do my Quarto project and its a small pain to start up and enter R each time from the command line.

Thanks for continued support.

aronatkins commented 7 months ago

@JT-39 Ah. Things are more clear now. Given that you don't have R dependencies, it is correct for the manifest to lack a listing of R packages. There was an issue using an earlier version of rsconnect where it incorrectly included R packages for all Quarto content. It's also fine to use either R or Python tools to produce that manifest, since you're not using either language.

From everything you've told me, the manifest produced from the rsconnect write-manifest command should accurately describe your content.

To restate: Because your Quarto project is only Markdown content (no R or Python executable code), you can use either:

# from R
rsconnect::writeManifest()
# from the terminal using rsconnect-python
rsconnect write-manifest quarto .
aronatkins commented 7 months ago

@JT-39 I'm going to close this for now, but please let me know if we've misunderstood what's happening with your project.

JT-39 commented 7 months ago

Hi @aronatkins,

Unfortunately I am still having trouble with rsconnect write-manifest quarto . from the command line.

If I use this to create my manifest.json, then try to deploy I get this build error message (In the Get application type section):

Starting: Get application type
==============================================================================
Task         : Python script
Description  : Run a Python file or inline script
Version      : 0.236.1
Author       : Microsoft Corporation
Help         : https://docs.microsoft.com/azure/devops/pipelines/tasks/utility/python-script
==============================================================================
"C:\Program Files\Python311\python.exe" E:\Agents\rsconnect-01\_work\16\s\posit-deploy\scripts\application_type.py
Traceback (most recent call last):
  File "E:\Agents\rsconnect-01\_work\16\s\posit-deploy\scripts\application_type.py", line 52, in <module>
    r_version = data["platform"]
                ~~~~^^^^^^^^^^^^
KeyError: 'platform'

##[error]The process 'C:\Program Files\Python311\python.exe' failed with exit code 1
Finishing: Get application type

I am using rsconnect-python version 1.22.0.

I can try and create a reprex tomorrow.

aronatkins commented 7 months ago

@JT-39 - Could you also tell me more about the application_type.py script? That filename doesn't immediately look familiar. I'll look around more in the morning.

If that's custom code, I do not believe that it should require that platform be present in the manifest. That field communicates the R version and isn't included by rsconnect-python. The rsconnect R package always adds version to the manifest, but it won't be present in manifest generated by all tools.

JT-39 commented 7 months ago

Hi @aronatkins , apologies for the slow response, been a busy day before the bank holiday weekend.

I haven't had a chance to look any further yet, but I will do and it will likely be next week now if thats okay?

You are right though, I think it will actually be to do with something on my side with application_type.py. I didn't write the build pipeline, but will investigate. Thanks and have a nice weekend! :)

aronatkins commented 7 months ago

@JT-39 - sounds good. I'll keep this open until next week and we can reevaluate once you learn more.

JT-39 commented 7 months ago

@aronatkins, hope you had a nice weekend.

So I looked into the application_type.py file.

The platform is being used to identify the R version: r_version = data["platform"]

Which is then used to determine the deployment type (i.e., if not an R based deployment use the content type and directory path rather than the manifest.json and no directory path): deploy_type = content_type if r_version is None else "manifest"

Does this help/ anyway we can not use data["platform"] (I realise this is limited info, so if needed I can check with the owner and see if I can share more).

Thanks Jake

aronatkins commented 7 months ago

@JT-39 Welcome back. I envy your bank holidays.

It sounds like your deployment tools are making some assumptions about the structure of the manifest and which fields are present for different types of content.

Make sure that folks understand that the R version is optional and may not appear for Python content, so maybe they want something like:

r_version = data.get("platform")

R content will also enumerate its set of package requirements:

r_packages = data.get("packages")

With those code snippets, you'll get None when the JSON data does not contain that field.

If a manifest.json is created for static content (an HTML upload) using rsconnect::writeManifest(), that manifest will indicate an R version because it was created by the R package. The content, however, does not require R. That may or may not be an important distinction in your environment.

In case it is helpful, Connect currently believes content requires R when the following holds:

def uses_r(data):
    app_mode = data.get("metadata",{}).get("appmode")
    if app_mode in ["shiny", "rmd-shiny", "rmd-static", "api"]:
        return True
    if app_mode in ["quarto-shiny", "quarto-static"]:
        quarto_engines = data.get("quarto", {}).get("engines", [])
        return "knitr" in quarto_engines
    return False

When code similar to that function returns true, the R version is used to identify the target R installation. Note that these decisions have changed over time as new content types are added and may not be what is needed in your environment.

Let us know if you've got additional questions.

JT-39 commented 7 months ago

@aronatkins thanks for that detailed reply.

I think it is beginning to make sense... I have relayed this info to the developer of the pipeline and can report back what he says.

You can likely close this now if you like (if I am still able to comment any updates?).

Thanks for all your help :)