Skip to content

Commit 62682f7

Browse files
MagicRBMic92
authored andcommitted
Report nix-builds using BuildRequests for early reports
Signed-off-by: magic_rb <[email protected]>
1 parent 69f6990 commit 62682f7

File tree

2 files changed

+101
-50
lines changed

2 files changed

+101
-50
lines changed

buildbot_nix/__init__.py

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -364,28 +364,18 @@ def produceEventForBuildRequestsById(
364364
event: str,
365365
result: None | int,
366366
) -> Generator[Any, object, None]:
367-
buildrequest_ids = list(buildrequest_ids)
368-
369-
def gotBuild(key: int, build: Any) -> None:
370-
if build['buildrequestid'] in buildrequest_ids:
371-
log.info("got build {key} : {build}", key = key, build = build)
372-
# we only care about the first started build
373-
buildrequest_ids.remove(int(build['buildrequestid']))
374-
if not buildrequest_ids:
375-
del self.consumers[build['buildrequestid']]
376-
self.produceEventForBuild(event, build, result)
377-
378367
for buildrequest_id in buildrequest_ids:
379-
builds: Any = yield self.master.data.get(('buildrequests', str(buildrequest_id), 'builds'))
380-
381-
if not builds:
382-
# only subscribe to new builds, if there are no existing builds
383-
self.consumers[buildrequest_id] = yield self.master.mq.startConsuming(
384-
gotBuild, ('builds', None, None)
385-
)
386-
# send starts for any exsting builds
387-
for build in builds:
388-
self.produceEventForBuild(event, build, result)
368+
builds: Any = yield self.master.data.get(('buildrequests', str(buildrequest_id), 'builds'))
369+
370+
# only report with `buildrequets` if there are no builds to report for
371+
if not builds:
372+
buildrequest: Any = yield self.master.data.get(('buildrequests', str(buildrequest_id)))
373+
if result is not None:
374+
buildrequest["results"] = result
375+
self.master.mq.produce(("buildrequests", str(buildrequest['buildrequestid']), event), copy.deepcopy(buildrequest))
376+
else:
377+
for build in builds:
378+
self.produceEventForBuild(event, build, result)
389379

