schedule only appropriate flavors based on critpath groups
All checks were successful
CI via Tox / checkwiki (pull_request) Successful in 51s
CI via Tox / tox (pull_request) Successful in 2m11s
AI Code Review / ai-review (pull_request_target) Successful in 42s

Signed-off-by: Adam Williamson <awilliam@redhat.com>
This commit is contained in:
Adam Williamson 2026-07-07 17:14:21 -07:00
commit e7df0f5214
2 changed files with 66 additions and 16 deletions

View file

@ -249,7 +249,7 @@ class OpenQAScheduler(object):
self.logger.info(tmpl, ', '.join(flavors), advisory)
self._update_schedule(advisory, version, flavors, force=force, updic=update)
def _jobs_from_flag(self, flag, version, branch, prid):
def _jobs_from_flag(self, flag, version, branch, prid, pkgname):
""""""
if flag.get("comment") == "RPM build succeeded." and flag.get("user", {}).get("name") == "packit":
if not f" {branch} " in flag.get("username", ""):
@ -259,9 +259,19 @@ class OpenQAScheduler(object):
if not task.isdigit():
self.logger.warning("Task ID %s unexpected!", task)
return False
self.logger.info("Running tests on pull request %s", prid)
# discover appropriate flavors
critpath = SESSION.get("https://bodhi.fedoraproject.org/get_critpath_components").json()
groups = [group for group in critpath if pkgname in critpath[group]]
# this is hella janky, but we'll just construct an update
# dict realistic enough we can reuse get_critpath_flavors
fakeupd = {"critpath_groups": " ".join(groups), "release": {"version": version}}
flavors = schedule.get_critpath_flavors(fakeupd) or None
flavtext = "all"
if flavors:
flavtext = ", ".join(flavors)
build = f"PR-{task}-{prid}"
self._update_schedule(build, version, None, force=True, updic=None)
self.logger.info("Running tests for %s flavors on pull request %s", flavtext, prid)
self._update_schedule(build, version, flavors, force=True, updic=None)
overviewurl = f"{self.openqa_baseurl}/tests/overview?"
params = {"distri": "fedora", "build": f"{build}-NOREPORT", "groupid": 2, "version": version}
overviewurl += urlencode(params)
@ -286,6 +296,7 @@ class OpenQAScheduler(object):
self.logger.debug("Not a comment requesting openQA tests")
return
prid = pr.get("project", {}).get("fullname", "") + "#" + str(pr.get("id", ""))
pkgname = pr.get("project", {}).get("name", "")
version, branch = _version_from_branch(body, self.logger)
if not version:
return
@ -293,7 +304,7 @@ class OpenQAScheduler(object):
flags = SESSION.get(flagurl, timeout=60).json().get("flags", [{}])
flags.sort(key=lambda x: int(x.get("date_updated")))
for flag in flags:
ret = self._jobs_from_flag(flag, version, branch, prid)
ret = self._jobs_from_flag(flag, version, branch, prid, pkgname)
if ret:
task, overviewurl = ret
_set_pr_flag(flagurl, "pending", task, overviewurl, self.logger)
@ -317,7 +328,8 @@ class OpenQAScheduler(object):
return
flag = body.get("flag", {})
prid = pr.get("project", {}).get("fullname", "") + "#" + str(pr["id"])
ret = self._jobs_from_flag(flag, version, branch, prid)
pkgname = pr.get("project", {}).get("name", "")
ret = self._jobs_from_flag(flag, version, branch, prid, pkgname)
if ret:
task, overviewurl = ret
flagurl = fullurl.replace("/rpms", "/api/0/rpms") + "/flag"

View file

