|
|
@@ -71,31 +71,33 @@ def _find_executable(name: str) -> str | None:
|
|
|
def parse_version(version: str) -> tuple:
|
|
|
"""Parse version string into tuple for comparison.
|
|
|
|
|
|
- Returns (major, minor, patch, is_prerelease, prerelease_num)
|
|
|
+ Returns (major, minor, patch, micro, is_prerelease, prerelease_num)
|
|
|
where is_prerelease is 0 for release, 1 for prerelease.
|
|
|
This ensures releases sort higher than prereleases of same version.
|
|
|
|
|
|
Examples:
|
|
|
- "0.1.5" -> (0, 1, 5, 0, 0) # release
|
|
|
- "0.1.5b7" -> (0, 1, 5, 1, 7) # beta 7
|
|
|
- "0.1.5b10" -> (0, 1, 5, 1, 10) # beta 10
|
|
|
+ "0.1.5" -> (0, 1, 5, 0, 0, 0) # release
|
|
|
+ "0.1.5b7" -> (0, 1, 5, 0, 1, 7) # beta 7
|
|
|
+ "0.1.5b10" -> (0, 1, 5, 0, 1, 10) # beta 10
|
|
|
+ "0.1.8.1" -> (0, 1, 8, 1, 0, 0) # patch release
|
|
|
"""
|
|
|
# Remove 'v' prefix if present
|
|
|
version = version.lstrip("v")
|
|
|
|
|
|
- # Match version pattern: major.minor.patch[b|beta|alpha|rc]N
|
|
|
- match = re.match(r"(\d+)\.(\d+)\.(\d+)(?:b|beta|alpha|rc)?(\d+)?", version)
|
|
|
+ # Match version pattern: major.minor.patch[.micro][b|beta|alpha|rc]N
|
|
|
+ match = re.match(r"(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?(?:b|beta|alpha|rc)?(\d+)?", version)
|
|
|
|
|
|
if match:
|
|
|
major = int(match.group(1))
|
|
|
minor = int(match.group(2))
|
|
|
patch = int(match.group(3))
|
|
|
- prerelease_num = int(match.group(4)) if match.group(4) else 0
|
|
|
+ micro = int(match.group(4)) if match.group(4) else 0
|
|
|
+ prerelease_num = int(match.group(5)) if match.group(5) else 0
|
|
|
|
|
|
# Check if this is a prerelease (has b/beta/alpha/rc suffix)
|
|
|
is_prerelease = 1 if re.search(r"[a-zA-Z]", version.split(".")[-1]) else 0
|
|
|
|
|
|
- return (major, minor, patch, is_prerelease, prerelease_num)
|
|
|
+ return (major, minor, patch, micro, is_prerelease, prerelease_num)
|
|
|
|
|
|
# Fallback: try simple split
|
|
|
parts = []
|
|
|
@@ -106,7 +108,7 @@ def parse_version(version: str) -> tuple:
|
|
|
num = "".join(c for c in part if c.isdigit())
|
|
|
parts.append(int(num) if num else 0)
|
|
|
|
|
|
- return tuple(parts) + (0, 0)
|
|
|
+ return tuple(parts) + (0, 0, 0)
|
|
|
|
|
|
|
|
|
def is_newer_version(latest: str, current: str) -> bool:
|
|
|
@@ -121,9 +123,9 @@ def is_newer_version(latest: str, current: str) -> bool:
|
|
|
latest_parsed = parse_version(latest)
|
|
|
current_parsed = parse_version(current)
|
|
|
|
|
|
- # Compare (major, minor, patch) first
|
|
|
- latest_base = latest_parsed[:3]
|
|
|
- current_base = current_parsed[:3]
|
|
|
+ # Compare (major, minor, patch, micro) first
|
|
|
+ latest_base = latest_parsed[:4]
|
|
|
+ current_base = current_parsed[:4]
|
|
|
|
|
|
if latest_base > current_base:
|
|
|
return True
|
|
|
@@ -133,8 +135,8 @@ def is_newer_version(latest: str, current: str) -> bool:
|
|
|
# Same base version - compare prerelease status
|
|
|
# is_prerelease: 0 = release, 1 = prerelease
|
|
|
# Release (0) should be "greater" than prerelease (1)
|
|
|
- latest_is_prerelease = latest_parsed[3] if len(latest_parsed) > 3 else 0
|
|
|
- current_is_prerelease = current_parsed[3] if len(current_parsed) > 3 else 0
|
|
|
+ latest_is_prerelease = latest_parsed[4] if len(latest_parsed) > 4 else 0
|
|
|
+ current_is_prerelease = current_parsed[4] if len(current_parsed) > 4 else 0
|
|
|
|
|
|
if latest_is_prerelease < current_is_prerelease:
|
|
|
# latest is release, current is prerelease -> latest is newer
|
|
|
@@ -145,8 +147,8 @@ def is_newer_version(latest: str, current: str) -> bool:
|
|
|
|
|
|
# Both are same type (both release or both prerelease)
|
|
|
# Compare prerelease numbers
|
|
|
- latest_prerelease_num = latest_parsed[4] if len(latest_parsed) > 4 else 0
|
|
|
- current_prerelease_num = current_parsed[4] if len(current_parsed) > 4 else 0
|
|
|
+ latest_prerelease_num = latest_parsed[5] if len(latest_parsed) > 5 else 0
|
|
|
+ current_prerelease_num = current_parsed[5] if len(current_parsed) > 5 else 0
|
|
|
|
|
|
return latest_prerelease_num > current_prerelease_num
|
|
|
|