1{ config, lib, pkgs, ... }:
2with lib;
3let
4 cfg = config.services.jenkins;
5in {
6 options = {
7 services.jenkins = {
8 enable = mkOption {
9 type = types.bool;
10 default = false;
11 description = ''
12 Whether to enable the jenkins continuous integration server.
13 '';
14 };
15
16 user = mkOption {
17 default = "jenkins";
18 type = types.str;
19 description = ''
20 User the jenkins server should execute under.
21 '';
22 };
23
24 group = mkOption {
25 default = "jenkins";
26 type = types.str;
27 description = ''
28 If the default user "jenkins" is configured then this is the primary
29 group of that user.
30 '';
31 };
32
33 extraGroups = mkOption {
34 type = types.listOf types.str;
35 default = [ ];
36 example = [ "wheel" "dialout" ];
37 description = ''
38 List of extra groups that the "jenkins" user should be a part of.
39 '';
40 };
41
42 home = mkOption {
43 default = "/var/lib/jenkins";
44 type = types.path;
45 description = ''
46 The path to use as JENKINS_HOME. If the default user "jenkins" is configured then
47 this is the home of the "jenkins" user.
48 '';
49 };
50
51 listenAddress = mkOption {
52 default = "0.0.0.0";
53 example = "localhost";
54 type = types.str;
55 description = ''
56 Specifies the bind address on which the jenkins HTTP interface listens.
57 The default is the wildcard address.
58 '';
59 };
60
61 port = mkOption {
62 default = 8080;
63 type = types.int;
64 description = ''
65 Specifies port number on which the jenkins HTTP interface listens.
66 The default is 8080.
67 '';
68 };
69
70 prefix = mkOption {
71 default = "";
72 example = "/jenkins";
73 type = types.str;
74 description = ''
75 Specifies a urlPrefix to use with jenkins.
76 If the example /jenkins is given, the jenkins server will be
77 accessible using localhost:8080/jenkins.
78 '';
79 };
80
81 packages = mkOption {
82 default = [ pkgs.stdenv pkgs.git pkgs.jdk config.programs.ssh.package pkgs.nix ];
83 defaultText = "[ pkgs.stdenv pkgs.git pkgs.jdk config.programs.ssh.package pkgs.nix ]";
84 type = types.listOf types.package;
85 description = ''
86 Packages to add to PATH for the jenkins process.
87 '';
88 };
89
90 environment = mkOption {
91 default = { };
92 type = with types; attrsOf str;
93 description = ''
94 Additional environment variables to be passed to the jenkins process.
95 As a base environment, jenkins receives NIX_PATH from
96 <option>environment.sessionVariables</option>, NIX_REMOTE is set to
97 "daemon" and JENKINS_HOME is set to the value of
98 <option>services.jenkins.home</option>.
99 This option has precedence and can be used to override those
100 mentioned variables.
101 '';
102 };
103
104 extraOptions = mkOption {
105 type = types.listOf types.str;
106 default = [ ];
107 example = [ "--debug=9" ];
108 description = ''
109 Additional command line arguments to pass to Jenkins.
110 '';
111 };
112 };
113 };
114
115 config = mkIf cfg.enable {
116 users.extraGroups = optional (cfg.group == "jenkins") {
117 name = "jenkins";
118 gid = config.ids.gids.jenkins;
119 };
120
121 users.extraUsers = optional (cfg.user == "jenkins") {
122 name = "jenkins";
123 description = "jenkins user";
124 createHome = true;
125 home = cfg.home;
126 group = cfg.group;
127 extraGroups = cfg.extraGroups;
128 useDefaultShell = true;
129 uid = config.ids.uids.jenkins;
130 };
131
132 systemd.services.jenkins = {
133 description = "Jenkins Continuous Integration Server";
134 after = [ "network.target" ];
135 wantedBy = [ "multi-user.target" ];
136
137 environment =
138 let
139 selectedSessionVars =
140 lib.filterAttrs (n: v: builtins.elem n [ "NIX_PATH" ])
141 config.environment.sessionVariables;
142 in
143 selectedSessionVars //
144 { JENKINS_HOME = cfg.home;
145 NIX_REMOTE = "daemon";
146 } //
147 cfg.environment;
148
149 path = cfg.packages;
150
151 # Force .war (re)extraction, or else we might run stale Jenkins.
152 preStart = ''
153 rm -rf ${cfg.home}/war
154 '';
155
156 script = ''
157 ${pkgs.jdk}/bin/java -jar ${pkgs.jenkins}/webapps/jenkins.war --httpListenAddress=${cfg.listenAddress} \
158 --httpPort=${toString cfg.port} \
159 --prefix=${cfg.prefix} \
160 ${concatStringsSep " " cfg.extraOptions}
161 '';
162
163 postStart = ''
164 until [[ $(${pkgs.curl.bin}/bin/curl -s --head -w '\n%{http_code}' http://${cfg.listenAddress}:${toString cfg.port}${cfg.prefix} | tail -n1) =~ ^(200|403)$ ]]; do
165 sleep 1
166 done
167 '';
168
169 serviceConfig = {
170 User = cfg.user;
171 };
172 };
173 };
174}