Combine build reports for GitHub if there are too many

Signed-off-by: magic_rb <richard@brezak.sk>
This commit is contained in:
magic_rb 2024-07-11 20:56:15 +02:00
parent 7480ee03b3
commit dd2df67009
3 changed files with 88 additions and 6 deletions

View file

@ -59,12 +59,14 @@ class BuildTrigger(Trigger):
builds_scheduler: str, builds_scheduler: str,
skipped_builds_scheduler: str, skipped_builds_scheduler: str,
jobs: list[dict[str, Any]], jobs: list[dict[str, Any]],
report_status: bool,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
if "name" not in kwargs: if "name" not in kwargs:
kwargs["name"] = "trigger" kwargs["name"] = "trigger"
self.project = project self.project = project
self.jobs = jobs self.jobs = jobs
self.report_status = report_status
self.config = None self.config = None
self.builds_scheduler = builds_scheduler self.builds_scheduler = builds_scheduler
self.skipped_builds_scheduler = skipped_builds_scheduler self.skipped_builds_scheduler = skipped_builds_scheduler
@ -102,6 +104,7 @@ class BuildTrigger(Trigger):
props.setProperty("virtual_builder_name", name, source) props.setProperty("virtual_builder_name", name, source)
props.setProperty("status_name", f"nix-build .#checks.{attr}", source) props.setProperty("status_name", f"nix-build .#checks.{attr}", source)
props.setProperty("virtual_builder_tags", "", source) props.setProperty("virtual_builder_tags", "", source)
props.setProperty("report_status", self.report_status, source)
drv_path = job.get("drvPath") drv_path = job.get("drvPath")
system = job.get("system") system = job.get("system")
@ -145,6 +148,15 @@ class BuildTrigger(Trigger):
return {"step": f"({', '.join(summary)})"} return {"step": f"({', '.join(summary)})"}
class NixBuildCombined(steps.BuildStep):
"""Shows the error message of a failed evaluation."""
name = "nix-build-combined"
def run(self) -> Generator[Any, object, Any]:
return self.build.results
class NixEvalCommand(buildstep.ShellMixin, steps.BuildStep): class NixEvalCommand(buildstep.ShellMixin, steps.BuildStep):
"""Parses the output of `nix-eval-jobs` and triggers a `nix-build` build for """Parses the output of `nix-eval-jobs` and triggers a `nix-build` build for
every attribute. every attribute.
@ -190,16 +202,32 @@ class NixEvalCommand(buildstep.ShellMixin, steps.BuildStep):
if not system or system in self.supported_systems: # report eval errors if not system or system in self.supported_systems: # report eval errors
filtered_jobs.append(job) filtered_jobs.append(job)
self.number_of_jobs = len(filtered_jobs)
self.build.addStepsAfterCurrentStep( self.build.addStepsAfterCurrentStep(
[ [ # noqa: RUF005
BuildTrigger( BuildTrigger(
self.project, self.project,
builds_scheduler=f"{project_id}-nix-build", builds_scheduler=f"{project_id}-nix-build",
skipped_builds_scheduler=f"{project_id}-nix-skipped-build", skipped_builds_scheduler=f"{project_id}-nix-skipped-build",
name="build flake", name="build flake",
jobs=filtered_jobs, jobs=filtered_jobs,
report_status=(self.number_of_jobs <= 2),
), ),
], ]
+ [
Trigger(
waitForFinish=True,
schedulerNames=[f"{project_id}-nix-build-combined"],
haltOnFailure=True,
flunkOnFailure=True,
sourceStamps=[],
alwaysUseLatest=False,
updateSourceStamp=False,
),
]
if self.number_of_jobs > 2
else [],
) )
return result return result
@ -600,6 +628,23 @@ def nix_register_gcroot_config(
factory=factory, factory=factory,
) )
def nix_build_combined_config(
project: GitProject,
worker_names: list[str],
) -> BuilderConfig:
factory = util.BuildFactory()
factory.addStep(NixBuildCombined())
return util.BuilderConfig(
name=f"{project.name}/nix-build-combined",
project=project.name,
workernames=worker_names,
collapseRequests=False,
env={},
factory=factory,
properties=dict(status_name="nix-build-combined"),
)
def config_for_project( def config_for_project(
config: dict[str, Any], config: dict[str, Any],
@ -653,6 +698,11 @@ def config_for_project(
name=f"{project.project_id}-nix-skipped-build", name=f"{project.project_id}-nix-skipped-build",
builderNames=[f"{project.name}/nix-skipped-build"], builderNames=[f"{project.name}/nix-skipped-build"],
), ),
# this is triggered from `nix-eval` when the build contains too many outputs
schedulers.Triggerable(
name=f"{project.project_id}-nix-build-combined",
builderNames=[f"{project.name}/nix-build-combined"],
),
schedulers.Triggerable( schedulers.Triggerable(
name=f"{project.project_id}-nix-register-gcroot", name=f"{project.project_id}-nix-register-gcroot",
builderNames=[f"{project.name}/nix-register-gcroot"], builderNames=[f"{project.name}/nix-register-gcroot"],
@ -693,6 +743,7 @@ def config_for_project(
), ),
nix_skipped_build_config(project, [SKIPPED_BUILDER_NAME]), nix_skipped_build_config(project, [SKIPPED_BUILDER_NAME]),
nix_register_gcroot_config(project, worker_names), nix_register_gcroot_config(project, worker_names),
nix_build_combined_config(project, worker_names),
], ],
) )

