1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 jenkinsCfg = config.services.jenkins; 7 cfg = config.services.jenkins.jobBuilder; 8 9in { 10 options = { 11 services.jenkins.jobBuilder = { 12 enable = mkOption { 13 type = types.bool; 14 default = false; 15 description = '' 16 Whether or not to enable the Jenkins Job Builder (JJB) service. It 17 allows defining jobs for Jenkins in a declarative manner. 18 19 Jobs managed through the Jenkins WebUI (or by other means) are left 20 unchanged. 21 22 Note that it really is declarative configuration; if you remove a 23 previously defined job, the corresponding job directory will be 24 deleted. 25 26 Please see the Jenkins Job Builder documentation for more info: 27 <link xlink:href="http://docs.openstack.org/infra/jenkins-job-builder/"> 28 http://docs.openstack.org/infra/jenkins-job-builder/</link> 29 ''; 30 }; 31 32 yamlJobs = mkOption { 33 default = ""; 34 type = types.lines; 35 example = '' 36 - job: 37 name: jenkins-job-test-1 38 builders: 39 - shell: echo 'Hello world!' 40 ''; 41 description = '' 42 Job descriptions for Jenkins Job Builder in YAML format. 43 ''; 44 }; 45 46 jsonJobs = mkOption { 47 default = [ ]; 48 type = types.listOf types.str; 49 example = literalExample '' 50 [ 51 ''' 52 [ { "job": 53 { "name": "jenkins-job-test-2", 54 "builders": [ "shell": "echo 'Hello world!'" ] 55 } 56 } 57 ] 58 ''' 59 ] 60 ''; 61 description = '' 62 Job descriptions for Jenkins Job Builder in JSON format. 63 ''; 64 }; 65 66 nixJobs = mkOption { 67 default = [ ]; 68 type = types.listOf types.attrs; 69 example = literalExample '' 70 [ { job = 71 { name = "jenkins-job-test-3"; 72 builders = [ 73 { shell = "echo 'Hello world!'"; } 74 ]; 75 }; 76 } 77 ] 78 ''; 79 description = '' 80 Job descriptions for Jenkins Job Builder in Nix format. 81 82 This is a trivial wrapper around jsonJobs, using builtins.toJSON 83 behind the scene. 84 ''; 85 }; 86 }; 87 }; 88 89 config = mkIf (jenkinsCfg.enable && cfg.enable) { 90 systemd.services.jenkins-job-builder = { 91 description = "Jenkins Job Builder Service"; 92 # JJB can run either before or after jenkins. We chose after, so we can 93 # always use curl to notify (running) jenkins to reload its config. 94 after = [ "jenkins.service" ]; 95 wantedBy = [ "multi-user.target" ]; 96 97 path = with pkgs; [ jenkins-job-builder curl ]; 98 99 # Q: Why manipulate files directly instead of using "jenkins-jobs upload [...]"? 100 # A: Because this module is for administering a local jenkins install, 101 # and using local file copy allows us to not worry about 102 # authentication. 103 script = 104 let 105 yamlJobsFile = builtins.toFile "jobs.yaml" cfg.yamlJobs; 106 jsonJobsFiles = 107 map (x: (builtins.toFile "jobs.json" x)) 108 (cfg.jsonJobs ++ [(builtins.toJSON cfg.nixJobs)]); 109 jobBuilderOutputDir = "/run/jenkins-job-builder/output"; 110 # Stamp file is placed in $JENKINS_HOME/jobs/$JOB_NAME/ to indicate 111 # ownership. Enables tracking and removal of stale jobs. 112 ownerStamp = ".config-xml-managed-by-nixos-jenkins-job-builder"; 113 in 114 '' 115 rm -rf ${jobBuilderOutputDir} 116 cur_decl_jobs=/run/jenkins-job-builder/declarative-jobs 117 rm -f "$cur_decl_jobs" 118 119 # Create / update jobs 120 mkdir -p ${jobBuilderOutputDir} 121 for inputFile in ${yamlJobsFile} ${concatStringsSep " " jsonJobsFiles}; do 122 HOME="${jenkinsCfg.home}" "${pkgs.jenkins-job-builder}/bin/jenkins-jobs" --ignore-cache test -o "${jobBuilderOutputDir}" "$inputFile" 123 done 124 125 for file in "${jobBuilderOutputDir}/"*; do 126 test -f "$file" || continue 127 jobname="$(basename $file)" 128 jobdir="${jenkinsCfg.home}/jobs/$jobname" 129 echo "Creating / updating job \"$jobname\"" 130 mkdir -p "$jobdir" 131 touch "$jobdir/${ownerStamp}" 132 cp "$file" "$jobdir/config.xml" 133 echo "$jobname" >> "$cur_decl_jobs" 134 done 135 136 # Remove stale jobs 137 for file in "${jenkinsCfg.home}"/jobs/*/${ownerStamp}; do 138 test -f "$file" || continue 139 jobdir="$(dirname $file)" 140 jobname="$(basename "$jobdir")" 141 grep --quiet --line-regexp "$jobname" "$cur_decl_jobs" 2>/dev/null && continue 142 echo "Deleting stale job \"$jobname\"" 143 rm -rf "$jobdir" 144 done 145 146 echo "Asking Jenkins to reload config" 147 curl --silent -X POST http://${jenkinsCfg.listenAddress}:${toString jenkinsCfg.port}${jenkinsCfg.prefix}/reload 148 ''; 149 serviceConfig = { 150 User = jenkinsCfg.user; 151 RuntimeDirectory = "jenkins-job-builder"; 152 }; 153 }; 154 }; 155}