diff --git a/CHANGELOG.md b/CHANGELOG.md index a9a54a99..7b4eea10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - The `CONNECT_REQUEST_TIMEOUT` environment variable, which configures the request timeout for all blocking HTTP and HTTPS operations. This value translates into seconds (e.g., `CONNECT_REQUEST_TIMEOUT=60` is equivalent to 60 seconds.) By default, this value is 300. +### Fixed + +- Extra files were not being included in deploy Voila. + +- Error message to indicate the Python also has to be configured in Connect. + ## [1.15.0] - 2023-03-15 ### Added diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 0ff27c38..74965fb2 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -1686,7 +1686,7 @@ def create_voila_manifest( raise RSConnectException(MULTI_NOTEBOOK_EXC_MSG) _warn_on_ignored_entrypoint(entrypoint) deploy_dir = entrypoint = abspath(path) - extra_files = validate_extra_files(deploy_dir, extra_files) + extra_files = validate_extra_files(deploy_dir, extra_files, use_abspath=True) excludes = list(excludes) if excludes else [] excludes.extend([environment.filename, "manifest.json"]) excludes.extend(list_environment_dirs(deploy_dir)) diff --git a/rsconnect/main.py b/rsconnect/main.py index 97e412a3..d575ee14 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -927,7 +927,6 @@ def deploy_voila( kwargs = locals() set_verbosity(verbose) app_mode = AppModes.JUPYTER_VOILA - kwargs["extra_files"] = extra_files = validate_extra_files(dirname(path), extra_files) environment = create_python_environment( path if isdir(path) else dirname(path), force_generate, diff --git a/tests/test_bundle.py b/tests/test_bundle.py index 3c65f492..c762708a 100644 --- a/tests/test_bundle.py +++ b/tests/test_bundle.py @@ -814,6 +814,7 @@ def test_is_not_executable(self): bqplot_ipynb = os.path.join(bqplot_dir, "bqplot.ipynb") dashboard_dir = os.path.join(cur_dir, "./testdata/voila/dashboard/") dashboard_ipynb = os.path.join(dashboard_dir, "dashboard.ipynb") +dashboard_extra_ipynb = os.path.join(dashboard_dir, "bqplot.ipynb") multivoila_dir = os.path.join(cur_dir, "./testdata/voila/multi-voila/") nonexistent_dir = os.path.join(cur_dir, "./testdata/nonexistent/") nonexistent_file = os.path.join(cur_dir, "nonexistent.txt") @@ -985,6 +986,46 @@ def test_create_voila_manifest_2(path, entrypoint): assert ans == json.loads(manifest.flattened_copy.json) +def test_create_voila_manifest_extra(): + environment = Environment( + conda=None, + contents="numpy\nipywidgets\nbqplot\n", + error=None, + filename="requirements.txt", + locale="en_US.UTF-8", + package_manager="pip", + pip="23.0.1", + python="3.8.12", + source="file", + ) + ans = { + "version": 1, + "locale": "en_US.UTF-8", + "metadata": {"appmode": "jupyter-voila", "entrypoint": "dashboard.ipynb"}, + "python": { + "version": "3.8.12", + "package_manager": {"name": "pip", "version": "23.0.1", "package_file": "requirements.txt"}, + }, + "files": { + "requirements.txt": {"checksum": "d51994456975ff487749acc247ae6d63"}, + "bqplot.ipynb": {"checksum": "79f8622228eded646a3038848de5ffd9"}, + "dashboard.ipynb": {"checksum": "6b42a0730d61e5344a3e734f5bbeec25"}, + }, + } + manifest = create_voila_manifest( + dashboard_ipynb, + None, + environment, + app_mode=AppModes.JUPYTER_VOILA, + extra_files=[dashboard_extra_ipynb], + excludes=None, + force_generate=True, + image=None, + multi_notebook=False, + ) + assert ans == json.loads(manifest.flattened_copy.json) + + @pytest.mark.parametrize( ( "path", @@ -1353,6 +1394,54 @@ def test_make_voila_bundle_2( assert ans == json.loads(tar.extractfile("manifest.json").read().decode("utf-8")) +def test_make_voila_bundle_extra(): + environment = Environment( + conda=None, + contents="numpy\nipywidgets\nbqplot\n", + error=None, + filename="requirements.txt", + locale="en_US.UTF-8", + package_manager="pip", + pip="23.0.1", + python="3.8.12", + source="file", + ) + ans = { + "version": 1, + "locale": "en_US.UTF-8", + "metadata": {"appmode": "jupyter-voila", "entrypoint": "dashboard.ipynb"}, + "python": { + "version": "3.8.12", + "package_manager": {"name": "pip", "version": "23.0.1", "package_file": "requirements.txt"}, + }, + "files": { + "requirements.txt": {"checksum": "d51994456975ff487749acc247ae6d63"}, + "bqplot.ipynb": {"checksum": "79f8622228eded646a3038848de5ffd9"}, + "dashboard.ipynb": {"checksum": "6b42a0730d61e5344a3e734f5bbeec25"}, + }, + } + with make_voila_bundle( + dashboard_ipynb, + None, + extra_files=[dashboard_extra_ipynb], + excludes=None, + force_generate=True, + environment=environment, + image=None, + multi_notebook=False, + ) as bundle, tarfile.open(mode="r:gz", fileobj=bundle) as tar: + names = sorted(tar.getnames()) + assert names == [ + "bqplot.ipynb", + "dashboard.ipynb", + "manifest.json", + "requirements.txt", + ] + reqs = tar.extractfile("requirements.txt").read() + assert reqs == b"numpy\nipywidgets\nbqplot\n" + assert ans == json.loads(tar.extractfile("manifest.json").read().decode("utf-8")) + + single_file_index_dir = os.path.join(cur_dir, "./testdata/html_tests/single_file_index") single_file_index_file = os.path.join(cur_dir, "./testdata/html_tests/single_file_index/index.html") single_file_nonindex_dir = os.path.join(cur_dir, "./testdata/html_tests/single_file_nonindex")