mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-06 23:49:24 +00:00
Update crewAI CLI with various enhancements and fixes
- Updated `create_json_crew.py` to require `crewai[tools]>=1.14.7`. - Enhanced `git.py` with improved repository initialization, including automatic initial commit creation and exclusion patterns for initial commits. - Modified `install_crew.py` to allow error handling during installation with an optional `raise_on_error` parameter. - Expanded `plus_api.py` to include methods for creating and updating crews from ZIP files. - Introduced a new `archive.py` for creating deployable ZIP archives of CrewAI projects, ensuring local artifacts are excluded. - Updated `run_crew.py` to manage JSON crew dependencies and run crews in the project's environment. - Enhanced deployment logic in `main.py` to handle ZIP uploads and improve user feedback during deployment processes. - Added tests for new functionalities and ensured existing tests reflect recent changes in behavior and requirements.
This commit is contained in:
@@ -1,9 +1,29 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import cached_property
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
|
||||
_INITIAL_COMMIT_EXCLUDE_PATTERNS = [
|
||||
".crewai/",
|
||||
".env",
|
||||
".env.*",
|
||||
".mypy_cache/",
|
||||
".pytest_cache/",
|
||||
".ruff_cache/",
|
||||
".tox/",
|
||||
".venv/",
|
||||
"__pycache__/",
|
||||
"build/",
|
||||
"dist/",
|
||||
"env/",
|
||||
"venv/",
|
||||
]
|
||||
|
||||
|
||||
class Repository:
|
||||
def __init__(self, path: str = ".") -> None:
|
||||
def __init__(self, path: str = ".", fetch: bool = True) -> None:
|
||||
self.path = path
|
||||
|
||||
if not self.is_git_installed():
|
||||
@@ -12,7 +32,8 @@ class Repository:
|
||||
if not self.is_git_repo:
|
||||
raise ValueError(f"{self.path} is not a Git repository.")
|
||||
|
||||
self.fetch()
|
||||
if fetch:
|
||||
self.fetch()
|
||||
|
||||
@staticmethod
|
||||
def is_git_installed() -> bool:
|
||||
@@ -30,7 +51,33 @@ class Repository:
|
||||
|
||||
def fetch(self) -> None:
|
||||
"""Fetch latest updates from the remote."""
|
||||
subprocess.run(["git", "fetch"], cwd=self.path, check=True) # noqa: S607
|
||||
result = subprocess.run(
|
||||
["git", "fetch"], # noqa: S607
|
||||
cwd=self.path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
return
|
||||
if "No remote repository specified" in result.stderr:
|
||||
return
|
||||
raise subprocess.CalledProcessError(
|
||||
result.returncode,
|
||||
["git", "fetch"],
|
||||
output=result.stdout,
|
||||
stderr=result.stderr,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, path: str = ".") -> Repository:
|
||||
"""Initialize a Git repository and create an initial commit if needed."""
|
||||
if not cls.is_git_installed():
|
||||
raise ValueError("Git is not installed or not found in your PATH.")
|
||||
|
||||
subprocess.run(["git", "init"], cwd=path, check=True) # noqa: S607
|
||||
repository = cls(path=path, fetch=False)
|
||||
repository.create_initial_commit_if_needed()
|
||||
return repository
|
||||
|
||||
def status(self) -> str:
|
||||
"""Get the git status in porcelain format."""
|
||||
@@ -71,6 +118,74 @@ class Repository:
|
||||
return False
|
||||
return True
|
||||
|
||||
def has_commits(self) -> bool:
|
||||
"""Return True if the repository has at least one commit."""
|
||||
try:
|
||||
subprocess.run(
|
||||
["git", "rev-parse", "--verify", "HEAD"], # noqa: S607
|
||||
cwd=self.path,
|
||||
capture_output=True,
|
||||
check=True,
|
||||
text=True,
|
||||
)
|
||||
return True
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
|
||||
def create_initial_commit_if_needed(self) -> bool:
|
||||
"""Create a local initial commit when the repository has no commits."""
|
||||
if self.has_commits():
|
||||
return False
|
||||
|
||||
self._ensure_initial_commit_excludes()
|
||||
subprocess.run(["git", "add", "."], cwd=self.path, check=True) # noqa: S607
|
||||
command = [
|
||||
"git",
|
||||
"-c",
|
||||
"user.name=CrewAI",
|
||||
"-c",
|
||||
"user.email=deploy@crewai.com",
|
||||
"commit",
|
||||
"--allow-empty",
|
||||
"-m",
|
||||
"Initial crew",
|
||||
]
|
||||
subprocess.run( # noqa: S603
|
||||
command,
|
||||
cwd=self.path,
|
||||
check=True,
|
||||
)
|
||||
return True
|
||||
|
||||
def _ensure_initial_commit_excludes(self) -> None:
|
||||
"""Add local-only ignore patterns before auto-staging an initial commit."""
|
||||
exclude_file = Path(self.path) / ".git" / "info" / "exclude"
|
||||
exclude_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
existing = exclude_file.read_text() if exclude_file.exists() else ""
|
||||
existing_lines = set(existing.splitlines())
|
||||
missing_patterns = [
|
||||
pattern
|
||||
for pattern in _INITIAL_COMMIT_EXCLUDE_PATTERNS
|
||||
if pattern not in existing_lines
|
||||
]
|
||||
if not missing_patterns:
|
||||
return
|
||||
|
||||
prefix = "" if existing.endswith("\n") or not existing else "\n"
|
||||
patterns = "\n".join(missing_patterns)
|
||||
exclude_file.write_text(
|
||||
f"{existing}{prefix}# CrewAI deploy auto-commit excludes\n{patterns}\n"
|
||||
)
|
||||
|
||||
def deployable_files(self) -> list[str]:
|
||||
"""Return files tracked by Git or untracked and not ignored."""
|
||||
output = subprocess.check_output(
|
||||
["git", "ls-files", "--cached", "--others", "--exclude-standard"], # noqa: S607
|
||||
cwd=self.path,
|
||||
encoding="utf-8",
|
||||
)
|
||||
return [line for line in output.splitlines() if line]
|
||||
|
||||
def origin_url(self) -> str | None:
|
||||
"""Get the Git repository's remote URL."""
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user