88import logging
99from datetime import datetime
1010from os import getenv
11- from typing import List , Tuple , Type , Optional
11+ from typing import Tuple , Type , Optional
1212
1313from celery import Task
1414
1515from packit .config import JobConfig , JobType , PackageConfig , Deployment
1616from packit .exceptions import PackitException
17- from packit .utils .koji_helper import KojiHelper
18- from specfile .utils import NEVR
1917from packit_service .config import ServiceConfig
2018from packit_service .constants import (
2119 MSG_RETRIGGER ,
2826 PipelineModel ,
2927 BodhiUpdateTargetModel ,
3028 KojiBuildTargetModel ,
31- SidetagGroupModel ,
32- SidetagModel ,
3329)
3430from packit_service .worker .checker .abstract import Checker
3531from packit_service .worker .checker .bodhi import (
4642 IssueCommentGitlabEvent ,
4743)
4844from packit_service .worker .events .koji import KojiBuildEvent
45+ from packit_service .worker .helpers .sidetag import SidetagHelper
4946from packit_service .worker .handlers .abstract import (
5047 TaskName ,
5148 configured_as ,
7673logger = logging .getLogger (__name__ )
7774
7875
79- class SidetagHelper :
80- _koji_helper : Optional [KojiHelper ] = None
81- _sidetag_group : Optional [SidetagGroupModel ] = None
82-
83- def __init__ (self , job_config : JobConfig ) -> None :
84- self .job_config = job_config
85-
86- @property
87- def koji_helper (self ) -> KojiHelper :
88- if not self ._koji_helper :
89- self ._koji_helper = KojiHelper ()
90- return self ._koji_helper
91-
92- @property
93- def sidetag_group (self ) -> SidetagGroupModel :
94- if not self ._sidetag_group :
95- self ._sidetag_group = SidetagGroupModel .get_by_name (
96- self .job_config .sidetag_group
97- )
98- return self ._sidetag_group
99-
100- def get_builds_in_sidetag (self , dist_git_branch : str ) -> Tuple [str , List [str ]]:
101- """
102- Gets the latest builds of required packages from a sidetag identified
103- by the configured sidetag group and the specified dist-git branch.
104-
105- Args:
106- dist_git_branch: dist-git branch.
107-
108- Returns:
109- A tuple where the first element is the name of the sidetag
110- and the second element is a list of NVRs.
111-
112- Raises:
113- PackitException if a sidetag was not found or if job dependencies
114- are not satisfied.
115- """
116- sidetag = self .sidetag_group .get_sidetag_by_target (dist_git_branch )
117- if not sidetag :
118- raise PackitException (
119- f"No sidetag found for { self .sidetag_group .name } and { dist_git_branch } "
120- )
121-
122- builds = self .koji_helper .get_builds_in_tag (sidetag .koji_name )
123- tagged_packages = {b ["package_name" ] for b in builds }
124-
125- # check if dependencies are satisfied within the sidetag
126- dependencies = set (self .job_config .dependencies or [])
127- dependencies .add (self .job_config .downstream_package_name ) # include self
128- if not dependencies <= tagged_packages :
129- missing = dependencies - tagged_packages
130- raise PackitException (f"Missing dependencies for Bodhi update: { missing } " )
131-
132- nvrs = []
133- for package in dependencies :
134- latest_stable_nvr = self .koji_helper .get_latest_stable_nvr (
135- package , dist_git_branch , include_candidate = True
136- )
137- latest_build_in_sidetag = max (
138- (b for b in builds if b ["package_name" ] == package ),
139- key = lambda b : NEVR .from_string (b ["nvr" ]),
140- )
141- # exclude NVRs that are already in stable or candidate tags - if a build
142- # has been manually tagged into the sidetag to satisfy dependencies,
143- # we don't want it in the update
144- if NEVR .from_string (latest_build_in_sidetag ["nvr" ]) > NEVR .from_string (
145- latest_stable_nvr
146- ):
147- nvrs .append (latest_build_in_sidetag ["nvr" ])
148-
149- return sidetag .koji_name , nvrs
150-
151- def remove_sidetag (self , koji_name : str ) -> None :
152- """Removes the specified sidetag."""
153- self .koji_helper .remove_sidetag (koji_name )
154- if sidetag := SidetagModel .get_by_koji_name (koji_name ):
155- sidetag .delete ()
156-
157-
15876class BodhiUpdateHandler (
15977 RetriableJobHandler , PackitAPIWithDownstreamMixin , GetKojiBuildData
16078):
16179 topic = "org.fedoraproject.prod.buildsys.build.state.change"
16280
163- _sidetag_helper : Optional [SidetagHelper ] = None
164-
165- @property
166- def sidetag_helper (self ) -> SidetagHelper :
167- if not self ._sidetag_helper :
168- self ._sidetag_helper = SidetagHelper (self .job_config )
169- return self ._sidetag_helper
170-
17181 def __init__ (
17282 self ,
17383 package_config : PackageConfig ,
@@ -194,8 +104,21 @@ def run(self) -> TaskResults:
194104 errors = {}
195105 for target_model in group .grouped_targets :
196106 try :
107+ existing_alias = None
108+ if target_model .sidetag :
109+ # get update alias from previous run(s) from the same sidetag (if any)
110+ if model := BodhiUpdateTargetModel .get_first_successful_by_sidetag (
111+ target_model .sidetag
112+ ):
113+ existing_alias = model .alias
114+
197115 logger .debug (
198- f"Create update for dist-git branch: { target_model .target } "
116+ (
117+ f"Edit update { existing_alias } "
118+ if existing_alias
119+ else "Create update "
120+ )
121+ + f"for dist-git branch: { target_model .target } "
199122 f"and nvrs: { target_model .koji_nvrs } "
200123 + (
201124 f" from sidetag: { target_model .sidetag } ."
@@ -208,21 +131,13 @@ def run(self) -> TaskResults:
208131 update_type = "enhancement" ,
209132 koji_builds = target_model .koji_nvrs .split (), # it accepts NVRs, not build IDs
210133 sidetag = target_model .sidetag ,
134+ alias = existing_alias ,
211135 )
212136 if not result :
213137 # update was already created
214138 target_model .set_status ("skipped" )
215139 continue
216140
217- if target_model .sidetag :
218- # remove the sidetag now; Bodhi would remove it for us
219- # when the update hits stable, but we would be blocked
220- # from creating new updates until that happens
221- logger .debug (f"Removing sidetag { target_model .sidetag } " )
222- # we need Kerberos ticket to remove a sidetag
223- self .packit_api .init_kerberos_ticket ()
224- self .sidetag_helper .remove_sidetag (target_model .sidetag )
225-
226141 alias , url = result
227142 target_model .set_status ("success" )
228143 target_model .set_alias (alias )
@@ -290,17 +205,30 @@ def _get_or_create_bodhi_update_group_model(self) -> BodhiUpdateGroupModel:
290205 group = BodhiUpdateGroupModel .create (run_model )
291206
292207 for koji_build_data in self :
208+ sidetag = builds = None
293209 if self .job_config .sidetag_group :
294- sidetag , builds = self .sidetag_helper .get_builds_in_sidetag (
295- koji_build_data .dist_git_branch
210+ sidetag = SidetagHelper .get_sidetag (
211+ self .job_config .sidetag_group , koji_build_data .dist_git_branch
212+ )
213+ # check if dependencies are satisfied within the sidetag
214+ dependencies = set (self .job_config .dependencies or [])
215+ dependencies .add (
216+ self .job_config .downstream_package_name # include self
217+ )
218+ if missing_dependencies := sidetag .get_missing_dependencies (
219+ dependencies
220+ ):
221+ raise PackitException (
222+ f"Missing dependencies for Bodhi update: { missing_dependencies } "
223+ )
224+ builds = " " .join (
225+ str (b ) for b in sidetag .get_builds_suitable_for_update (dependencies )
296226 )
297- else :
298- sidetag = builds = None
299227
300228 BodhiUpdateTargetModel .create (
301229 target = koji_build_data .dist_git_branch ,
302- koji_nvrs = koji_build_data .nvr if not builds else " " . join ( builds ) ,
303- sidetag = sidetag ,
230+ koji_nvrs = koji_build_data .nvr if not builds else builds ,
231+ sidetag = sidetag . koji_name if sidetag else None ,
304232 status = "queued" ,
305233 bodhi_update_group = group ,
306234 )
0 commit comments