|
7 | 7 | from collections import defaultdict |
8 | 8 | from collections.abc import Generator |
9 | 9 | from dataclasses import dataclass |
| 10 | +from datetime import UTC, datetime |
10 | 11 | from pathlib import Path |
11 | 12 | from typing import TYPE_CHECKING, Any |
12 | 13 |
|
|
40 | 41 | from twisted.logger import Logger |
41 | 42 | from twisted.python.failure import Failure |
42 | 43 |
|
43 | | -from . import models |
44 | | -from pydantic import TypeAdapter |
45 | | -from . import failed_builds |
| 44 | +from . import failed_builds, models |
46 | 45 | from .gitea_projects import GiteaBackend |
47 | 46 | from .github_projects import ( |
48 | 47 | GithubBackend, |
@@ -196,13 +195,14 @@ def schedule_eval_failure( |
196 | 195 | return (self.failed_eval_scheduler, props) |
197 | 196 |
|
198 | 197 | def schedule_cached_failure( |
199 | | - self, job: NixEvalJobSuccess, report_status: bool |
| 198 | + self, job: NixEvalJobSuccess, report_status: bool, first_failure: datetime |
200 | 199 | ) -> tuple[str, Properties]: |
201 | 200 | source = "nix-eval-nix" |
202 | 201 |
|
203 | 202 | props = BuildTrigger.set_common_properties( |
204 | 203 | Properties(), source, job, report_status |
205 | 204 | ) |
| 205 | + props.setProperty("first_failure", str(first_failure), source) |
206 | 206 |
|
207 | 207 | return (self.cached_failure_scheduler, props) |
208 | 208 |
|
@@ -442,32 +442,29 @@ def run(self) -> Generator[Any, Any, None]: |
442 | 442 | # check which jobs should be scheduled now |
443 | 443 | schedule_now = [] |
444 | 444 | for build in list(build_schedule_order): |
| 445 | + failed_build = failed_builds.check_build(build.drvPath) |
445 | 446 | if job_closures.get(build.drvPath): |
446 | 447 | pass |
447 | | - elif ( |
448 | | - failed_builds.check_build(build.drvPath) |
449 | | - and self.build.reason != "rebuild" |
450 | | - ): |
451 | | - failed_builds.remove_build(build.drvPath) |
| 448 | + elif failed_build is not None and self.build.reason != "rebuild": |
452 | 449 | scheduler_log.addStdout( |
453 | | - f"\t- skipping {build.attr} due to cached failure\n" |
| 450 | + f"\t- skipping {build.attr} due to cached failure, first failed at {failed_build.time}\n" |
454 | 451 | ) |
455 | 452 | build_schedule_order.remove(build) |
456 | 453 |
|
457 | 454 | brids, results_deferred = yield self.schedule( |
458 | 455 | ss_for_trigger, |
459 | | - *self.schedule_cached_failure(build, self.report_status), |
| 456 | + *self.schedule_cached_failure( |
| 457 | + build, self.report_status, failed_build.time |
| 458 | + ), |
460 | 459 | ) |
461 | 460 | scheduled.append( |
462 | 461 | BuildTrigger.ScheduledJob(build, brids, results_deferred) |
463 | 462 | ) |
464 | 463 | self.brids.extend(brids.values()) |
465 | | - elif ( |
466 | | - failed_builds.check_build(build.drvPath) |
467 | | - and self.build.reason == "rebuild" |
468 | | - ): |
| 464 | + elif failed_build is not None and self.build.reason == "rebuild": |
| 465 | + failed_builds.remove_build(build.drvPath) |
469 | 466 | scheduler_log.addStdout( |
470 | | - f"\t- not skipping {build.attr} with cached failure due to rebuild\n" |
| 467 | + f"\t- not skipping {build.attr} with cached failure due to rebuild, first failed at {failed_build.time}\n" |
471 | 468 | ) |
472 | 469 |
|
473 | 470 | build_schedule_order.remove(build) |
@@ -516,7 +513,7 @@ def run(self) -> Generator[Any, Any, None]: |
516 | 513 |
|
517 | 514 | # if it failed, remove all dependent jobs, schedule placeholders and add them to the list of scheduled jobs |
518 | 515 | if result != SUCCESS: |
519 | | - failed_builds.add_build(job.drvPath) |
| 516 | + failed_builds.add_build(job.drvPath, datetime.now(tz=UTC)) |
520 | 517 |
|
521 | 518 | removed = self.get_failed_dependents( |
522 | 519 | job, build_schedule_order, job_closures |
@@ -707,7 +704,13 @@ def run(self) -> Generator[Any, object, int]: |
707 | 704 | # show eval error |
708 | 705 | error_log: StreamLog = yield self.addLog("nix_error") |
709 | 706 | error_log.addStderr( |
710 | | - f"{attr} was failed because it has failed previous and its failure has been cached.\n" |
| 707 | + "\n".join( |
| 708 | + [ |
| 709 | + f"{attr} was failed because it has failed previously and its failure has been cached.", |
| 710 | + f" first failure time: {self.getProperty('first_failure')}", |
| 711 | + ] |
| 712 | + ) |
| 713 | + + "\n" |
711 | 714 | ) |
712 | 715 | return util.FAILURE |
713 | 716 |
|
|
0 commit comments