mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-05-01 07:13:00 +00:00
feat: improve enterprise release resilience and UX
- Add --skip-to-enterprise flag to resume just Phase 3 after a failure - Add --prerelease=allow to uv sync for alpha/beta/rc versions - Retry uv sync up to 10 times to handle PyPI CDN propagation delay - Update pyproject.toml [project] version field (fixes apps/api version) - Print PR URL after creating enterprise bump PR
This commit is contained in:
@@ -156,6 +156,33 @@ def update_version_in_file(file_path: Path, new_version: str) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def update_pyproject_version(file_path: Path, new_version: str) -> bool:
|
||||||
|
"""Update the [project] version field in a pyproject.toml file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path: Path to pyproject.toml file.
|
||||||
|
new_version: New version string.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if version was updated, False otherwise.
|
||||||
|
"""
|
||||||
|
if not file_path.exists():
|
||||||
|
return False
|
||||||
|
|
||||||
|
content = file_path.read_text()
|
||||||
|
new_content = re.sub(
|
||||||
|
r'^(version\s*=\s*")[^"]+(")',
|
||||||
|
rf"\g<1>{new_version}\2",
|
||||||
|
content,
|
||||||
|
count=1,
|
||||||
|
flags=re.MULTILINE,
|
||||||
|
)
|
||||||
|
if new_content != content:
|
||||||
|
file_path.write_text(new_content)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
_DEFAULT_WORKSPACE_PACKAGES: Final[list[str]] = [
|
_DEFAULT_WORKSPACE_PACKAGES: Final[list[str]] = [
|
||||||
"crewai",
|
"crewai",
|
||||||
"crewai-tools",
|
"crewai-tools",
|
||||||
@@ -1141,6 +1168,11 @@ def _release_enterprise(version: str, is_prerelease: bool, dry_run: bool) -> Non
|
|||||||
|
|
||||||
pyproject = pkg_dir / "pyproject.toml"
|
pyproject = pkg_dir / "pyproject.toml"
|
||||||
if pyproject.exists():
|
if pyproject.exists():
|
||||||
|
if update_pyproject_version(pyproject, version):
|
||||||
|
console.print(
|
||||||
|
f"[green]✓[/green] Updated version in: "
|
||||||
|
f"{pyproject.relative_to(repo_dir)}"
|
||||||
|
)
|
||||||
if update_pyproject_dependencies(
|
if update_pyproject_dependencies(
|
||||||
pyproject, version, extra_packages=list(_ENTERPRISE_EXTRA_PACKAGES)
|
pyproject, version, extra_packages=list(_ENTERPRISE_EXTRA_PACKAGES)
|
||||||
):
|
):
|
||||||
@@ -1159,19 +1191,35 @@ def _release_enterprise(version: str, is_prerelease: bool, dry_run: bool) -> Non
|
|||||||
_wait_for_pypi("crewai", version)
|
_wait_for_pypi("crewai", version)
|
||||||
|
|
||||||
console.print("\nSyncing workspace...")
|
console.print("\nSyncing workspace...")
|
||||||
run_command(
|
sync_cmd = [
|
||||||
[
|
"uv",
|
||||||
"uv",
|
"sync",
|
||||||
"sync",
|
"--refresh-package",
|
||||||
"--refresh-package",
|
"crewai",
|
||||||
"crewai",
|
"--refresh-package",
|
||||||
"--refresh-package",
|
"crewai-tools",
|
||||||
"crewai-tools",
|
"--refresh-package",
|
||||||
"--refresh-package",
|
"crewai-files",
|
||||||
"crewai-files",
|
]
|
||||||
],
|
if is_prerelease:
|
||||||
cwd=repo_dir,
|
sync_cmd.append("--prerelease=allow")
|
||||||
)
|
|
||||||
|
max_retries = 10
|
||||||
|
for attempt in range(1, max_retries + 1):
|
||||||
|
try:
|
||||||
|
run_command(sync_cmd, cwd=repo_dir)
|
||||||
|
break
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
if attempt == max_retries:
|
||||||
|
console.print(
|
||||||
|
f"[red]Error:[/red] uv sync failed after {max_retries} attempts"
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
console.print(
|
||||||
|
f"[yellow]uv sync failed (attempt {attempt}/{max_retries}),"
|
||||||
|
f" retrying in {_PYPI_POLL_INTERVAL}s...[/yellow]"
|
||||||
|
)
|
||||||
|
time.sleep(_PYPI_POLL_INTERVAL)
|
||||||
console.print("[green]✓[/green] Workspace synced")
|
console.print("[green]✓[/green] Workspace synced")
|
||||||
|
|
||||||
# --- branch, commit, push, PR ---
|
# --- branch, commit, push, PR ---
|
||||||
@@ -1187,7 +1235,7 @@ def _release_enterprise(version: str, is_prerelease: bool, dry_run: bool) -> Non
|
|||||||
run_command(["git", "push", "-u", "origin", branch_name], cwd=repo_dir)
|
run_command(["git", "push", "-u", "origin", branch_name], cwd=repo_dir)
|
||||||
console.print("[green]✓[/green] Branch pushed")
|
console.print("[green]✓[/green] Branch pushed")
|
||||||
|
|
||||||
run_command(
|
pr_url = run_command(
|
||||||
[
|
[
|
||||||
"gh",
|
"gh",
|
||||||
"pr",
|
"pr",
|
||||||
@@ -1204,6 +1252,7 @@ def _release_enterprise(version: str, is_prerelease: bool, dry_run: bool) -> Non
|
|||||||
cwd=repo_dir,
|
cwd=repo_dir,
|
||||||
)
|
)
|
||||||
console.print("[green]✓[/green] Enterprise bump PR created")
|
console.print("[green]✓[/green] Enterprise bump PR created")
|
||||||
|
console.print(f"[cyan]PR URL:[/cyan] {pr_url}")
|
||||||
|
|
||||||
_poll_pr_until_merged(branch_name, "enterprise bump PR", repo=enterprise_repo)
|
_poll_pr_until_merged(branch_name, "enterprise bump PR", repo=enterprise_repo)
|
||||||
|
|
||||||
@@ -1570,7 +1619,18 @@ def tag(dry_run: bool, no_edit: bool) -> None:
|
|||||||
is_flag=True,
|
is_flag=True,
|
||||||
help="Skip the enterprise release phase",
|
help="Skip the enterprise release phase",
|
||||||
)
|
)
|
||||||
def release(version: str, dry_run: bool, no_edit: bool, skip_enterprise: bool) -> None:
|
@click.option(
|
||||||
|
"--skip-to-enterprise",
|
||||||
|
is_flag=True,
|
||||||
|
help="Skip phases 1 & 2, run only the enterprise release phase",
|
||||||
|
)
|
||||||
|
def release(
|
||||||
|
version: str,
|
||||||
|
dry_run: bool,
|
||||||
|
no_edit: bool,
|
||||||
|
skip_enterprise: bool,
|
||||||
|
skip_to_enterprise: bool,
|
||||||
|
) -> None:
|
||||||
"""Full release: bump versions, tag, and publish a GitHub release.
|
"""Full release: bump versions, tag, and publish a GitHub release.
|
||||||
|
|
||||||
Combines bump and tag into a single workflow. Creates a version bump PR,
|
Combines bump and tag into a single workflow. Creates a version bump PR,
|
||||||
@@ -1583,11 +1643,19 @@ def release(version: str, dry_run: bool, no_edit: bool, skip_enterprise: bool) -
|
|||||||
dry_run: Show what would be done without making changes.
|
dry_run: Show what would be done without making changes.
|
||||||
no_edit: Skip editing release notes.
|
no_edit: Skip editing release notes.
|
||||||
skip_enterprise: Skip the enterprise release phase.
|
skip_enterprise: Skip the enterprise release phase.
|
||||||
|
skip_to_enterprise: Skip phases 1 & 2, run only the enterprise release phase.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
check_gh_installed()
|
check_gh_installed()
|
||||||
|
|
||||||
if not skip_enterprise:
|
if skip_enterprise and skip_to_enterprise:
|
||||||
|
console.print(
|
||||||
|
"[red]Error:[/red] Cannot use both --skip-enterprise "
|
||||||
|
"and --skip-to-enterprise"
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if not skip_enterprise or skip_to_enterprise:
|
||||||
missing: list[str] = []
|
missing: list[str] = []
|
||||||
if not _ENTERPRISE_REPO:
|
if not _ENTERPRISE_REPO:
|
||||||
missing.append("ENTERPRISE_REPO")
|
missing.append("ENTERPRISE_REPO")
|
||||||
@@ -1606,6 +1674,15 @@ def release(version: str, dry_run: bool, no_edit: bool, skip_enterprise: bool) -
|
|||||||
cwd = Path.cwd()
|
cwd = Path.cwd()
|
||||||
lib_dir = cwd / "lib"
|
lib_dir = cwd / "lib"
|
||||||
|
|
||||||
|
is_prerelease = _is_prerelease(version)
|
||||||
|
|
||||||
|
if skip_to_enterprise:
|
||||||
|
_release_enterprise(version, is_prerelease, dry_run)
|
||||||
|
console.print(
|
||||||
|
f"\n[green]✓[/green] Enterprise release [bold]{version}[/bold] complete!"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
console.print("Checking git status...")
|
console.print("Checking git status...")
|
||||||
check_git_clean()
|
check_git_clean()
|
||||||
|
|||||||
Reference in New Issue
Block a user