# Copyright 2026 European Union
# Author: Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu)
# SPDX-License-Identifier: EUPL-1.2
"""
Release validation checks for MAFw.
This module contains non-git business logic checks used during the release
process: tag parsing and missing-release detection.
"""
from __future__ import annotations
from mafw.devtools import DevtoolsError
from mafw.devtools.git import get_last_stable_tag
from mafw.devtools.release.versioning import (
STABLE_TAG_PATTERN,
classify_version,
parse_version,
)
[docs]
def parse_stable_tag(tag: str) -> tuple[int, int, int]:
"""Parse a stable tag in the form ``vX.Y.Z``.
:param tag: Stable tag value.
:type tag: str
:return: Parsed stable version tuple.
:rtype: tuple[int, int, int]
:raises DevtoolsError: If the tag does not match ``vX.Y.Z``.
"""
match = STABLE_TAG_PATTERN.fullmatch(tag)
if match is None:
raise DevtoolsError(f'Unsupported stable tag format "{tag}".')
return (
int(match.group('major')),
int(match.group('minor')),
int(match.group('micro')),
)
[docs]
def check_missing_release(current_version: str) -> None:
"""Prevent creating a new release when a stable release is already missing a tag.
:param current_version: Current project version.
:type current_version: str
:raises DevtoolsError: If a missing stable release is detected.
"""
current_kind = classify_version(current_version)
if current_kind != 'stable':
return
current_major, current_minor, current_micro, _ = parse_version(current_version)
last_stable_tag = get_last_stable_tag()
if last_stable_tag is None:
return
last_major, last_minor, last_micro = parse_stable_tag(last_stable_tag)
current_tuple = (current_major, current_minor, current_micro)
last_tuple = (last_major, last_minor, last_micro)
if current_tuple > last_tuple:
raise DevtoolsError(
'Missing stable release detected: '
f'last stable tag is {last_stable_tag}, but current version is v{current_version}. '
'Tag the current stable version first, or disable this check with '
'--without-missing-release-check.'
)