From a93fe8c8531f79d9c6da99b0bb6a21deee7a9c49 Mon Sep 17 00:00:00 2001 From: Joep van Delft Date: Mon, 17 Aug 2026 09:49:54 +0200 Subject: [PATCH 1/2] fix(elliott): broaden cutoff filter to accept any post-fix status at timestamp The bug sweep's cutoff filter previously required bugs to have been in VERIFIED state at the assembly basis timestamp. This was too strict: a bug that was MODIFIED or ON_QA at the cutoff (already fixed, just not yet verified) would be incorrectly excluded. Now the historical check accepts MODIFIED, ON_QA, VERIFIED, RELEASE_PENDING, or CLOSED at the cutoff time. The initial search still ensures the bug is currently VERIFIED, so only bugs that are both verified now AND were already fixed at the cutoff are swept. rh-pre-commit.version: 2.4.0 rh-pre-commit.check-secrets: ENABLED --- elliott/elliottlib/cli/find_bugs_sweep_cli.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/elliott/elliottlib/cli/find_bugs_sweep_cli.py b/elliott/elliottlib/cli/find_bugs_sweep_cli.py index 4d57b0b3bc..a8dd03917d 100644 --- a/elliott/elliottlib/cli/find_bugs_sweep_cli.py +++ b/elliott/elliottlib/cli/find_bugs_sweep_cli.py @@ -274,14 +274,19 @@ async def get_bugs_sweep(runtime: Runtime, find_bugs_obj, bug_tracker, filter_at 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"Filtering bugs that were in a post-fix state ({len(bugs)}) at the " f"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} From 7dd8c77c6e30e864ed7a3a020f15ac8557d8b1e9 Mon Sep 17 00:00:00 2001 From: Joep van Delft Date: Mon, 17 Aug 2026 09:50:02 +0200 Subject: [PATCH 2/2] fix(pyartcd): move sweep_bugs to after rebase, before builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously sweep_bugs (MODIFIED → ON_QA) ran in the finally block after builds completed and images were synced. This meant a bug's ON_QA status was set well after basis.time (build completion time), making it unreliable as a signal for the cutoff filter in elliott find-bugs:sweep. Moving the call to right after rebase_images ensures that by the time builds complete (and basis.time is derived), all bugs whose fixes were incorporated in the rebase are already ON_QA. This makes the cutoff filter's check for "ON_QA at timestamp" semantically correct: a bug was ON_QA at basis.time iff its fix was included in the rebase. rh-pre-commit.version: 2.4.0 rh-pre-commit.check-secrets: ENABLED --- elliott/elliottlib/cli/find_bugs_sweep_cli.py | 5 +---- pyartcd/pyartcd/pipelines/ocp4_konflux.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/elliott/elliottlib/cli/find_bugs_sweep_cli.py b/elliott/elliottlib/cli/find_bugs_sweep_cli.py index a8dd03917d..c64675068d 100644 --- a/elliott/elliottlib/cli/find_bugs_sweep_cli.py +++ b/elliott/elliottlib/cli/find_bugs_sweep_cli.py @@ -273,10 +273,7 @@ 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 were in a post-fix state ({len(bugs)}) at 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, 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()