Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion elliott/elliottlib/shipment_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def get_shipment_configs_from_mr(

filename = file_path.split('/')[-1]
parts = filename.replace('.yaml', '').replace('.yml', '')
kind = next((k for k in kinds if k in parts), None)
kind = _get_shipment_config_kind(parts, kinds)
if not kind:
continue

Expand All @@ -228,6 +228,34 @@ def get_shipment_configs_from_mr(
return shipment_configs


def _get_shipment_config_kind(filename_stem: str, kinds: Tuple[str, ...]) -> str | None:
"""
Extracts a shipment kind from a filename, preserving an optional RHEL suffix.

New multi-RHEL shipment files use names such as ``image-el9`` and
``microshift-bootc-el10``. The suffix must remain part of the returned key so
that multiple RHEL-specific configs can coexist in one merge request.

Args:
filename_stem: Shipment filename without its YAML extension.
kinds: Base shipment kinds accepted by the caller.
Returns:
The matching base or RHEL-qualified shipment kind, if any.
"""
for kind in sorted(kinds, key=len, reverse=True):
qualified_match = re.search(rf"(?:^|\.)({re.escape(kind)}-el\d+)(?:\.|$)", filename_stem)
if qualified_match:
return qualified_match.group(1)

base_match = re.search(rf"(?:^|\.){re.escape(kind)}(?:\.|$)", filename_stem)
if base_match:
return kind

# Preserve the historical substring matching for unusual legacy filenames such as
# ``rpm-extra.yaml``.
return next((kind for kind in kinds if kind in filename_stem), None)


def get_shipment_config_from_mr(mr_url: str, kind: str) -> ShipmentConfig | None:
"""Fetch a specific shipment config from a merge request URL."""
shipment_configs = get_shipment_configs_from_mr(mr_url)
Expand Down
26 changes: 26 additions & 0 deletions elliott/tests/test_shipment_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,32 @@ def test_get_shipment_configs_by_kind_all_default_kinds(self, mock_gitlab_class)
expected_kinds = {"fbc", "image", "extras", "microshift-bootc", "metadata"}
self.assertEqual(set(result.keys()), expected_kinds)

@patch('artcommonlib.gitlab.gitlab.Gitlab')
@patch.dict(os.environ, {'GITLAB_TOKEN': 'test-token'})
def test_get_shipment_configs_preserves_rhel_qualified_kinds(self, mock_gitlab_class):
"""RHEL-qualified shipment files remain distinct when parsed from one MR."""
mock_gitlab = mock_gitlab_class.return_value
mock_gitlab.projects.get.side_effect = [self.mock_project, self.mock_source_project]

self.mock_project.mergerequests.get.return_value = self.mock_mr
self.mock_mr.source_project_id = "source-project-id"
self.mock_mr.source_branch = "test-branch"

self.mock_diff_info.id = "diff-id"
self.mock_mr.diffs.list.return_value = [self.mock_diff_info]
self.mock_mr.diffs.get.return_value = self.mock_diff
self.mock_diff.diffs = [
{'new_path': 'microshift-bootc-el9.yaml', 'old_path': None},
{'new_path': 'microshift-bootc-el10.yaml', 'old_path': None},
]

self.mock_file_content.decode.return_value.decode.return_value = self.sample_yaml_content
self.mock_source_project.files.get.return_value = self.mock_file_content

result = shipment_utils.get_shipment_configs_from_mr(self.test_mr_url)

self.assertEqual(set(result), {'microshift-bootc-el9', 'microshift-bootc-el10'})


class TestGroupFiltering(unittest.TestCase):
"""Test cases for group-based filtering in get_shipment_configs_from_mr"""
Expand Down
26 changes: 3 additions & 23 deletions pyartcd/pyartcd/pipelines/binary_release_konflux.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from artcommonlib.build_visibility import is_nvr_embargoed
from artcommonlib.constants import SHIPMENT_DATA_URL_TEMPLATE
from artcommonlib.gitlab import GitLabClient
from artcommonlib.release_util import isolate_el_version_in_release
from artcommonlib.util import new_roundtrip_yaml_handler
from elliottlib.shipment_model import (
Environments,
Expand All @@ -32,6 +31,7 @@
from pyartcd.click_validators import validate_release_date
from pyartcd.git import GitRepository
from pyartcd.runtime import Runtime
from pyartcd.shipment_utils import get_release_plan_names, group_nvrs_by_rhel_version

yaml = new_roundtrip_yaml_handler()

Expand Down Expand Up @@ -237,14 +237,7 @@ def _group_nvrs_by_rhel_version(nvrs: List[str]) -> Dict[str, List[str]]:
NVRs without a detectable .el* suffix go under the 'default' key.
Returns an OrderedDict-like dict sorted by key for deterministic ordering.
"""
groups: Dict[str, List[str]] = {}
for nvr in nvrs:
# The release field is the last hyphen-delimited segment of an NVR
release = nvr.rsplit('-', 1)[-1] if '-' in nvr else nvr
el_ver = isolate_el_version_in_release(release)
key = f"el{el_ver}" if el_ver is not None else "default"
groups.setdefault(key, []).append(nvr)
return dict(sorted(groups.items()))
return group_nvrs_by_rhel_version(nvrs)

async def create_snapshot(self, builds: List[str]) -> Optional[Snapshot]:
"""
Expand Down Expand Up @@ -316,21 +309,8 @@ def create_shipment_config(self, snapshot: Snapshot, rhel_suffix: Optional[str]
fbc=False,
)

stage_rpa = "n/a"
prod_rpa = "n/a"
config_path = self.shipment_data_repo._directory / "config.yaml"
if config_path.exists():
with open(config_path, 'r') as f:
shipment_config = stdlib_yaml.safe_load(f) or {}
applications = shipment_config.get("applications", {})
# Try RHEL-versioned key first (e.g. 'oc-mirror-2-0-el9'), then fall back to the
# plain application name for products that don't split by RHEL version.
lookup_key = f"{application}-{rhel_suffix}" if rhel_suffix else application
app_env_config = (applications.get(lookup_key) or applications.get(application) or {}).get(
"environments", {}
)
stage_rpa = app_env_config.get("stage", {}).get("releasePlan", "n/a")
prod_rpa = app_env_config.get("prod", {}).get("releasePlan", "n/a")
stage_rpa, prod_rpa = get_release_plan_names(config_path, application, rhel_suffix)

if stage_rpa == "n/a" or prod_rpa == "n/a":
effective_key = f"{application}-{rhel_suffix}" if rhel_suffix else application
Expand Down
Loading
Loading