diff --git a/lib/cli/src/crewai_cli/update_crew.py b/lib/cli/src/crewai_cli/update_crew.py index 3935e2f5f..db250fda7 100644 --- a/lib/cli/src/crewai_cli/update_crew.py +++ b/lib/cli/src/crewai_cli/update_crew.py @@ -89,7 +89,7 @@ def migrate_pyproject(input_file: str, output_file: str) -> None: lock_file = "poetry.lock" lock_backup = "poetry-old.lock" if os.path.exists(lock_file): - os.rename(lock_file, lock_backup) + os.replace(lock_file, lock_backup) else: pass diff --git a/lib/cli/tests/test_update_crew.py b/lib/cli/tests/test_update_crew.py new file mode 100644 index 000000000..751074df2 --- /dev/null +++ b/lib/cli/tests/test_update_crew.py @@ -0,0 +1,63 @@ +"""Tests for the ``crewai update`` pyproject migration.""" + +from pathlib import Path + +import pytest + +from crewai_cli.update_crew import migrate_pyproject + + +_POETRY_PYPROJECT = """\ +[tool.poetry] +name = "demo" +version = "0.1.0" +description = "demo crew" +authors = ["Demo Author "] + +[tool.poetry.dependencies] +python = ">=3.10,<3.14" +crewai = "^1.0.0" +""" + + +def _write_project(tmp_path: Path) -> None: + """Create a minimal Poetry-style project with a lock file in ``tmp_path``.""" + (tmp_path / "pyproject.toml").write_text(_POETRY_PYPROJECT, encoding="utf-8") + (tmp_path / "poetry.lock").write_text("current lock\n", encoding="utf-8") + + +def test_migrate_pyproject_backs_up_lock_file( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """The migration renames ``poetry.lock`` to ``poetry-old.lock`` and backs up pyproject.""" + monkeypatch.chdir(tmp_path) + _write_project(tmp_path) + + migrate_pyproject("pyproject.toml", "pyproject.toml") + + assert not (tmp_path / "poetry.lock").exists() + assert (tmp_path / "poetry-old.lock").read_text(encoding="utf-8") == ( + "current lock\n" + ) + assert (tmp_path / "pyproject-old.toml").exists() + + +def test_migrate_pyproject_overwrites_existing_lock_backup( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """A second ``crewai update`` must replace a stale ``poetry-old.lock``. + + ``os.rename`` refuses to overwrite an existing destination on Windows + (``FileExistsError``) while POSIX replaces it silently, so re-running the + migration used to fail only on Windows. + """ + monkeypatch.chdir(tmp_path) + _write_project(tmp_path) + (tmp_path / "poetry-old.lock").write_text("stale backup\n", encoding="utf-8") + + migrate_pyproject("pyproject.toml", "pyproject.toml") + + assert not (tmp_path / "poetry.lock").exists() + assert (tmp_path / "poetry-old.lock").read_text(encoding="utf-8") == ( + "current lock\n" + )