]> git.immae.eu Git - perso/Immae/Config/Nix.git/blob - modules/private/buildbot/default.nix
Add chatons infos
[perso/Immae/Config/Nix.git] / modules / private / buildbot / default.nix
1 { lib, pkgs, config, ... }:
2 let
3 varDir = "/var/lib/buildbot";
4 bb-python = buildbot.pythonModule;
5 buildbot = pkgs.immae-buildbot;
6 in
7 {
8 options = {
9 myServices.buildbot.enable = lib.mkOption {
10 type = lib.types.bool;
11 default = false;
12 description = ''
13 Whether to enable buildbot.
14 '';
15 };
16 };
17
18 config = lib.mkIf config.myServices.buildbot.enable {
19 myServices.chatonsProperties.hostings.buildbot = {
20 file.datetime = "2022-08-21T10:37:00";
21 hosting = {
22 name = "Buildbot";
23 description = "Python-based continuous integration testing framework";
24 type = "INSTANCE";
25 website = "https://git.immae.eu";
26 logo = "https://www.buildbot.net/img/icon.png";
27 status.level = "OK";
28 status.description = "OK";
29 registration.load = "OPEN";
30 install.type = "PACKAGE";
31 guide.user = "https://www.immae.eu/docs/forge-logicielle.html";
32 };
33 software = {
34 name = "Buildbot";
35 website = "https://www.buildbot.net/";
36 license.url = "https://github.com/buildbot/buildbot/blob/master/LICENSE";
37 license.name = "GNU General Public License v2.0";
38 version = pkgs.buildbot.version;
39 source.url = "https://github.com/buildbot/buildbot";
40 };
41 };
42 nixpkgs.overlays = [
43 (self: super: {
44 follow-systemd-unit = self.writeScriptBin "follow-systemd-unit" ''
45 #!${self.stdenv.shell}
46
47 set -euo pipefail
48
49 service=$1
50 before_invocation_id=$2
51
52 get_id() {
53 systemctl show -p InvocationID --value "$service"
54 }
55
56 while [ "$(get_id)" = "$before_invocation_id" ]; do sleep 1; done
57
58 invocation_id="$(get_id)"
59 cursor="$(mktemp)"
60 trap "rm -f $cursor" EXIT
61
62 get_logs() {
63 journalctl --quiet --cursor-file=$cursor INVOCATION_ID=$invocation_id + _SYSTEMD_INVOCATION_ID=$invocation_id
64 }
65
66 while [ -n "$(systemctl show -p Job --value "$service")" ]; do
67 get_logs
68 done
69 get_logs
70 '';
71 })
72 ];
73 ids.uids.buildbot = config.myEnv.buildbot.user.uid;
74 ids.gids.buildbot = config.myEnv.buildbot.user.gid;
75
76 users.groups.buildbot.gid = config.ids.gids.buildbot;
77 users.users.buildbot = {
78 name = "buildbot";
79 uid = config.ids.uids.buildbot;
80 group = "buildbot";
81 description = "Buildbot user";
82 home = varDir;
83 extraGroups = [ "keys" "systemd-journal" ];
84 useDefaultShell = true;
85 openssh.authorizedKeys.keys = [ config.myEnv.buildbot.ssh_key.public ];
86 };
87
88 services.websites.env.tools.watchPaths = lib.attrsets.mapAttrsToList
89 (k: project: config.secrets.fullPaths."buildbot/${project.name}/webhook-httpd-include")
90 config.myEnv.buildbot.projects;
91
92 services.websites.env.tools.vhostConfs.git.extraConfig = lib.attrsets.mapAttrsToList (k: project: ''
93 RedirectMatch permanent "^/buildbot/${project.name}$" "/buildbot/${project.name}/"
94 RewriteEngine On
95 RewriteRule ^/buildbot/${project.name}/ws(.*)$ unix:///run/buildbot/${project.name}.sock|ws://git.immae.eu/ws$1 [P,NE,QSA,L]
96 ProxyPass /buildbot/${project.name}/ unix:///run/buildbot/${project.name}.sock|http://${project.name}-git.immae.eu/
97 ProxyPassReverse /buildbot/${project.name}/ unix:///run/buildbot/${project.name}.sock|http://${project.name}-git.immae.eu/
98 <Location /buildbot/${project.name}/>
99 Use LDAPConnect
100 Require ldap-group cn=users,ou=${project.name},cn=buildbot,ou=services,dc=immae,dc=eu
101
102 SetEnvIf X-Url-Scheme https HTTPS=1
103 ProxyPreserveHost On
104 </Location>
105 <Location /buildbot/${project.name}/change_hook/base>
106 <RequireAny>
107 Require local
108 Require ldap-group cn=users,ou=${project.name},cn=buildbot,ou=services,dc=immae,dc=eu
109 Include ${config.secrets.fullPaths."buildbot/${project.name}/webhook-httpd-include"}
110 </RequireAny>
111 </Location>
112 '') config.myEnv.buildbot.projects;
113
114 system.activationScripts = lib.attrsets.mapAttrs' (k: project: lib.attrsets.nameValuePair "buildbot-${project.name}" {
115 deps = [ "users" "wrappers" ];
116 text = ''
117 install -m 755 -o buildbot -g buildbot -d ${varDir}/${project.name}
118
119 ${project.activationScript}
120 '';
121 }) config.myEnv.buildbot.projects;
122
123 secrets.keys = lib.listToAttrs (
124 lib.lists.flatten (
125 lib.attrsets.mapAttrsToList (k: project:
126 lib.attrsets.mapAttrsToList (k: v:
127 (lib.nameValuePair "buildbot/${project.name}/${k}" {
128 permissions = "0600";
129 user = "buildbot";
130 group = "buildbot";
131 text = if builtins.isFunction v then v pkgs config else v;
132 })
133 ) project.secrets
134 ++ [
135 (lib.nameValuePair "buildbot/${project.name}/webhook-httpd-include" {
136 permissions = "0600";
137 user = "wwwrun";
138 group = "wwwrun";
139 text = lib.optionalString (project.webhookTokens != null) ''
140 Require expr "req('Access-Key') in { ${builtins.concatStringsSep ", " (map (x: "'${x}'") project.webhookTokens)} }"
141 '';
142 })
143 (lib.nameValuePair "buildbot/${project.name}/environment_file" {
144 permissions = "0600";
145 user = "buildbot";
146 group = "buildbot";
147 text = let
148 project_env = with lib.attrsets;
149 mapAttrs' (k: v: nameValuePair "BUILDBOT_${k}" (if builtins.isFunction v then v pkgs else v)) project.environment //
150 {
151 BUILDBOT_PROJECT_DIR = ./projects + "/${project.name}";
152 BUILDBOT_WORKER_PORT = builtins.toString project.workerPort;
153 BUILDBOT_HOST = config.hostEnv.fqdn;
154 BUILDBOT_VIRT_URL = "qemu+ssh://libvirt@dilion.immae.eu/system";
155 };
156 in builtins.concatStringsSep "\n"
157 (lib.mapAttrsToList (envK: envV: "${envK}=${envV}") project_env);
158 })
159 ]
160 ) config.myEnv.buildbot.projects
161 )
162 ) // {
163 "buildbot/ldap" = {
164 permissions = "0600";
165 user = "buildbot";
166 group = "buildbot";
167 text = config.myEnv.buildbot.ldap.password;
168 };
169 "buildbot/worker_password" = {
170 permissions = "0600";
171 user = "buildbot";
172 group = "buildbot";
173 text = config.myEnv.buildbot.workerPassword;
174 };
175 "buildbot/ssh_key" = {
176 permissions = "0600";
177 user = "buildbot";
178 group = "buildbot";
179 text = config.myEnv.buildbot.ssh_key.private;
180 };
181 "buildbot/ssh_known_hosts" = {
182 permissions = "0644";
183 user = "buildbot";
184 group = "buildbot";
185 text = ''
186 git.immae.eu ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIFbhFTl2A2RJn5L51yxJM4XfCS2ZaiSX/jo9jFSdghF
187 eldiron ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIFbhFTl2A2RJn5L51yxJM4XfCS2ZaiSX/jo9jFSdghF
188 phare.normalesup.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN2GomItXICXpCtCFRMT2xuerqx2nLMO/3mNUuWyzFr1
189 '';
190 };
191 };
192
193 services.filesWatcher = lib.attrsets.mapAttrs' (k: project: lib.attrsets.nameValuePair "buildbot-${project.name}" {
194 restart = true;
195 paths = [
196 config.secrets.fullPaths."buildbot/ldap"
197 config.secrets.fullPaths."buildbot/worker_password"
198 config.secrets.fullPaths."buildbot/ssh_key"
199 config.secrets.fullPaths."buildbot/${project.name}/environment_file"
200 ] ++ lib.attrsets.mapAttrsToList (k: v: config.secrets.fullPaths."buildbot/${project.name}/${k}") project.secrets;
201 }) config.myEnv.buildbot.projects;
202
203 systemd.slices.buildbot = {
204 description = "buildbot slice";
205 };
206
207 networking.firewall.allowedTCPPorts = lib.attrsets.mapAttrsToList (k: v: v.workerPort) config.myEnv.buildbot.projects;
208 systemd.services = lib.attrsets.mapAttrs' (k: project: lib.attrsets.nameValuePair "buildbot-${project.name}" {
209 description = "Buildbot Continuous Integration Server ${project.name}.";
210 after = [ "network-online.target" ];
211 wantedBy = [ "multi-user.target" ];
212 path = project.packages pkgs;
213 preStart = let
214 master-cfg = "${buildbot.buildbot_common}/${bb-python.pythonForBuild.sitePackages}/buildbot_common/master.cfg";
215 tac_file = pkgs.writeText "buildbot.tac" ''
216 import os
217
218 from twisted.application import service
219 from buildbot.master import BuildMaster
220
221 basedir = '${varDir}/${project.name}'
222 rotateLength = 10000000
223 maxRotatedFiles = 10
224 configfile = '${master-cfg}'
225
226 # Default umask for server
227 umask = None
228
229 # if this is a relocatable tac file, get the directory containing the TAC
230 if basedir == '.':
231 import os
232 basedir = os.path.abspath(os.path.dirname(__file__))
233
234 # note: this line is matched against to check that this is a buildmaster
235 # directory; do not edit it.
236 application = service.Application('buildmaster')
237 from twisted.python.logfile import LogFile
238 from twisted.python.log import ILogObserver, FileLogObserver
239 logfile = LogFile.fromFullPath(os.path.join(basedir, "twistd.log"), rotateLength=rotateLength,
240 maxRotatedFiles=maxRotatedFiles)
241 application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
242
243 m = BuildMaster(basedir, configfile, umask)
244 m.setServiceParent(application)
245 m.log_rotation.rotateLength = rotateLength
246 m.log_rotation.maxRotatedFiles = maxRotatedFiles
247 '';
248 in ''
249 if [ ! -f ${varDir}/${project.name}/buildbot.tac ]; then
250 ${buildbot}/bin/buildbot create-master -c "${master-cfg}" "${varDir}/${project.name}"
251 rm -f ${varDir}/${project.name}/master.cfg.sample
252 rm -f ${varDir}/${project.name}/buildbot.tac
253 fi
254 ln -sf ${tac_file} ${varDir}/${project.name}/buildbot.tac
255 # different buildbots may be trying that simultaneously, add the || true to avoid complaining in case of race
256 install -Dm600 -o buildbot -g buildbot -T ${config.secrets.fullPaths."buildbot/ssh_key"} ${varDir}/buildbot_key || true
257 install -Dm600 -o buildbot -g buildbot -T ${config.secrets.fullPaths."buildbot/ssh_known_hosts"} ${varDir}/buildbot_hosts || true
258 buildbot_secrets=${varDir}/${project.name}/secrets
259 install -m 0700 -o buildbot -g buildbot -d $buildbot_secrets
260 install -Dm600 -o buildbot -g buildbot -T ${config.secrets.fullPaths."buildbot/ldap"} $buildbot_secrets/ldap
261 install -Dm600 -o buildbot -g buildbot -T ${config.secrets.fullPaths."buildbot/worker_password"} $buildbot_secrets/worker_password
262 ${builtins.concatStringsSep "\n" (lib.attrsets.mapAttrsToList
263 (k: v: "install -Dm600 -o buildbot -g buildbot -T ${config.secrets.fullPaths."buildbot/${project.name}/${k}"} $buildbot_secrets/${k}") project.secrets
264 )}
265 ${buildbot}/bin/buildbot upgrade-master ${varDir}/${project.name}
266 '';
267 environment = let
268 HOME = "${varDir}/${project.name}";
269 PYTHONPATH = "${bb-python.withPackages (self:
270 buildbot.common_packages self ++
271 [ (buildbot.buildbot_config project) ]
272 )}/${bb-python.sitePackages}${if project.pythonPathHome then ":${varDir}/${project.name}/.local/${bb-python.sitePackages}" else ""}";
273 in { inherit PYTHONPATH HOME; };
274
275 serviceConfig = {
276 Slice = "buildbot.slice";
277 Type = "forking";
278 User = "buildbot";
279 Group = "buildbot";
280 RuntimeDirectory = "buildbot";
281 RuntimeDirectoryPreserve = "yes";
282 StateDirectory = "buildbot";
283 SupplementaryGroups = "keys";
284 WorkingDirectory = "${varDir}/${project.name}";
285 ExecStart = "${buildbot}/bin/buildbot start";
286 EnvironmentFile = config.secrets.fullPaths."buildbot/${project.name}/environment_file";
287 };
288 }) config.myEnv.buildbot.projects;
289 };
290 }