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 plugins = mkOption { 105 default = null; 106 type = types.nullOr (types.attrsOf types.package); 107 description = '' 108 A set of plugins to activate. Note that this will completely 109 remove and replace any previously installed plugins. If you 110 have manually-installed plugins that you want to keep while 111 using this module, set this option to 112 <literal>null</literal>. You can generate this set with a 113 tool such as <literal>jenkinsPlugins2nix</literal>. 114 ''; 115 example = literalExample '' 116 import path/to/jenkinsPlugins2nix-generated-plugins.nix { inherit (pkgs) fetchurl stdenv; } 117 ''; 118 }; 119 120 extraOptions = mkOption { 121 type = types.listOf types.str; 122 default = [ ]; 123 example = [ "--debug=9" ]; 124 description = '' 125 Additional command line arguments to pass to Jenkins. 126 ''; 127 }; 128 129 extraJavaOptions = mkOption { 130 type = types.listOf types.str; 131 default = [ ]; 132 example = [ "-Xmx80m" ]; 133 description = '' 134 Additional command line arguments to pass to the Java run time (as opposed to Jenkins). 135 ''; 136 }; 137 }; 138 }; 139 140 config = mkIf cfg.enable { 141 users.extraGroups = optional (cfg.group == "jenkins") { 142 name = "jenkins"; 143 gid = config.ids.gids.jenkins; 144 }; 145 146 users.extraUsers = optional (cfg.user == "jenkins") { 147 name = "jenkins"; 148 description = "jenkins user"; 149 createHome = true; 150 home = cfg.home; 151 group = cfg.group; 152 extraGroups = cfg.extraGroups; 153 useDefaultShell = true; 154 uid = config.ids.uids.jenkins; 155 }; 156 157 systemd.services.jenkins = { 158 description = "Jenkins Continuous Integration Server"; 159 after = [ "network.target" ]; 160 wantedBy = [ "multi-user.target" ]; 161 162 environment = 163 let 164 selectedSessionVars = 165 lib.filterAttrs (n: v: builtins.elem n [ "NIX_PATH" ]) 166 config.environment.sessionVariables; 167 in 168 selectedSessionVars // 169 { JENKINS_HOME = cfg.home; 170 NIX_REMOTE = "daemon"; 171 } // 172 cfg.environment; 173 174 path = cfg.packages; 175 176 # Force .war (re)extraction, or else we might run stale Jenkins. 177 178 preStart = 179 let replacePlugins = 180 if isNull cfg.plugins 181 then "" 182 else 183 let pluginCmds = lib.attrsets.mapAttrsToList 184 (n: v: "cp ${v} ${cfg.home}/plugins/${n}.hpi") 185 cfg.plugins; 186 in '' 187 rm -r ${cfg.home}/plugins || true 188 mkdir -p ${cfg.home}/plugins 189 ${lib.strings.concatStringsSep "\n" pluginCmds} 190 ''; 191 in '' 192 rm -rf ${cfg.home}/war 193 ${replacePlugins} 194 ''; 195 196 script = '' 197 ${pkgs.jdk}/bin/java ${concatStringsSep " " cfg.extraJavaOptions} -jar ${pkgs.jenkins}/webapps/jenkins.war --httpListenAddress=${cfg.listenAddress} \ 198 --httpPort=${toString cfg.port} \ 199 --prefix=${cfg.prefix} \ 200 ${concatStringsSep " " cfg.extraOptions} 201 ''; 202 203 postStart = '' 204 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 205 sleep 1 206 done 207 ''; 208 209 serviceConfig = { 210 User = cfg.user; 211 }; 212 }; 213 }; 214}