390380
@defer.inlineCallbacks
391381
def produceEventForBuildById(
@@ -415,7 +405,8 @@ def run(self) -> Generator[Any, Any, None]:
415405
who's dependencies have completed successfully. If a job fails, we recursively fail every jobs which
416406
depends on it.
417407
We also run fake builds for failed evaluations so that they nicely show up in the UI and also Forge
418-
reports.
408+
reports. The reporting is based on custom events and logic, see `BuildNixEvalStatusGenerator` for
409+
the receiving side.
419410
"""
420411
if self.combine_builds:
421412
self.produceEventForBuildById("started-nix-build", self.build.buildid, None)
@@ -535,9 +526,6 @@ def run(self) -> Generator[Any, Any, None]:
535526
scheduler_log.addStdout(
536527
f"Found finished build {job.attr}, result {util.Results[result].upper()}\n"
537528
)
538-
log.info(
539-
f"Found finished build {job.attr}, result {util.Results[result].upper()}, brids: {brids}"
540-
)
541529
if not self.combine_builds:
542530
self.produceEventForBuildRequestsById(brids.values(), "finished-nix-build", result)
543531

buildbot_nix/nix_status_generator.py

Lines changed: 88 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99
from buildbot.process.build import Build
1010
from buildbot.reporters.utils import getDetailsForBuild
1111
from datetime import datetime, timezone
12+
from buildbot.reporters import utils
13+
from buildbot.process.buildrequest import BuildRequest
14+
from buildbot.process.properties import Properties
15+
from buildbot.process.results import CANCELLED
16+
from twisted.logger import Logger
17+
18+
log = Logger()
1219

1320
@implementer(IReportGenerator)
1421
class BuildNixEvalStatusGenerator(BuildStatusGeneratorMixin):
@@ -17,6 +24,8 @@ class BuildNixEvalStatusGenerator(BuildStatusGeneratorMixin):
1724
('builds', None, 'finished-nix-eval'),
1825
('builds', None, 'started-nix-build'),
1926
('builds', None, 'finished-nix-build'),
27+
('buildrequests', None, 'started-nix-build'),
28+
('buildrequests', None, 'canceled-nix-build'),
2029
]
2130

2231
compare_attrs = ['start_formatter', 'end_formatter']
@@ -43,34 +52,88 @@ def __init__(
4352
if self.end_formatter is None:
4453
self.end_formatter = MessageFormatterRenderable('Build done.')
4554

55+
# TODO: copy pasted from buildbot, make it static upstream and reuse
56+
@staticmethod
4657
@defer.inlineCallbacks
47-
def generate(self, master: BuildMaster, reporter: ReporterBase, key: tuple[str, None | Any, str], build: Build) -> defer.Generator[Any, object, Any]:
48-
_, _, event = key
49-
is_new = event == 'new'
50-
51-
formatter = self.start_formatter if is_new else self.end_formatter
52-
53-
yield getDetailsForBuild(
54-
master,
55-
build,
56-
want_properties=formatter.want_properties,
57-
want_steps=formatter.want_steps,
58-
want_logs=formatter.want_logs,
59-
want_logs_content=formatter.want_logs_content,
58+
def partial_build_dict(master: BuildMaster, buildrequest: BuildRequest) -> defer.Generator[Any, object, dict[str, Any]]:
59+
brdict: Any = yield master.db.buildrequests.getBuildRequest(buildrequest['buildrequestid'])
60+
bdict = {}
61+
62+
props = Properties()
63+
buildrequest = yield BuildRequest.fromBrdict(master, brdict)
64+
builder = yield master.botmaster.getBuilderById(brdict["builderid"])
65+
66+
yield Build.setup_properties_known_before_build_starts(props, [buildrequest], builder)
67+
Build.setupBuildProperties(props, [buildrequest])
68+
69+
bdict['properties'] = props.asDict()
70+
yield utils.get_details_for_buildrequest(master, brdict, bdict)
71+
return bdict
72+
73+
# TODO: copy pasted from buildbot, somehow reuse
74+
@defer.inlineCallbacks
75+
def buildrequest_message(self, master: BuildMaster, build: dict[str, Any]) -> defer.Generator[Any, Any, dict[str, Any]]:
76+
patches = self._get_patches_for_build(build)
77+
users: list[str] = []
78+
buildmsg = yield self.start_formatter.format_message_for_build(
79+
master, build, is_buildset=True, mode=self.mode, users=users
6080
)
6181

62-
if not self.is_message_needed_by_props(build):
63-
return None
82+
return {
83+
'body': buildmsg['body'],
84+
'subject': buildmsg['subject'],
85+
'type': buildmsg['type'],
86+
'results': build['results'],
87+
'builds': [build],
88+
"buildset": build["buildset"],
89+
'users': list(users),
90+
'patches': patches,
91+
'logs': [],
92+
"extra_info": buildmsg["extra_info"],
93+
}
94+
95+
@defer.inlineCallbacks
96+
def generate(self, master: BuildMaster, reporter: ReporterBase, key: tuple[str, None | Any, str], data: Build | BuildRequest) -> defer.Generator[Any, Any, Any]:
97+
what, _, event = key
98+
log.info("{what} produced {event}", what = what, event = event)
99+
if what == "builds":
100+
is_new = event == 'new'
101+
102+
formatter = self.start_formatter if is_new else self.end_formatter
103+
104+
yield getDetailsForBuild(
105+
master,
106+
data,
107+
want_properties=formatter.want_properties,
108+
want_steps=formatter.want_steps,
109+
want_logs=formatter.want_logs,
110+
want_logs_content=formatter.want_logs_content,
111+
)
112+
113+
if not self.is_message_needed_by_props(data):
114+
return None
115+
116+
report = yield self.build_message(formatter, master, reporter, data)
117+
reportT: dict[str, Any] = cast(dict[str, Any], report)
118+
119+
if event == "started-nix-eval" or event == "finished-nix-eval":
120+
reportT["builds"][0]["properties"]["status_name"] = ("nix-eval", "generator")
121+
if (event == "started-nix-build" or event == "finished-nix-build") and reportT["builds"][0]["properties"]["status_name"][0] == "nix-eval":
122+
reportT["builds"][0]["properties"]["status_name"] = ("nix-build", "generator")
123+
if event == "finished-nix-eval" or event == "finished-nix-build":
124+
reportT["builds"][0]["complete"] = True
125+
reportT["builds"][0]["complete_at"] = datetime.now(tz=timezone.utc)
126+
127+
return reportT
128+
elif what == "buildrequests":
129+
build: dict[str, Any] = yield self.partial_build_dict(master, data)
64130

65-
report = yield self.build_message(formatter, master, reporter, build)
66-
reportT: dict[str, Any] = cast(dict[str, Any], report)
131+
if event == 'canceled-nix-build':
132+
build['complete'] = True
133+
build['results'] = CANCELLED
67134

68-
if event == "started-nix-eval" or event == "finished-nix-eval":
69-
reportT["builds"][0]["properties"]["status_name"] = ("nix-eval", "generator")
70-
if (event == "started-nix-build" or event == "finished-nix-build") and reportT["builds"][0]["properties"]["status_name"][0] == "nix-eval":
71-
reportT["builds"][0]["properties"]["status_name"] = ("nix-build", "generator")
72-
if event == "finished-nix-eval" or event == "finished-nix-build":
73-
reportT["builds"][0]["complete"] = True
74-
reportT["builds"][0]["complete_at"] = datetime.now(tz=timezone.utc)
135+
if not self.is_message_needed_by_props(build):
136+
return None
75137

76-
return reportT
138+
report = yield self.buildrequest_message(master, build)
139+
return report

0 commit comments

Comments
 (0)