nixos/ssh: use correct executable for grep in ssh-askpass-wrapper (#373746)
[NixPkgs.git] / nixos / modules / services / continuous-integration / buildbot / master.nix
blob398a1c09973e6310993cd5f46327f85f8c39b774
1 # NixOS module for Buildbot continuous integration server.
3   config,
4   lib,
5   options,
6   pkgs,
7   ...
8 }:
9 let
10   cfg = config.services.buildbot-master;
11   opt = options.services.buildbot-master;
13   package = cfg.package.python.pkgs.toPythonModule cfg.package;
14   python = cfg.package.python;
16   escapeStr = lib.escape [ "'" ];
18   defaultMasterCfg = pkgs.writeText "master.cfg" ''
19     from buildbot.plugins import *
20     ${cfg.extraImports}
21     factory = util.BuildFactory()
22     c = BuildmasterConfig = dict(
23      workers       = [${lib.concatStringsSep "," cfg.workers}],
24      protocols     = { 'pb': {'port': ${toString cfg.pbPort} } },
25      title         = '${escapeStr cfg.title}',
26      titleURL      = '${escapeStr cfg.titleUrl}',
27      buildbotURL   = '${escapeStr cfg.buildbotUrl}',
28      db            = dict(db_url='${escapeStr cfg.dbUrl}'),
29      www           = dict(port=${toString cfg.port}),
30      change_source = [ ${lib.concatStringsSep "," cfg.changeSource} ],
31      schedulers    = [ ${lib.concatStringsSep "," cfg.schedulers} ],
32      builders      = [ ${lib.concatStringsSep "," cfg.builders} ],
33      services      = [ ${lib.concatStringsSep "," cfg.reporters} ],
34      configurators = [ ${lib.concatStringsSep "," cfg.configurators} ],
35     )
36     for step in [ ${lib.concatStringsSep "," cfg.factorySteps} ]:
37       factory.addStep(step)
39     ${cfg.extraConfig}
40   '';
42   tacFile = pkgs.writeText "buildbot-master.tac" ''
43     import os
45     from twisted.application import service
46     from buildbot.master import BuildMaster
48     basedir = '${cfg.buildbotDir}'
50     configfile = '${cfg.masterCfg}'
52     # Default umask for server
53     umask = None
55     # note: this line is matched against to check that this is a buildmaster
56     # directory; do not edit it.
57     application = service.Application('buildmaster')
59     m = BuildMaster(basedir, configfile, umask)
60     m.setServiceParent(application)
61   '';
65   options = {
66     services.buildbot-master = {
68       factorySteps = lib.mkOption {
69         type = lib.types.listOf lib.types.str;
70         description = "Factory Steps";
71         default = [ ];
72         example = [
73           "steps.Git(repourl='https://github.com/buildbot/pyflakes.git', mode='incremental')"
74           "steps.ShellCommand(command=['trial', 'pyflakes'])"
75         ];
76       };
78       changeSource = lib.mkOption {
79         type = lib.types.listOf lib.types.str;
80         description = "List of Change Sources.";
81         default = [ ];
82         example = [
83           "changes.GitPoller('https://github.com/buildbot/pyflakes.git', workdir='gitpoller-workdir', branch='master', pollinterval=300)"
84         ];
85       };
87       configurators = lib.mkOption {
88         type = lib.types.listOf lib.types.str;
89         description = "Configurator Steps, see https://docs.buildbot.net/latest/manual/configuration/configurators.html";
90         default = [ ];
91         example = [
92           "util.JanitorConfigurator(logHorizon=timedelta(weeks=4), hour=12, dayOfWeek=6)"
93         ];
94       };
96       enable = lib.mkOption {
97         type = lib.types.bool;
98         default = false;
99         description = "Whether to enable the Buildbot continuous integration server.";
100       };
102       extraConfig = lib.mkOption {
103         type = lib.types.lines;
104         description = "Extra configuration to append to master.cfg";
105         default = "c['buildbotNetUsageData'] = None";
106       };
108       extraImports = lib.mkOption {
109         type = lib.types.lines;
110         description = "Extra python imports to prepend to master.cfg";
111         default = "";
112         example = "from buildbot.process.project import Project";
113       };
115       masterCfg = lib.mkOption {
116         type = lib.types.path;
117         description = "Optionally pass master.cfg path. Other options in this configuration will be ignored.";
118         default = defaultMasterCfg;
119         defaultText = lib.literalMD ''generated configuration file'';
120         example = "/etc/nixos/buildbot/master.cfg";
121       };
123       schedulers = lib.mkOption {
124         type = lib.types.listOf lib.types.str;
125         description = "List of Schedulers.";
126         default = [
127           "schedulers.SingleBranchScheduler(name='all', change_filter=util.ChangeFilter(branch='master'), treeStableTimer=None, builderNames=['runtests'])"
128           "schedulers.ForceScheduler(name='force',builderNames=['runtests'])"
129         ];
130       };
132       builders = lib.mkOption {
133         type = lib.types.listOf lib.types.str;
134         description = "List of Builders.";
135         default = [
136           "util.BuilderConfig(name='runtests',workernames=['example-worker'],factory=factory)"
137         ];
138       };
140       workers = lib.mkOption {
141         type = lib.types.listOf lib.types.str;
142         description = "List of Workers.";
143         default = [ "worker.Worker('example-worker', 'pass')" ];
144       };
146       reporters = lib.mkOption {
147         default = [ ];
148         type = lib.types.listOf lib.types.str;
149         description = "List of reporter objects used to present build status to various users.";
150       };
152       user = lib.mkOption {
153         default = "buildbot";
154         type = lib.types.str;
155         description = "User the buildbot server should execute under.";
156       };
158       group = lib.mkOption {
159         default = "buildbot";
160         type = lib.types.str;
161         description = "Primary group of buildbot user.";
162       };
164       extraGroups = lib.mkOption {
165         type = lib.types.listOf lib.types.str;
166         default = [ ];
167         description = "List of extra groups that the buildbot user should be a part of.";
168       };
170       home = lib.mkOption {
171         default = "/home/buildbot";
172         type = lib.types.path;
173         description = "Buildbot home directory.";
174       };
176       buildbotDir = lib.mkOption {
177         default = "${cfg.home}/master";
178         defaultText = lib.literalExpression ''"''${config.${opt.home}}/master"'';
179         type = lib.types.path;
180         description = "Specifies the Buildbot directory.";
181       };
183       pbPort = lib.mkOption {
184         default = 9989;
185         type = lib.types.either lib.types.str lib.types.int;
186         example = "'tcp:9990:interface=127.0.0.1'";
187         description = ''
188           The buildmaster will listen on a TCP port of your choosing
189           for connections from workers.
190           It can also use this port for connections from remote Change Sources,
191           status clients, and debug tools.
192           This port should be visible to the outside world, and you’ll need to tell
193           your worker admins about your choice.
194           If put in (single) quotes, this can also be used as a connection string,
195           as defined in the [ConnectionStrings guide](https://twistedmatrix.com/documents/current/core/howto/endpoints.html).
196         '';
197       };
199       listenAddress = lib.mkOption {
200         default = "0.0.0.0";
201         type = lib.types.str;
202         description = "Specifies the bind address on which the buildbot HTTP interface listens.";
203       };
205       buildbotUrl = lib.mkOption {
206         default = "http://localhost:8010/";
207         type = lib.types.str;
208         description = "Specifies the Buildbot URL.";
209       };
211       title = lib.mkOption {
212         default = "Buildbot";
213         type = lib.types.str;
214         description = "Specifies the Buildbot Title.";
215       };
217       titleUrl = lib.mkOption {
218         default = "Buildbot";
219         type = lib.types.str;
220         description = "Specifies the Buildbot TitleURL.";
221       };
223       dbUrl = lib.mkOption {
224         default = "sqlite:///state.sqlite";
225         type = lib.types.str;
226         description = "Specifies the database connection string.";
227       };
229       port = lib.mkOption {
230         default = 8010;
231         type = lib.types.port;
232         description = "Specifies port number on which the buildbot HTTP interface listens.";
233       };
235       package = lib.mkPackageOption pkgs "buildbot-full" {
236         example = "buildbot";
237       };
239       packages = lib.mkOption {
240         default = [ pkgs.git ];
241         defaultText = lib.literalExpression "[ pkgs.git ]";
242         type = lib.types.listOf lib.types.package;
243         description = "Packages to add to PATH for the buildbot process.";
244       };
246       pythonPackages = lib.mkOption {
247         type = lib.types.functionTo (lib.types.listOf lib.types.package);
248         default = pythonPackages: with pythonPackages; [ ];
249         defaultText = lib.literalExpression "pythonPackages: with pythonPackages; [ ]";
250         description = "Packages to add the to the PYTHONPATH of the buildbot process.";
251         example = lib.literalExpression "pythonPackages: with pythonPackages; [ requests ]";
252       };
253     };
254   };
256   config = lib.mkIf cfg.enable {
257     users.groups = lib.optionalAttrs (cfg.group == "buildbot") {
258       buildbot = { };
259     };
261     users.users = lib.optionalAttrs (cfg.user == "buildbot") {
262       buildbot = {
263         description = "Buildbot User.";
264         isNormalUser = true;
265         createHome = true;
266         inherit (cfg) home group extraGroups;
267         useDefaultShell = true;
268       };
269     };
271     systemd.services.buildbot-master = {
272       description = "Buildbot Continuous Integration Server.";
273       after = [ "network.target" ];
274       wantedBy = [ "multi-user.target" ];
275       path = cfg.packages ++ cfg.pythonPackages python.pkgs;
276       environment.PYTHONPATH = "${
277         python.withPackages (self: cfg.pythonPackages self ++ [ package ])
278       }/${python.sitePackages}";
280       preStart = ''
281         mkdir -vp "${cfg.buildbotDir}"
282         # Link the tac file so buildbot command line tools recognize the directory
283         ln -sf "${tacFile}" "${cfg.buildbotDir}/buildbot.tac"
284         ${cfg.package}/bin/buildbot create-master --db "${cfg.dbUrl}" "${cfg.buildbotDir}"
285         rm -f buildbot.tac.new master.cfg.sample
286       '';
288       serviceConfig = {
289         Type = "simple";
290         User = cfg.user;
291         Group = cfg.group;
292         WorkingDirectory = cfg.home;
293         # NOTE: call twistd directly with stdout logging for systemd
294         ExecStart = "${python.pkgs.twisted}/bin/twistd -o --nodaemon --pidfile= --logfile - --python ${cfg.buildbotDir}/buildbot.tac";
295         # To reload on upgrade, set the following in your configuration:
296         # systemd.services.buildbot-master.reloadIfChanged = true;
297         ExecReload = [
298           "${pkgs.coreutils}/bin/ln -sf ${tacFile} ${cfg.buildbotDir}/buildbot.tac"
299           "${pkgs.coreutils}/bin/kill -HUP $MAINPID"
300         ];
301       };
302     };
303   };
305   imports = [
306     (lib.mkRenamedOptionModule
307       [ "services" "buildbot-master" "bpPort" ]
308       [ "services" "buildbot-master" "pbPort" ]
309     )
310     (lib.mkRemovedOptionModule [ "services" "buildbot-master" "status" ] ''
311       Since Buildbot 0.9.0, status targets are deprecated and ignored.
312       Review your configuration and migrate to reporters (available at services.buildbot-master.reporters).
313     '')
314   ];
316   meta.maintainers = lib.teams.buildbot.members;