|
13 | 13 | from __future__ import annotations |
14 | 14 |
|
15 | 15 | import re |
| 16 | +import uuid |
16 | 17 | import warnings |
17 | 18 | from pathlib import Path |
18 | 19 |
|
|
30 | 31 | wait_for_session_active, |
31 | 32 | workbench_login, |
32 | 33 | ) |
33 | | -from vip_tests.workbench.exec import terminal_run |
| 34 | +from vip_tests.workbench.exec import ExecError, terminal_run |
34 | 35 | from vip_tests.workbench.pages import Homepage, NewSessionDialog, VSCodeSession |
35 | 36 |
|
36 | 37 | pytestmark = pytest.mark.order(60) |
|
40 | 41 | # Timeout for rsconnect deploy, which bundles, uploads, and deploys. |
41 | 42 | _DEPLOY_TIMEOUT_MS = 180_000 |
42 | 43 |
|
| 44 | +# Timeout for the short venv-management commands (create, pip install, cleanup). |
| 45 | +_VENV_SETUP_TIMEOUT_MS = 120_000 |
| 46 | +_VENV_QUICK_TIMEOUT_MS = 30_000 |
| 47 | + |
43 | 48 |
|
44 | 49 | @scenario( |
45 | 50 | "test_publish_to_connect.feature", |
@@ -182,25 +187,78 @@ def deploy_python_shiny_via_terminal( |
182 | 187 | connect_client, |
183 | 188 | _connect_created_guids: list, |
184 | 189 | ): |
185 | | - """Run ``rsconnect deploy shiny`` in the VS Code terminal and register the GUID.""" |
| 190 | + """Run ``rsconnect deploy shiny`` in the VS Code terminal and register the GUID. |
| 191 | +
|
| 192 | + ``rsconnect-python`` is not assumed to be on PATH. We create a throwaway |
| 193 | + venv from whatever ``python3`` the session provides, install |
| 194 | + ``rsconnect-python`` into it, deploy with that venv's ``rsconnect``, and |
| 195 | + tear the venv down afterwards. A missing ``python3`` fails the preflight. |
| 196 | + """ |
186 | 197 | # Open the integrated terminal. |
187 | 198 | page.keyboard.press("Control+`") |
188 | 199 | terminal_input = page.locator(VSCodeSession.TERMINAL_INPUT) |
189 | 200 | expect(terminal_input).to_be_visible(timeout=TIMEOUT_SESSION_START) |
190 | 201 |
|
| 202 | + # Preflight: a Python interpreter must be on PATH to build the venv. |
| 203 | + # Prefer ``python3`` but accept ``python`` so sessions without the |
| 204 | + # ``python3`` symlink still qualify. |
| 205 | + try: |
| 206 | + python_bin = terminal_run( |
| 207 | + page, |
| 208 | + "command -v python3 || command -v python", |
| 209 | + timeout=_VENV_QUICK_TIMEOUT_MS, |
| 210 | + readback_lang="python", |
| 211 | + ).strip() |
| 212 | + except ExecError as exc: |
| 213 | + raise AssertionError( |
| 214 | + "Neither python3 nor python is on PATH in the Workbench session; " |
| 215 | + "cannot create a venv to install rsconnect-python for deployment." |
| 216 | + ) from exc |
| 217 | + |
191 | 218 | title = f"vip_test_shiny_{unique_session_name(_FILENAME)}" |
| 219 | + venv_dir = f"/tmp/vip_rsconnect_venv_{uuid.uuid4().hex}" |
| 220 | + rsconnect_bin = f"{venv_dir}/bin/rsconnect" |
192 | 221 |
|
193 | | - output = terminal_run( |
194 | | - page, |
195 | | - ( |
196 | | - f"rsconnect deploy shiny {python_shiny_bundle_path} " |
197 | | - f"--server {connect_url} " |
198 | | - f"--api-key {vip_config.connect.api_key} " |
199 | | - f"--title {title}" |
200 | | - ), |
201 | | - timeout=_DEPLOY_TIMEOUT_MS, |
202 | | - readback_lang="python", |
203 | | - ) |
| 222 | + try: |
| 223 | + # Create the venv and install rsconnect-python into it. |
| 224 | + terminal_run( |
| 225 | + page, |
| 226 | + f"{python_bin} -m venv {venv_dir}", |
| 227 | + timeout=_VENV_SETUP_TIMEOUT_MS, |
| 228 | + readback_lang="python", |
| 229 | + ) |
| 230 | + terminal_run( |
| 231 | + page, |
| 232 | + f"{venv_dir}/bin/pip install --quiet --upgrade rsconnect-python", |
| 233 | + timeout=_VENV_SETUP_TIMEOUT_MS, |
| 234 | + readback_lang="python", |
| 235 | + ) |
| 236 | + |
| 237 | + output = terminal_run( |
| 238 | + page, |
| 239 | + ( |
| 240 | + f"{rsconnect_bin} deploy shiny {python_shiny_bundle_path} " |
| 241 | + f"--server {connect_url} " |
| 242 | + f"--api-key {vip_config.connect.api_key} " |
| 243 | + f"--title {title}" |
| 244 | + ), |
| 245 | + timeout=_DEPLOY_TIMEOUT_MS, |
| 246 | + readback_lang="python", |
| 247 | + ) |
| 248 | + finally: |
| 249 | + # Tear down the throwaway venv regardless of deploy outcome. |
| 250 | + try: |
| 251 | + terminal_run( |
| 252 | + page, |
| 253 | + f"rm -rf {venv_dir}", |
| 254 | + timeout=_VENV_QUICK_TIMEOUT_MS, |
| 255 | + readback_lang="python", |
| 256 | + ) |
| 257 | + except ExecError: |
| 258 | + warnings.warn( |
| 259 | + f"Failed to remove temporary venv at {venv_dir}; manual cleanup may be required.", |
| 260 | + stacklevel=2, |
| 261 | + ) |
204 | 262 |
|
205 | 263 | # Primary: stable API title lookup (version-independent). |
206 | 264 | content = connect_client._find_content_by_name(title) |
|
0 commit comments