From ce74cdf02c86f2f0fbbe5286d19969d82492ac52 Mon Sep 17 00:00:00 2001 From: Joao Moura Date: Mon, 15 Jun 2026 10:06:30 -0700 Subject: [PATCH] fix(cli): load json runner from source checkout --- lib/cli/src/crewai_cli/run_crew.py | 56 ++++++++++++++++++++++--- lib/cli/tests/test_run_crew.py | 66 ++++++++++++++++++++++++++++-- 2 files changed, 113 insertions(+), 9 deletions(-) diff --git a/lib/cli/src/crewai_cli/run_crew.py b/lib/cli/src/crewai_cli/run_crew.py index 34f82e68e..ab4a86e84 100644 --- a/lib/cli/src/crewai_cli/run_crew.py +++ b/lib/cli/src/crewai_cli/run_crew.py @@ -35,12 +35,46 @@ class CrewType(Enum): # crewai.utilities.string_utils (_VARIABLE_PATTERN), including hyphens — # otherwise placeholders are interpolated at runtime but never prompted for. _INPUT_PLACEHOLDER_RE = re.compile(r"(? bool: @@ -238,6 +272,15 @@ def _install_json_crew_dependencies() -> None: raise SystemExit(1) from e +def _find_local_crewai_source_dir() -> Path | None: + """Return the repo's CrewAI source dir when running from a source checkout.""" + for parent in Path(__file__).resolve().parents: + candidate = parent / "lib" / "crewai" / "src" + if (candidate / "crewai" / "project" / "json_loader.py").is_file(): + return candidate + return None + + def _run_json_crew_in_project_env(trained_agents_file: str | None = None) -> Any: """Run JSON crews from the project's uv-managed environment.""" if not (Path.cwd() / "pyproject.toml").is_file(): @@ -247,6 +290,9 @@ def _run_json_crew_in_project_env(trained_agents_file: str | None = None) -> Any command = ["uv", "run", "--no-sync", "python", "-c", _JSON_CREW_RUNNER_CODE] env = build_env_with_all_tool_credentials() + env[_CREWAI_CLI_RUNNER_PACKAGE_DIR_ENV] = str(Path(__file__).resolve().parent) + if local_crewai_source_dir := _find_local_crewai_source_dir(): + env[_CREWAI_RUNNER_SOURCE_DIR_ENV] = str(local_crewai_source_dir) if trained_agents_file: env[CREWAI_TRAINED_AGENTS_FILE_ENV] = trained_agents_file diff --git a/lib/cli/tests/test_run_crew.py b/lib/cli/tests/test_run_crew.py index 3ebbe89a9..cdb8218be 100644 --- a/lib/cli/tests/test_run_crew.py +++ b/lib/cli/tests/test_run_crew.py @@ -3,6 +3,7 @@ import os from pathlib import Path import subprocess +import sys import pytest from crewai_core.constants import CREWAI_TRAINED_AGENTS_FILE_ENV @@ -56,6 +57,18 @@ def test_json_run_uses_project_env_when_pyproject_exists(monkeypatch, tmp_path: trained_agents_file="trained.pkl" ) + expected_env = { + "EXISTING": "value", + run_crew_module._CREWAI_CLI_RUNNER_PACKAGE_DIR_ENV: str( + Path(run_crew_module.__file__).resolve().parent + ), + CREWAI_TRAINED_AGENTS_FILE_ENV: "trained.pkl", + } + if local_crewai_source_dir := run_crew_module._find_local_crewai_source_dir(): + expected_env[run_crew_module._CREWAI_RUNNER_SOURCE_DIR_ENV] = str( + local_crewai_source_dir + ) + assert install_calls == [True] assert subprocess_calls == [ ( @@ -71,15 +84,60 @@ def test_json_run_uses_project_env_when_pyproject_exists(monkeypatch, tmp_path: "capture_output": False, "text": True, "check": True, - "env": { - "EXISTING": "value", - CREWAI_TRAINED_AGENTS_FILE_ENV: "trained.pkl", - }, + "env": expected_env, }, ) ] +def test_json_runner_code_loads_current_cli_package_over_project_env(tmp_path: Path): + old_parent = tmp_path / "old" + old_pkg = old_parent / "crewai_cli" + old_pkg.mkdir(parents=True) + (old_pkg / "__init__.py").write_text("") + (old_pkg / "run_crew.py").write_text("raise ImportError('old package used')\n") + old_crewai_project = old_parent / "crewai" / "project" + old_crewai_project.mkdir(parents=True) + (old_parent / "crewai" / "__init__.py").write_text("") + (old_crewai_project / "__init__.py").write_text("") + (old_crewai_project / "json_loader.py").write_text( + "raise ImportError('old crewai used')\n" + ) + + current_pkg = tmp_path / "current" / "crewai_cli" + current_pkg.mkdir(parents=True) + marker = tmp_path / "marker.txt" + (current_pkg / "__init__.py").write_text("") + (current_pkg / "run_crew.py").write_text( + "from pathlib import Path\n" + "from crewai.project.json_loader import SOURCE\n" + "def _run_json_crew(trained_agents_file=None):\n" + f" Path({str(marker)!r}).write_text(SOURCE + ':' + (trained_agents_file or ''))\n" + ) + current_crewai_project = tmp_path / "current_crewai_src" / "crewai" / "project" + current_crewai_project.mkdir(parents=True) + (tmp_path / "current_crewai_src" / "crewai" / "__init__.py").write_text("") + (current_crewai_project / "__init__.py").write_text("") + (current_crewai_project / "json_loader.py").write_text("SOURCE = 'current'\n") + + env = os.environ.copy() + env["PYTHONPATH"] = str(old_parent) + env[run_crew_module._CREWAI_CLI_RUNNER_PACKAGE_DIR_ENV] = str(current_pkg) + env[run_crew_module._CREWAI_RUNNER_SOURCE_DIR_ENV] = str( + tmp_path / "current_crewai_src" + ) + env[CREWAI_TRAINED_AGENTS_FILE_ENV] = "trained.pkl" + + subprocess.run( + [sys.executable, "-c", run_crew_module._JSON_CREW_RUNNER_CODE], + check=True, + env=env, + cwd=tmp_path, + ) + + assert marker.read_text() == "current:trained.pkl" + + def test_json_run_without_pyproject_runs_in_process(monkeypatch, tmp_path: Path): monkeypatch.chdir(tmp_path) called: dict = {}