--- /dev/null
+{ lib, pkgs, config, myconfig, ... }:
+let
+ varDir = "/var/lib/buildbot";
+ buildbot_common = pkgs.python3Packages.buildPythonPackage rec {
+ name = "buildbot_common";
+ src = ./common;
+ format = "other";
+ installPhase = ''
+ mkdir -p $out/${pkgs.python3.pythonForBuild.sitePackages}
+ cp -a $src $out/${pkgs.python3.pythonForBuild.sitePackages}/buildbot_common
+ '';
+ };
+ buildbot = pkgs.python3Packages.buildbot-full;
+in
+{
+ options = {
+ myServices.buildbot.enable = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether to enable buildbot.
+ '';
+ };
+ };
+
+ config = lib.mkIf config.myServices.buildbot.enable {
+ ids.uids.buildbot = myconfig.env.buildbot.user.uid;
+ ids.gids.buildbot = myconfig.env.buildbot.user.gid;
+
+ users.groups.buildbot.gid = config.ids.gids.buildbot;
+ users.users.buildbot = {
+ name = "buildbot";
+ uid = config.ids.uids.buildbot;
+ group = "buildbot";
+ description = "Buildbot user";
+ home = varDir;
+ extraGroups = [ "keys" ];
+ };
+
+ services.websites.tools.vhostConfs.git.extraConfig = lib.attrsets.mapAttrsToList (k: project: ''
+ RedirectMatch permanent "^/buildbot/${project.name}$" "/buildbot/${project.name}/"
+ RewriteEngine On
+ RewriteRule ^/buildbot/${project.name}/ws(.*)$ unix:///run/buildbot/${project.name}.sock|ws://git.immae.eu/ws$1 [P,NE,QSA,L]
+ ProxyPass /buildbot/${project.name}/ unix:///run/buildbot/${project.name}.sock|http://${project.name}-git.immae.eu/
+ ProxyPassReverse /buildbot/${project.name}/ unix:///run/buildbot/${project.name}.sock|http://${project.name}-git.immae.eu/
+ <Location /buildbot/${project.name}/>
+ Use LDAPConnect
+ Require ldap-group cn=users,ou=${project.name},cn=buildbot,ou=services,dc=immae,dc=eu
+
+ SetEnvIf X-Url-Scheme https HTTPS=1
+ ProxyPreserveHost On
+ </Location>
+ <Location /buildbot/${project.name}/change_hook/base>
+ <RequireAny>
+ Require local
+ Require ldap-group cn=users,ou=${project.name},cn=buildbot,ou=services,dc=immae,dc=eu
+ Include /var/secrets/buildbot/${project.name}/webhook-httpd-include
+ </RequireAny>
+ </Location>
+ '') myconfig.env.buildbot.projects;
+
+ system.activationScripts = lib.attrsets.mapAttrs' (k: project: lib.attrsets.nameValuePair "buildbot-${project.name}" {
+ deps = [ "users" "wrappers" ];
+ text = project.activationScript;
+ }) myconfig.env.buildbot.projects;
+
+ secrets.keys = (
+ lib.lists.flatten (
+ lib.attrsets.mapAttrsToList (k: project:
+ lib.attrsets.mapAttrsToList (k: v:
+ {
+ permissions = "0600";
+ user = "buildbot";
+ group = "buildbot";
+ text = v;
+ dest = "buildbot/${project.name}/${k}";
+ }
+ ) project.secrets
+ ++ [
+ {
+ permissions = "0600";
+ user = "wwwrun";
+ group = "wwwrun";
+ text = lib.optionalString (lib.attrsets.hasAttr "webhookTokens" project) ''
+ Require expr "req('Access-Key') in { ${builtins.concatStringsSep ", " (map (x: "'${x}'") project.webhookTokens)} }"
+ '';
+ dest = "buildbot/${project.name}/webhook-httpd-include";
+ }
+ ]
+ ) myconfig.env.buildbot.projects
+ )
+ ) ++ [
+ {
+ permissions = "0600";
+ user = "buildbot";
+ group = "buildbot";
+ text = myconfig.env.buildbot.ldap.password;
+ dest = "buildbot/ldap";
+ }
+ {
+ permissions = "0600";
+ user = "buildbot";
+ group = "buildbot";
+ text = builtins.readFile "${myconfig.privateFiles}/buildbot_ssh_key";
+ dest = "buildbot/ssh_key";
+ }
+ ];
+
+ systemd.services = lib.attrsets.mapAttrs' (k: project: lib.attrsets.nameValuePair "buildbot-${project.name}" {
+ description = "Buildbot Continuous Integration Server ${project.name}.";
+ after = [ "network-online.target" ];
+ wantedBy = [ "multi-user.target" ];
+ path = project.packages pkgs ++ (project.pythonPackages buildbot.pythonModule pkgs);
+ preStart = let
+ master-cfg = "${buildbot_common}/${pkgs.python3.pythonForBuild.sitePackages}/buildbot_common/master.cfg";
+ tac_file = pkgs.writeText "buildbot.tac" ''
+ import os
+
+ from twisted.application import service
+ from buildbot.master import BuildMaster
+
+ basedir = '${varDir}/${project.name}'
+ rotateLength = 10000000
+ maxRotatedFiles = 10
+ configfile = '${master-cfg}'
+
+ # Default umask for server
+ umask = None
+
+ # if this is a relocatable tac file, get the directory containing the TAC
+ if basedir == '.':
+ import os
+ basedir = os.path.abspath(os.path.dirname(__file__))
+
+ # note: this line is matched against to check that this is a buildmaster
+ # directory; do not edit it.
+ application = service.Application('buildmaster')
+ from twisted.python.logfile import LogFile
+ from twisted.python.log import ILogObserver, FileLogObserver
+ logfile = LogFile.fromFullPath(os.path.join(basedir, "twistd.log"), rotateLength=rotateLength,
+ maxRotatedFiles=maxRotatedFiles)
+ application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
+
+ m = BuildMaster(basedir, configfile, umask)
+ m.setServiceParent(application)
+ m.log_rotation.rotateLength = rotateLength
+ m.log_rotation.maxRotatedFiles = maxRotatedFiles
+ '';
+ in ''
+ if [ ! -f ${varDir}/${project.name}/buildbot.tac ]; then
+ ${buildbot}/bin/buildbot create-master -c "${master-cfg}" "${varDir}/${project.name}"
+ rm -f ${varDir}/${project.name}/master.cfg.sample
+ rm -f ${varDir}/${project.name}/buildbot.tac
+ fi
+ ln -sf ${tac_file} ${varDir}/${project.name}/buildbot.tac
+ # different buildbots may be trying that simultaneously, add the || true to avoid complaining in case of race
+ install -Dm600 -o buildbot -g buildbot -T /var/secrets/buildbot/ssh_key ${varDir}/buildbot_key || true
+ buildbot_secrets=${varDir}/${project.name}/secrets
+ install -m 0700 -o buildbot -g buildbot -d $buildbot_secrets
+ install -Dm600 -o buildbot -g buildbot -T /var/secrets/buildbot/ldap $buildbot_secrets/ldap
+ ${builtins.concatStringsSep "\n" (lib.attrsets.mapAttrsToList
+ (k: v: "install -Dm600 -o buildbot -g buildbot -T /var/secrets/buildbot/${project.name}/${k} $buildbot_secrets/${k}") project.secrets
+ )}
+ '';
+ environment = let
+ project_env = lib.attrsets.mapAttrs' (k: v: lib.attrsets.nameValuePair "BUILDBOT_${k}" v) project.environment;
+ buildbot_config = pkgs.python3Packages.buildPythonPackage (rec {
+ name = "buildbot_config-${project.name}";
+ src = ./projects + "/${project.name}";
+ format = "other";
+ installPhase = ''
+ mkdir -p $out/${pkgs.python3.pythonForBuild.sitePackages}
+ cp -a $src $out/${pkgs.python3.pythonForBuild.sitePackages}/buildbot_config
+ '';
+ });
+ HOME = "${varDir}/${project.name}";
+ PYTHONPATH = "${buildbot.pythonModule.withPackages (self: project.pythonPackages self pkgs ++ [
+ pkgs.python3Packages.wokkel
+ pkgs.python3Packages.treq pkgs.python3Packages.ldap3 buildbot
+ pkgs.python3Packages.buildbot-worker
+ buildbot_common buildbot_config
+ ])}/${buildbot.pythonModule.sitePackages}${if project.pythonPathHome then ":${varDir}/${project.name}/.local/${pkgs.python3.pythonForBuild.sitePackages}" else ""}";
+ in project_env // { inherit PYTHONPATH HOME; };
+
+ serviceConfig = {
+ Type = "forking";
+ User = "buildbot";
+ Group = "buildbot";
+ RuntimeDirectory = "buildbot";
+ RuntimeDirectoryPreserve = "yes";
+ StateDirectory = "buildbot";
+ SupplementaryGroups = "keys";
+ WorkingDirectory = "${varDir}/${project.name}";
+ ExecStart = "${buildbot}/bin/buildbot start";
+ };
+ }) myconfig.env.buildbot.projects;
+ };
+}