Skip to content
Merged
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
1 change: 1 addition & 0 deletions rsconnect/subprocesses/inspect_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ def uv_export(dirname: str, lock_filename: str, uv_path: str = "uv"):
lock_path = lock_filename
if not os.path.isabs(lock_filename):
lock_path = os.path.join(dirname, lock_filename)
lock_path = os.path.abspath(lock_path)

if not os.path.exists(lock_path):
raise EnvironmentException("uv.lock not found: %s" % lock_filename)
Expand Down
12 changes: 10 additions & 2 deletions tests/test_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,8 @@ def test_requirements_file_rejects_editable_outside_bundle(tmp_path: Path):


@pytest.mark.parametrize("fallback", [False, True])
def test_uv_lock_export(tmp_path, monkeypatch, fallback):
@pytest.mark.parametrize("lock_location", ["absolute_dir", "bare", "relative", "absolute_lock"])
def test_uv_lock_export(tmp_path, monkeypatch, fallback, lock_location):
project_dir = tmp_path / "project"
project_dir.mkdir()
(project_dir / "pyproject.toml").write_text(
Expand Down Expand Up @@ -398,7 +399,14 @@ def test_uv_lock_export(tmp_path, monkeypatch, fallback):
monkeypatch.setattr(rsconnect.environment.uv, "find_uv_bin", missing_bundled_uv)
else:
monkeypatch.setenv("PATH", "")
env = Environment.create_python_environment(str(project_dir), requirements_file="uv.lock", python=str(python))
monkeypatch.chdir(project_dir)
dirname, lock_file = {
"absolute_dir": (str(project_dir), "uv.lock"),
"bare": ("", "uv.lock"),
"relative": (".", "./uv.lock"),
"absolute_lock": ("", str(project_dir / "uv.lock")),
}[lock_location]
env = Environment.create_python_environment(dirname, requirements_file=lock_file, python=str(python))

assert env.filename == "requirements.txt.lock"
assert "aiofiles==24.1.0" in env.contents
Expand Down
32 changes: 32 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import re
import shutil
import subprocess
import sys
import venv
from os.path import join
Expand All @@ -18,6 +19,7 @@
from rsconnect import VERSION
from rsconnect.api import RSConnectClient, RSConnectServer
from rsconnect.bundle import file_checksum
from rsconnect.exception import RSConnectException
from rsconnect.json_web_token import SECRET_KEY_ENV
from rsconnect.log import console_logger, logger as rs_logger
from rsconnect.main import cli, env_management_callback, make_notebook_html_bundle
Expand Down Expand Up @@ -2392,6 +2394,36 @@ def test_missing_requirements_reports_content_directory(
assert f"The requirements file '{expected_file}' does not exist in '{expected_directory}'." in result.output


@pytest.mark.parametrize(
("command", "filename"),
[
(["write-manifest", "notebook"], "nb.ipynb"),
(["write-manifest", "quarto"], "doc.qmd"),
(["deploy", "notebook"], "nb.ipynb"),
(["deploy", "quarto"], "doc.qmd"),
],
)
@pytest.mark.parametrize("prefix", ["", "./"])
def test_document_commands_export_uv_lock_for_bare_and_relative_paths(tmp_path, monkeypatch, command, filename, prefix):
monkeypatch.chdir(tmp_path)
(tmp_path / "pyproject.toml").write_text("[project]\nname='demo'\nversion='0.0.0'\n")
(tmp_path / filename).write_text('{"cells":[],"metadata":{},"nbformat":4,"nbformat_minor":5}')
subprocess.run([sys.executable, "-m", "uv", "lock", "--offline"], check=True)

with mock.patch("rsconnect.main.which_quarto", return_value="quarto"), mock.patch(
"rsconnect.main.quarto_inspect", return_value={"engines": ["jupyter"]}
), mock.patch("rsconnect.main.RSConnectExecutor", side_effect=RSConnectException("deployment reached")) as executor:
result = CliRunner().invoke(cli, [*command, "-r", "uv.lock", "--exclude-renv", prefix + filename])

if command[0] == "deploy":
assert "deployment reached" in result.output
executor.assert_called_once()
else:
assert result.exit_code == 0, result.output
assert (tmp_path / "manifest.json").exists()
assert (tmp_path / "requirements.txt.lock").exists()


class TestWriteManifestNodeJS:
def test_help(self):
runner = CliRunner()
Expand Down
Loading