@@ -31,6 +31,14 @@ def _strip_to_first_digit(tag):
3131 return tag [match .start () :] if match else tag
3232
3333
34+ def _parse_version_safely (tag ):
35+ """Parse version string safely, returning None on failure."""
36+ try :
37+ return Version .from_string (_strip_to_first_digit (tag ))
38+ except Exception :
39+ return None
40+
41+
3442class PluginRegistryManager :
3543 """Handles plugin registry operations."""
3644
@@ -83,17 +91,11 @@ def get_registry_plugin_latest_version(self, plugin):
8391 Returns:
8492 Version string (latest tag or empty string)
8593 """
86- versioning_scheme = plugin .versioning_scheme
87- url = plugin .git_url
88-
89- if not versioning_scheme :
90- return ''
91-
92- if not url :
94+ if not plugin .versioning_scheme or not plugin .git_url :
9395 return ''
9496
9597 try :
96- tags = self ._fetch_version_tags (url , versioning_scheme )
98+ tags = self ._fetch_version_tags (plugin . git_url , plugin . versioning_scheme )
9799 return tags [0 ] if tags else ''
98100 except Exception :
99101 return ''
@@ -162,7 +164,9 @@ def _sort_tags(self, tags, versioning_scheme):
162164 if versioning_scheme == 'semver' :
163165 # Use picard.version for proper semver sorting
164166 try :
165- return sorted (tags , key = lambda t : Version .from_string (_strip_to_first_digit (t )), reverse = True )
167+ return sorted (
168+ tags , key = lambda t : _parse_version_safely (t ) or Version .from_string ('0.0.0' ), reverse = True
169+ )
166170 except Exception as e :
167171 log .warning ('Failed to parse semver tags: %s' , e )
168172 return sorted (tags , key = _strip_to_first_digit , reverse = True )
@@ -172,18 +176,19 @@ def _sort_tags(self, tags, versioning_scheme):
172176 else :
173177 # Custom regex: try version parsing, fall back to natural sort
174178 def sort_key (tag ):
179+ version = _parse_version_safely (tag )
180+ if version :
181+ return (0 , version )
182+
183+ # Natural sort: split into text and number parts
175184 stripped = _strip_to_first_digit (tag )
176- try :
177- return (0 , Version .from_string (stripped ))
178- except Exception :
179- # Natural sort: split into text and number parts
180- parts = []
181- for part in re .split (r'(\d+)' , stripped ):
182- if part .isdigit ():
183- parts .append ((0 , int (part )))
184- else :
185- parts .append ((1 , part ))
186- return (1 , parts )
185+ parts = []
186+ for part in re .split (r'(\d+)' , stripped ):
187+ if part .isdigit ():
188+ parts .append ((0 , int (part )))
189+ else :
190+ parts .append ((1 , part ))
191+ return (1 , parts )
187192
188193 return sorted (tags , key = sort_key , reverse = True )
189194
@@ -231,10 +236,13 @@ def _find_newer_version_tag(self, url, current_tag, versioning_scheme):
231236 # Use version parsing for semver/calver, lexicographic for custom regex
232237 if versioning_scheme in ('semver' , 'calver' ):
233238 try :
234- current_version = Version .from_string (_strip_to_first_digit (current_tag ))
239+ current_version = _parse_version_safely (current_tag )
240+ if not current_version :
241+ return None
242+
235243 for tag in tags :
236- tag_version = Version . from_string ( _strip_to_first_digit ( tag ) )
237- if tag_version > current_version :
244+ tag_version = _parse_version_safely ( tag )
245+ if tag_version and tag_version > current_version :
238246 return tag
239247 return None
240248 except Exception :
0 commit comments