From c3346978d5235d8450746cceda9225bec02d99fa Mon Sep 17 00:00:00 2001 From: magic_rb Date: Thu, 11 Jul 2024 20:55:12 +0200 Subject: [PATCH] Support per installation tokens directly in `GitHubStatusPush` Signed-off-by: magic_rb --- buildbot_nix/github_projects.py | 116 +----------------- ...allation-tokens-in-GithubStatusPush-.patch | 66 ++++++++++ nix/master.nix | 17 ++- 3 files changed, 86 insertions(+), 113 deletions(-) create mode 100644 nix/0001-Support-per-installation-tokens-in-GithubStatusPush-.patch diff --git a/buildbot_nix/github_projects.py b/buildbot_nix/github_projects.py index 2249bc3..58af41e 100644 --- a/buildbot_nix/github_projects.py +++ b/buildbot_nix/github_projects.py @@ -2,7 +2,6 @@ import json import os import signal from abc import ABC, abstractmethod -from collections.abc import Callable from dataclasses import dataclass from itertools import starmap from pathlib import Path @@ -257,114 +256,6 @@ class ReloadGithubProjects(ThreadDeferredBuildStep): return util.SUCCESS -class GitHubAppStatusPush(GitHubStatusPush): - token_source: Callable[[int], RepoToken] - project_id_source: Callable[[str], int] - saved_args: dict[str, Any] - saved_kwargs: dict[str, Any] - - def checkConfig( - self, - token_source: Callable[[int], RepoToken], - project_id_source: Callable[[str], int], - context: Any = None, - baseURL: Any = None, - verbose: Any = False, - debug: Any = None, - verify: Any = None, - generators: Any = None, - **kwargs: dict[str, Any], - ) -> Any: - if generators is None: - generators = self._create_default_generators() - - if "token" in kwargs: - del kwargs["token"] - super().checkConfig( - token="", - context=context, - baseURL=baseURL, - verbose=verbose, - debug=debug, - verify=verify, - generators=generators, - **kwargs, - ) - - def reconfigService( - self, - token_source: Callable[[int], RepoToken], - project_id_source: Callable[[str], int], - context: Any = None, - baseURL: Any = None, - verbose: Any = False, - debug: Any = None, - verify: Any = None, - generators: Any = None, - **kwargs: dict[str, Any], - ) -> Any: - if "saved_args" not in self or self.saved_args is None: - self.saved_args = {} - self.token_source = token_source - self.project_id_source = project_id_source - self.saved_kwargs = kwargs - self.saved_args["context"] = context - self.saved_args["baseURL"] = baseURL - self.saved_args["verbose"] = verbose - self.saved_args["debug"] = debug - self.saved_args["verify"] = verify - self.saved_args["generators"] = generators - - if generators is None: - generators = self._create_default_generators() - - if "token" in kwargs: - del kwargs["token"] - super().reconfigService( - token="", - context=context, - baseURL=baseURL, - verbose=verbose, - debug=debug, - verify=verify, - generators=generators, - **kwargs, - ) - - def sendMessage(self, reports: Any) -> Any: - build = reports[0]["builds"][0] - sourcestamps = build["buildset"].get("sourcestamps") - if not sourcestamps: - return None - - for sourcestamp in sourcestamps: - build["buildset"]["sourcestamps"] = [sourcestamp] - - token: str - - if "project" in sourcestamp and sourcestamp["project"] != "": - token = self.token_source( - self.project_id_source(sourcestamp["project"]) - ).get() - else: - token = "" - - super().reconfigService( - token, - context=self.saved_args["context"], - baseURL=self.saved_args["baseURL"], - verbose=self.saved_args["verbose"], - debug=self.saved_args["debug"], - verify=self.saved_args["verify"], - generators=self.saved_args["generators"], - **self.saved_kwargs, - ) - - return super().sendMessage(reports) - - return None - - class GithubAuthBackend(ABC): @abstractmethod def get_general_token(self) -> RepoToken: @@ -496,9 +387,10 @@ class GithubAppAuthBackend(GithubAuthBackend): return [GitHubAppSecretService(self.installation_tokens, self.jwt_token)] def create_reporter(self) -> ReporterBase: - return GitHubAppStatusPush( - token_source=lambda iid: self.installation_tokens[iid], - project_id_source=lambda project: self.project_id_map[project], + return GitHubStatusPush( + token=lambda project: self.installation_tokens[ + self.project_id_map[project] + ].get(), # Since we dynamically create build steps, # we use `virtual_builder_name` in the webinterface # so that we distinguish what has beeing build diff --git a/nix/0001-Support-per-installation-tokens-in-GithubStatusPush-.patch b/nix/0001-Support-per-installation-tokens-in-GithubStatusPush-.patch new file mode 100644 index 0000000..63facce --- /dev/null +++ b/nix/0001-Support-per-installation-tokens-in-GithubStatusPush-.patch @@ -0,0 +1,66 @@ +From 65dd3e63dc0dc34a531eee703baff715ba04d0a0 Mon Sep 17 00:00:00 2001 +From: magic_rb +Date: Wed, 10 Jul 2024 13:00:48 +0200 +Subject: [PATCH 1/3] Support per installation tokens in `GithubStatusPush` + reporter + +Signed-off-by: magic_rb +--- + master/buildbot/reporters/github.py | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +diff --git a/master/buildbot/reporters/github.py b/master/buildbot/reporters/github.py +index 3873c2676..e61e6495a 100644 +--- a/master/buildbot/reporters/github.py ++++ b/master/buildbot/reporters/github.py +@@ -70,7 +70,14 @@ class GitHubStatusPush(ReporterBase): + generators=None, + **kwargs, + ): +- token = yield self.renderSecrets(token) ++ headers = {} ++ if not callable(token): ++ token = yield self.renderSecrets(token) ++ headers['Authorization'] = 'token ' + token ++ else: ++ self.token_cache = {} ++ self.token_source = token ++ + self.debug = debug + self.verify = verify + self.verbose = verbose +@@ -89,7 +96,7 @@ class GitHubStatusPush(ReporterBase): + self._http = yield httpclientservice.HTTPClientService.getService( + self.master, + baseURL, +- headers={'Authorization': 'token ' + token, 'User-Agent': 'Buildbot'}, ++ headers={'User-Agent': 'Buildbot'} | headers, + debug=self.debug, + verify=self.verify, + ) +@@ -135,6 +142,11 @@ class GitHubStatusPush(ReporterBase): + txgithub is based on twisted's webclient agent, which is much less reliable and featureful + as txrequest (support for proxy, connection pool, keep alive, retry, etc) + """ ++ headers = {} ++ ++ if hasattr(self, 'token_source'): ++ headers['Authorization'] = 'token ' + self.token_source(f"{repo_user}/{repo_name}") ++ + payload = {'state': state} + + if description is not None: +@@ -147,7 +159,9 @@ class GitHubStatusPush(ReporterBase): + payload['context'] = context + + return self._http.post( +- '/'.join(['/repos', repo_user, repo_name, 'statuses', sha]), json=payload ++ '/'.join(['/repos', repo_user, repo_name, 'statuses', sha]), ++ json=payload, ++ headers=headers, + ) + + def is_status_2xx(self, code): +-- +2.44.1 + diff --git a/nix/master.nix b/nix/master.nix index 4085053..6daf4fa 100644 --- a/nix/master.nix +++ b/nix/master.nix @@ -409,7 +409,22 @@ in in "${if hasSSL then "https" else "http"}://${cfg.domain}/"; dbUrl = config.services.buildbot-nix.master.dbUrl; - package = cfg.package; + package = + let + fixPatch = { patch, name }: + pkgs.runCommand "${name}.patch" { } '' + sed -r \ + 's~^(---|\+\+\+) (a|b)/master/(.+)$~\1 \2/\3~' \ + ${patch} >$out + ''; + in + cfg.package.overrideAttrs (old: { + patches = + old.patches ++ [ + ./0001-Support-per-installation-tokens-in-GithubStatusPush-.patch #; + + ]; + }); pythonPackages = ps: [ ps.requests ps.treq