Merge pull request #217 from MagicRB/combined-build-reports-github
Combined build reports GitHub
This commit is contained in:
commit
2620f2cd31
|
@ -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.
|
||||||
|
@ -153,7 +165,11 @@ class NixEvalCommand(buildstep.ShellMixin, steps.BuildStep):
|
||||||
project: GitProject
|
project: GitProject
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, project: GitProject, supported_systems: list[str], **kwargs: Any
|
self,
|
||||||
|
project: GitProject,
|
||||||
|
supported_systems: list[str],
|
||||||
|
job_report_limit: int | None,
|
||||||
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
kwargs = self.setupShellMixin(kwargs)
|
kwargs = self.setupShellMixin(kwargs)
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
@ -161,6 +177,7 @@ class NixEvalCommand(buildstep.ShellMixin, steps.BuildStep):
|
||||||
self.observer = logobserver.BufferLogObserver()
|
self.observer = logobserver.BufferLogObserver()
|
||||||
self.addLogObserver("stdio", self.observer)
|
self.addLogObserver("stdio", self.observer)
|
||||||
self.supported_systems = supported_systems
|
self.supported_systems = supported_systems
|
||||||
|
self.job_report_limit = job_report_limit
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def run(self) -> Generator[Any, object, Any]:
|
def run(self) -> Generator[Any, object, Any]:
|
||||||
|
@ -190,6 +207,8 @@ 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(
|
||||||
[
|
[
|
||||||
BuildTrigger(
|
BuildTrigger(
|
||||||
|
@ -198,8 +217,28 @@ class NixEvalCommand(buildstep.ShellMixin, steps.BuildStep):
|
||||||
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.job_report_limit is None
|
||||||
|
or self.number_of_jobs <= self.job_report_limit
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
+ (
|
||||||
|
[
|
||||||
|
Trigger(
|
||||||
|
waitForFinish=True,
|
||||||
|
schedulerNames=[f"{project_id}-nix-build-combined"],
|
||||||
|
haltOnFailure=True,
|
||||||
|
flunkOnFailure=True,
|
||||||
|
sourceStamps=[],
|
||||||
|
alwaysUseLatest=False,
|
||||||
|
updateSourceStamp=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
if self.job_report_limit is not None
|
||||||
|
and self.number_of_jobs > self.job_report_limit
|
||||||
|
else []
|
||||||
),
|
),
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -360,6 +399,7 @@ def nix_eval_config(
|
||||||
eval_lock: MasterLock,
|
eval_lock: MasterLock,
|
||||||
worker_count: int,
|
worker_count: int,
|
||||||
max_memory_size: int,
|
max_memory_size: int,
|
||||||
|
job_report_limit: int | None,
|
||||||
) -> BuilderConfig:
|
) -> BuilderConfig:
|
||||||
"""Uses nix-eval-jobs to evaluate hydraJobs from flake.nix in parallel.
|
"""Uses nix-eval-jobs to evaluate hydraJobs from flake.nix in parallel.
|
||||||
For each evaluated attribute a new build pipeline is started.
|
For each evaluated attribute a new build pipeline is started.
|
||||||
|
@ -385,6 +425,7 @@ def nix_eval_config(
|
||||||
env={},
|
env={},
|
||||||
name="evaluate flake",
|
name="evaluate flake",
|
||||||
supported_systems=supported_systems,
|
supported_systems=supported_systems,
|
||||||
|
job_report_limit=job_report_limit,
|
||||||
command=[
|
command=[
|
||||||
"nix-eval-jobs",
|
"nix-eval-jobs",
|
||||||
"--workers",
|
"--workers",
|
||||||
|
@ -492,6 +533,7 @@ def nix_build_config(
|
||||||
updateSourceStamp=False,
|
updateSourceStamp=False,
|
||||||
doStepIf=do_register_gcroot_if,
|
doStepIf=do_register_gcroot_if,
|
||||||
copy_properties=["out_path", "attr"],
|
copy_properties=["out_path", "attr"],
|
||||||
|
set_properties={"report_status": False},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
factory.addStep(
|
factory.addStep(
|
||||||
|
@ -556,6 +598,7 @@ def nix_skipped_build_config(
|
||||||
updateSourceStamp=False,
|
updateSourceStamp=False,
|
||||||
doStepIf=do_register_gcroot_if,
|
doStepIf=do_register_gcroot_if,
|
||||||
copy_properties=["out_path", "attr"],
|
copy_properties=["out_path", "attr"],
|
||||||
|
set_properties={"report_status": False},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return util.BuilderConfig(
|
return util.BuilderConfig(
|
||||||
|
@ -601,6 +644,24 @@ def nix_register_gcroot_config(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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],
|
||||||
project: GitProject,
|
project: GitProject,
|
||||||
|
@ -610,6 +671,7 @@ def config_for_project(
|
||||||
nix_eval_max_memory_size: int,
|
nix_eval_max_memory_size: int,
|
||||||
eval_lock: MasterLock,
|
eval_lock: MasterLock,
|
||||||
post_build_steps: list[steps.BuildStep],
|
post_build_steps: list[steps.BuildStep],
|
||||||
|
job_report_limit: int | None,
|
||||||
outputs_path: Path | None = None,
|
outputs_path: Path | None = None,
|
||||||
build_retries: int = 1,
|
build_retries: int = 1,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -653,6 +715,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"],
|
||||||
|
@ -680,6 +747,7 @@ def config_for_project(
|
||||||
worker_names,
|
worker_names,
|
||||||
git_url=project.get_project_url(),
|
git_url=project.get_project_url(),
|
||||||
supported_systems=nix_supported_systems,
|
supported_systems=nix_supported_systems,
|
||||||
|
job_report_limit=job_report_limit,
|
||||||
worker_count=nix_eval_worker_count,
|
worker_count=nix_eval_worker_count,
|
||||||
max_memory_size=nix_eval_max_memory_size,
|
max_memory_size=nix_eval_max_memory_size,
|
||||||
eval_lock=eval_lock,
|
eval_lock=eval_lock,
|
||||||
|
@ -693,6 +761,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),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -842,7 +911,7 @@ class NixConfigurator(ConfiguratorBase):
|
||||||
backends["github"] = GithubBackend(self.config.github, self.config.url)
|
backends["github"] = GithubBackend(self.config.github, self.config.url)
|
||||||
|
|
||||||
if self.config.gitea is not None:
|
if self.config.gitea is not None:
|
||||||
backends["gitea"] = GiteaBackend(self.config.gitea)
|
backends["gitea"] = GiteaBackend(self.config.gitea, self.config.url)
|
||||||
|
|
||||||
auth: AuthBase | None = (
|
auth: AuthBase | None = (
|
||||||
backends[self.config.auth_backend].create_auth()
|
backends[self.config.auth_backend].create_auth()
|
||||||
|
@ -895,6 +964,7 @@ class NixConfigurator(ConfiguratorBase):
|
||||||
self.config.eval_max_memory_size,
|
self.config.eval_max_memory_size,
|
||||||
eval_lock,
|
eval_lock,
|
||||||
[x.to_buildstep() for x in self.config.post_build_steps],
|
[x.to_buildstep() for x in self.config.post_build_steps],
|
||||||
|
self.config.job_report_limit,
|
||||||
self.config.outputs_path,
|
self.config.outputs_path,
|
||||||
self.config.build_retries,
|
self.config.build_retries,
|
||||||
)
|
)
|
||||||
|
|
|
@ -172,3 +172,11 @@ def model_validate_project_cache(cls: type[_T], project_cache_file: Path) -> lis
|
||||||
|
|
||||||
def model_dump_project_cache(repos: list[_T]) -> str:
|
def model_dump_project_cache(repos: list[_T]) -> str:
|
||||||
return json.dumps([repo.model_dump() for repo in repos])
|
return json.dumps([repo.model_dump() for repo in repos])
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
|
from collections.abc import Callable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
@ -13,12 +14,14 @@ from buildbot.www.avatar import AvatarBase
|
||||||
from buildbot_gitea.auth import GiteaAuth # type: ignore[import]
|
from buildbot_gitea.auth import GiteaAuth # type: ignore[import]
|
||||||
from buildbot_gitea.reporter import GiteaStatusPush # type: ignore[import]
|
from buildbot_gitea.reporter import GiteaStatusPush # type: ignore[import]
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from twisted.internet import defer
|
||||||
from twisted.logger import Logger
|
from twisted.logger import Logger
|
||||||
from twisted.python import log
|
from twisted.python import log
|
||||||
|
|
||||||
from .common import (
|
from .common import (
|
||||||
ThreadDeferredBuildStep,
|
ThreadDeferredBuildStep,
|
||||||
atomic_write_file,
|
atomic_write_file,
|
||||||
|
filter_for_combined_builds,
|
||||||
filter_repos_by_topic,
|
filter_repos_by_topic,
|
||||||
http_request,
|
http_request,
|
||||||
model_dump_project_cache,
|
model_dump_project_cache,
|
||||||
|
@ -105,11 +108,43 @@ class GiteaProject(GitProject):
|
||||||
return False # self.data["owner"]["type"] == "Organization"
|
return False # self.data["owner"]["type"] == "Organization"
|
||||||
|
|
||||||
|
|
||||||
|
class ModifyingGiteaStatusPush(GiteaStatusPush):
|
||||||
|
def checkConfig(
|
||||||
|
self,
|
||||||
|
modifyingFilter: Callable[[Any], Any | None] = lambda x: x, # noqa: N803
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> Any:
|
||||||
|
self.modifyingFilter = modifyingFilter
|
||||||
|
|
||||||
|
return super().checkConfig(**kwargs)
|
||||||
|
|
||||||
|
def reconfigService(
|
||||||
|
self,
|
||||||
|
modifyingFilter: Callable[[Any], Any | None] = lambda x: x, # noqa: N803
|
||||||
|
**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
|
||||||
|
|
||||||
|
|
||||||
class GiteaBackend(GitBackend):
|
class GiteaBackend(GitBackend):
|
||||||
config: GiteaConfig
|
config: GiteaConfig
|
||||||
|
webhook_secret: str
|
||||||
|
instance_url: str
|
||||||
|
|
||||||
def __init__(self, config: GiteaConfig) -> None:
|
def __init__(self, config: GiteaConfig, instance_url: str) -> None:
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.instance_url = instance_url
|
||||||
|
|
||||||
def create_reload_builder(self, worker_names: list[str]) -> BuilderConfig:
|
def create_reload_builder(self, worker_names: list[str]) -> BuilderConfig:
|
||||||
"""Updates the flake an opens a PR for it."""
|
"""Updates the flake an opens a PR for it."""
|
||||||
|
@ -118,7 +153,10 @@ class GiteaBackend(GitBackend):
|
||||||
ReloadGiteaProjects(self.config, self.config.project_cache_file),
|
ReloadGiteaProjects(self.config, self.config.project_cache_file),
|
||||||
)
|
)
|
||||||
factory.addStep(
|
factory.addStep(
|
||||||
CreateGiteaProjectHooks(self.config),
|
CreateGiteaProjectHooks(
|
||||||
|
self.config,
|
||||||
|
self.instance_url,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
return util.BuilderConfig(
|
return util.BuilderConfig(
|
||||||
name=self.reload_builder_name,
|
name=self.reload_builder_name,
|
||||||
|
@ -127,11 +165,12 @@ class GiteaBackend(GitBackend):
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_reporter(self) -> ReporterBase:
|
def create_reporter(self) -> ReporterBase:
|
||||||
return GiteaStatusPush(
|
return ModifyingGiteaStatusPush(
|
||||||
self.config.instance_url,
|
baseURL=self.config.instance_url,
|
||||||
Interpolate(self.config.token),
|
token=Interpolate(self.config.token),
|
||||||
context=Interpolate("buildbot/%(prop:status_name)s"),
|
context=Interpolate("buildbot/%(prop:status_name)s"),
|
||||||
context_pr=Interpolate("buildbot/%(prop:status_name)s"),
|
context_pr=Interpolate("buildbot/%(prop:status_name)s"),
|
||||||
|
modifyingFilter=filter_for_combined_builds,
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_change_hook(self) -> dict[str, Any]:
|
def create_change_hook(self) -> dict[str, Any]:
|
||||||
|
@ -198,14 +237,19 @@ class GiteaBackend(GitBackend):
|
||||||
|
|
||||||
|
|
||||||
def create_repo_hook(
|
def create_repo_hook(
|
||||||
token: str, webhook_secret: str, owner: str, repo: str, webhook_url: str
|
token: str,
|
||||||
|
webhook_secret: str,
|
||||||
|
owner: str,
|
||||||
|
repo: str,
|
||||||
|
gitea_url: str,
|
||||||
|
instance_url: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
hooks = paginated_github_request(
|
hooks = paginated_github_request(
|
||||||
f"{webhook_url}/api/v1/repos/{owner}/{repo}/hooks?limit=100",
|
f"{gitea_url}/api/v1/repos/{owner}/{repo}/hooks?limit=100",
|
||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
config = dict(
|
config = dict(
|
||||||
url=webhook_url + "change_hook/gitea",
|
url=instance_url + "change_hook/gitea",
|
||||||
content_type="json",
|
content_type="json",
|
||||||
insecure_ssl="0",
|
insecure_ssl="0",
|
||||||
secret=webhook_secret,
|
secret=webhook_secret,
|
||||||
|
@ -223,13 +267,13 @@ def create_repo_hook(
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
for hook in hooks:
|
for hook in hooks:
|
||||||
if hook["config"]["url"] == webhook_url + "change_hook/gitea":
|
if hook["config"]["url"] == instance_url + "change_hook/gitea":
|
||||||
log.msg(f"hook for {owner}/{repo} already exists")
|
log.msg(f"hook for {owner}/{repo} already exists")
|
||||||
return
|
return
|
||||||
|
|
||||||
log.msg(f"creating hook for {owner}/{repo}")
|
log.msg(f"creating hook for {owner}/{repo}")
|
||||||
http_request(
|
http_request(
|
||||||
f"{webhook_url}/api/v1/repos/{owner}/{repo}/hooks",
|
f"{gitea_url}/api/v1/repos/{owner}/{repo}/hooks",
|
||||||
method="POST",
|
method="POST",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
data=data,
|
data=data,
|
||||||
|
@ -240,13 +284,16 @@ class CreateGiteaProjectHooks(ThreadDeferredBuildStep):
|
||||||
name = "create_gitea_project_hooks"
|
name = "create_gitea_project_hooks"
|
||||||
|
|
||||||
config: GiteaConfig
|
config: GiteaConfig
|
||||||
|
instance_url: str
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
config: GiteaConfig,
|
config: GiteaConfig,
|
||||||
|
instance_url: str,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.instance_url = instance_url
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def run_deferred(self) -> None:
|
def run_deferred(self) -> None:
|
||||||
|
@ -254,11 +301,12 @@ class CreateGiteaProjectHooks(ThreadDeferredBuildStep):
|
||||||
|
|
||||||
for repo in repos:
|
for repo in repos:
|
||||||
create_repo_hook(
|
create_repo_hook(
|
||||||
self.config.token,
|
token=self.config.token,
|
||||||
self.config.webhook_secret,
|
webhook_secret=self.config.webhook_secret,
|
||||||
repo.owner.login,
|
owner=repo.owner.login,
|
||||||
repo.name,
|
repo=repo.name,
|
||||||
self.config.instance_url,
|
gitea_url=self.config.instance_url,
|
||||||
|
instance_url=self.instance_url,
|
||||||
)
|
)
|
||||||
|
|
||||||
def run_post(self) -> Any:
|
def run_post(self) -> Any:
|
||||||
|
|
|
@ -2,6 +2,7 @@ import json
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from itertools import starmap
|
from itertools import starmap
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -18,12 +19,14 @@ from buildbot.www.auth import AuthBase
|
||||||
from buildbot.www.avatar import AvatarBase, AvatarGitHub
|
from buildbot.www.avatar import AvatarBase, AvatarGitHub
|
||||||
from buildbot.www.oauth2 import GitHubAuth
|
from buildbot.www.oauth2 import GitHubAuth
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
from twisted.internet import defer
|
||||||
from twisted.logger import Logger
|
from twisted.logger import Logger
|
||||||
from twisted.python import log
|
from twisted.python import log
|
||||||
|
|
||||||
from .common import (
|
from .common import (
|
||||||
ThreadDeferredBuildStep,
|
ThreadDeferredBuildStep,
|
||||||
atomic_write_file,
|
atomic_write_file,
|
||||||
|
filter_for_combined_builds,
|
||||||
filter_repos_by_topic,
|
filter_repos_by_topic,
|
||||||
http_request,
|
http_request,
|
||||||
model_dump_project_cache,
|
model_dump_project_cache,
|
||||||
|
@ -310,6 +313,35 @@ class GithubAuthBackend(ABC):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ModifyingGitHubStatusPush(GitHubStatusPush):
|
||||||
|
def checkConfig(
|
||||||
|
self,
|
||||||
|
modifyingFilter: Callable[[Any], Any | None] = lambda x: x, # noqa: N803
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> Any:
|
||||||
|
self.modifyingFilter = modifyingFilter
|
||||||
|
|
||||||
|
return super().checkConfig(**kwargs)
|
||||||
|
|
||||||
|
def reconfigService(
|
||||||
|
self,
|
||||||
|
modifyingFilter: Callable[[Any], Any | None] = lambda x: x, # noqa: N803
|
||||||
|
**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
|
||||||
|
|
||||||
|
|
||||||
class GithubLegacyAuthBackend(GithubAuthBackend):
|
class GithubLegacyAuthBackend(GithubAuthBackend):
|
||||||
auth_type: GitHubLegacyConfig
|
auth_type: GitHubLegacyConfig
|
||||||
|
|
||||||
|
@ -329,12 +361,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 +449,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(
|
||||||
|
|
|
@ -179,6 +179,7 @@ class BuildbotNixConfig(BaseModel):
|
||||||
outputs_path: Path | None
|
outputs_path: Path | None
|
||||||
url: str
|
url: str
|
||||||
post_build_steps: list[PostBuildStep]
|
post_build_steps: list[PostBuildStep]
|
||||||
|
job_report_limit: int | None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def nix_workers_secret(self) -> str:
|
def nix_workers_secret(self) -> str:
|
||||||
|
|
|
@ -339,6 +339,17 @@ in
|
||||||
default = null;
|
default = null;
|
||||||
example = "/var/www/buildbot/nix-outputs";
|
example = "/var/www/buildbot/nix-outputs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
jobReportLimit = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.ints.unsigned;
|
||||||
|
description = ''
|
||||||
|
The max number of build jobs per `nix-eval` `buildbot-nix` will report to backends (GitHub, Gitea, etc.).
|
||||||
|
If set to `null`, report everything, if set to `n` (some unsiggned intereger), report builds individually
|
||||||
|
as long as the number of builds is less than or equal to `n`, then report builds using a combined
|
||||||
|
`nix-build-combined` build.
|
||||||
|
'';
|
||||||
|
default = 50;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
config = lib.mkMerge [
|
config = lib.mkMerge [
|
||||||
|
@ -487,6 +498,7 @@ in
|
||||||
outputs_path = cfg.outputsPath;
|
outputs_path = cfg.outputsPath;
|
||||||
url = config.services.buildbot-nix.master.webhookBaseUrl;
|
url = config.services.buildbot-nix.master.webhookBaseUrl;
|
||||||
post_build_steps = cfg.postBuildSteps;
|
post_build_steps = cfg.postBuildSteps;
|
||||||
|
job_report_limit=if cfg.jobReportLimit == null then "None" else builtins.toJSON cfg.jobReportLimit;
|
||||||
}}").read_text()))
|
}}").read_text()))
|
||||||
)
|
)
|
||||||
''
|
''
|
||||||
|
@ -500,7 +512,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
|
||||||
|
|
Loading…
Reference in a new issue