diff --git a/elliott/elliottlib/cli/find_bugs_sweep_cli.py b/elliott/elliottlib/cli/find_bugs_sweep_cli.py index 4d57b0b3bc..c64675068d 100644 --- a/elliott/elliottlib/cli/find_bugs_sweep_cli.py +++ b/elliott/elliottlib/cli/find_bugs_sweep_cli.py @@ -273,15 +273,17 @@ async def get_bugs_sweep(runtime: Runtime, find_bugs_obj, bug_tracker, filter_at sweep_cutoff_timestamp = await get_sweep_cutoff_timestamp(runtime) if sweep_cutoff_timestamp: utc_ts = datetime.fromtimestamp(sweep_cutoff_timestamp, tz=timezone.utc) - logger.info( - f"Filtering bugs that have changed ({len(bugs)}) to one of the desired statuses before the " - f"cutoff time {utc_ts}..." - ) + logger.info(f"Filtering bugs that were in a post-fix state ({len(bugs)}) at the cutoff time {utc_ts}...") + # The initial search already ensures bugs are currently VERIFIED (or whatever + # find_bugs_obj.status requires). For the historical cutoff check we use a broader + # set: any status indicating the bug was already fixed at the cutoff time qualifies, + # even if it had not yet been verified. + cutoff_eligible_statuses = {'ON_QA', 'VERIFIED', 'RELEASE_PENDING', 'CLOSED'} qualified_bugs = [] unqualified_bugs = [] for chunk_of_bugs in chunk(bugs, constants.BUG_LOOKUP_CHUNK_SIZE): qualified_bugs_chunk = bug_tracker.filter_bugs_by_cutoff_event( - chunk_of_bugs, find_bugs_obj.status, sweep_cutoff_timestamp, verbose=runtime.debug + chunk_of_bugs, cutoff_eligible_statuses, sweep_cutoff_timestamp, verbose=runtime.debug ) qualified_bugs.extend(qualified_bugs_chunk) not_qualified = {b.id for b in chunk_of_bugs} - {b.id for b in qualified_bugs_chunk} diff --git a/pyartcd/pyartcd/pipelines/ocp4_konflux.py b/pyartcd/pyartcd/pipelines/ocp4_konflux.py index 4d04bf0762..9d2b503414 100644 --- a/pyartcd/pyartcd/pipelines/ocp4_konflux.py +++ b/pyartcd/pyartcd/pipelines/ocp4_konflux.py @@ -621,7 +621,9 @@ async def mirror_streams_to_ci(self): async def sweep_bugs(self): """ - Find MODIFIED bugs for the target-releases, and set them to ON_QA + Find MODIFIED bugs for the target-releases, and set them to ON_QA. + Called after rebase so that ON_QA is set before builds complete, making + the bug status a reliable indicator that the fix was included in the rebase. """ if self.assembly != 'stream': @@ -901,6 +903,12 @@ async def rebase_and_build_images(self): await self.rebase_images(f"v{self.version}.0", self.release) + # Sweep bugs to ON_QA right after rebase: at this point we've pulled all source that + # includes the fixes, so any MODIFIED bug for this release has been incorporated. + # Doing this before build_images ensures ON_QA is set before basis.time (build completion), + # making the cutoff filter in elliott find-bugs:sweep reliable. + await self.sweep_bugs() + # ART-14540: Notify component owners about missing branch protection record_log_path = Path(self.runtime.doozer_working) / "record.log" if record_log_path.exists(): @@ -1068,7 +1076,7 @@ async def _run_pipeline(self): await self.sync_images() if uses_konflux_imagestream_override(self.version): - await self.sweep_bugs() + # sweep_bugs (MODIFIED → ON_QA) now runs in rebase_and_build_images, after rebase. await self.sweep_golang_bugs() if not self.runtime.dry_run: await self.sweep_second_fix_bugs()