Compare commits

...

2 Commits

Author SHA1 Message Date
Devin AI
2d74f60d1d fix: remove unused imports to resolve lint errors
Co-Authored-By: João <joao@crewai.com>
2025-08-01 02:57:53 +00:00
Devin AI
6bb596d2e0 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>
2025-08-01 02:54:46 +00:00
3 changed files with 170 additions and 7 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

@@ -1,6 +1,5 @@
import subprocess
from enum import Enum
from typing import List, Optional
import click
from packaging import version
@@ -14,13 +13,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 +46,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 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)