@ -415,6 +415,7 @@ DGPLZTEST = Message(
"project": {
"full_url": "https://src.fedoraproject.org/rpms/fedfind",
"fullname": "rpms/fedfind",
"name": "fedfind",
}
}
}
@ -444,6 +445,12 @@ GOODFLAG = {
"url": "https://koji.fedoraproject.org/koji/taskinfo?taskID=147313590",
}
# same, but a bit older
OLDGOODFLAG = copy.deepcopy(GOODFLAG)
OLDGOODFLAG["date_updated"] = "1781905000"
OLDGOODFLAG["username"] = "Packit - scratch build - rawhide [abc1234]"
OLDGOODFLAG["url"] = "https://koji.fedoraproject.org/koji/taskinfo?taskID=147313400"
# dist-git PR flag update message indicating a completed scratch build
# with "/openqa test" comment
DGFLAGSCRATCH = Message(
@ -461,6 +468,7 @@ DGFLAGSCRATCH = Message(
"project": {
"full_url": "https://src.fedoraproject.org/rpms/fedfind",
"fullname": "rpms/fedfind",
"name": "fedfind",
}
}
}
@ -545,6 +553,31 @@ STGS = (STGWIKI, STGRDB, STGSCHED)
# we don't include TESTSCHED as its values are hardcoded
TESTS = (TESTWIKI, TESTRDB)
# these are the values MockResponse returns for a flag query and
# any other query (respectively), so tests can update them on the fly
RESPFLAGS = [copy.deepcopy(OLDGOODFLAG), copy.deepcopy(GOODFLAG)]
RESPGROUPS = {}
def reset_resp():
global RESPFLAGS
global RESPGROUPS
RESPFLAGS = [copy.deepcopy(OLDGOODFLAG), copy.deepcopy(GOODFLAG)]
RESPGROUPS = {}
class MockResponse:
"""Mock requests response which returns different content
depending on the requested URL.
"""
def __init__(self, *args, **kwargs):
self.url = args[0]
def json(self):
if "/flag" in self.url:
return {"flags": RESPFLAGS}
else:
return RESPGROUPS
@pytest.mark.usefixtures("ffmock")
class TestConsumers:
@ -665,7 +698,7 @@ class TestConsumers:
#fake_schedule.reset_mock()
@mock.patch('fedora_openqa.consumer.SESSION.get', autospec=True)
@mock.patch('fedora_openqa.consumer.SESSION.get', autospec=True, side_effect=MockResponse)
@mock.patch('fedora_openqa.consumer._set_pr_flag', autospec=True)
@mock.patch('fedfind.helpers.get_current_release', return_value=38, autospec=True)
@mock.patch('fedora_openqa.schedule.jobs_from_update', return_value=[1], autospec=True)
@ -682,23 +715,27 @@ class TestConsumers:
path here is pretty different from all others and requires
different mocks, so it's split out.
"""
reset_resp()
archcount = len(consumer.update_arches)
flag = copy.deepcopy(GOODFLAG)
flag2 = copy.deepcopy(GOODFLAG)
# a bit earlier
flag2["date_updated"] = "1781905000"
flag2["username"] = "Packit - scratch build - rawhide [abc1234]"
flag2["url"] = "https://koji.fedoraproject.org/koji/taskinfo?taskID=147313400"
mockget.return_value.json.return_value = {"flags": [flag2, flag]}
consumer(DGPLZTEST)
# should only have scheduled for *one* of the flags, so only
# archcount jobs
assert mockjfu.call_count == archcount
assert mockjfu.call_args[1]["openqa_hostname"] == oqah
assert mockjfu.call_args[1]["flavors"] == None
assert mockjfu.call_args[0][1] == "39"
mockjfu.reset_mock()
# only return one flag, for easy tweaking
mockget.return_value.json.return_value = {"flags": [flag]}
# test flavor discovery
global RESPGROUPS
RESPGROUPS = {"critical-path-apps": ["fedfind"]}
consumer(DGPLZTEST)
assert mockjfu.call_args[1]["flavors"] == {"kde", "server", "workstation"}
mockjfu.reset_mock()
# test different flag values
# only return one flag - the second - for easy tweaking
global RESPFLAGS
flag = RESPFLAGS[1]
RESPFLAGS = [flag]
# tweak flag attributes to expect no jobs
flag["comment"] = "RPM build failed."
consumer(DGPLZTEST)
@ -727,6 +764,7 @@ class TestConsumers:
assert mockjfu.call_count == 0
@mock.patch('fedora_openqa.consumer.SESSION.get', autospec=True)
@mock.patch('fedora_openqa.consumer._set_pr_flag', autospec=True)
@mock.patch('fedfind.helpers.get_current_release', return_value=38, autospec=True)
@mock.patch('fedora_openqa.schedule.jobs_from_update', return_value=[1], autospec=True)
@ -738,7 +776,7 @@ class TestConsumers:
(TESTSCHED, 'localhost'),
]
)
def test_scheduler_distgit_flag(self, mockjfu, mockgcr, mockspf, consumer, oqah):
def test_scheduler_distgit_flag(self, mockjfu, mockgcr, mockspf, mockget, consumer, oqah):
"""Test OpenQAScheduler with dist-git PR flag updates. The
code path here is pretty different from all others and
requires different mocks, so it's split out.