1# NixOS module for Buildbot continous integration server.
2
3{ config, lib, pkgs, ... }:
4
5with lib;
6
7let
8 cfg = config.services.buildbot-master;
9 escapeStr = s: escape ["'"] s;
10 masterCfg = if cfg.masterCfg == null then pkgs.writeText "master.cfg" ''
11 from buildbot.plugins import *
12 factory = util.BuildFactory()
13 c = BuildmasterConfig = dict(
14 workers = [${concatStringsSep "," cfg.workers}],
15 protocols = { 'pb': {'port': ${toString cfg.bpPort} } },
16 title = '${escapeStr cfg.title}',
17 titleURL = '${escapeStr cfg.titleUrl}',
18 buildbotURL = '${escapeStr cfg.buildbotUrl}',
19 db = dict(db_url='${escapeStr cfg.dbUrl}'),
20 www = dict(port=${toString cfg.port}),
21 change_source = [ ${concatStringsSep "," cfg.changeSource} ],
22 schedulers = [ ${concatStringsSep "," cfg.schedulers} ],
23 builders = [ ${concatStringsSep "," cfg.builders} ],
24 status = [ ${concatStringsSep "," cfg.status} ],
25 )
26 for step in [ ${concatStringsSep "," cfg.factorySteps} ]:
27 factory.addStep(step)
28
29 ${cfg.extraConfig}
30 ''
31 else cfg.masterCfg;
32
33in {
34 options = {
35 services.buildbot-master = {
36
37 factorySteps = mkOption {
38 type = types.listOf types.str;
39 description = "Factory Steps";
40 default = [];
41 example = [
42 "steps.Git(repourl='git://github.com/buildbot/pyflakes.git', mode='incremental')"
43 "steps.ShellCommand(command=['trial', 'pyflakes'])"
44 ];
45 };
46
47 changeSource = mkOption {
48 type = types.listOf types.str;
49 description = "List of Change Sources.";
50 default = [];
51 example = [
52 "changes.GitPoller('git://github.com/buildbot/pyflakes.git', workdir='gitpoller-workdir', branch='master', pollinterval=300)"
53 ];
54 };
55
56 enable = mkOption {
57 type = types.bool;
58 default = false;
59 description = "Whether to enable the Buildbot continuous integration server.";
60 };
61
62 extraConfig = mkOption {
63 type = types.str;
64 description = "Extra configuration to append to master.cfg";
65 default = "c['buildbotNetUsageData'] = None";
66 };
67
68 masterCfg = mkOption {
69 type = types.nullOr types.path;
70 description = "Optionally pass master.cfg path. Other options in this configuration will be ignored.";
71 default = null;
72 example = "/etc/nixos/buildbot/master.cfg";
73 };
74
75 schedulers = mkOption {
76 type = types.listOf types.str;
77 description = "List of Schedulers.";
78 default = [
79 "schedulers.SingleBranchScheduler(name='all', change_filter=util.ChangeFilter(branch='master'), treeStableTimer=None, builderNames=['runtests'])"
80 "schedulers.ForceScheduler(name='force',builderNames=['runtests'])"
81 ];
82 };
83
84 builders = mkOption {
85 type = types.listOf types.str;
86 description = "List of Builders.";
87 default = [
88 "util.BuilderConfig(name='runtests',workernames=['example-worker'],factory=factory)"
89 ];
90 };
91
92 workers = mkOption {
93 type = types.listOf types.str;
94 description = "List of Workers.";
95 default = [ "worker.Worker('example-worker', 'pass')" ];
96 };
97
98 status = mkOption {
99 default = [];
100 type = types.listOf types.str;
101 description = "List of status notification endpoints.";
102 };
103
104 user = mkOption {
105 default = "buildbot";
106 type = types.str;
107 description = "User the buildbot server should execute under.";
108 };
109
110 group = mkOption {
111 default = "buildbot";
112 type = types.str;
113 description = "Primary group of buildbot user.";
114 };
115
116 extraGroups = mkOption {
117 type = types.listOf types.str;
118 default = [];
119 description = "List of extra groups that the buildbot user should be a part of.";
120 };
121
122 home = mkOption {
123 default = "/home/buildbot";
124 type = types.path;
125 description = "Buildbot home directory.";
126 };
127
128 buildbotDir = mkOption {
129 default = "${cfg.home}/master";
130 type = types.path;
131 description = "Specifies the Buildbot directory.";
132 };
133
134 bpPort = mkOption {
135 default = 9989;
136 type = types.int;
137 description = "Port where the master will listen to Buildbot Worker.";
138 };
139
140 listenAddress = mkOption {
141 default = "0.0.0.0";
142 type = types.str;
143 description = "Specifies the bind address on which the buildbot HTTP interface listens.";
144 };
145
146 buildbotUrl = mkOption {
147 default = "http://localhost:8010/";
148 type = types.str;
149 description = "Specifies the Buildbot URL.";
150 };
151
152 title = mkOption {
153 default = "Buildbot";
154 type = types.str;
155 description = "Specifies the Buildbot Title.";
156 };
157
158 titleUrl = mkOption {
159 default = "Buildbot";
160 type = types.str;
161 description = "Specifies the Buildbot TitleURL.";
162 };
163
164 dbUrl = mkOption {
165 default = "sqlite:///state.sqlite";
166 type = types.str;
167 description = "Specifies the database connection string.";
168 };
169
170 port = mkOption {
171 default = 8010;
172 type = types.int;
173 description = "Specifies port number on which the buildbot HTTP interface listens.";
174 };
175
176 package = mkOption {
177 type = types.package;
178 default = pkgs.buildbot-full;
179 defaultText = "pkgs.buildbot-full";
180 description = "Package to use for buildbot.";
181 example = literalExample "pkgs.buildbot-full";
182 };
183
184 packages = mkOption {
185 default = with pkgs; [ python27Packages.twisted git ];
186 example = literalExample "[ pkgs.git ]";
187 type = types.listOf types.package;
188 description = "Packages to add to PATH for the buildbot process.";
189 };
190 };
191 };
192
193 config = mkIf cfg.enable {
194 users.extraGroups = optional (cfg.group == "buildbot") {
195 name = "buildbot";
196 };
197
198 users.extraUsers = optional (cfg.user == "buildbot") {
199 name = "buildbot";
200 description = "Buildbot User.";
201 isNormalUser = true;
202 createHome = true;
203 home = cfg.home;
204 group = cfg.group;
205 extraGroups = cfg.extraGroups;
206 useDefaultShell = true;
207 };
208
209 systemd.services.buildbot-master = {
210 description = "Buildbot Continuous Integration Server.";
211 after = [ "network-online.target" ];
212 wantedBy = [ "multi-user.target" ];
213 path = cfg.packages;
214
215 preStart = ''
216 env > envvars
217 mkdir -vp ${cfg.buildbotDir}
218 ln -sfv ${masterCfg} ${cfg.buildbotDir}/master.cfg
219 rm -fv $cfg.buildbotDir}/buildbot.tac
220 ${cfg.package}/bin/buildbot create-master ${cfg.buildbotDir}
221 '';
222
223 serviceConfig = {
224 Type = "simple";
225 User = cfg.user;
226 Group = cfg.group;
227 WorkingDirectory = cfg.home;
228 ExecStart = "${cfg.package}/bin/buildbot start --nodaemon ${cfg.buildbotDir}";
229 };
230
231 };
232 };
233
234 meta.maintainers = with lib.maintainers; [ nand0p mic92 ];
235
236}