mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-08-10 16:32:28 +00:00
refactor: drop the console announcement when minting project_id
Minting now happens silently. With no message to print, the (id, created) tuple had no consumer, so simplify the API rather than keep the flag around for a hypothetical caller: - get_or_create_project_id() returns `str | None` instead of `tuple[str | None, bool]`. - Remove crewai_cli.utils.ensure_project_id, which existed only to print the message and discard the flag. The four call sites (crewai create crew, crewai create flow, crewai run, and tool-repository login) now call get_or_create_project_id directly. - Update tests for the simplified signature; still 18 tests covering minting, stability, table placement, formatting preservation, five pyproject layouts, and missing/malformed/read-only handling. Behaviour is otherwise unchanged: minting stays restricted to CLI commands the user invoked, library code still only reads via get_project_id, and a missing or read-only pyproject.toml still returns None rather than raising. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UNumDnNbiyw3pv1WakAe6t
This commit is contained in:
@@ -20,7 +20,7 @@ from crewai_cli.input_prompt import (
|
||||
)
|
||||
from crewai_cli.utils import (
|
||||
build_env_with_all_tool_credentials,
|
||||
ensure_project_id,
|
||||
get_or_create_project_id,
|
||||
is_dmn_mode_enabled,
|
||||
)
|
||||
from crewai_cli.version import get_crewai_tools_dependency, get_crewai_version
|
||||
@@ -620,7 +620,7 @@ def run_crew(
|
||||
"""
|
||||
# Backfills projects created before project_id existed. Only here, in a
|
||||
# command the user explicitly invoked - never from the SDK during kickoff.
|
||||
ensure_project_id()
|
||||
get_or_create_project_id()
|
||||
|
||||
# --definition is a pure override: run that flow directly.
|
||||
if definition is not None:
|
||||
|
||||
@@ -16,7 +16,7 @@ from crewai_cli.config import Settings
|
||||
from crewai_cli.constants import DEFAULT_CREWAI_ENTERPRISE_URL
|
||||
from crewai_cli.utils import (
|
||||
build_env_with_tool_repository_credentials,
|
||||
ensure_project_id,
|
||||
get_or_create_project_id,
|
||||
get_project_description,
|
||||
get_project_name,
|
||||
get_project_version,
|
||||
@@ -231,7 +231,7 @@ class ToolCommand(BaseCommand, PlusAPIMixin):
|
||||
get_user_id = _require_get_user_id()
|
||||
login_response = self.plus_api_client.login_to_tool_repository(
|
||||
user_identifier=get_user_id(),
|
||||
project_id=ensure_project_id(),
|
||||
project_id=get_or_create_project_id(),
|
||||
)
|
||||
|
||||
if login_response.status_code != 200:
|
||||
|
||||
@@ -31,7 +31,6 @@ __all__ = [
|
||||
"build_env_with_tool_repository_credentials",
|
||||
"copy_template",
|
||||
"enable_prompt_line_editing",
|
||||
"ensure_project_id",
|
||||
"fetch_and_json_env_file",
|
||||
"get_or_create_project_id",
|
||||
"get_project_description",
|
||||
@@ -53,32 +52,6 @@ console = Console()
|
||||
_TEMPLATE_TOKEN_RE = re.compile(r"{{([a-zA-Z_][a-zA-Z0-9_]*)}}")
|
||||
|
||||
|
||||
def ensure_project_id(pyproject_path: str | Path = "pyproject.toml") -> str | None:
|
||||
"""Return the project's id, minting one and telling the user if it was added.
|
||||
|
||||
Safe to call from any CLI command: returns None rather than raising when
|
||||
there is no project, or when ``pyproject.toml`` is not writable.
|
||||
|
||||
Args:
|
||||
pyproject_path: Path to the project's ``pyproject.toml``.
|
||||
|
||||
Returns:
|
||||
The project id, or None if one could not be read or created.
|
||||
"""
|
||||
project_id, created = get_or_create_project_id(pyproject_path)
|
||||
|
||||
if created:
|
||||
console.print(
|
||||
f"Added [bold]project_id[/bold] to {pyproject_path} under "
|
||||
# Escaped: Rich would otherwise parse [tool.crewai] as a style tag.
|
||||
r"[bold]\[tool.crewai][/bold] so this project's runs and traces stay "
|
||||
"linked. Commit it to share that link with your team.",
|
||||
style="dim",
|
||||
)
|
||||
|
||||
return project_id
|
||||
|
||||
|
||||
def is_dmn_mode_enabled() -> bool:
|
||||
"""Return True when the enterprise non-interactive mode is enabled."""
|
||||
value = os.environ.get("CREWAI_DMN")
|
||||
|
||||
@@ -252,7 +252,7 @@ def get_project_id(pyproject_path: str | Path = "pyproject.toml") -> str | None:
|
||||
|
||||
def get_or_create_project_id(
|
||||
pyproject_path: str | Path = "pyproject.toml",
|
||||
) -> tuple[str | None, bool]:
|
||||
) -> str | None:
|
||||
"""Return the project's id, minting and persisting one if absent.
|
||||
|
||||
Writes ``project_id`` into the ``[tool.crewai]`` table so it is committed
|
||||
@@ -267,36 +267,34 @@ def get_or_create_project_id(
|
||||
pyproject_path: Path to the project's ``pyproject.toml``.
|
||||
|
||||
Returns:
|
||||
A ``(project_id, created)`` tuple. ``created`` is True only when an id
|
||||
was minted and written on this call, so callers can tell the user. Both
|
||||
values are ``(None, False)`` when the file is missing or not writable -
|
||||
this is best-effort and never raises.
|
||||
The project id, or None when ``pyproject.toml`` is missing, malformed,
|
||||
or not writable. Best-effort - never raises.
|
||||
"""
|
||||
existing = get_project_id(pyproject_path)
|
||||
if existing:
|
||||
return existing, False
|
||||
return existing
|
||||
|
||||
path = Path(pyproject_path)
|
||||
if not path.is_file():
|
||||
return None, False
|
||||
return None
|
||||
|
||||
try:
|
||||
content = path.read_text(encoding="utf-8")
|
||||
except OSError:
|
||||
return None, False
|
||||
return None
|
||||
|
||||
project_id = str(uuid.uuid4())
|
||||
updated = _insert_project_id(content, project_id)
|
||||
if updated is None:
|
||||
return None, False
|
||||
return None
|
||||
|
||||
try:
|
||||
path.write_text(updated, encoding="utf-8")
|
||||
except OSError:
|
||||
# Read-only checkout, permissions, container FS - not worth failing over.
|
||||
return None, False
|
||||
return None
|
||||
|
||||
return project_id, True
|
||||
return project_id
|
||||
|
||||
|
||||
def _insert_project_id(content: str, project_id: str) -> str | None:
|
||||
|
||||
@@ -38,24 +38,22 @@ def test_returns_none_when_no_id_configured(pyproject):
|
||||
|
||||
|
||||
def test_mints_and_persists_an_id(pyproject):
|
||||
project_id, created = get_or_create_project_id(pyproject)
|
||||
project_id = get_or_create_project_id(pyproject)
|
||||
|
||||
assert created is True
|
||||
assert uuid.UUID(project_id)
|
||||
assert get_project_id(pyproject) == project_id
|
||||
|
||||
|
||||
def test_id_is_stable_across_calls(pyproject):
|
||||
first, created_first = get_or_create_project_id(pyproject)
|
||||
second, created_second = get_or_create_project_id(pyproject)
|
||||
first = get_or_create_project_id(pyproject)
|
||||
second = get_or_create_project_id(pyproject)
|
||||
|
||||
assert created_first is True
|
||||
assert created_second is False, "must not mint a second id"
|
||||
assert first == second
|
||||
assert first == second, "must not mint a second id"
|
||||
assert uuid.UUID(first)
|
||||
|
||||
|
||||
def test_id_lands_in_the_tool_crewai_table(pyproject):
|
||||
project_id, _ = get_or_create_project_id(pyproject)
|
||||
project_id = get_or_create_project_id(pyproject)
|
||||
|
||||
data = parse_toml(pyproject.read_text())
|
||||
assert data["tool"]["crewai"]["project_id"] == project_id
|
||||
@@ -102,9 +100,9 @@ def test_produces_valid_toml_for_varied_layouts(tmp_path, source, label):
|
||||
path = tmp_path / "pyproject.toml"
|
||||
path.write_text(source)
|
||||
|
||||
project_id, created = get_or_create_project_id(path)
|
||||
project_id = get_or_create_project_id(path)
|
||||
|
||||
assert created is True, label
|
||||
assert project_id is not None, label
|
||||
data = parse_toml(path.read_text())
|
||||
assert data["tool"]["crewai"]["project_id"] == project_id, label
|
||||
|
||||
@@ -124,10 +122,7 @@ def test_id_does_not_leak_into_a_neighbouring_table(tmp_path):
|
||||
|
||||
|
||||
def test_missing_file_is_not_an_error(tmp_path):
|
||||
project_id, created = get_or_create_project_id(tmp_path / "nope.toml")
|
||||
|
||||
assert project_id is None
|
||||
assert created is False
|
||||
assert get_or_create_project_id(tmp_path / "nope.toml") is None
|
||||
|
||||
|
||||
def test_malformed_toml_is_not_an_error(tmp_path):
|
||||
@@ -141,12 +136,11 @@ def test_read_only_file_is_not_an_error(pyproject):
|
||||
"""A read-only checkout must not break the command that called this."""
|
||||
pyproject.chmod(0o444)
|
||||
try:
|
||||
project_id, created = get_or_create_project_id(pyproject)
|
||||
project_id = get_or_create_project_id(pyproject)
|
||||
finally:
|
||||
pyproject.chmod(0o644)
|
||||
|
||||
assert project_id is None
|
||||
assert created is False
|
||||
|
||||
|
||||
def test_get_project_id_never_creates_anything(pyproject):
|
||||
@@ -171,7 +165,7 @@ def test_ids_are_unique_across_projects(tmp_path):
|
||||
path = tmp_path / name / "pyproject.toml"
|
||||
path.parent.mkdir()
|
||||
path.write_text(CREW_PYPROJECT)
|
||||
project_id, _ = get_or_create_project_id(path)
|
||||
project_id = get_or_create_project_id(path)
|
||||
ids.add(project_id)
|
||||
|
||||
assert len(ids) == 3
|
||||
|
||||
Reference in New Issue
Block a user