mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-09 08:08:32 +00:00
Merge pull request #1269 from crewAIInc/tm-fix-cli-for-py310
Add py 3.10 support back to CLI + fixes
This commit is contained in:
@@ -2,6 +2,8 @@ from os import getenv
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from crewai.cli.deploy.utils import get_crewai_version
|
||||||
|
|
||||||
|
|
||||||
class CrewAPI:
|
class CrewAPI:
|
||||||
"""
|
"""
|
||||||
@@ -13,6 +15,7 @@ class CrewAPI:
|
|||||||
self.headers = {
|
self.headers = {
|
||||||
"Authorization": f"Bearer {api_key}",
|
"Authorization": f"Bearer {api_key}",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
"User-Agent": f"CrewAI-CLI/{get_crewai_version()}",
|
||||||
}
|
}
|
||||||
self.base_url = getenv(
|
self.base_url = getenv(
|
||||||
"CREWAI_BASE_URL", "https://dev.crewai.com/crewai_plus/api/v1/crews"
|
"CREWAI_BASE_URL", "https://dev.crewai.com/crewai_plus/api/v1/crews"
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ class DeployCommand:
|
|||||||
raise SystemExit
|
raise SystemExit
|
||||||
|
|
||||||
self.project_name = get_project_name()
|
self.project_name = get_project_name()
|
||||||
|
if self.project_name is None:
|
||||||
|
console.print("No project name found. Please ensure your project has a valid pyproject.toml file.", style="bold red")
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
self.client = CrewAPI(api_key=access_token)
|
self.client = CrewAPI(api_key=access_token)
|
||||||
|
|
||||||
def _handle_error(self, json_response: Dict[str, Any]) -> None:
|
def _handle_error(self, json_response: Dict[str, Any]) -> None:
|
||||||
@@ -123,6 +127,11 @@ class DeployCommand:
|
|||||||
env_vars = fetch_and_json_env_file()
|
env_vars = fetch_and_json_env_file()
|
||||||
remote_repo_url = get_git_remote_url()
|
remote_repo_url = get_git_remote_url()
|
||||||
|
|
||||||
|
if remote_repo_url is None:
|
||||||
|
console.print("No remote repository URL found.", style="bold red")
|
||||||
|
console.print("Please ensure your project has a valid remote repository.", style="yellow")
|
||||||
|
return
|
||||||
|
|
||||||
self._confirm_input(env_vars, remote_repo_url)
|
self._confirm_input(env_vars, remote_repo_url)
|
||||||
payload = self._create_payload(env_vars, remote_repo_url)
|
payload = self._create_payload(env_vars, remote_repo_url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,46 @@
|
|||||||
|
import sys
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import tomllib
|
from rich.console import Console
|
||||||
|
|
||||||
from ..authentication.utils import TokenManager
|
from ..authentication.utils import TokenManager
|
||||||
|
|
||||||
|
console = Console()
|
||||||
|
|
||||||
def get_git_remote_url() -> str:
|
|
||||||
|
if sys.version_info >= (3, 11):
|
||||||
|
import tomllib
|
||||||
|
|
||||||
|
|
||||||
|
# Drop the simple_toml_parser when we move to python3.11
|
||||||
|
def simple_toml_parser(content):
|
||||||
|
result = {}
|
||||||
|
current_section = result
|
||||||
|
for line in content.split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith('[') and line.endswith(']'):
|
||||||
|
# New section
|
||||||
|
section = line[1:-1].split('.')
|
||||||
|
current_section = result
|
||||||
|
for key in section:
|
||||||
|
current_section = current_section.setdefault(key, {})
|
||||||
|
elif '=' in line:
|
||||||
|
key, value = line.split('=', 1)
|
||||||
|
key = key.strip()
|
||||||
|
value = value.strip().strip('"')
|
||||||
|
current_section[key] = value
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def parse_toml(content):
|
||||||
|
if sys.version_info >= (3, 11):
|
||||||
|
return tomllib.loads(content)
|
||||||
|
else:
|
||||||
|
return simple_toml_parser(content)
|
||||||
|
|
||||||
|
|
||||||
|
def get_git_remote_url() -> str | None:
|
||||||
"""Get the Git repository's remote URL."""
|
"""Get the Git repository's remote URL."""
|
||||||
try:
|
try:
|
||||||
# Run the git remote -v command
|
# Run the git remote -v command
|
||||||
@@ -23,21 +57,22 @@ def get_git_remote_url() -> str:
|
|||||||
if matches:
|
if matches:
|
||||||
return matches[0] # Return the first match (origin URL)
|
return matches[0] # Return the first match (origin URL)
|
||||||
else:
|
else:
|
||||||
print("No origin remote found.")
|
console.print("No origin remote found.", style="bold red")
|
||||||
return "No remote URL found"
|
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
return f"Error running trying to fetch the Git Repository: {e}"
|
console.print(f"Error running trying to fetch the Git Repository: {e}", style="bold red")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return "Git command not found. Make sure Git is installed and in your PATH."
|
console.print("Git command not found. Make sure Git is installed and in your PATH.", style="bold red")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_project_name(pyproject_path: str = "pyproject.toml"):
|
def get_project_name(pyproject_path: str = "pyproject.toml") -> str | None:
|
||||||
"""Get the project name from the pyproject.toml file."""
|
"""Get the project name from the pyproject.toml file."""
|
||||||
try:
|
try:
|
||||||
# Read the pyproject.toml file
|
# Read the pyproject.toml file
|
||||||
with open(pyproject_path, "rb") as f:
|
with open(pyproject_path, "r") as f:
|
||||||
pyproject_content = tomllib.load(f)
|
pyproject_content = parse_toml(f.read())
|
||||||
|
|
||||||
# Extract the project name
|
# Extract the project name
|
||||||
project_name = pyproject_content["tool"]["poetry"]["name"]
|
project_name = pyproject_content["tool"]["poetry"]["name"]
|
||||||
@@ -51,36 +86,39 @@ def get_project_name(pyproject_path: str = "pyproject.toml"):
|
|||||||
print(f"Error: {pyproject_path} not found.")
|
print(f"Error: {pyproject_path} not found.")
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print(f"Error: {pyproject_path} is not a valid pyproject.toml file.")
|
print(f"Error: {pyproject_path} is not a valid pyproject.toml file.")
|
||||||
except tomllib.TOMLDecodeError:
|
except tomllib.TOMLDecodeError if sys.version_info >= (3, 11) else Exception as e: # type: ignore
|
||||||
print(f"Error: {pyproject_path} is not a valid TOML file.")
|
print(
|
||||||
|
f"Error: {pyproject_path} is not a valid TOML file."
|
||||||
|
if sys.version_info >= (3, 11)
|
||||||
|
else f"Error reading the pyproject.toml file: {e}"
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error reading the pyproject.toml file: {e}")
|
print(f"Error reading the pyproject.toml file: {e}")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_crewai_version(pyproject_path: str = "pyproject.toml") -> str:
|
def get_crewai_version(poetry_lock_path: str = "poetry.lock") -> str:
|
||||||
"""Get the version number of crewai from the pyproject.toml file."""
|
"""Get the version number of crewai from the poetry.lock file."""
|
||||||
try:
|
try:
|
||||||
# Read the pyproject.toml file
|
with open(poetry_lock_path, "r") as f:
|
||||||
with open("pyproject.toml", "rb") as f:
|
lock_content = f.read()
|
||||||
pyproject_content = tomllib.load(f)
|
|
||||||
|
|
||||||
# Extract the version number of crewai
|
match = re.search(
|
||||||
crewai_version = pyproject_content["tool"]["poetry"]["dependencies"]["crewai"][
|
r'\[\[package\]\]\s*name\s*=\s*"crewai"\s*version\s*=\s*"([^"]+)"',
|
||||||
"version"
|
lock_content,
|
||||||
]
|
re.DOTALL,
|
||||||
|
)
|
||||||
return crewai_version
|
if match:
|
||||||
|
return match.group(1)
|
||||||
|
else:
|
||||||
|
print("crewai package not found in poetry.lock")
|
||||||
|
return "no-version-found"
|
||||||
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print(f"Error: {pyproject_path} not found.")
|
print(f"Error: {poetry_lock_path} not found.")
|
||||||
except KeyError:
|
|
||||||
print(f"Error: {pyproject_path} is not a valid pyproject.toml file.")
|
|
||||||
except tomllib.TOMLDecodeError:
|
|
||||||
print(f"Error: {pyproject_path} is not a valid TOML file.")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error reading the pyproject.toml file: {e}")
|
print(f"Error reading the poetry.lock file: {e}")
|
||||||
|
|
||||||
return "no-version-found"
|
return "no-version-found"
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class TestCrewAPI(unittest.TestCase):
|
|||||||
{
|
{
|
||||||
"Authorization": f"Bearer {self.api_key}",
|
"Authorization": f"Bearer {self.api_key}",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
"User-Agent": "CrewAI-CLI/no-version-found"
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
import sys
|
||||||
|
|
||||||
from crewai.cli.deploy.main import DeployCommand
|
from crewai.cli.deploy.main import DeployCommand
|
||||||
|
from crewai.cli.deploy.utils import parse_toml
|
||||||
|
|
||||||
class TestDeployCommand(unittest.TestCase):
|
class TestDeployCommand(unittest.TestCase):
|
||||||
@patch("crewai.cli.deploy.main.get_auth_token")
|
@patch("crewai.cli.deploy.main.get_auth_token")
|
||||||
@@ -151,3 +152,68 @@ class TestDeployCommand(unittest.TestCase):
|
|||||||
self.assertIn(
|
self.assertIn(
|
||||||
"Crew 'test_project' removed successfully", fake_out.getvalue()
|
"Crew 'test_project' removed successfully", fake_out.getvalue()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.version_info < (3, 11), "Requires Python 3.11+")
|
||||||
|
def test_parse_toml_python_311_plus(self):
|
||||||
|
toml_content = """
|
||||||
|
[tool.poetry]
|
||||||
|
name = "test_project"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.11"
|
||||||
|
crewai = { extras = ["tools"], version = ">=0.51.0,<1.0.0" }
|
||||||
|
"""
|
||||||
|
parsed = parse_toml(toml_content)
|
||||||
|
self.assertEqual(parsed['tool']['poetry']['name'], 'test_project')
|
||||||
|
|
||||||
|
@patch('builtins.open', new_callable=unittest.mock.mock_open, read_data="""
|
||||||
|
[tool.poetry]
|
||||||
|
name = "test_project"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.10"
|
||||||
|
crewai = { extras = ["tools"], version = ">=0.51.0,<1.0.0" }
|
||||||
|
""")
|
||||||
|
def test_get_project_name_python_310(self, mock_open):
|
||||||
|
from crewai.cli.deploy.utils import get_project_name
|
||||||
|
project_name = get_project_name()
|
||||||
|
self.assertEqual(project_name, 'test_project')
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.version_info < (3, 11), "Requires Python 3.11+")
|
||||||
|
@patch('builtins.open', new_callable=unittest.mock.mock_open, read_data="""
|
||||||
|
[tool.poetry]
|
||||||
|
name = "test_project"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.11"
|
||||||
|
crewai = { extras = ["tools"], version = ">=0.51.0,<1.0.0" }
|
||||||
|
""")
|
||||||
|
def test_get_project_name_python_311_plus(self, mock_open):
|
||||||
|
from crewai.cli.deploy.utils import get_project_name
|
||||||
|
project_name = get_project_name()
|
||||||
|
self.assertEqual(project_name, 'test_project')
|
||||||
|
|
||||||
|
@patch('builtins.open', new_callable=unittest.mock.mock_open, read_data="""
|
||||||
|
[[package]]
|
||||||
|
name = "crewai"
|
||||||
|
version = "0.51.1"
|
||||||
|
description = "Some description"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.10,<4.0"
|
||||||
|
""")
|
||||||
|
def test_get_crewai_version(self, mock_open):
|
||||||
|
from crewai.cli.deploy.utils import get_crewai_version
|
||||||
|
version = get_crewai_version()
|
||||||
|
self.assertEqual(version, '0.51.1')
|
||||||
|
|
||||||
|
@patch('builtins.open', side_effect=FileNotFoundError)
|
||||||
|
def test_get_crewai_version_file_not_found(self, mock_open):
|
||||||
|
from crewai.cli.deploy.utils import get_crewai_version
|
||||||
|
with patch('sys.stdout', new=StringIO()) as fake_out:
|
||||||
|
version = get_crewai_version()
|
||||||
|
self.assertEqual(version, 'no-version-found')
|
||||||
|
self.assertIn("Error: poetry.lock not found.", fake_out.getvalue())
|
||||||
|
|||||||
Reference in New Issue
Block a user