3434 from collections .abc import Iterator
3535
3636 from openqabot .types .pullrequest import PullRequest
37- from openqabot .types .types import Repos
37+ from openqabot .types .types import VERSION_EXTRACT_REGEX , ProductVersion , Repos
3838
3939ARCHS = {"x86_64" , "aarch64" , "ppc64le" , "s390x" }
4040
@@ -58,7 +58,6 @@ class BuildResults:
5858
5959PROJECT_PRODUCT_REGEX = re .compile (r".*:PullRequest:\d+:(.*)" )
6060SCMSYNC_REGEX = re .compile (r".*/products/(.*)#([\d\.]{2,6})$" )
61- VERSION_EXTRACT_REGEX = re .compile (r"[.\d]+" )
6261OBS_PROJECT_SHOW_REGEX = re .compile (r".*/project/show/([^/\s\?\#\)]+)" )
6362# Regex to find all HTTPS URLs, excluding common trailing punctuation like dots or parentheses
6463# that are likely part of the surrounding text (e.g. at the end of a sentence or in Markdown).
@@ -162,10 +161,10 @@ def get_product_name(obs_project: str) -> str:
162161 return product_match .group (1 ) if product_match else ""
163162
164163
165- def get_product_name_and_version_from_scmsync (scmsync_url : str ) -> tuple [str , str ]:
164+ def get_product_name_and_version_from_scmsync (scmsync_url : str ) -> tuple [str , ProductVersion | None ]:
166165 """Extract product name and version from an scmsync URL."""
167166 m = SCMSYNC_REGEX .search (scmsync_url )
168- return (m .group (1 ), m .group (2 )) if m else ("" , "" )
167+ return (m .group (1 ), ProductVersion ( m .group (2 ))) if m else ("" , None )
169168
170169
171170def compute_repo_url_for_job_setting (
@@ -291,14 +290,17 @@ def add_reviews(submission: dict[str, Any], reviews: list[Any]) -> int:
291290 return len (qam_states )
292291
293292
294- def _extract_version (name : str , prefix : str ) -> str :
293+ def _extract_version (name : str , prefix : str ) -> ProductVersion | None :
295294 """Extract version number from a package name string."""
296295 remainder = name .removeprefix (prefix )
297- return next ((part for part in remainder .split ("-" ) if VERSION_EXTRACT_REGEX .search (part )), "" )
296+ return next (
297+ (ProductVersion (part ) for part in remainder .split ("-" ) if VERSION_EXTRACT_REGEX .fullmatch (part )),
298+ None ,
299+ )
298300
299301
300302@lru_cache (maxsize = 512 )
301- def get_product_version_from_repo_listing (project : str , product_name : str , repository : str ) -> str :
303+ def get_product_version_from_repo_listing (project : str , product_name : str , repository : str ) -> ProductVersion | None :
302304 """Determine the product version by inspecting an OBS repository listing."""
303305 project_path = project .replace (":" , ":/" )
304306 url = f"{ config .settings .obs_download_url } /{ project_path } /{ repository } /repo?jsontable"
@@ -309,34 +311,34 @@ def get_product_version_from_repo_listing(project: str, product_name: str, repos
309311 data = r .json ()["data" ]
310312 except requests .exceptions .HTTPError as e :
311313 log .warning ("Repo ignored: Could not query repository '%s' (%s->%s): %s" , repository , product_name , project , e )
312- return ""
314+ return None
313315 # Catching both because requests' JSONDecodeError might not inherit from json's
314316 except (json .JSONDecodeError , requests .exceptions .JSONDecodeError ) as e :
315317 log .info ("Invalid JSON document at '%s', ignoring: %s" , url , e )
316- return ""
318+ return None
317319 except requests .exceptions .RequestException as e :
318320 log .warning ("Product version unresolved: Could not read from '%s': %s" , url , e )
319- return ""
321+ return None
320322 versions = (_extract_version (entry ["name" ], start ) for entry in data if entry ["name" ].startswith (start ))
321- return next ((v for v in versions if len ( v ) > 0 ), "" )
323+ return next ((v for v in versions if v is not None ), None )
322324
323325
324- def _get_product_version (res : etree ._Element , project : str , product_name : str ) -> str :
326+ def _get_product_version (res : etree ._Element , project : str , product_name : str ) -> ProductVersion | None :
325327 """Extract product version from scmsync element or repository listing."""
326328 # read product version from scmsync element if possible, e.g. 15.99
327329 product_version = next (
328330 (
329331 pv
330- for _ , pv in (get_product_name_and_version_from_scmsync (e .text ) for e in res .findall ("scmsync" ))
331- if len ( pv ) > 0
332+ for _ , pv in (get_product_name_and_version_from_scmsync (e .text ) for e in res .findall ("scmsync" ) if e . text )
333+ if pv is not None
332334 ),
333- "" ,
335+ None ,
334336 )
335337
336338 # read product version from directory listing if the project is for a concrete product
337339 if (
338- len ( product_name ) != 0
339- and len ( product_version ) == 0
340+ product_name
341+ and product_version is None
340342 and ("all" in config .settings .obs_products_set or product_name in config .settings .obs_products_set )
341343 ):
342344 product_version = get_product_version_from_repo_listing (project , product_name , res .get ("repository" ))
@@ -359,9 +361,9 @@ def add_channel_for_build_result(
359361 product_version = _get_product_version (res , project , product_name )
360362
361363 # append product version to channel if known; otherwise skip channel if this is for a concrete product
362- if len ( product_version ) > 0 :
364+ if product_version is not None :
363365 channel = f"{ channel } #{ product_version } "
364- elif len ( product_name ) > 0 :
366+ elif product_name :
365367 log .debug ("Channel skipped: Product version for build result %s:%s could not be determined" , project , arch )
366368 return channel
367369
0 commit comments