feat: add --active flag to crewai run command

- Add --active flag to use currently active environment instead of creating virtual environment
- Implement --no-sync flag usage when --active is specified
- Add comprehensive tests for new functionality
- Fixes #3249

Co-Authored-By: João <joao@crewai.com>
This commit is contained in:
Devin AI
2025-08-01 02:54:46 +00:00
parent 88ed91561f
commit 6bb596d2e0
3 changed files with 170 additions and 6 deletions

View File

@@ -214,9 +214,10 @@ def install(context):
@crewai.command()
def run():
@click.option("--active", is_flag=True, help="Use the currently active environment instead of creating a virtual environment")
def run(active):
"""Run the Crew."""
run_crew()
run_crew(active)
@crewai.command()

View File

@@ -14,13 +14,16 @@ class CrewType(Enum):
FLOW = "flow"
def run_crew() -> None:
def run_crew(active: bool = False) -> None:
"""
Run the crew or flow by running a command in the UV environment.
Starting from version 0.103.0, this command can be used to run both
standard crews and flows. For flows, it detects the type from pyproject.toml
and automatically runs the appropriate command.
Args:
active: If True, use the currently active environment instead of creating a virtual environment
"""
crewai_version = get_crewai_version()
min_required_version = "0.71.0"
@@ -44,17 +47,23 @@ def run_crew() -> None:
click.echo(f"Running the {'Flow' if is_flow else 'Crew'}")
# Execute the appropriate command
execute_command(crew_type)
execute_command(crew_type, active)
def execute_command(crew_type: CrewType) -> None:
def execute_command(crew_type: CrewType, active: bool = False) -> None:
"""
Execute the appropriate command based on crew type.
Args:
crew_type: The type of crew to run
active: If True, use the currently active environment instead of creating a virtual environment
"""
command = ["uv", "run", "kickoff" if crew_type == CrewType.FLOW else "run_crew"]
command = ["uv", "run"]
if active:
command.append("--no-sync")
command.append("kickoff" if crew_type == CrewType.FLOW else "run_crew")
try:
subprocess.run(command, capture_output=False, text=True, check=True)

154
tests/cli/test_run_crew.py Normal file
View File

@@ -0,0 +1,154 @@
import subprocess
from unittest import mock
import pytest
from click.testing import CliRunner
from crewai.cli.cli import run
from crewai.cli.run_crew import run_crew, execute_command, CrewType
@pytest.fixture
def runner():
return CliRunner()
@mock.patch("crewai.cli.run_crew.execute_command")
@mock.patch("crewai.cli.run_crew.read_toml")
@mock.patch("crewai.cli.run_crew.get_crewai_version")
def test_run_crew_without_active_flag(mock_version, mock_toml, mock_execute, runner):
"""Test that run command works without --active flag (default behavior)."""
mock_version.return_value = "0.148.0"
mock_toml.return_value = {"tool": {"crewai": {"type": "standard"}}}
result = runner.invoke(run)
assert result.exit_code == 0
mock_execute.assert_called_once_with(CrewType.STANDARD, False)
@mock.patch("crewai.cli.run_crew.execute_command")
@mock.patch("crewai.cli.run_crew.read_toml")
@mock.patch("crewai.cli.run_crew.get_crewai_version")
def test_run_crew_with_active_flag(mock_version, mock_toml, mock_execute, runner):
"""Test that run command works with --active flag."""
mock_version.return_value = "0.148.0"
mock_toml.return_value = {"tool": {"crewai": {"type": "standard"}}}
result = runner.invoke(run, ["--active"])
assert result.exit_code == 0
mock_execute.assert_called_once_with(CrewType.STANDARD, True)
@mock.patch("crewai.cli.run_crew.execute_command")
@mock.patch("crewai.cli.run_crew.read_toml")
@mock.patch("crewai.cli.run_crew.get_crewai_version")
def test_run_flow_with_active_flag(mock_version, mock_toml, mock_execute, runner):
"""Test that run command works with --active flag for flows."""
mock_version.return_value = "0.148.0"
mock_toml.return_value = {"tool": {"crewai": {"type": "flow"}}}
result = runner.invoke(run, ["--active"])
assert result.exit_code == 0
mock_execute.assert_called_once_with(CrewType.FLOW, True)
@mock.patch("crewai.cli.run_crew.subprocess.run")
def test_execute_command_standard_crew_without_active(mock_subprocess_run):
"""Test execute_command for standard crew without active flag."""
mock_subprocess_run.return_value = subprocess.CompletedProcess(
args=["uv", "run", "run_crew"], returncode=0
)
execute_command(CrewType.STANDARD, active=False)
mock_subprocess_run.assert_called_once_with(
["uv", "run", "run_crew"],
capture_output=False,
text=True,
check=True,
)
@mock.patch("crewai.cli.run_crew.subprocess.run")
def test_execute_command_standard_crew_with_active(mock_subprocess_run):
"""Test execute_command for standard crew with active flag."""
mock_subprocess_run.return_value = subprocess.CompletedProcess(
args=["uv", "run", "--no-sync", "run_crew"], returncode=0
)
execute_command(CrewType.STANDARD, active=True)
mock_subprocess_run.assert_called_once_with(
["uv", "run", "--no-sync", "run_crew"],
capture_output=False,
text=True,
check=True,
)
@mock.patch("crewai.cli.run_crew.subprocess.run")
def test_execute_command_flow_with_active(mock_subprocess_run):
"""Test execute_command for flow with active flag."""
mock_subprocess_run.return_value = subprocess.CompletedProcess(
args=["uv", "run", "--no-sync", "kickoff"], returncode=0
)
execute_command(CrewType.FLOW, active=True)
mock_subprocess_run.assert_called_once_with(
["uv", "run", "--no-sync", "kickoff"],
capture_output=False,
text=True,
check=True,
)
@mock.patch("crewai.cli.run_crew.subprocess.run")
def test_execute_command_flow_without_active(mock_subprocess_run):
"""Test execute_command for flow without active flag."""
mock_subprocess_run.return_value = subprocess.CompletedProcess(
args=["uv", "run", "kickoff"], returncode=0
)
execute_command(CrewType.FLOW, active=False)
mock_subprocess_run.assert_called_once_with(
["uv", "run", "kickoff"],
capture_output=False,
text=True,
check=True,
)
@mock.patch("crewai.cli.run_crew.subprocess.run")
@mock.patch("crewai.cli.run_crew.click.echo")
def test_execute_command_handles_subprocess_error(mock_echo, mock_subprocess_run):
"""Test that execute_command properly handles subprocess errors."""
mock_subprocess_run.side_effect = subprocess.CalledProcessError(
returncode=1,
cmd=["uv", "run", "--no-sync", "run_crew"],
output="Error output"
)
execute_command(CrewType.STANDARD, active=True)
mock_subprocess_run.assert_called_once_with(
["uv", "run", "--no-sync", "run_crew"],
capture_output=False,
text=True,
check=True,
)
@mock.patch("crewai.cli.run_crew.subprocess.run")
@mock.patch("crewai.cli.run_crew.click.echo")
def test_execute_command_handles_general_exception(mock_echo, mock_subprocess_run):
"""Test that execute_command properly handles general exceptions."""
mock_subprocess_run.side_effect = Exception("Unexpected error")
execute_command(CrewType.STANDARD, active=True)
mock_echo.assert_called_once_with("An unexpected error occurred: Unexpected error", err=True)