{ nodes, config, lib, pkgs, ... }: let cfg = config.bagel.services.buildbot; cfgGerrit = nodes.gerrit01.config.bagel.services.gerrit; ssh-keys = import ../../common/ssh-keys.nix; inherit (lib) mkEnableOption mkOption mkIf types; in { options.bagel.services.buildbot = { enable = mkEnableOption "Buildbot"; domain = mkOption { type = types.str; }; builders = mkOption { type = types.listOf types.str; description = "List of builders to configure for Buildbot"; example = [ "builder-2" "builder-3" ]; }; }; config = mkIf cfg.enable { networking.firewall.allowedTCPPorts = [ 80 443 ]; age.secrets.buildbot-worker-password.file = ../../secrets/buildbot-worker-password.age; age.secrets.buildbot-oauth-secret.file = ../../secrets/buildbot-oauth-secret.age; age.secrets.buildbot-workers.file = ../../secrets/buildbot-workers.age; age.secrets.buildbot-service-key.file = ../../secrets/buildbot-service-key.age; age.secrets.buildbot-signing-key = { file = ../../secrets/buildbot-signing-key.age; owner = "buildbot-worker"; group = "buildbot-worker"; }; age.secrets.buildbot-remote-builder-key = { file = ../../secrets/buildbot-remote-builder-key.age; owner = "buildbot-worker"; group = "buildbot-worker"; }; services.nginx.virtualHosts.${cfg.domain} = { forceSSL = true; enableACME = true; extraConfig = '' add_header Access-Control-Allow-Credentials 'true' always; add_header Access-Control-Allow-Origin 'https://cl.forkos.org' always; ''; }; services.buildbot-nix.worker = { enable = true; workerPasswordFile = config.age.secrets.buildbot-worker-password.path; # All credits to eldritch horrors for this beauty. workerArchitectures = { # nix-eval-jobs runs under a lock, error reports do not (but are cheap) other = 8; } // ( lib.filterAttrs (n: v: lib.elem n config.services.buildbot-nix.coordinator.buildSystems) (lib.zipAttrsWith (_: lib.foldl' lib.add 0) (lib.concatMap (m: map (s: { ${s} = m.maxJobs; }) m.systems) config.services.buildbot-nix.coordinator.buildMachines)) ); }; services.buildbot-nix.coordinator = { enable = true; inherit (cfg) domain; oauth2 = { name = "Lix"; clientId = "forkos-buildbot"; clientSecretFile = config.age.secrets.buildbot-oauth-secret.path; resourceEndpoint = "https://identity.lix.systems"; authUri = "https://identity.lix.systems/realms/lix-project/protocol/openid-connect/auth"; tokenUri = "https://identity.lix.systems/realms/lix-project/protocol/openid-connect/token"; userinfoUri = "https://identity.lix.systems/realms/lix-project/protocol/openid-connect/userinfo"; }; # TODO(raito): this is not really necessary, we never have remote buildbot workers. # we can replace all of this with automatic localworker generation on buildbot-nix side. workersFile = config.age.secrets.buildbot-workers.path; allowedOrigins = [ "*.forkos.org" ]; # TODO(raito): is that really necessary when we can just collect buildMachines' systems? buildSystems = [ "x86_64-linux" ]; buildMachines = map (n: { hostName = nodes.${n}.config.networking.fqdn; protocol = "ssh-ng"; # Follows Hydra. maxJobs = 8; sshKey = config.age.secrets.buildbot-remote-builder-key.path; sshUser = "buildbot"; systems = [ "x86_64-linux" ]; supportedFeatures = nodes.${n}.config.nix.settings.system-features; # Contrary to how Nix works, here we can specify non-base64 public host keys. publicHostKey = ssh-keys.machines.${n}; } ) cfg.builders; gerrit = { domain = cfgGerrit.canonicalDomain; # Manually managed account… # TODO: https://git.lix.systems/the-distro/infra/issues/69 username = "buildbot"; port = cfgGerrit.port; privateKeyFile = config.age.secrets.buildbot-service-key.path; projects = [ "buildbot-test" "nixpkgs" "infra" ]; }; evalWorkerCount = 6; evalMaxMemorySize = "4096"; signingKeyFile = config.age.secrets.buildbot-signing-key.path; }; nix.settings.keep-derivations = true; nix.gc = { automatic = true; dates = "hourly"; }; }; }