Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure Mend Bolt for GitHub #1

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .whitesource
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"scanSettings": {
"baseBranches": []
},
"checkRunSettings": {
"vulnerableCheckRunConclusionLevel": "failure",
"displayMode": "diff",
"useMendCheckNames": true
},
"issueSettings": {
"minSeverityLevel": "LOW",
"issueType": "DEPENDENCY"
}
}
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added

- You can now deploy Gradio applications. This requires Posit Connect release 2024.11.0
or later. Use `rsconnect deploy gradio` to deploy, or `rsconnect write-manifest gradio`
to create a manifest file.

### Changed

- The `rsconnect content build run --poll-wait` argument specifies an integral
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ This package provides a CLI (command-line interface) for interacting
with and deploying to Posit Connect. This is also used by the
[`rsconnect-jupyter`](https://github.com/rstudio/rsconnect-jupyter) package to deploy
Jupyter notebooks via the Jupyter web console. Many types of content supported by Posit
Connect may be deployed by this package, including WSGI-style APIs, Dash, Streamlit, and
Bokeh applications.
Connect may be deployed by this package, including WSGI-style APIs, Dash, Streamlit,
Gradio, and Bokeh applications.

Content types not directly supported by the CLI may also be deployed if they include a
prepared `manifest.json` file. See ["Deploying R or Other
Expand Down Expand Up @@ -288,9 +288,10 @@ You can deploy a variety of APIs and applications using sub-commands of the
* `dash`: Python Dash apps
* `streamlit`: Streamlit apps
* `bokeh`: Bokeh server apps
* `gradio`: Gradio apps

All options below apply equally to the `api`, `fastapi`, `dash`, `streamlit`,
and `bokeh` sub-commands.
`gradio`, and `bokeh` sub-commands.

#### Including Extra Files

Expand Down Expand Up @@ -470,8 +471,8 @@ rsconnect deploy notebook --title "My Notebook" my-notebook.ipynb
```

When using `rsconnect deploy api`, `rsconnect deploy fastapi`, `rsconnect deploy dash`,
`rsconnect deploy streamlit`, or `rsconnect deploy bokeh`, the title is derived from the directory
containing the API or application.
`rsconnect deploy streamlit`, `rsconnect deploy bokeh`, or `rsconnect deploy gradio`,
the title is derived from the directory containing the API or application.

When using `rsconnect deploy manifest`, the title is derived from the primary
filename referenced in the manifest.
Expand Down Expand Up @@ -726,7 +727,7 @@ rsconnect content search --help
# -c, --cacert FILENAME The path to trusted TLS CA certificates.
# --published Search only published content.
# --unpublished Search only unpublished content.
# --content-type [unknown|shiny|rmd-static|rmd-shiny|static|api|tensorflow-saved-model|jupyter-static|python-api|python-dash|python-streamlit|python-bokeh|python-fastapi|quarto-shiny|quarto-static]
# --content-type [unknown|shiny|rmd-static|rmd-shiny|static|api|tensorflow-saved-model|jupyter-static|python-api|python-dash|python-streamlit|python-bokeh|python-fastapi|python-gradio|quarto-shiny|quarto-static]
# Filter content results by content type.
# --r-version VERSIONSEARCHFILTER
# Filter content results by R version.
Expand Down
3 changes: 2 additions & 1 deletion rsconnect/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1738,7 +1738,7 @@ def deploy_app(
generate_deploy_python(app_mode=AppModes.STREAMLIT_APP, alias="streamlit", min_version="1.8.4")
generate_deploy_python(app_mode=AppModes.BOKEH_APP, alias="bokeh", min_version="1.8.4")
generate_deploy_python(app_mode=AppModes.PYTHON_SHINY, alias="shiny", min_version="2022.07.0")

generate_deploy_python(app_mode=AppModes.PYTHON_GRADIO, alias="gradio", min_version="2024.11.0")

@deploy.command(
name="other-content",
Expand Down Expand Up @@ -2272,6 +2272,7 @@ def manifest_writer(
generate_write_manifest_python(AppModes.PYTHON_FASTAPI, alias="fastapi")
generate_write_manifest_python(AppModes.PYTHON_SHINY, alias="shiny")
generate_write_manifest_python(AppModes.STREAMLIT_APP, alias="streamlit")
generate_write_manifest_python(AppModes.PYTHON_GRADIO, alias="gradio")


# noinspection SpellCheckingInspection
Expand Down
3 changes: 3 additions & 0 deletions rsconnect/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class AppModes:
STATIC_QUARTO = AppMode(14, "quarto-static", "Quarto Document", ".qmd")
PYTHON_SHINY = AppMode(15, "python-shiny", "Python Shiny Application")
JUPYTER_VOILA = AppMode(16, "jupyter-voila", "Jupyter Voila Application")
PYTHON_GRADIO = AppMode(17, "python-gradio", "Gradio Application")

_modes = [
UNKNOWN,
Expand All @@ -116,6 +117,7 @@ class AppModes:
STATIC_QUARTO,
PYTHON_SHINY,
JUPYTER_VOILA,
PYTHON_GRADIO
]

Modes = Literal[
Expand All @@ -136,6 +138,7 @@ class AppModes:
"quarto-static",
"python-shiny",
"jupyter-voila",
"python-gradio"
]

_cloud_to_connect_modes = {
Expand Down
86 changes: 75 additions & 11 deletions tests/test_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -1059,9 +1059,7 @@ def test_make_tensorflow_manifest_empty(self):
manifest,
{
"version": 1,
"metadata": {
"appmode": "tensorflow-saved-model"
},
"metadata": {"appmode": "tensorflow-saved-model"},
"files": {},
},
)
Expand All @@ -1082,9 +1080,7 @@ def test_make_tensorflow_manifest(self):
manifest,
{
"version": 1,
"metadata": {
"appmode": "tensorflow-saved-model"
},
"metadata": {"appmode": "tensorflow-saved-model"},
"files": {
"1/saved_model.pb": {"checksum": mock.ANY},
},
Expand All @@ -1105,17 +1101,15 @@ def test_make_tensorflow_bundle(self):
[
"1/saved_model.pb",
"manifest.json",
],
)
],
)
manifest_data = tar.extractfile("manifest.json").read().decode("utf-8")
manifest = json.loads(manifest_data)
self.assertEqual(
manifest,
{
"version": 1,
"metadata": {
"appmode": "tensorflow-saved-model"
},
"metadata": {"appmode": "tensorflow-saved-model"},
"files": {
"1/saved_model.pb": {"checksum": mock.ANY},
},
Expand Down Expand Up @@ -2909,6 +2903,76 @@ def test_make_manifest_bundle():
assert manifest["files"].keys() == bundle_json["files"].keys()


gradio_dir = os.path.join(cur_dir, "./testdata/gradio")
gradio_file = os.path.join(cur_dir, "./testdata/gradio/app.py")


def test_make_api_manifest_gradio():
gradio_dir_ans = {
"version": 1,
"locale": "en_US.UTF-8",
"metadata": {"appmode": "python-gradio"},
"python": {
"version": "3.8.12",
"package_manager": {"name": "pip", "version": "23.0.1", "package_file": "requirements.txt"},
},
"files": {
"requirements.txt": {"checksum": "381ccadfb8d4848add470e33033b198f"},
"app.py": {"checksum": "22feec76e9c02ac6b5a34a083e2983b6"},
},
}
environment = create_python_environment(
gradio_dir,
)
manifest, _ = make_api_manifest(
gradio_dir,
None,
AppModes.PYTHON_GRADIO,
environment,
None,
None,
)

assert gradio_dir_ans["metadata"] == manifest["metadata"]
assert gradio_dir_ans["files"].keys() == manifest["files"].keys()


def test_make_api_bundle_gradio():
gradio_dir_ans = {
"version": 1,
"locale": "en_US.UTF-8",
"metadata": {"appmode": "python-gradio"},
"python": {
"version": "3.8.12",
"package_manager": {"name": "pip", "version": "23.0.1", "package_file": "requirements.txt"},
},
"files": {
"requirements.txt": {"checksum": "381ccadfb8d4848add470e33033b198f"},
"app.py": {"checksum": "22feec76e9c02ac6b5a34a083e2983b6"},
},
}
environment = create_python_environment(
gradio_dir,
)
with make_api_bundle(
gradio_dir,
None,
AppModes.PYTHON_GRADIO,
environment,
None,
None,
) as bundle, tarfile.open(mode="r:gz", fileobj=bundle) as tar:
names = sorted(tar.getnames())
assert names == [
"app.py",
"manifest.json",
"requirements.txt",
]
bundle_json = json.loads(tar.extractfile("manifest.json").read().decode("utf-8"))
assert gradio_dir_ans["metadata"] == bundle_json["metadata"]
assert gradio_dir_ans["files"].keys() == bundle_json["files"].keys()


empty_manifest_file = os.path.join(cur_dir, "./testdata/Manifest_data/empty_manifest.json")
missing_file_manifest = os.path.join(cur_dir, "./testdata/Manifest_data/missing_file_manifest.json")

Expand Down
12 changes: 12 additions & 0 deletions tests/testdata/gradio/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import gradio as gr

def greet(name, intensity):
return "Hello, " + name + "!" * int(intensity)

demo = gr.Interface(
fn=greet,
inputs=["text", "slider"],
outputs=["text"],
)

demo.launch(auth = ("username", "password"))
1 change: 1 addition & 0 deletions tests/testdata/gradio/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
gradio