From 6d3c8a92c41af154b8645b4a85958986ff0b0303 Mon Sep 17 00:00:00 2001 From: Romain Paquet Date: Wed, 1 Oct 2025 14:21:25 +0200 Subject: [PATCH] add buildbot --- clanServices/buildbot/default.nix | 158 +++++++++++++++++++++++++ clanServices/buildbot/flake-module.nix | 4 + clanServices/flake-module.nix | 1 + flake.lock | 95 ++++++++++++++- flake.nix | 3 + infra/templates/turifer.dev.zone | 2 + machines/flake-module.nix | 29 +++++ machines/genepi/glance-config.nix | 5 + 8 files changed, 295 insertions(+), 2 deletions(-) create mode 100644 clanServices/buildbot/default.nix create mode 100644 clanServices/buildbot/flake-module.nix diff --git a/clanServices/buildbot/default.nix b/clanServices/buildbot/default.nix new file mode 100644 index 0000000..9ccab43 --- /dev/null +++ b/clanServices/buildbot/default.nix @@ -0,0 +1,158 @@ +{ self, ... }: +{ lib, ... }: +{ + _class = "clan.service"; + manifest.name = "buildbot"; + + roles.master = { + interface.options = { + domain = lib.mkOption { + type = lib.types.str; + description = "Domain name under which the buildbot frontend is reachable"; + example = "https://buildbot.example.com"; + }; + admins = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "List of usernames allowed to authenticate to the buildbot frontend"; + example = [ "Mic92" ]; + }; + topic = lib.mkOption { + type = lib.types.str; + description = "Name of the topic attached to repositories that should be built"; + example = "buildbot-nix"; + }; + gitea.instanceUrl = lib.mkOption { + type = lib.types.str; + description = "URL of the Gitea instance"; + example = "https://git.example.com"; + }; + }; + + perInstance = + { + settings, + roles, + ... + }: + { + nixosModule = + { + config, + lib, + pkgs, + ... + }: + { + imports = [ + self.inputs.buildbot-nix.nixosModules.buildbot-master + ]; + + services.buildbot-nix.master = { + enable = true; + workersFile = config.clan.core.vars.generators.buildbot.files.workers-file.path; + inherit (settings) domain admins; + + authBackend = "gitea"; + gitea = { + enable = true; + inherit (settings.gitea) instanceUrl; + inherit (settings) topic; + + tokenFile = config.clan.core.vars.generators.buildbot.files.api-token.path; + webhookSecretFile = config.clan.core.vars.generators.buildbot.files.webhook-secret.path; + + oauthId = config.clan.core.vars.generators.buildbot.files.oauth-id.value; + oauthSecretFile = config.clan.core.vars.generators.buildbot.files.oauth-secret.path; + }; + }; + + clan.core.vars.generators.buildbot = { + prompts.api-token = { + description = "gitea API token"; + type = "hidden"; + persist = true; + }; + prompts.webhook-secret = { + description = "gitea webhook secret"; + type = "hidden"; + persist = true; + }; + prompts.oauth-id = { + description = "oauth client id"; + persist = true; + }; + files.oauth-id.secret = false; + prompts.oauth-secret = { + description = "oauth secret"; + type = "hidden"; + persist = true; + }; + + dependencies = [ "buildbot-worker" ]; + files.workers-file.secret = true; + runtimeInputs = [ pkgs.python3 ]; + script = '' + python3 - << EOF + import os + import json + + password_path = os.path.join(os.environ.get("in"), "buildbot-worker/worker-password") + password = open(password_path).read().strip() + + workers = [ + { + "name": "${config.networking.hostName}", + "pass": password, + "cores": 4, + }, + ]; + + workers_file_path = os.path.join(os.environ.get("out"), "workers-file") + with open(workers_file_path, "w") as workers_file: + workers_file.write(json.dumps(workers)) + + EOF + ''; + }; + }; + }; + }; + + roles.worker = { + perInstance = + { + settings, + roles, + ... + }: + { + nixosModule = + { + config, + lib, + pkgs, + ... + }: + { + imports = [ + self.inputs.buildbot-nix.nixosModules.buildbot-worker + ]; + + services.buildbot-nix.worker = { + enable = true; + workerPasswordFile = config.clan.core.vars.generators.buildbot-worker.files.worker-password.path; + }; + + clan.core.vars.generators.buildbot-worker = { + files.worker-password = { }; + runtimeInputs = [ + pkgs.openssl + ]; + script = '' + openssl rand -hex 32 > "$out"/worker-password + ''; + }; + }; + }; + }; +} diff --git a/clanServices/buildbot/flake-module.nix b/clanServices/buildbot/flake-module.nix new file mode 100644 index 0000000..867a703 --- /dev/null +++ b/clanServices/buildbot/flake-module.nix @@ -0,0 +1,4 @@ +{ self, lib, ... }: +{ + clan.modules."@rpqt/buildbot" = lib.modules.importApply ./default.nix { inherit self; }; +} diff --git a/clanServices/flake-module.nix b/clanServices/flake-module.nix index 64844d9..34805a6 100644 --- a/clanServices/flake-module.nix +++ b/clanServices/flake-module.nix @@ -1,5 +1,6 @@ { imports = [ + ./buildbot/flake-module.nix ./prometheus/flake-module.nix ]; } diff --git a/flake.lock b/flake.lock index 99c23bb..793678a 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,28 @@ { "nodes": { + "buildbot-nix": { + "inputs": { + "flake-parts": "flake-parts", + "hercules-ci-effects": "hercules-ci-effects", + "nixpkgs": [ + "nixpkgs" + ], + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1758897213, + "narHash": "sha256-pLZgNsmCMhTWd8aRuGkK23ik5nclpIn1flnURKH6QjI=", + "owner": "nix-community", + "repo": "buildbot-nix", + "rev": "985d069a2a45cf4a571a4346107671adc2bd2a16", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "buildbot-nix", + "type": "github" + } + }, "clan-core": { "inputs": { "data-mesher": "data-mesher", @@ -15,7 +38,7 @@ ], "sops-nix": "sops-nix", "systems": "systems", - "treefmt-nix": "treefmt-nix" + "treefmt-nix": "treefmt-nix_2" }, "locked": { "lastModified": 1757595727, @@ -100,6 +123,27 @@ } }, "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "buildbot-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1756770412, + "narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "4524271976b625a4a605beefd893f270620fd751", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { "inputs": { "nixpkgs-lib": [ "nixpkgs" @@ -137,6 +181,31 @@ "type": "github" } }, + "hercules-ci-effects": { + "inputs": { + "flake-parts": [ + "buildbot-nix", + "flake-parts" + ], + "nixpkgs": [ + "buildbot-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1758022363, + "narHash": "sha256-ENUhCRWgSX4ni751HieNuQoq06dJvApV/Nm89kh+/A0=", + "owner": "hercules-ci", + "repo": "hercules-ci-effects", + "rev": "1a3667d33e247ad35ca250698d63f49a5453d824", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "hercules-ci-effects", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -384,9 +453,10 @@ }, "root": { "inputs": { + "buildbot-nix": "buildbot-nix", "clan-core": "clan-core", "disko": "disko_2", - "flake-parts": "flake-parts", + "flake-parts": "flake-parts_2", "home-manager": "home-manager", "ignis": "ignis", "impermanence": "impermanence", @@ -485,6 +555,27 @@ } }, "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "buildbot-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1758728421, + "narHash": "sha256-ySNJ008muQAds2JemiyrWYbwbG+V7S5wg3ZVKGHSFu8=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "5eda4ee8121f97b218f7cc73f5172098d458f1d1", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, + "treefmt-nix_2": { "inputs": { "nixpkgs": [ "clan-core", diff --git a/flake.nix b/flake.nix index 7a63b86..34ad27d 100644 --- a/flake.nix +++ b/flake.nix @@ -86,6 +86,9 @@ srvos.inputs.nixpkgs.follows = "nixpkgs"; vicinae.url = "github:vicinaehq/vicinae"; + + buildbot-nix.url = "github:nix-community/buildbot-nix"; + buildbot-nix.inputs.nixpkgs.follows = "nixpkgs"; }; nixConfig = { diff --git a/infra/templates/turifer.dev.zone b/infra/templates/turifer.dev.zone index 4a087d6..1dd4622 100644 --- a/infra/templates/turifer.dev.zone +++ b/infra/templates/turifer.dev.zone @@ -21,6 +21,8 @@ git.turifer.dev. 10800 IN A ${crocus_ipv4_address} git.turifer.dev. 10800 IN AAAA ${crocus_ipv6_address} %{ for addr in verbena_ipv4_addresses ~} +buildbot.turifer.dev. 10800 IN A ${addr} %{ endfor ~} %{ for addr in verbena_ipv6_addresses ~} +buildbot.turifer.dev. 10800 IN AAAA ${addr} %{ endfor ~} diff --git a/machines/flake-module.nix b/machines/flake-module.nix index f7fa3ba..5882452 100644 --- a/machines/flake-module.nix +++ b/machines/flake-module.nix @@ -180,6 +180,35 @@ }; }; }; + + buildbot = { + module.input = "self"; + module.name = "@rpqt/buildbot"; + + roles.master.machines.verbena = { + settings = { + domain = "buildbot.turifer.dev"; + admins = [ "rpqt" ]; + topic = "buildbot-nix"; + gitea.instanceUrl = "https://git.turifer.dev"; + }; + }; + + roles.master.extraModules = [ + { + services.nginx.virtualHosts."buildbot.turifer.dev" = { + enableACME = true; + forceSSL = true; + }; + + security.acme.certs."buildbot.turifer.dev" = { + email = "admin@turifer.dev"; + }; + } + ]; + + roles.worker.machines.verbena = { }; + }; }; }; } diff --git a/machines/genepi/glance-config.nix b/machines/genepi/glance-config.nix index f333fd7..3016681 100644 --- a/machines/genepi/glance-config.nix +++ b/machines/genepi/glance-config.nix @@ -84,6 +84,11 @@ url = "https://cloud.home.rpqt.fr"; icon = "sh:nextcloud"; } + { + title = "Buildbot"; + url = "https://buildbot.turifer.dev"; + icon = "https://www.buildbot.net/img/full_logo.svg"; + } ]; } {