]> git.immae.eu Git - perso/Immae/Config/Nix.git/blobdiff - modules/private/buildbot/default.nix
Move rest of the modules outside of nixops
[perso/Immae/Config/Nix.git] / modules / private / buildbot / default.nix
diff --git a/modules/private/buildbot/default.nix b/modules/private/buildbot/default.nix
new file mode 100644 (file)
index 0000000..fa6a6f2
--- /dev/null
@@ -0,0 +1,198 @@
+{ 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;
+  };
+}