View file

@ -5,7 +5,7 @@ from abc import ABC, abstractmethod
from dataclasses import dataclass from dataclasses import dataclass
from itertools import starmap from itertools import starmap
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any, Callable
from buildbot.config.builder import BuilderConfig from buildbot.config.builder import BuilderConfig
from buildbot.plugins import util from buildbot.plugins import util
@ -20,6 +20,7 @@ from buildbot.www.oauth2 import GitHubAuth
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field
from twisted.logger import Logger from twisted.logger import Logger
from twisted.python import log from twisted.python import log
from twisted.internet import defer
from .common import ( from .common import (
ThreadDeferredBuildStep, ThreadDeferredBuildStep,
@ -309,6 +310,32 @@ class GithubAuthBackend(ABC):
) -> list[BuildStep]: ) -> list[BuildStep]:
pass pass
class ModifyingGitHubStatusPush(GitHubStatusPush):
def checkConfig(self, modifyingFilter: Callable[[Any], Any | None] = lambda x: x, **kwargs: Any) -> Any:
self.modifyingFilter = modifyingFilter
return super().checkConfig(**kwargs)
def reconfigService(self, modifyingFilter: Callable[[Any], Any | None] = lambda x: x, **kwargs: Any) -> Any:
self.modifyingFilter = modifyingFilter
return super().reconfigService(**kwargs)
@defer.inlineCallbacks
def sendMessage(self, reports: Any) -> Any:
reports = self.modifyingFilter(reports)
if reports is None:
return
result = yield super().sendMessage(reports)
return result
def filter_for_combined_builds(reports: Any) -> Any | None:
properties = reports[0]["builds"][0]["properties"]
if "report_status" in properties and not properties["report_status"][0]:
return None
return reports
class GithubLegacyAuthBackend(GithubAuthBackend): class GithubLegacyAuthBackend(GithubAuthBackend):
auth_type: GitHubLegacyConfig auth_type: GitHubLegacyConfig
@ -329,12 +356,13 @@ class GithubLegacyAuthBackend(GithubAuthBackend):
return [GitHubLegacySecretService(self.token)] return [GitHubLegacySecretService(self.token)]
def create_reporter(self) -> ReporterBase: def create_reporter(self) -> ReporterBase:
return GitHubStatusPush( return ModifyingGitHubStatusPush(
token=self.token.get(), token=self.token.get(),
# Since we dynamically create build steps, # Since we dynamically create build steps,
# we use `virtual_builder_name` in the webinterface # we use `virtual_builder_name` in the webinterface
# so that we distinguish what has beeing build # so that we distinguish what has beeing build
context=Interpolate("buildbot/%(prop:status_name)s"), context=Interpolate("buildbot/%(prop:status_name)s"),
modifyingFilter=filter_for_combined_builds,
) )
def create_reload_builder_steps( def create_reload_builder_steps(
@ -416,12 +444,13 @@ class GithubAppAuthBackend(GithubAuthBackend):
self.project_id_map[props["projectname"]] self.project_id_map[props["projectname"]]
].get() ].get()
return GitHubStatusPush( return ModifyingGitHubStatusPush(
token=WithProperties("%(github_token)s", github_token=get_github_token), token=WithProperties("%(github_token)s", github_token=get_github_token),
# Since we dynamically create build steps, # Since we dynamically create build steps,
# we use `virtual_builder_name` in the webinterface # we use `virtual_builder_name` in the webinterface
# so that we distinguish what has beeing build # so that we distinguish what has beeing build
context=Interpolate("buildbot/%(prop:status_name)s"), context=Interpolate("buildbot/%(prop:status_name)s"),
modifyingFilter=filter_for_combined_builds,
) )
def create_reload_builder_steps( def create_reload_builder_steps(

View file

@ -482,7 +482,9 @@ in
dbUrl = config.services.buildbot-nix.master.dbUrl; dbUrl = config.services.buildbot-nix.master.dbUrl;
package = cfg.buildbotNixpkgs.buildbot.overrideAttrs (old: { package = cfg.buildbotNixpkgs.buildbot.overrideAttrs (old: {
patches = old.patches ++ [ ./0001-master-reporters-github-render-token-for-each-reques.patch ]; patches = old.patches ++ [
./0001-master-reporters-github-render-token-for-each-reques.patch
];
}); });
pythonPackages = pythonPackages =
let let