diff --git a/packages/uipath/pyproject.toml b/packages/uipath/pyproject.toml index ac00d3736..9305fe497 100644 --- a/packages/uipath/pyproject.toml +++ b/packages/uipath/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath" -version = "2.14.9" +version = "2.14.10" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" diff --git a/packages/uipath/src/uipath/_cli/cli_new.py b/packages/uipath/src/uipath/_cli/cli_new.py index 692eee452..6d021a867 100644 --- a/packages/uipath/src/uipath/_cli/cli_new.py +++ b/packages/uipath/src/uipath/_cli/cli_new.py @@ -1,6 +1,5 @@ import json import os -import re import shutil import uuid @@ -15,33 +14,11 @@ console = ConsoleLogger() -FALLBACK_UIPATH_MINOR = "2.14" - - -def _minor_range_spec(major: int, minor: int) -> str: - return f"uipath>={major}.{minor}.0, <{major}.{minor + 1}.0" - - -def _fallback_uipath_dependency_spec() -> str: - major, minor = (int(part) for part in FALLBACK_UIPATH_MINOR.split(".")) - return _minor_range_spec(major, minor) - - -def _uipath_dependency_spec() -> str: - from . import _get_safe_version - - installed = _get_safe_version() - # Strip pre-release/dev/local suffixes: only the leading "major.minor" matters. - match = re.match(r"^(\d+)\.(\d+)", installed) - if match is None: - fallback = _fallback_uipath_dependency_spec() - console.warning( - f"Could not determine the installed 'uipath' version ('{installed}'); " - f"falling back to '{fallback}'. Pin it manually if needed." - ) - return fallback - - return _minor_range_spec(int(match.group(1)), int(match.group(2))) +# The `uipath` minor release that scaffolded projects are pinned to. +# Deliberately a constant: the guard test in tests/cli/test_new.py fails on +# every minor bump so the scaffold (pin, template, hints) gets reviewed +# alongside the release rather than drifting silently. +UIPATH_SCAFFOLD_MINOR = "2.14" def generate_script(target_directory): @@ -55,13 +32,14 @@ def generate_script(target_directory): def generate_pyproject(target_directory, project_name): project_toml_path = os.path.join(target_directory, PYTHON_CONFIGURATION_FILE) + major, minor = (int(part) for part in UIPATH_SCAFFOLD_MINOR.split(".")) toml_content = f"""[project] name = "{project_name}" version = "0.0.1" description = "{project_name}" authors = [{{ name = "John Doe", email = "john.doe@myemail.com" }}] dependencies = [ - "{_uipath_dependency_spec()}" + "uipath>={major}.{minor}.0, <{major}.{minor + 1}.0" ] requires-python = ">=3.11" """ diff --git a/packages/uipath/tests/cli/test_new.py b/packages/uipath/tests/cli/test_new.py index 02ce357d7..d2d278a62 100644 --- a/packages/uipath/tests/cli/test_new.py +++ b/packages/uipath/tests/cli/test_new.py @@ -7,6 +7,7 @@ from click.testing import CliRunner from packaging.specifiers import SpecifierSet +from packaging.version import Version from uipath._cli import cli from uipath._cli.middlewares import MiddlewareResult @@ -98,51 +99,24 @@ def test_new_project_error_handling(self, runner: CliRunner, temp_dir: str) -> N assert "Created 'main.py' file." not in result.output -class TestUipathDependencySpec: - """The scaffolded pin must follow the installed uipath version.""" - - def _written_pin(self, temp_dir: str) -> str: - from uipath._cli.cli_new import generate_pyproject - - generate_pyproject(temp_dir, "demo") - with open(os.path.join(temp_dir, "pyproject.toml")) as f: - content = f.read() - match = re.search(r'"(uipath[^"]*)"', content) - assert match is not None, content - return match.group(1) - - def test_pin_derived_from_installed_version(self, temp_dir: str) -> None: - with patch("uipath._cli._get_safe_version", return_value="2.14.7"): - assert self._written_pin(temp_dir) == "uipath>=2.14.0, <2.15.0" - - def test_pin_strips_prerelease_suffix(self, temp_dir: str) -> None: - with patch("uipath._cli._get_safe_version", return_value="2.15.0rc1"): - assert self._written_pin(temp_dir) == "uipath>=2.15.0, <2.16.0" - - def test_pin_falls_back_when_version_unknown(self, temp_dir: str) -> None: - from uipath._cli.cli_new import _fallback_uipath_dependency_spec, console - - # _get_safe_version() returns "unknown" on PackageNotFoundError. - with ( - patch("uipath._cli._get_safe_version", return_value="unknown"), - patch.object(console, "warning") as mock_warning, - ): - assert self._written_pin(temp_dir) == _fallback_uipath_dependency_spec() - mock_warning.assert_called_once() - assert ( - "Could not determine the installed 'uipath' version" - in (mock_warning.call_args.args[0]) - ) +class TestUipathScaffoldPin: + """The scaffolded pin must admit the uipath release it ships with.""" - def test_fallback_pin_admits_installed_uipath(self) -> None: - """Guard: a release PR that forgets to bump FALLBACK_UIPATH_MINOR fails CI.""" - from uipath._cli.cli_new import _fallback_uipath_dependency_spec + def test_scaffold_pin_admits_installed_uipath(self) -> None: + """Guard: fails on every minor bump so the scaffold gets reviewed. - spec = _fallback_uipath_dependency_spec().removeprefix("uipath") - installed = version("uipath") - assert SpecifierSet(spec).contains(installed, prereleases=True), ( - f"fallback pin '{spec}' does not admit installed uipath {installed}; " - "bump FALLBACK_UIPATH_MINOR in cli_new.py" + When this fails, review the scaffold in ``cli_new.py`` (pin constant, + ``main.py`` template, post-scaffold hints) for the new minor, then bump + ``UIPATH_SCAFFOLD_MINOR``. + """ + from uipath._cli.cli_new import UIPATH_SCAFFOLD_MINOR + + installed = Version(version("uipath")) + installed_minor = f"{installed.major}.{installed.minor}" + assert UIPATH_SCAFFOLD_MINOR == installed_minor, ( + f"uipath minor changed to {installed_minor} but UIPATH_SCAFFOLD_MINOR is " + f"{UIPATH_SCAFFOLD_MINOR}; review the scaffold in cli_new.py (pin, " + f"template, hints) and bump the constant" ) def test_scaffolded_pin_contains_installed_uipath( @@ -150,8 +124,8 @@ def test_scaffolded_pin_contains_installed_uipath( ) -> None: """Regression guard: runs against the real installed package, not a mock. - A stale hard-coded range would make ``uv sync`` downgrade the project's - venv right after ``uipath new``. + A stale range would make ``uv sync`` downgrade the project's venv right + after ``uipath new``. """ with runner.isolated_filesystem(temp_dir=temp_dir): result = runner.invoke(cli, ["new", "demo"]) diff --git a/packages/uipath/uv.lock b/packages/uipath/uv.lock index 030c1b50f..ebb821062 100644 --- a/packages/uipath/uv.lock +++ b/packages/uipath/uv.lock @@ -2599,7 +2599,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.14.9" +version = "2.14.10" source = { editable = "." } dependencies = [ { name = "applicationinsights" },