From 70fa6e3ad2cd0337a53985690c86d8d3ce298bb2 Mon Sep 17 00:00:00 2001 From: Anton Bobrov Date: Tue, 2 Jun 2026 15:53:35 +0200 Subject: [PATCH 1/3] Fix applicability check for non-existent z-stream branches --- ymir/agents/tasks.py | 30 ++++++++++++++++++++------- ymir/agents/triage_agent.py | 35 ++++++++++++++++++++++++-------- ymir/tools/privileged/distgit.py | 5 +---- 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/ymir/agents/tasks.py b/ymir/agents/tasks.py index c727b8bf3..826efc6bc 100644 --- a/ymir/agents/tasks.py +++ b/ymir/agents/tasks.py @@ -457,6 +457,7 @@ async def clone_and_prep_sources( dist_git_branch: str, available_tools: list[Tool], jira_issue: str, + ref: str | None = None, ) -> tuple[Path, Path, bool]: """ Clone dist-git repo and run centpkg/rhpkg sources + prep. @@ -466,6 +467,10 @@ async def clone_and_prep_sources( Falls back to manual archive extraction if prep fails (e.g. missing language-specific RPM macros). When using the fallback, downstream patches are NOT applied — the source is pristine upstream. + + When *ref* is provided (a commit SHA), the repo is cloned with all + refs and that specific commit is checked out. This is used when the + target branch does not exist yet but we know the base commit from Koji. """ working_dir = Path(os.environ["GIT_REPO_BASEPATH"]) / "applicability" / jira_issue working_dir.mkdir(parents=True, exist_ok=True) @@ -475,13 +480,24 @@ async def clone_and_prep_sources( namespace = "centos-stream" if is_cs_branch(dist_git_branch) else "rhel" repository = f"https://gitlab.com/redhat/{namespace}/rpms/{package}" - await run_tool( - "clone_repository", - repository=repository, - branch=dist_git_branch, - clone_path=str(local_clone), - available_tools=available_tools, - ) + if ref: + await run_tool( + "clone_repository", + repository=repository, + clone_path=str(local_clone), + available_tools=available_tools, + ) + exit_code, _, stderr = await run_subprocess(["git", "checkout", ref], cwd=local_clone) + if exit_code != 0: + raise RuntimeError(f"Failed to checkout ref {ref}: {stderr}") + else: + await run_tool( + "clone_repository", + repository=repository, + branch=dist_git_branch, + clone_path=str(local_clone), + available_tools=available_tools, + ) await run_tool( "download_sources", diff --git a/ymir/agents/triage_agent.py b/ymir/agents/triage_agent.py index 701b511ab..8e12d387b 100644 --- a/ymir/agents/triage_agent.py +++ b/ymir/agents/triage_agent.py @@ -59,6 +59,7 @@ TriageOutputSchema as OutputSchema, ) from ymir.common.version_utils import is_older_zstream, normalize_fix_version, parse_rhel_version +from ymir.common.utils import get_latest_candidate_build from ymir.tools.unprivileged.commands import RunShellCommandTool ## UpstreamSearchTool is currently unmaintained and disabled. @@ -961,10 +962,12 @@ async def check_cve_applicability(state): patch_urls = getattr(data, "patch_urls", None) or [] - # For z-stream branches, check if the branch actually exists; - # fall back to CentOS Stream for source analysis since we only - # need to read the source, not push to the branch. + # For z-stream branches, check if the branch actually exists. + # For older z-streams whose branch doesn't exist yet, resolve + # the base commit from Koji so we analyze the right source + # (not CentOS Stream, which may already contain the fix). clone_branch = state.target_branch + base_ref = None parsed = parse_rhel_version(state.target_branch) if parsed: major_version = parsed[0] @@ -975,11 +978,26 @@ async def check_cve_applicability(state): package=package, ) if state.target_branch not in available_branches: - clone_branch = f"c{major_version}s" - logger.info( - f"Branch {state.target_branch} not found for {package}, " - f"using {clone_branch} for applicability analysis" - ) + if await is_older_zstream(state.target_branch): + try: + _, base_ref = await get_latest_candidate_build(package, state.target_branch) + logger.info( + f"Branch {state.target_branch} not found for {package}, " + f"using base ref {base_ref} for applicability analysis" + ) + except Exception as e: + logger.warning( + f"Could not resolve base ref for {state.target_branch}: {e} — " + f"skipping applicability check" + ) + state.applicability_check_skipped = True + return "comment_in_jira" + else: + clone_branch = f"c{major_version}s" + logger.info( + f"Branch {state.target_branch} not found for {package}, " + f"using {clone_branch} for applicability analysis" + ) except Exception as e: logger.warning(f"Failed to check branches for {package}: {e}") @@ -989,6 +1007,7 @@ async def check_cve_applicability(state): dist_git_branch=clone_branch, available_tools=gateway_tools, jira_issue=state.jira_issue, + ref=base_ref, ) except Exception as e: logger.warning(f"Could not prep sources for applicability check: {e}") diff --git a/ymir/tools/privileged/distgit.py b/ymir/tools/privileged/distgit.py index d18206519..f20018b02 100644 --- a/ymir/tools/privileged/distgit.py +++ b/ymir/tools/privileged/distgit.py @@ -85,12 +85,9 @@ async def _run( branch, ) else: - # ref is a commit SHA from the Koji build source URL (git+https://...#). - # It may point to a commit on an older Z-stream branch (via tag inheritance) - # which is expected — we're branching from the last known good build. + _, ref = await get_latest_candidate_build(package, branch) # The push can fail transiently due to bastion network issues; the beeai # framework will retry the whole tool call in that case. - _, ref = await get_latest_candidate_build(package, branch) push_infos = await asyncio.to_thread( repo.remotes.origin.push, f"{ref}:refs/heads/{branch}" ) From 80f55280aad8c4cf0f5bd08d69c4ff45eb14ed44 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 14:29:17 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ymir/agents/triage_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ymir/agents/triage_agent.py b/ymir/agents/triage_agent.py index 8e12d387b..6a687c7a7 100644 --- a/ymir/agents/triage_agent.py +++ b/ymir/agents/triage_agent.py @@ -58,8 +58,8 @@ from ymir.common.models import ( TriageOutputSchema as OutputSchema, ) -from ymir.common.version_utils import is_older_zstream, normalize_fix_version, parse_rhel_version from ymir.common.utils import get_latest_candidate_build +from ymir.common.version_utils import is_older_zstream, normalize_fix_version, parse_rhel_version from ymir.tools.unprivileged.commands import RunShellCommandTool ## UpstreamSearchTool is currently unmaintained and disabled. From cea55be0d2ec5692e412297fe228b70dc7c7fbaa Mon Sep 17 00:00:00 2001 From: Anton Bobrov <8211973+antbob@users.noreply.github.com> Date: Tue, 2 Jun 2026 16:44:35 +0200 Subject: [PATCH 3/3] Update ymir/agents/triage_agent.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- ymir/agents/triage_agent.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ymir/agents/triage_agent.py b/ymir/agents/triage_agent.py index 6a687c7a7..8c2846bf4 100644 --- a/ymir/agents/triage_agent.py +++ b/ymir/agents/triage_agent.py @@ -991,6 +991,8 @@ async def check_cve_applicability(state): f"skipping applicability check" ) state.applicability_check_skipped = True + if state.triage_result.resolution == Resolution.REBUILD: + return "consolidate_rebuild_siblings" return "comment_in_jira" else: clone_branch = f"c{major_version}s"