1 # NixOS module for Buildbot continuous integration server.
2 { config, lib, options, pkgs, ... }:
4 cfg = config.services.buildbot-master;
5 opt = options.services.buildbot-master;
7 package = pkgs.python3.pkgs.toPythonModule cfg.package;
8 python = package.pythonModule;
10 escapeStr = lib.escape [ "'" ];
12 defaultMasterCfg = pkgs.writeText "master.cfg" ''
13 from buildbot.plugins import *
15 factory = util.BuildFactory()
16 c = BuildmasterConfig = dict(
17 workers = [${lib.concatStringsSep "," cfg.workers}],
18 protocols = { 'pb': {'port': ${toString cfg.pbPort} } },
19 title = '${escapeStr cfg.title}',
20 titleURL = '${escapeStr cfg.titleUrl}',
21 buildbotURL = '${escapeStr cfg.buildbotUrl}',
22 db = dict(db_url='${escapeStr cfg.dbUrl}'),
23 www = dict(port=${toString cfg.port}),
24 change_source = [ ${lib.concatStringsSep "," cfg.changeSource} ],
25 schedulers = [ ${lib.concatStringsSep "," cfg.schedulers} ],
26 builders = [ ${lib.concatStringsSep "," cfg.builders} ],
27 services = [ ${lib.concatStringsSep "," cfg.reporters} ],
28 configurators = [ ${lib.concatStringsSep "," cfg.configurators} ],
30 for step in [ ${lib.concatStringsSep "," cfg.factorySteps} ]:
36 tacFile = pkgs.writeText "buildbot-master.tac" ''
39 from twisted.application import service
40 from buildbot.master import BuildMaster
42 basedir = '${cfg.buildbotDir}'
44 configfile = '${cfg.masterCfg}'
46 # Default umask for server
49 # note: this line is matched against to check that this is a buildmaster
50 # directory; do not edit it.
51 application = service.Application('buildmaster')
53 m = BuildMaster(basedir, configfile, umask)
54 m.setServiceParent(application)
59 services.buildbot-master = {
61 factorySteps = lib.mkOption {
62 type = lib.types.listOf lib.types.str;
63 description = "Factory Steps";
66 "steps.Git(repourl='https://github.com/buildbot/pyflakes.git', mode='incremental')"
67 "steps.ShellCommand(command=['trial', 'pyflakes'])"
71 changeSource = lib.mkOption {
72 type = lib.types.listOf lib.types.str;
73 description = "List of Change Sources.";
76 "changes.GitPoller('https://github.com/buildbot/pyflakes.git', workdir='gitpoller-workdir', branch='master', pollinterval=300)"
80 configurators = lib.mkOption {
81 type = lib.types.listOf lib.types.str;
82 description = "Configurator Steps, see https://docs.buildbot.net/latest/manual/configuration/configurators.html";
85 "util.JanitorConfigurator(logHorizon=timedelta(weeks=4), hour=12, dayOfWeek=6)"
89 enable = lib.mkOption {
90 type = lib.types.bool;
92 description = "Whether to enable the Buildbot continuous integration server.";
95 extraConfig = lib.mkOption {
97 description = "Extra configuration to append to master.cfg";
98 default = "c['buildbotNetUsageData'] = None";
101 extraImports = lib.mkOption {
102 type = lib.types.str;
103 description = "Extra python imports to prepend to master.cfg";
105 example = "from buildbot.process.project import Project";
108 masterCfg = lib.mkOption {
109 type = lib.types.path;
110 description = "Optionally pass master.cfg path. Other options in this configuration will be ignored.";
111 default = defaultMasterCfg;
112 defaultText = lib.literalMD ''generated configuration file'';
113 example = "/etc/nixos/buildbot/master.cfg";
116 schedulers = lib.mkOption {
117 type = lib.types.listOf lib.types.str;
118 description = "List of Schedulers.";
120 "schedulers.SingleBranchScheduler(name='all', change_filter=util.ChangeFilter(branch='master'), treeStableTimer=None, builderNames=['runtests'])"
121 "schedulers.ForceScheduler(name='force',builderNames=['runtests'])"
125 builders = lib.mkOption {
126 type = lib.types.listOf lib.types.str;
127 description = "List of Builders.";
129 "util.BuilderConfig(name='runtests',workernames=['example-worker'],factory=factory)"
133 workers = lib.mkOption {
134 type = lib.types.listOf lib.types.str;
135 description = "List of Workers.";
136 default = [ "worker.Worker('example-worker', 'pass')" ];
139 reporters = lib.mkOption {
141 type = lib.types.listOf lib.types.str;
142 description = "List of reporter objects used to present build status to various users.";
145 user = lib.mkOption {
146 default = "buildbot";
147 type = lib.types.str;
148 description = "User the buildbot server should execute under.";
151 group = lib.mkOption {
152 default = "buildbot";
153 type = lib.types.str;
154 description = "Primary group of buildbot user.";
157 extraGroups = lib.mkOption {
158 type = lib.types.listOf lib.types.str;
160 description = "List of extra groups that the buildbot user should be a part of.";
163 home = lib.mkOption {
164 default = "/home/buildbot";
165 type = lib.types.path;
166 description = "Buildbot home directory.";
169 buildbotDir = lib.mkOption {
170 default = "${cfg.home}/master";
171 defaultText = lib.literalExpression ''"''${config.${opt.home}}/master"'';
172 type = lib.types.path;
173 description = "Specifies the Buildbot directory.";
176 pbPort = lib.mkOption {
178 type = lib.types.either lib.types.str lib.types.int;
179 example = "'tcp:9990:interface=127.0.0.1'";
181 The buildmaster will listen on a TCP port of your choosing
182 for connections from workers.
183 It can also use this port for connections from remote Change Sources,
184 status clients, and debug tools.
185 This port should be visible to the outside world, and you’ll need to tell
186 your worker admins about your choice.
187 If put in (single) quotes, this can also be used as a connection string,
188 as defined in the [ConnectionStrings guide](https://twistedmatrix.com/documents/current/core/howto/endpoints.html).
192 listenAddress = lib.mkOption {
194 type = lib.types.str;
195 description = "Specifies the bind address on which the buildbot HTTP interface listens.";
198 buildbotUrl = lib.mkOption {
199 default = "http://localhost:8010/";
200 type = lib.types.str;
201 description = "Specifies the Buildbot URL.";
204 title = lib.mkOption {
205 default = "Buildbot";
206 type = lib.types.str;
207 description = "Specifies the Buildbot Title.";
210 titleUrl = lib.mkOption {
211 default = "Buildbot";
212 type = lib.types.str;
213 description = "Specifies the Buildbot TitleURL.";
216 dbUrl = lib.mkOption {
217 default = "sqlite:///state.sqlite";
218 type = lib.types.str;
219 description = "Specifies the database connection string.";
222 port = lib.mkOption {
224 type = lib.types.port;
225 description = "Specifies port number on which the buildbot HTTP interface listens.";
228 package = lib.mkPackageOption pkgs "buildbot-full" {
229 example = "buildbot";
232 packages = lib.mkOption {
233 default = [ pkgs.git ];
234 defaultText = lib.literalExpression "[ pkgs.git ]";
235 type = lib.types.listOf lib.types.package;
236 description = "Packages to add to PATH for the buildbot process.";
239 pythonPackages = lib.mkOption {
240 type = lib.types.functionTo (lib.types.listOf lib.types.package);
241 default = pythonPackages: with pythonPackages; [ ];
242 defaultText = lib.literalExpression "pythonPackages: with pythonPackages; [ ]";
243 description = "Packages to add the to the PYTHONPATH of the buildbot process.";
244 example = lib.literalExpression "pythonPackages: with pythonPackages; [ requests ]";
249 config = lib.mkIf cfg.enable {
250 users.groups = lib.optionalAttrs (cfg.group == "buildbot") {
254 users.users = lib.optionalAttrs (cfg.user == "buildbot") {
256 description = "Buildbot User.";
259 inherit (cfg) home group extraGroups;
260 useDefaultShell = true;
264 systemd.services.buildbot-master = {
265 description = "Buildbot Continuous Integration Server.";
266 after = [ "network.target" ];
267 wantedBy = [ "multi-user.target" ];
268 path = cfg.packages ++ cfg.pythonPackages python.pkgs;
269 environment.PYTHONPATH = "${python.withPackages (self: cfg.pythonPackages self ++ [ package ])}/${python.sitePackages}";
272 mkdir -vp "${cfg.buildbotDir}"
273 # Link the tac file so buildbot command line tools recognize the directory
274 ln -sf "${tacFile}" "${cfg.buildbotDir}/buildbot.tac"
275 ${cfg.package}/bin/buildbot create-master --db "${cfg.dbUrl}" "${cfg.buildbotDir}"
276 rm -f buildbot.tac.new master.cfg.sample
283 WorkingDirectory = cfg.home;
284 # NOTE: call twistd directly with stdout logging for systemd
285 ExecStart = "${python.pkgs.twisted}/bin/twistd -o --nodaemon --pidfile= --logfile - --python ${cfg.buildbotDir}/buildbot.tac";
286 # To reload on upgrade, set the following in your configuration:
287 # systemd.services.buildbot-master.reloadIfChanged = true;
289 "${pkgs.coreutils}/bin/ln -sf ${tacFile} ${cfg.buildbotDir}/buildbot.tac"
290 "${pkgs.coreutils}/bin/kill -HUP $MAINPID"
297 (lib.mkRenamedOptionModule [ "services" "buildbot-master" "bpPort" ] [ "services" "buildbot-master" "pbPort" ])
298 (lib.mkRemovedOptionModule [ "services" "buildbot-master" "status" ] ''
299 Since Buildbot 0.9.0, status targets are deprecated and ignored.
300 Review your configuration and migrate to reporters (available at services.buildbot-master.reporters).
304 meta.maintainers = lib.teams.buildbot.members;