1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 cfg = config.services.jenkins; 9 jenkinsUrl = "http://${cfg.listenAddress}:${toString cfg.port}${cfg.prefix}"; 10in 11{ 12 options = { 13 services.jenkins = { 14 enable = lib.mkOption { 15 type = lib.types.bool; 16 default = false; 17 description = '' 18 Whether to enable the jenkins continuous integration server. 19 ''; 20 }; 21 22 user = lib.mkOption { 23 default = "jenkins"; 24 type = lib.types.str; 25 description = '' 26 User the jenkins server should execute under. 27 ''; 28 }; 29 30 group = lib.mkOption { 31 default = "jenkins"; 32 type = lib.types.str; 33 description = '' 34 If the default user "jenkins" is configured then this is the primary 35 group of that user. 36 ''; 37 }; 38 39 extraGroups = lib.mkOption { 40 type = lib.types.listOf lib.types.str; 41 default = [ ]; 42 example = [ 43 "wheel" 44 "dialout" 45 ]; 46 description = '' 47 List of extra groups that the "jenkins" user should be a part of. 48 ''; 49 }; 50 51 home = lib.mkOption { 52 default = "/var/lib/jenkins"; 53 type = lib.types.path; 54 description = '' 55 The path to use as JENKINS_HOME. If the default user "jenkins" is configured then 56 this is the home of the "jenkins" user. 57 ''; 58 }; 59 60 listenAddress = lib.mkOption { 61 default = "0.0.0.0"; 62 example = "localhost"; 63 type = lib.types.str; 64 description = '' 65 Specifies the bind address on which the jenkins HTTP interface listens. 66 The default is the wildcard address. 67 ''; 68 }; 69 70 port = lib.mkOption { 71 default = 8080; 72 type = lib.types.port; 73 description = '' 74 Specifies port number on which the jenkins HTTP interface listens. 75 The default is 8080. 76 ''; 77 }; 78 79 prefix = lib.mkOption { 80 default = ""; 81 example = "/jenkins"; 82 type = lib.types.str; 83 description = '' 84 Specifies a urlPrefix to use with jenkins. 85 If the example /jenkins is given, the jenkins server will be 86 accessible using localhost:8080/jenkins. 87 ''; 88 }; 89 90 package = lib.mkPackageOption pkgs "jenkins" { }; 91 92 packages = lib.mkOption { 93 default = [ 94 pkgs.stdenv 95 pkgs.git 96 pkgs.jdk17 97 config.programs.ssh.package 98 pkgs.nix 99 ]; 100 defaultText = lib.literalExpression "[ pkgs.stdenv pkgs.git pkgs.jdk17 config.programs.ssh.package pkgs.nix ]"; 101 type = lib.types.listOf lib.types.package; 102 description = '' 103 Packages to add to PATH for the jenkins process. 104 ''; 105 }; 106 107 environment = lib.mkOption { 108 default = { }; 109 type = with lib.types; attrsOf str; 110 description = '' 111 Additional environment variables to be passed to the jenkins process. 112 As a base environment, jenkins receives NIX_PATH from 113 {option}`environment.sessionVariables`, NIX_REMOTE is set to 114 "daemon" and JENKINS_HOME is set to the value of 115 {option}`services.jenkins.home`. 116 This option has precedence and can be used to override those 117 mentioned variables. 118 ''; 119 }; 120 121 plugins = lib.mkOption { 122 default = null; 123 type = lib.types.nullOr (lib.types.attrsOf lib.types.package); 124 description = '' 125 A set of plugins to activate. Note that this will completely 126 remove and replace any previously installed plugins. If you 127 have manually-installed plugins that you want to keep while 128 using this module, set this option to 129 `null`. You can generate this set with a 130 tool such as `jenkinsPlugins2nix`. 131 ''; 132 example = lib.literalExpression '' 133 import path/to/jenkinsPlugins2nix-generated-plugins.nix { inherit (pkgs) fetchurl stdenv; } 134 ''; 135 }; 136 137 extraOptions = lib.mkOption { 138 type = lib.types.listOf lib.types.str; 139 default = [ ]; 140 example = [ "--debug=9" ]; 141 description = '' 142 Additional command line arguments to pass to Jenkins. 143 ''; 144 }; 145 146 extraJavaOptions = lib.mkOption { 147 type = lib.types.listOf lib.types.str; 148 default = [ ]; 149 example = [ "-Xmx80m" ]; 150 description = '' 151 Additional command line arguments to pass to the Java run time (as opposed to Jenkins). 152 ''; 153 }; 154 155 withCLI = lib.mkOption { 156 type = lib.types.bool; 157 default = false; 158 description = '' 159 Whether to make the CLI available. 160 161 More info about the CLI available at 162 [ 163 https://www.jenkins.io/doc/book/managing/cli](https://www.jenkins.io/doc/book/managing/cli) . 164 ''; 165 }; 166 }; 167 }; 168 169 config = lib.mkIf cfg.enable { 170 environment = { 171 # server references the dejavu fonts 172 systemPackages = [ 173 pkgs.dejavu_fonts 174 ] ++ lib.optional cfg.withCLI cfg.package; 175 176 variables = 177 { } 178 // lib.optionalAttrs cfg.withCLI { 179 # Make it more convenient to use the `jenkins-cli`. 180 JENKINS_URL = jenkinsUrl; 181 }; 182 }; 183 184 users.groups = lib.optionalAttrs (cfg.group == "jenkins") { 185 jenkins.gid = config.ids.gids.jenkins; 186 }; 187 188 users.users = lib.optionalAttrs (cfg.user == "jenkins") { 189 jenkins = { 190 description = "jenkins user"; 191 createHome = true; 192 home = cfg.home; 193 group = cfg.group; 194 extraGroups = cfg.extraGroups; 195 useDefaultShell = true; 196 uid = config.ids.uids.jenkins; 197 }; 198 }; 199 200 systemd.services.jenkins = { 201 description = "Jenkins Continuous Integration Server"; 202 after = [ "network.target" ]; 203 wantedBy = [ "multi-user.target" ]; 204 205 environment = 206 let 207 selectedSessionVars = lib.filterAttrs ( 208 n: v: builtins.elem n [ "NIX_PATH" ] 209 ) config.environment.sessionVariables; 210 in 211 selectedSessionVars 212 // { 213 JENKINS_HOME = cfg.home; 214 NIX_REMOTE = "daemon"; 215 } 216 // cfg.environment; 217 218 path = cfg.packages; 219 220 # Force .war (re)extraction, or else we might run stale Jenkins. 221 222 preStart = 223 let 224 replacePlugins = lib.optionalString (cfg.plugins != null) ( 225 let 226 pluginCmds = lib.mapAttrsToList (n: v: "cp ${v} ${cfg.home}/plugins/${n}.jpi") cfg.plugins; 227 in 228 '' 229 rm -r ${cfg.home}/plugins || true 230 mkdir -p ${cfg.home}/plugins 231 ${lib.concatStringsSep "\n" pluginCmds} 232 '' 233 ); 234 in 235 '' 236 rm -rf ${cfg.home}/war 237 ${replacePlugins} 238 ''; 239 240 # For reference: https://wiki.jenkins.io/display/JENKINS/JenkinsLinuxStartupScript 241 script = '' 242 ${pkgs.jdk17}/bin/java ${lib.concatStringsSep " " cfg.extraJavaOptions} -jar ${cfg.package}/webapps/jenkins.war --httpListenAddress=${cfg.listenAddress} \ 243 --httpPort=${toString cfg.port} \ 244 --prefix=${cfg.prefix} \ 245 -Djava.awt.headless=true \ 246 ${lib.concatStringsSep " " cfg.extraOptions} 247 ''; 248 249 postStart = '' 250 until [[ $(${pkgs.curl.bin}/bin/curl -L -s --head -w '\n%{http_code}' ${jenkinsUrl} | tail -n1) =~ ^(200|403)$ ]]; do 251 sleep 1 252 done 253 ''; 254 255 serviceConfig = { 256 User = cfg.user; 257 StateDirectory = lib.mkIf (lib.hasPrefix "/var/lib/jenkins" cfg.home) "jenkins"; 258 # For (possible) socket use 259 RuntimeDirectory = "jenkins"; 260 }; 261 }; 262 }; 263}