diff --git a/packit_service/models.py b/packit_service/models.py index f20f7a3e3..7c831128e 100644 --- a/packit_service/models.py +++ b/packit_service/models.py @@ -2350,6 +2350,20 @@ def get_all_projects(cls) -> Set["GitProjectModel"]: projects = [branch.project for branch in project_event_branches] return set(projects) + @classmethod + def get_first_successful_by_sidetag( + cls, sidetag: str + ) -> Optional["BodhiUpdateTargetModel"]: + with sa_session_transaction() as session: + return ( + session.query(BodhiUpdateTargetModel) + .filter( + BodhiUpdateTargetModel.status == "success", + BodhiUpdateTargetModel.sidetag == sidetag, + ) + .first() + ) + class BodhiUpdateGroupModel(ProjectAndEventsConnector, GroupModel, Base): __tablename__ = "bodhi_update_groups" diff --git a/packit_service/worker/handlers/bodhi.py b/packit_service/worker/handlers/bodhi.py index c9cd45e89..958455ae3 100644 --- a/packit_service/worker/handlers/bodhi.py +++ b/packit_service/worker/handlers/bodhi.py @@ -8,14 +8,12 @@ import logging from datetime import datetime from os import getenv -from typing import List, Tuple, Type, Optional +from typing import Tuple, Type, Optional from celery import Task from packit.config import JobConfig, JobType, PackageConfig, Deployment from packit.exceptions import PackitException -from packit.utils.koji_helper import KojiHelper -from specfile.utils import NEVR from packit_service.config import ServiceConfig from packit_service.constants import ( MSG_RETRIGGER, @@ -28,8 +26,6 @@ PipelineModel, BodhiUpdateTargetModel, KojiBuildTargetModel, - SidetagGroupModel, - SidetagModel, ) from packit_service.worker.checker.abstract import Checker from packit_service.worker.checker.bodhi import ( @@ -46,6 +42,7 @@ IssueCommentGitlabEvent, ) from packit_service.worker.events.koji import KojiBuildEvent +from packit_service.worker.helpers.sidetag import SidetagHelper from packit_service.worker.handlers.abstract import ( TaskName, configured_as, @@ -76,98 +73,11 @@ logger = logging.getLogger(__name__) -class SidetagHelper: - _koji_helper: Optional[KojiHelper] = None - _sidetag_group: Optional[SidetagGroupModel] = None - - def __init__(self, job_config: JobConfig) -> None: - self.job_config = job_config - - @property - def koji_helper(self) -> KojiHelper: - if not self._koji_helper: - self._koji_helper = KojiHelper() - return self._koji_helper - - @property - def sidetag_group(self) -> SidetagGroupModel: - if not self._sidetag_group: - self._sidetag_group = SidetagGroupModel.get_by_name( - self.job_config.sidetag_group - ) - return self._sidetag_group - - def get_builds_in_sidetag(self, dist_git_branch: str) -> Tuple[str, List[str]]: - """ - Gets the latest builds of required packages from a sidetag identified - by the configured sidetag group and the specified dist-git branch. - - Args: - dist_git_branch: dist-git branch. - - Returns: - A tuple where the first element is the name of the sidetag - and the second element is a list of NVRs. - - Raises: - PackitException if a sidetag was not found or if job dependencies - are not satisfied. - """ - sidetag = self.sidetag_group.get_sidetag_by_target(dist_git_branch) - if not sidetag: - raise PackitException( - f"No sidetag found for {self.sidetag_group.name} and {dist_git_branch}" - ) - - builds = self.koji_helper.get_builds_in_tag(sidetag.koji_name) - tagged_packages = {b["package_name"] for b in builds} - - # check if dependencies are satisfied within the sidetag - dependencies = set(self.job_config.dependencies or []) - dependencies.add(self.job_config.downstream_package_name) # include self - if not dependencies <= tagged_packages: - missing = dependencies - tagged_packages - raise PackitException(f"Missing dependencies for Bodhi update: {missing}") - - nvrs = [] - for package in dependencies: - latest_stable_nvr = self.koji_helper.get_latest_stable_nvr( - package, dist_git_branch, include_candidate=True - ) - latest_build_in_sidetag = max( - (b for b in builds if b["package_name"] == package), - key=lambda b: NEVR.from_string(b["nvr"]), - ) - # exclude NVRs that are already in stable or candidate tags - if a build - # has been manually tagged into the sidetag to satisfy dependencies, - # we don't want it in the update - if NEVR.from_string(latest_build_in_sidetag["nvr"]) > NEVR.from_string( - latest_stable_nvr - ): - nvrs.append(latest_build_in_sidetag["nvr"]) - - return sidetag.koji_name, nvrs - - def remove_sidetag(self, koji_name: str) -> None: - """Removes the specified sidetag.""" - self.koji_helper.remove_sidetag(koji_name) - if sidetag := SidetagModel.get_by_koji_name(koji_name): - sidetag.delete() - - class BodhiUpdateHandler( RetriableJobHandler, PackitAPIWithDownstreamMixin, GetKojiBuildData ): topic = "org.fedoraproject.prod.buildsys.build.state.change" - _sidetag_helper: Optional[SidetagHelper] = None - - @property - def sidetag_helper(self) -> SidetagHelper: - if not self._sidetag_helper: - self._sidetag_helper = SidetagHelper(self.job_config) - return self._sidetag_helper - def __init__( self, package_config: PackageConfig, @@ -194,8 +104,21 @@ def run(self) -> TaskResults: errors = {} for target_model in group.grouped_targets: try: + existing_alias = None + if target_model.sidetag: + # get update alias from previous run(s) from the same sidetag (if any) + if model := BodhiUpdateTargetModel.get_first_successful_by_sidetag( + target_model.sidetag + ): + existing_alias = model.alias + logger.debug( - f"Create update for dist-git branch: {target_model.target} " + ( + f"Edit update {existing_alias} " + if existing_alias + else "Create update " + ) + + f"for dist-git branch: {target_model.target} " f"and nvrs: {target_model.koji_nvrs}" + ( f" from sidetag: {target_model.sidetag}." @@ -208,21 +131,13 @@ def run(self) -> TaskResults: update_type="enhancement", koji_builds=target_model.koji_nvrs.split(), # it accepts NVRs, not build IDs sidetag=target_model.sidetag, + alias=existing_alias, ) if not result: # update was already created target_model.set_status("skipped") continue - if target_model.sidetag: - # remove the sidetag now; Bodhi would remove it for us - # when the update hits stable, but we would be blocked - # from creating new updates until that happens - logger.debug(f"Removing sidetag {target_model.sidetag}") - # we need Kerberos ticket to remove a sidetag - self.packit_api.init_kerberos_ticket() - self.sidetag_helper.remove_sidetag(target_model.sidetag) - alias, url = result target_model.set_status("success") target_model.set_alias(alias) @@ -290,17 +205,30 @@ def _get_or_create_bodhi_update_group_model(self) -> BodhiUpdateGroupModel: group = BodhiUpdateGroupModel.create(run_model) for koji_build_data in self: + sidetag = builds = None if self.job_config.sidetag_group: - sidetag, builds = self.sidetag_helper.get_builds_in_sidetag( - koji_build_data.dist_git_branch + sidetag = SidetagHelper.get_sidetag( + self.job_config.sidetag_group, koji_build_data.dist_git_branch + ) + # check if dependencies are satisfied within the sidetag + dependencies = set(self.job_config.dependencies or []) + dependencies.add( + self.job_config.downstream_package_name # include self + ) + if missing_dependencies := sidetag.get_missing_dependencies( + dependencies + ): + raise PackitException( + f"Missing dependencies for Bodhi update: {missing_dependencies}" + ) + builds = " ".join( + str(b) for b in sidetag.get_builds_suitable_for_update(dependencies) ) - else: - sidetag = builds = None BodhiUpdateTargetModel.create( target=koji_build_data.dist_git_branch, - koji_nvrs=koji_build_data.nvr if not builds else " ".join(builds), - sidetag=sidetag, + koji_nvrs=koji_build_data.nvr if not builds else builds, + sidetag=sidetag.koji_name if sidetag else None, status="queued", bodhi_update_group=group, ) diff --git a/packit_service/worker/handlers/distgit.py b/packit_service/worker/handlers/distgit.py index 62391d496..2aa93672a 100644 --- a/packit_service/worker/handlers/distgit.py +++ b/packit_service/worker/handlers/distgit.py @@ -23,7 +23,6 @@ PackitDownloadFailedException, ReleaseSkippedPackitException, ) -from packit.utils.koji_helper import KojiHelper from packit_service import sentry_integration from packit_service.config import PackageConfigGetter, ServiceConfig from packit_service.constants import ( @@ -45,8 +44,6 @@ KojiBuildTargetModel, PipelineModel, KojiBuildGroupModel, - SidetagGroupModel, - SidetagModel, ) from packit_service.service.urls import ( get_propose_downstream_info_url, @@ -90,6 +87,7 @@ RetriableJobHandler, ) from packit_service.worker.handlers.mixin import GetProjectToSyncMixin +from packit_service.worker.helpers.sidetag import SidetagHelper from packit_service.worker.helpers.sync_release.propose_downstream import ( ProposeDownstreamJobHelper, ) @@ -716,8 +714,6 @@ class AbstractDownstreamKojiBuildHandler( topic = "org.fedoraproject.prod.pagure.git.receive" task_name = TaskName.downstream_koji_build - _koji_helper: Optional[KojiHelper] = None - def __init__( self, package_config: PackageConfig, @@ -737,12 +733,6 @@ def __init__( self._packit_api = None self._koji_group_model_id = koji_group_model_id - @property - def koji_helper(self): - if not self._koji_helper: - self._koji_helper = KojiHelper() - return self._koji_helper - @staticmethod def get_checkers() -> Tuple[Type[Checker], ...]: return ( @@ -781,13 +771,6 @@ def get_branches(self) -> List[str]: def run(self) -> TaskResults: errors = {} - if self.job_config.sidetag_group: - sidetag_group = SidetagGroupModel.get_or_create( - self.job_config.sidetag_group - ) - else: - sidetag_group = None - group = self._get_or_create_koji_group_model() for koji_build_model in group.grouped_targets: branch = koji_build_model.target @@ -804,32 +787,22 @@ def run(self) -> TaskResults: koji_build_model.set_status("pending") try: - if sidetag_group: - sidetag = SidetagModel.get_or_create(sidetag_group, branch) - if not sidetag.koji_name or not self.koji_helper.get_tag_info( - sidetag.koji_name + sidetag = None + if self.job_config.sidetag_group: + # we need Kerberos ticket to create a new sidetag + self.packit_api.init_kerberos_ticket() + sidetag = SidetagHelper.get_or_create_sidetag( + self.job_config.sidetag_group, branch + ) + # skip submitting build for a branch if dependencies + # are not satisfied within a sidetag + dependencies = set(self.job_config.dependencies or []) + if missing_dependencies := sidetag.get_missing_dependencies( + dependencies ): - # we need Kerberos ticket to create a new sidetag - self.packit_api.init_kerberos_ticket() - tag_info = self.koji_helper.create_sidetag(branch) - if not tag_info: - raise PackitException( - f"Failed to create sidetag for {branch}" - ) - sidetag.set_koji_name(tag_info["name"]) - else: - sidetag = None - - # skip submitting build for a branch if dependencies - # are not satisfied within a sidetag - if sidetag and self.job_config.dependencies: - builds = self.koji_helper.get_builds_in_tag(sidetag.koji_name) - tagged_packages = {b["package_name"] for b in builds} - if not set(self.job_config.dependencies) <= tagged_packages: - missing = set(self.job_config.dependencies) - tagged_packages logger.debug( f"Skipping downstream Koji build for branch {branch}, " - f"missing dependencies: {missing}" + f"missing dependencies: {missing_dependencies}" ) koji_build_model.set_status("skipped") continue @@ -1041,44 +1014,16 @@ class TagIntoSidetagHandler( ): task_name = TaskName.tag_into_sidetag - _koji_helper: Optional[KojiHelper] = None - - @property - def koji_helper(self): - if not self._koji_helper: - self._koji_helper = KojiHelper() - return self._koji_helper - @staticmethod def get_checkers() -> Tuple[Type[Checker], ...]: return (PermissionOnDistgit,) def run_for_branch(self, job_config: JobConfig, branch: str) -> None: - sidetag_group = SidetagGroupModel.get_or_create(job_config.sidetag_group) - sidetag = SidetagModel.get_or_create(sidetag_group, branch) # we need Kerberos ticket to tag a build into sidetag # and to create a new sidetag (if needed) self.packit_api.init_kerberos_ticket() - if not sidetag.koji_name or not self.koji_helper.get_tag_info( - sidetag.koji_name - ): - tag_info = self.koji_helper.create_sidetag(branch) - if not tag_info: - logger.error(f"Failed to create sidetag for {branch}") - return - sidetag.set_koji_name(tag_info["name"]) - if not ( - nvr := self.koji_helper.get_latest_stable_nvr( - job_config.downstream_package_name, branch - ) - ): - logger.debug( - "Failed to get the latest stable build " - f"of {job_config.downstream_package_name} for {branch}" - ) - return - logger.debug(f"Tagging {nvr} into {sidetag.koji_name}") - self.koji_helper.tag_build(nvr, sidetag.koji_name) + sidetag = SidetagHelper.get_or_create_sidetag(job_config.sidetag_group, branch) + sidetag.tag_latest_stable_build(job_config.downstream_package_name) def run(self) -> TaskResults: comment = self.data.event_dict.get("comment") diff --git a/packit_service/worker/handlers/koji.py b/packit_service/worker/handlers/koji.py index 4c836e9ce..6bd001d0b 100644 --- a/packit_service/worker/handlers/koji.py +++ b/packit_service/worker/handlers/koji.py @@ -20,7 +20,6 @@ ) from packit.config.package_config import PackageConfig from packit.constants import DISTGIT_INSTANCES -from packit.utils.koji_helper import KojiHelper from packit_service.config import PackageConfigGetter from packit_service.constants import ( KojiBuildState, @@ -34,7 +33,6 @@ AbstractProjectObjectDbType, KojiBuildTargetModel, ProjectEventModel, - SidetagModel, ) from packit_service.service.urls import ( get_koji_build_info_url, @@ -70,6 +68,7 @@ from packit_service.worker.handlers.distgit import DownstreamKojiBuildHandler from packit_service.worker.handlers.bodhi import BodhiUpdateFromSidetagHandler from packit_service.worker.handlers.mixin import GetKojiBuildJobHelperMixin +from packit_service.worker.helpers.sidetag import SidetagHelper from packit_service.worker.helpers.build.koji_build import KojiBuildJobHelper from packit_service.worker.mixin import ConfigFromEventMixin from packit_service.worker.mixin import PackitAPIWithDownstreamMixin @@ -378,25 +377,15 @@ class KojiBuildTagHandler( ): task_name = TaskName.koji_build_tag - _koji_helper: Optional[KojiHelper] = None - - @property - def koji_helper(self): - if not self._koji_helper: - self._koji_helper = KojiHelper() - return self._koji_helper - @staticmethod def get_checkers() -> Tuple[Type[Checker], ...]: return (SidetagExists,) def run(self) -> TaskResults: dg_base_url = getenv("DISTGIT_URL", DISTGIT_INSTANCES["fedpkg"].url) - sidetag = SidetagModel.get_by_koji_name(self.data.tag_name) - sidetag_group = sidetag.sidetag_group.name - builds = self.koji_helper.get_builds_in_tag(self.data.tag_name) - tagged_packages = {b["package_name"] for b in builds} - logger.debug(f"Packages tagged into {self.data.tag_name}: {tagged_packages}") + sidetag = SidetagHelper.get_sidetag_by_koji_name(self.data.tag_name) + tagged_packages = sidetag.get_packages() + logger.debug(f"Packages tagged into {sidetag.koji_name}: {tagged_packages}") packages_to_trigger = set() for package_name in tagged_packages: @@ -417,7 +406,7 @@ def run(self) -> TaskResults: for job in packages_config.get_job_views(): if ( job.type == JobType.koji_build - and job.sidetag_group == sidetag_group + and job.sidetag_group == sidetag.sidetag_group ): if job.dependents: packages_to_trigger.update(job.dependents) @@ -448,11 +437,11 @@ def run(self) -> TaskResults: if ( job.type in (JobType.koji_build, JobType.bodhi_update) and job.trigger == JobConfigTriggerType.koji_build - and job.sidetag_group == sidetag_group + and job.sidetag_group == sidetag.sidetag_group ): event_dict = self.data.get_dict().get("event_dict", {}) event_dict["project_url"] = distgit_project_url - event_dict["git_ref"] = sidetag.target + event_dict["git_ref"] = sidetag.dist_git_branch handler = ( DownstreamKojiBuildHandler if job.type == JobType.koji_build diff --git a/packit_service/worker/handlers/mixin.py b/packit_service/worker/handlers/mixin.py index 49908f4c2..1c7343684 100644 --- a/packit_service/worker/handlers/mixin.py +++ b/packit_service/worker/handlers/mixin.py @@ -16,7 +16,6 @@ ProjectEventModel, CoprBuildTargetModel, SRPMBuildModel, - SidetagModel, BuildStatus, ) from packit_service.worker.events.event import EventData @@ -25,6 +24,7 @@ from packit_service.worker.events.gitlab import MergeRequestCommentGitlabEvent from packit_service.worker.events.pagure import PullRequestCommentPagureEvent from packit_service.worker.handlers.abstract import CeleryTask +from packit_service.worker.helpers.sidetag import SidetagHelper, Sidetag from packit_service.worker.helpers.build.copr_build import CoprBuildJobHelper from packit_service.worker.helpers.build.koji_build import KojiBuildJobHelper from packit_service.worker.helpers.testing_farm import TestingFarmJobHelper @@ -183,7 +183,7 @@ class GetKojiBuildDataFromKojiBuildTagEventMixin( ConfigFromEventMixin, GetKojiBuildData ): _koji_build_tag_event: Optional[KojiBuildTagEvent] = None - _sidetag: Optional[SidetagModel] = None + _sidetag: Optional[Sidetag] = None @property def koji_build_tag_event(self) -> KojiBuildTagEvent: @@ -194,9 +194,9 @@ def koji_build_tag_event(self) -> KojiBuildTagEvent: return self._koji_build_tag_event @property - def sidetag(self) -> Optional[SidetagModel]: + def sidetag(self) -> Optional[Sidetag]: if not self._sidetag: - self._sidetag = SidetagModel.get_by_koji_name( + self._sidetag = SidetagHelper.get_sidetag_by_koji_name( self.koji_build_tag_event.tag_name ) return self._sidetag @@ -211,7 +211,7 @@ def _build_id(self) -> int: @property def _dist_git_branch(self) -> str: - return self.sidetag.target + return self.sidetag.dist_git_branch @property def _state(self) -> KojiBuildState: diff --git a/packit_service/worker/helpers/sidetag.py b/packit_service/worker/helpers/sidetag.py new file mode 100644 index 000000000..976f5cd43 --- /dev/null +++ b/packit_service/worker/helpers/sidetag.py @@ -0,0 +1,156 @@ +# Copyright Contributors to the Packit project. +# SPDX-License-Identifier: MIT + +import logging +from typing import Optional, Set + +from packit.exceptions import PackitException +from packit.utils.koji_helper import KojiHelper +from specfile.utils import NEVR +from packit_service.models import SidetagModel, SidetagGroupModel + +logger = logging.getLogger(__name__) + + +class Sidetag: + def __init__(self, sidetag: SidetagModel, koji_helper: KojiHelper) -> None: + self.sidetag = sidetag + self.koji_helper = koji_helper + + @property + def sidetag_group(self) -> str: + return self.sidetag.sidetag_group.name + + @property + def dist_git_branch(self) -> str: + return self.sidetag.target + + @property + def koji_name(self) -> str: + return self.sidetag.koji_name + + def get_builds(self) -> Set[NEVR]: + builds = self.koji_helper.get_builds_in_tag(self.koji_name) + return {NEVR.from_string(b["nvr"]) for b in builds} + + def get_packages(self) -> Set[str]: + return {b.name for b in self.get_builds()} + + def get_missing_dependencies(self, dependencies: Set[str]) -> Set[str]: + return dependencies - self.get_packages() + + def get_builds_suitable_for_update(self, dependencies: Set[str]) -> Set[NEVR]: + builds = self.get_builds() + result = set() + for package in dependencies: + latest_build = max(b for b in builds if b.name == package) + latest_stable_nvr = self.koji_helper.get_latest_stable_nvr( + package, self.dist_git_branch, include_candidate=True + ) + # exclude NVRs that are already in stable or candidate tags - if a build + # has been manually tagged into the sidetag to satisfy dependencies, + # we don't want it in the update + if not latest_stable_nvr or latest_build > NEVR.from_string( + latest_stable_nvr + ): + result.add(latest_build) + return result + + def tag_build(self, nvr: NEVR) -> None: + logger.debug(f"Tagging {nvr} into {self.koji_name}") + self.koji_helper.tag_build(str(nvr), self.koji_name) + + def tag_latest_stable_build(self, package: str) -> None: + latest_stable_nvr = self.koji_helper.get_latest_stable_nvr( + package, self.dist_git_branch + ) + if not latest_stable_nvr: + logger.debug(f"Failed to find the latest stable build of {package}") + self.tag_build(NEVR.from_string(latest_stable_nvr)) + + +class SidetagHelperMeta(type): + def __init__(cls, *args, **kwargs): + cls._koji_helper: Optional[KojiHelper] = None + + @property + def koji_helper(cls) -> KojiHelper: + if not cls._koji_helper: + cls._koji_helper = KojiHelper() + return cls._koji_helper + + +class SidetagHelper(metaclass=SidetagHelperMeta): + @classmethod + def get_sidetag(cls, sidetag_group: str, dist_git_branch: str) -> Sidetag: + """ + Gets an existing sidetag. + + Args: + sidetag_group: Sidetag group. + dist_git_branch: dist-git branch. + + Returns: + Sidetag object. + + Raises: + PackitException if the sidetag doesn't exist either in the database or in Koji. + """ + group = SidetagGroupModel.get_or_create(sidetag_group) + if not (sidetag := group.get_sidetag_by_target(dist_git_branch)): + raise PackitException( + f"Sidetag for {sidetag_group} and {dist_git_branch} was not found in the database" + ) + if not cls.koji_helper.get_tag_info(sidetag.koji_name): + raise PackitException( + f"Sidetag {sidetag.koji_name} no longer exists in Koji" + ) + return Sidetag(sidetag, cls.koji_helper) + + @classmethod + def get_sidetag_by_koji_name(cls, koji_name: str) -> Sidetag: + """ + Gets an existing sidetag by its Koji name. + + Args: + koji_name: Name of the sidetag in Koji. + + Returns: + Sidetag object. + + Raises: + PackitException if the sidetag doesn't exist either in the database or in Koji. + """ + if not (sidetag := SidetagModel.get_by_koji_name(koji_name)): + raise PackitException(f"Sidetag {koji_name} was not found in the database") + if not cls.koji_helper.get_tag_info(sidetag.koji_name): + raise PackitException(f"Sidetag {koji_name} no longer exists in Koji") + return Sidetag(sidetag, cls.koji_helper) + + @classmethod + def get_or_create_sidetag(cls, sidetag_group: str, dist_git_branch: str) -> Sidetag: + """ + Gets an existing sidetag or creates a new one if it doesn't exist either + in the database or in Koji. + + Kerberos ticket has to be initialized before calling this method in case + the sidetag needs to be created in Koji. + + Args: + sidetag_group: Sidetag group. + dist_git_branch: dist-git branch. + + Returns: + Sidetag object. + + Raises: + PackitException if the sidetag failed to be created in Koji. + """ + group = SidetagGroupModel.get_or_create(sidetag_group) + sidetag = SidetagModel.get_or_create(group, dist_git_branch) + if not sidetag.koji_name or not cls.koji_helper.get_tag_info(sidetag.koji_name): + tag_info = cls.koji_helper.create_sidetag(dist_git_branch) + if not tag_info: + raise PackitException(f"Failed to create sidetag for {dist_git_branch}") + sidetag.set_koji_name(tag_info["name"]) + return Sidetag(sidetag, cls.koji_helper) diff --git a/tests/integration/test_bodhi_update.py b/tests/integration/test_bodhi_update.py index 38a2aeee3..5035e2ff8 100644 --- a/tests/integration/test_bodhi_update.py +++ b/tests/integration/test_bodhi_update.py @@ -82,6 +82,7 @@ def test_bodhi_update_for_unknown_koji_build(koji_build_completed_old_format): update_type="enhancement", koji_builds=["packit-0.43.0-1.fc36"], sidetag=None, + alias=None, ).and_return(("alias", "url")) # Database structure @@ -180,6 +181,7 @@ def test_bodhi_update_for_unknown_koji_build_failed(koji_build_completed_old_for update_type="enhancement", koji_builds=["packit-0.43.0-1.fc36"], sidetag=None, + alias=None, ).and_raise(PackitException, "Failed to create an update") pagure_project_mock.should_receive("get_issue_list").times(0) @@ -281,6 +283,7 @@ def test_bodhi_update_for_unknown_koji_build_failed_issue_created( update_type="enhancement", koji_builds=["packit-0.43.0-1.fc36"], sidetag=None, + alias=None, ).and_raise(PackitException, "Failed to create an update") issue_project_mock = flexmock(GithubProject) @@ -388,6 +391,7 @@ def test_bodhi_update_for_unknown_koji_build_failed_issue_comment( update_type="enhancement", koji_builds=["packit-0.43.0-1.fc36"], sidetag=None, + alias=None, ).and_raise(PackitException, "Failed to create an update") issue_project_mock = flexmock(GithubProject) @@ -503,6 +507,7 @@ def test_bodhi_update_build_not_tagged_yet( update_type="enhancement", koji_builds=["packit-0.43.0-1.fc36"], sidetag=None, + alias=None, ).and_raise(PackitException, "build not tagged") # no reporting should be done as the update is created on the second run @@ -575,6 +580,7 @@ def test_bodhi_update_build_not_tagged_yet( update_type="enhancement", koji_builds=["packit-0.43.0-1.fc36"], sidetag=None, + alias=None, ).and_return( None ) # tagged now @@ -698,6 +704,7 @@ def test_bodhi_update_for_known_koji_build(koji_build_completed_old_format): update_type="enhancement", koji_builds=["packit-0.43.0-1.fc36"], sidetag=None, + alias=None, ).and_return(("alias", "url")) group_model = flexmock( grouped_targets=[ @@ -839,6 +846,7 @@ def test_bodhi_update_fedora_stable_by_default(koji_build_completed_f36): update_type="enhancement", koji_builds=["python-ogr-0.34.0-1.fc36"], sidetag=None, + alias=None, ).once().and_return(("alias", "url")) flexmock(PipelineModel).should_receive("create").and_return(flexmock()) @@ -892,10 +900,12 @@ def test_bodhi_update_fedora_stable_by_default(koji_build_completed_f36): @pytest.mark.parametrize( - "missing_dependency", - [False, True], + "missing_dependency, existing_update", + [(False, None), (False, "FEDORA-2024-abcdef1234"), (True, None)], ) -def test_bodhi_update_from_sidetag(koji_build_tagged, missing_dependency): +def test_bodhi_update_from_sidetag( + koji_build_tagged, missing_dependency, existing_update +): """(Sidetag scenario.)""" build_id = 1234567 @@ -934,6 +944,10 @@ def test_bodhi_update_from_sidetag(koji_build_tagged, missing_dependency): if missing_dependency: builds_in_sidetag.pop() + flexmock(KojiHelper).should_receive("get_tag_info").with_args( + sidetag_name + ).and_return({"name": sidetag_name}) + flexmock(KojiHelper).should_receive("get_builds_in_tag").with_args( sidetag_name ).and_return(builds_in_sidetag) @@ -948,14 +962,17 @@ def test_bodhi_update_from_sidetag(koji_build_tagged, missing_dependency): "f40", include_candidate=True, ).and_return("packit-0.98.0-1.fc40") - flexmock(KojiHelper).should_receive("remove_sidetag").with_args(sidetag_name).times( - 0 if missing_dependency else 1 - ) flexmock(KojiBuildTargetModel).should_receive("get_by_task_id").with_args( task_id=task_id ).and_return(flexmock(target=dg_branch, get_project_event_model=lambda: None)) + flexmock(BodhiUpdateTargetModel).should_receive( + "get_first_successful_by_sidetag" + ).with_args(sidetag_name).and_return( + flexmock(alias=existing_update) if existing_update else None + ) + specfile_packit_yaml = ( "{'specfile_path': 'python-specfile.spec', 'synced_files': []," "'jobs': [{'trigger': 'commit', 'job': 'koji_build', 'sidetag_group': 'test'," @@ -1033,7 +1050,7 @@ def test_bodhi_update_from_sidetag(koji_build_tagged, missing_dependency): flexmock(PackitAPI).should_receive("init_kerberos_ticket").and_return() - def _create_update(dist_git_branch, update_type, koji_builds, sidetag): + def _create_update(dist_git_branch, update_type, koji_builds, sidetag, alias): assert dist_git_branch == dg_branch assert update_type == "enhancement" assert set(koji_builds) == { @@ -1041,6 +1058,7 @@ def _create_update(dist_git_branch, update_type, koji_builds, sidetag): "packit-0.99.0-1.fc40", } assert sidetag == sidetag_name + assert alias == (existing_update if existing_update else None) return "alias", "url" flexmock(PackitAPI).should_receive("create_update").replace_with( diff --git a/tests/integration/test_issue_comment.py b/tests/integration/test_issue_comment.py index 5129f717c..bcf6b0aa5 100644 --- a/tests/integration/test_issue_comment.py +++ b/tests/integration/test_issue_comment.py @@ -419,6 +419,7 @@ def test_issue_comment_retrigger_bodhi_update_handler( update_type="enhancement", koji_builds=["python-teamcity-messages.fc38"], sidetag=None, + alias=None, ).and_return(("alias", "url")) flexmock(KojiHelper).should_receive("get_latest_candidate_build").with_args( "python-teamcity-messages", "f38" @@ -435,6 +436,7 @@ def test_issue_comment_retrigger_bodhi_update_handler( update_type="enhancement", koji_builds=["python-teamcity-messages.fc37"], sidetag=None, + alias=None, ).and_return(("alias", "url")) flexmock(KojiHelper).should_receive("get_latest_candidate_build").with_args( "python-teamcity-messages", "f37" diff --git a/tests/integration/test_listen_to_fedmsg.py b/tests/integration/test_listen_to_fedmsg.py index e18e4758b..ae6406f9d 100644 --- a/tests/integration/test_listen_to_fedmsg.py +++ b/tests/integration/test_listen_to_fedmsg.py @@ -2579,11 +2579,22 @@ def test_koji_build_tag( flexmock(SidetagModel).should_receive("get_by_koji_name").with_args( "f40-build-side-12345" - ).and_return(flexmock(sidetag_group=flexmock(name="test"), target="f40")) + ).and_return( + flexmock( + sidetag_group=flexmock(name="test"), + target="f40", + koji_name="f40-build-side-12345", + ) + ) + flexmock(KojiHelper).should_receive("get_tag_info").with_args( + "f40-build-side-12345" + ).and_return({"name": "f40-build-side-12345"}) flexmock(KojiHelper).should_receive("get_builds_in_tag").with_args( "f40-build-side-12345" - ).and_return([{"package_name": "python-specfile"}]) + ).and_return( + [{"package_name": "python-specfile", "nvr": "python-specfile-0.32.0-1.fc40"}] + ) flexmock(PackageConfigGetter).should_receive( "get_package_config_from_repo" diff --git a/tests/integration/test_pr_comment.py b/tests/integration/test_pr_comment.py index 856e55810..584f84b86 100644 --- a/tests/integration/test_pr_comment.py +++ b/tests/integration/test_pr_comment.py @@ -2575,6 +2575,7 @@ def test_bodhi_update_retrigger_via_dist_git_pr_comment(pagure_pr_comment_added) update_type="enhancement", koji_builds=["123"], sidetag=None, + alias=None, ).once().and_return(("alias", "url")) flexmock(Pushgateway).should_receive("push").times(2).and_return() @@ -3049,10 +3050,10 @@ def test_koji_build_tag_via_dist_git_pr_comment(pagure_pr_comment_added, all_bra ).and_return(sidetag_group) flexmock(SidetagModel).should_receive("get_or_create").with_args( sidetag_group, "f39" - ).and_return(flexmock(koji_name="f39-build-side-12345")) + ).and_return(flexmock(koji_name="f39-build-side-12345", target="f39")) flexmock(SidetagModel).should_receive("get_or_create").with_args( sidetag_group, "f40" - ).and_return(flexmock(koji_name="f40-build-side-12345")) + ).and_return(flexmock(koji_name="f40-build-side-12345", target="f40")) flexmock(KojiHelper).should_receive("get_tag_info").with_args( "f39-build-side-12345" ).and_return(flexmock())