diff --git a/src/dstack/_internal/cli/utils/updates.py b/src/dstack/_internal/cli/utils/updates.py index 88542cc4b..418cefb3b 100644 --- a/src/dstack/_internal/cli/utils/updates.py +++ b/src/dstack/_internal/cli/utils/updates.py @@ -57,10 +57,22 @@ def _is_last_check_time_outdated() -> bool: ) +def is_update_available(current_version: str, latest_version: str) -> bool: + """ + Return True if latest_version is newer than current_version. + Pre-releases are only considered if the current version is also a pre-release. + """ + _current_version = pkg_version.parse(str(current_version)) + _latest_version = pkg_version.parse(str(latest_version)) + return _current_version < _latest_version and ( + not _latest_version.is_prerelease or _current_version.is_prerelease + ) + + def _check_version(): latest_version = get_latest_version() if latest_version is not None: - if pkg_version.parse(str(version.__version__)) < pkg_version.parse(latest_version): + if is_update_available(version.__version__, latest_version): console.print(f"A new version of dstack is available: [code]{latest_version}[/]\n") diff --git a/src/tests/_internal/cli/utils/test_updates.py b/src/tests/_internal/cli/utils/test_updates.py new file mode 100644 index 000000000..6e18c9e9b --- /dev/null +++ b/src/tests/_internal/cli/utils/test_updates.py @@ -0,0 +1,24 @@ +import pytest + +from dstack._internal.cli.utils import updates + + +@pytest.mark.parametrize( + "current_version,latest_version,expected", + [ + ("1.0.0", "1.0.1", True), # patch update, both releases + ("1.0.0", "2.0.0", True), # major update, both releases + ("1.0.0", "1.0.0", False), # same version + ("1.1.0", "1.0.9", False), # downgrade + ("1.0.0a1", "1.0.0", True), # pre-release to release (should show update) + ("1.0.0", "1.0.0a1", False), # release to pre-release (should NOT show update) + ("1.0.0b1", "1.0.0b2", True), # beta to beta (should show update) + ("1.0.0rc1", "1.0.0", True), # rc to release (should show update) + ("1.0.0", "1.0.0rc1", False), # release to rc (should NOT show update) + ("1.0.0a1", "1.0.0b1", True), # alpha to beta (should show update) + ("1.0.0b1", "1.0.0rc1", True), # beta to rc (should show update) + ("1.0.0rc1", "1.0.1a1", True), # rc to next alpha (should show update) + ], +) +def test_is_update_available(current_version, latest_version, expected): + assert updates.is_update_available(current_version, latest_version) == expected