-
Notifications
You must be signed in to change notification settings - Fork 6.3k
feat: add version check & integrate update notices #4350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
9eb18f9
feat: add version check & integrate update notices
greysonlalonde d34c3fe
Merge branch 'main' into gl/feat/version-update-message
greysonlalonde e0cc64d
chore: update test assumption
greysonlalonde 331990b
Merge branch 'gl/feat/version-update-message' of https://github.com/c…
greysonlalonde 61a5e65
Merge branch 'main' into gl/feat/version-update-message
greysonlalonde File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,107 @@ | ||
| """Version utilities for CrewAI CLI.""" | ||
|
|
||
| from collections.abc import Mapping | ||
| from datetime import datetime, timedelta | ||
| from functools import lru_cache | ||
| import importlib.metadata | ||
| import json | ||
| from pathlib import Path | ||
| from typing import Any, cast | ||
| from urllib import request | ||
| from urllib.error import URLError | ||
|
|
||
| import appdirs | ||
| from packaging.version import InvalidVersion, parse | ||
|
|
||
|
|
||
| @lru_cache(maxsize=1) | ||
| def _get_cache_file() -> Path: | ||
| """Get the path to the version cache file. | ||
|
|
||
| Cached to avoid repeated filesystem operations. | ||
| """ | ||
| cache_dir = Path(appdirs.user_cache_dir("crewai")) | ||
| cache_dir.mkdir(parents=True, exist_ok=True) | ||
| return cache_dir / "version_cache.json" | ||
|
|
||
|
|
||
| def get_crewai_version() -> str: | ||
| """Get the version number of CrewAI running the CLI""" | ||
| """Get the version number of CrewAI running the CLI.""" | ||
| return importlib.metadata.version("crewai") | ||
|
|
||
|
|
||
| def _is_cache_valid(cache_data: Mapping[str, Any]) -> bool: | ||
| """Check if the cache is still valid, less than 24 hours old.""" | ||
| if "timestamp" not in cache_data: | ||
| return False | ||
|
|
||
| try: | ||
| cache_time = datetime.fromisoformat(str(cache_data["timestamp"])) | ||
| return datetime.now() - cache_time < timedelta(hours=24) | ||
| except (ValueError, TypeError): | ||
| return False | ||
|
|
||
|
|
||
| def get_latest_version_from_pypi(timeout: int = 2) -> str | None: | ||
| """Get the latest version of CrewAI from PyPI. | ||
|
|
||
| Args: | ||
| timeout: Request timeout in seconds. | ||
|
|
||
| Returns: | ||
| Latest version string or None if unable to fetch. | ||
| """ | ||
| cache_file = _get_cache_file() | ||
| if cache_file.exists(): | ||
| try: | ||
| cache_data = json.loads(cache_file.read_text()) | ||
| if _is_cache_valid(cache_data): | ||
| return cast(str | None, cache_data.get("version")) | ||
| except (json.JSONDecodeError, OSError): | ||
| pass | ||
|
|
||
| try: | ||
| with request.urlopen( | ||
| "https://pypi.org/pypi/crewai/json", timeout=timeout | ||
| ) as response: | ||
| data = json.loads(response.read()) | ||
| latest_version = cast(str, data["info"]["version"]) | ||
|
|
||
| cache_data = { | ||
| "version": latest_version, | ||
| "timestamp": datetime.now().isoformat(), | ||
| } | ||
| cache_file.write_text(json.dumps(cache_data)) | ||
|
|
||
| return latest_version | ||
| except (URLError, json.JSONDecodeError, KeyError, OSError): | ||
| return None | ||
|
|
||
|
|
||
| def check_version() -> tuple[str, str | None]: | ||
| """Check current and latest versions. | ||
|
|
||
| Returns: | ||
| Tuple of (current_version, latest_version). | ||
| latest_version is None if unable to fetch from PyPI. | ||
| """ | ||
| current = get_crewai_version() | ||
| latest = get_latest_version_from_pypi() | ||
| return current, latest | ||
|
|
||
|
|
||
| def is_newer_version_available() -> tuple[bool, str, str | None]: | ||
| """Check if a newer version is available. | ||
|
|
||
| Returns: | ||
| Tuple of (is_newer, current_version, latest_version). | ||
| """ | ||
| current, latest = check_version() | ||
|
|
||
| if latest is None: | ||
| return False, current, None | ||
|
|
||
| try: | ||
| return parse(latest) > parse(current), current, latest | ||
| except (InvalidVersion, TypeError): | ||
| return False, current, latest | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.