1010from datetime import UTC , datetime
1111from pathlib import Path
1212from typing import TYPE_CHECKING , Any
13+ import typing
14+ import copy
1315
1416from buildbot .config .builder import BuilderConfig
1517from buildbot .configurators import ConfiguratorBase
@@ -69,11 +71,10 @@ class BuildbotNixError(Exception):
6971class BuildTrigger (buildstep .ShellMixin , steps .BuildStep ):
7072 """Dynamic trigger that creates a build for every attribute."""
7173
72- project : GitProject
7374
7475 successful_jobs : list [NixEvalJobSuccess ]
7576 failed_jobs : list [NixEvalJobError ]
76- report_status : bool
77+ combined_build : bool
7778 drv_info : dict [str , NixDerivation ]
7879 builds_scheduler : str
7980 skipped_builds_scheduler : str
@@ -108,21 +109,19 @@ def __iter__(self) -> Generator[Any, None, None]:
108109
109110 def __init__ (
110111 self ,
111- project : GitProject ,
112112 builds_scheduler : str ,
113113 skipped_builds_scheduler : str ,
114114 failed_eval_scheduler : str ,
115115 dependency_failed_scheduler : str ,
116116 cached_failure_scheduler : str ,
117117 successful_jobs : list [NixEvalJobSuccess ],
118118 failed_jobs : list [NixEvalJobError ],
119- report_status : bool ,
119+ combine_builds : bool ,
120120 ** kwargs : Any ,
121121 ) -> None :
122- self .project = project
123122 self .successful_jobs = successful_jobs
124123 self .failed_jobs = failed_jobs
125- self .report_status = report_status
124+ self .combine_builds = combine_builds
126125 self .config = None
127126 self .builds_scheduler = builds_scheduler
128127 self .skipped_builds_scheduler = skipped_builds_scheduler
@@ -170,35 +169,33 @@ def set_common_properties(
170169 props : Properties ,
171170 source : str ,
172171 job : NixEvalJob ,
173- report_status : bool ,
174172 ) -> Properties :
175173 props .setProperty ("virtual_builder_name" , f".#checks.{ job .attr } " , source )
176174 props .setProperty ("status_name" , f"nix-build .#checks.{ job .attr } " , source )
177175 props .setProperty ("virtual_builder_tags" , "" , source )
178176 props .setProperty ("attr" , job .attr , source )
179- props .setProperty ("report_status" , report_status , source )
180177
181178 return props
182179
183180 def schedule_eval_failure (
184- self , job : NixEvalJobError , report_status : bool
181+ self , job : NixEvalJobError
185182 ) -> tuple [str , Properties ]:
186183 source = "nix-eval-nix"
187184
188185 props = BuildTrigger .set_common_properties (
189- Properties (), source , job , report_status
186+ Properties (), source , job
190187 )
191188 props .setProperty ("error" , job .error , source )
192189
193190 return (self .failed_eval_scheduler , props )
194191
195192 def schedule_cached_failure (
196- self , job : NixEvalJobSuccess , report_status : bool , first_failure : datetime
193+ self , job : NixEvalJobSuccess , first_failure : datetime
197194 ) -> tuple [str , Properties ]:
198195 source = "nix-eval-nix"
199196
200197 props = BuildTrigger .set_common_properties (
201- Properties (), source , job , report_status
198+ Properties (), source , job
202199 )
203200 props .setProperty ("first_failure" , str (first_failure ), source )
204201
@@ -208,12 +205,11 @@ def schedule_dependency_failed(
208205 self ,
209206 job : NixEvalJobSuccess ,
210207 dependency : NixEvalJobSuccess ,
211- report_status : bool ,
212208 ) -> tuple [str , Properties ]:
213209 source = "nix-eval-nix"
214210
215211 props = BuildTrigger .set_common_properties (
216- Properties (), source , job , report_status
212+ Properties (), source , job
217213 )
218214 props .setProperty ("dependency.attr" , dependency .attr , source )
219215
@@ -223,7 +219,6 @@ def schedule_success(
223219 self ,
224220 build_props : Properties ,
225221 job : NixEvalJobSuccess ,
226- report_status : bool ,
227222 ) -> tuple [str , Properties ]:
228223 source = "nix-eval-nix"
229224
@@ -232,7 +227,7 @@ def schedule_success(
232227 out_path = job .outputs ["out" ] or None
233228
234229 props = BuildTrigger .set_common_properties (
235- Properties (), source , job , report_status
230+ Properties (), source , job
236231 )
237232 props .setProperty ("system" , system , source )
238233 props .setProperty ("drv_path" , drv_path , source )
@@ -355,8 +350,33 @@ def get_failed_dependents(
355350
356351 return removed
357352
353+ @defer .inlineCallbacks
354+ def produceEventForBuilds (
355+ self ,
356+ build_ids : list [int ],
357+ event : str ,
358+ result : None | int ,
359+ ) -> Generator [Any , object , None ]:
360+ for build_id in build_ids :
361+ build = yield self .master .data .get (('builds' , str (build_id )))
362+ buildT = typing .cast (dict [str , Any ], build )
363+ if result is not None :
364+ buildT ["results" ] = result
365+ self .master .mq .produce (("builds" , str (build_id ), event ), copy .deepcopy (buildT ))
366+
367+ @defer .inlineCallbacks
368+ def produceEvent (
369+ self ,
370+ event : str ,
371+ result : None | int ,
372+ ) -> Generator [Any , object , None ]:
373+ yield self .produceEventForBuilds ([self .build .buildid ], event , result )
374+
358375 @defer .inlineCallbacks
359376 def run (self ) -> Generator [Any , Any , None ]:
377+ if self .combine_builds :
378+ self .produceEvent ("started-nix-build" , None )
379+
360380 self .running = True
361381 build_props = self .build .getProperties ()
362382 ss_for_trigger = self .prepare_sourcestamp_list_for_trigger ()
@@ -370,7 +390,7 @@ def run(self) -> Generator[Any, Any, None]:
370390 scheduler_log .addStdout (f"\t - { failed_job .attr } failed eval\n " )
371391 brids , _ = yield self .schedule (
372392 ss_for_trigger ,
373- * self .schedule_eval_failure (failed_job , self . report_status ),
393+ * self .schedule_eval_failure (failed_job ),
374394 )
375395 self .brids .extend (brids )
376396
@@ -411,12 +431,14 @@ def run(self) -> Generator[Any, Any, None]:
411431 brids , results_deferred = yield self .schedule (
412432 ss_for_trigger ,
413433 * self .schedule_cached_failure (
414- build , self . report_status , failed_build .time
434+ build , failed_build .time
415435 ),
416436 )
417437 scheduled .append (
418438 BuildTrigger .ScheduledJob (build , brids , results_deferred )
419439 )
440+ if not self .combine_builds :
441+ self .produceEventForBuilds (brids .values (), "started-nix-build" , None )
420442 self .brids .extend (brids .values ())
421443 elif failed_build is not None and self .build .reason == "rebuild" :
422444 failed_builds .remove_build (build .drvPath )
@@ -438,13 +460,15 @@ def run(self) -> Generator[Any, Any, None]:
438460 scheduler_log .addStdout (f"\t - { job .attr } \n " )
439461 brids , results_deferred = yield self .schedule (
440462 ss_for_trigger ,
441- * self .schedule_success (build_props , job , self . report_status ),
463+ * self .schedule_success (build_props , job ),
442464 )
443465
444466 scheduled .append (
445467 BuildTrigger .ScheduledJob (job , brids , results_deferred )
446468 )
447469
470+ if not self .combine_builds :
471+ self .produceEventForBuilds (brids .values (), "started-nix-build" , None )
448472 self .brids .extend (brids .values ())
449473
450474 scheduler_log .addStdout ("Waiting...\n " )
@@ -467,6 +491,8 @@ def run(self) -> Generator[Any, Any, None]:
467491 scheduler_log .addStdout (
468492 f"Found finished build { job .attr } , result { util .Results [result ].upper ()} \n "
469493 )
494+ if not self .combine_builds :
495+ self .produceEventForBuilds (brids .values (), "finished-nix-build" , result )
470496
471497 # if it failed, remove all dependent jobs, schedule placeholders and add them to the list of scheduled jobs
472498 if result != SUCCESS :
@@ -477,7 +503,7 @@ def run(self) -> Generator[Any, Any, None]:
477503 )
478504 for removed_job in removed :
479505 scheduler , props = self .schedule_dependency_failed (
480- removed_job , job , self . report_status
506+ removed_job , job
481507 )
482508 brids , results_deferred = yield self .schedule (
483509 ss_for_trigger , scheduler , props
@@ -486,6 +512,8 @@ def run(self) -> Generator[Any, Any, None]:
486512 scheduled .append (
487513 BuildTrigger .ScheduledJob (removed_job , brids , results_deferred )
488514 )
515+ if not self .combine_builds :
516+ self .produceEventForBuilds (brids .values (), "started-nix-build" , None )
489517 self .brids .extend (brids .values ())
490518 scheduler_log .addStdout (
491519 "\t - removed jobs: "
@@ -501,7 +529,8 @@ def run(self) -> Generator[Any, Any, None]:
501529 for dep in job_closures :
502530 if job .drvPath in job_closures [dep ]:
503531 job_closures [dep ].remove (job .drvPath )
504-
532+ if self .combine_builds :
533+ self .produceEvent ("finished-nix-build" , overall_result )
505534 scheduler_log .addStdout ("Done!\n " )
506535 return overall_result
507536
@@ -517,15 +546,6 @@ def getCurrentSummary(self) -> dict[str, str]: # noqa: N802
517546 return {"step" : f"({ ', ' .join (summary )} )" }
518547
519548
520- class NixBuildCombined (steps .BuildStep ):
521- """Shows the error message of a failed evaluation."""
522-
523- name = "nix-build-combined"
524-
525- def run (self ) -> Generator [Any , object , Any ]:
526- return self .build .results
527-
528-
529549class NixEvalCommand (buildstep .ShellMixin , steps .BuildStep ):
530550 """Parses the output of `nix-eval-jobs` and triggers a `nix-build` build for
531551 every attribute.
@@ -548,14 +568,28 @@ def __init__(
548568 self .supported_systems = supported_systems
549569 self .job_report_limit = job_report_limit
550570
571+ @defer .inlineCallbacks
572+ def produceEvent (
573+ self ,
574+ event : str ,
575+ result : None | int
576+ ) -> Generator [Any , object , None ]:
577+ build = yield self .master .data .get (('builds' , str (self .build .buildid )))
578+ buildT = typing .cast (dict [str , Any ], build )
579+ if result is not None :
580+ buildT ["results" ] = result
581+ self .master .mq .produce (("builds" , str (self .build .buildid ), event ), copy .deepcopy (buildT ))
582+
551583 @defer .inlineCallbacks
552584 def run (self ) -> Generator [Any , object , Any ]:
585+ self .produceEvent ("started-nix-eval" , None )
553586 # run nix-eval-jobs --flake .#checks to generate the dict of stages
554587 cmd : remotecommand .RemoteCommand = yield self .makeRemoteShellCommand ()
555588 yield self .runCommand (cmd )
556589
557590 # if the command passes extract the list of stages
558591 result = cmd .results ()
592+ self .produceEvent ("finished-nix-eval" , result )
559593 if result == util .SUCCESS :
560594 # create a ShellCommand for each stage and add them to the build
561595 jobs : list [NixEvalJob ] = []
@@ -586,7 +620,6 @@ def run(self) -> Generator[Any, object, Any]:
586620 self .build .addStepsAfterCurrentStep (
587621 [
588622 BuildTrigger (
589- self .project ,
590623 builds_scheduler = f"{ self .project .project_id } -nix-build" ,
591624 skipped_builds_scheduler = f"{ self .project .project_id } -nix-skipped-build" ,
592625 failed_eval_scheduler = f"{ self .project .project_id } -nix-failed-eval" ,
@@ -595,30 +628,12 @@ def run(self) -> Generator[Any, object, Any]:
595628 name = "build flake" ,
596629 successful_jobs = successful_jobs ,
597630 failed_jobs = failed_jobs ,
598- report_status = (
599- self .job_report_limit is None
600- or self .number_of_jobs <= self .job_report_limit
631+ combine_builds = (
632+ self .job_report_limit is not None
633+ and self .number_of_jobs > self .job_report_limit
601634 ),
602635 ),
603636 ]
604- + (
605- [
606- Trigger (
607- waitForFinish = True ,
608- schedulerNames = [
609- f"{ self .project .project_id } -nix-build-combined"
610- ],
611- haltOnFailure = True ,
612- flunkOnFailure = True ,
613- sourceStamps = [],
614- alwaysUseLatest = False ,
615- updateSourceStamp = False ,
616- ),
617- ]
618- if self .job_report_limit is not None
619- and self .number_of_jobs > self .job_report_limit
620- else []
621- ),
622637 )
623638
624639 return result
@@ -1091,24 +1106,6 @@ def nix_register_gcroot_config(
10911106 )
10921107
10931108
1094- def nix_build_combined_config (
1095- project : GitProject ,
1096- worker_names : list [str ],
1097- ) -> BuilderConfig :
1098- factory = util .BuildFactory ()
1099- factory .addStep (NixBuildCombined ())
1100-
1101- return util .BuilderConfig (
1102- name = f"{ project .name } /nix-build-combined" ,
1103- project = project .name ,
1104- workernames = worker_names ,
1105- collapseRequests = False ,
1106- env = {},
1107- factory = factory ,
1108- properties = dict (status_name = "nix-build-combined" ),
1109- )
1110-
1111-
11121109def config_for_project (
11131110 config : dict [str , Any ],
11141111 project : GitProject ,
@@ -1176,11 +1173,6 @@ def config_for_project(
11761173 name = f"{ project .project_id } -nix-cached-failure" ,
11771174 builderNames = [f"{ project .name } /nix-cached-failure" ],
11781175 ),
1179- # this is triggered from `nix-eval` when the build contains too many outputs
1180- schedulers .Triggerable (
1181- name = f"{ project .project_id } -nix-build-combined" ,
1182- builderNames = [f"{ project .name } /nix-build-combined" ],
1183- ),
11841176 schedulers .Triggerable (
11851177 name = f"{ project .project_id } -nix-register-gcroot" ,
11861178 builderNames = [f"{ project .name } /nix-register-gcroot" ],
@@ -1224,7 +1216,6 @@ def config_for_project(
12241216 nix_dependency_failed_config (project , [SKIPPED_BUILDER_NAME ]),
12251217 nix_cached_failure_config (project , [SKIPPED_BUILDER_NAME ]),
12261218 nix_register_gcroot_config (project , worker_names ),
1227- nix_build_combined_config (project , worker_names ),
12281219 ],
12291220 )
12301221
0 commit comments