at master 8.7 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 cfg = config.services.gitolite; 9 # Use writeTextDir to not leak Nix store hash into file name 10 pubkeyFile = (pkgs.writeTextDir "gitolite-admin.pub" cfg.adminPubkey) + "/gitolite-admin.pub"; 11 hooks = lib.concatMapStrings (hook: "${hook} ") cfg.commonHooks; 12in 13{ 14 options = { 15 services.gitolite = { 16 enable = lib.mkOption { 17 type = lib.types.bool; 18 default = false; 19 description = '' 20 Enable gitolite management under the 21 `gitolite` user. After 22 switching to a configuration with Gitolite enabled, you can 23 then run `git clone gitolite@host:gitolite-admin.git` to manage it further. 24 ''; 25 }; 26 27 dataDir = lib.mkOption { 28 type = lib.types.str; 29 default = "/var/lib/gitolite"; 30 description = '' 31 The gitolite home directory used to store all repositories. If left as the default value 32 this directory will automatically be created before the gitolite server starts, otherwise 33 the sysadmin is responsible for ensuring the directory exists with appropriate ownership 34 and permissions. 35 ''; 36 }; 37 38 adminPubkey = lib.mkOption { 39 type = lib.types.str; 40 description = '' 41 Initial administrative public key for Gitolite. This should 42 be an SSH Public Key. Note that this key will only be used 43 once, upon the first initialization of the Gitolite user. 44 The key string cannot have any line breaks in it. 45 ''; 46 }; 47 48 enableGitAnnex = lib.mkOption { 49 type = lib.types.bool; 50 default = false; 51 description = '' 52 Enable git-annex support. Uses the `extraGitoliteRc` option 53 to apply the necessary configuration. 54 ''; 55 }; 56 57 commonHooks = lib.mkOption { 58 type = lib.types.listOf lib.types.path; 59 default = [ ]; 60 description = '' 61 A list of custom git hooks that get copied to `~/.gitolite/hooks/common`. 62 ''; 63 }; 64 65 extraGitoliteRc = lib.mkOption { 66 type = lib.types.lines; 67 default = ""; 68 example = lib.literalExpression '' 69 ''' 70 $RC{UMASK} = 0027; 71 $RC{SITE_INFO} = 'This is our private repository host'; 72 push( @{$RC{ENABLE}}, 'Kindergarten' ); # enable the command/feature 73 @{$RC{ENABLE}} = grep { $_ ne 'desc' } @{$RC{ENABLE}}; # disable the command/feature 74 ''' 75 ''; 76 description = '' 77 Extra configuration to append to the default `~/.gitolite.rc`. 78 79 This should be Perl code that modifies the `%RC` 80 configuration variable. The default `~/.gitolite.rc` 81 content is generated by invoking `gitolite print-default-rc`, 82 and extra configuration from this option is appended to it. The result 83 is placed to Nix store, and the `~/.gitolite.rc` file 84 becomes a symlink to it. 85 86 If you already have a customized (or otherwise changed) 87 `~/.gitolite.rc` file, NixOS will refuse to replace 88 it with a symlink, and the `gitolite-init` initialization service 89 will fail. In this situation, in order to use this option, you 90 will need to take any customizations you may have in 91 `~/.gitolite.rc`, convert them to appropriate Perl 92 statements, add them to this option, and remove the file. 93 94 See also the `enableGitAnnex` option. 95 ''; 96 }; 97 98 user = lib.mkOption { 99 type = lib.types.str; 100 default = "gitolite"; 101 description = '' 102 Gitolite user account. This is the username of the gitolite endpoint. 103 ''; 104 }; 105 106 description = lib.mkOption { 107 type = lib.types.str; 108 default = "Gitolite user"; 109 description = '' 110 Gitolite user account's description. 111 ''; 112 }; 113 114 group = lib.mkOption { 115 type = lib.types.str; 116 default = "gitolite"; 117 description = '' 118 Primary group of the Gitolite user account. 119 ''; 120 }; 121 }; 122 }; 123 124 config = lib.mkIf cfg.enable ( 125 let 126 manageGitoliteRc = cfg.extraGitoliteRc != ""; 127 rcDir = pkgs.runCommand "gitolite-rc" { preferLocalBuild = true; } rcDirScript; 128 rcDirScript = '' 129 mkdir "$out" 130 export HOME=temp-home 131 mkdir -p "$HOME/.gitolite/logs" # gitolite can't run without it 132 '${pkgs.gitolite}'/bin/gitolite print-default-rc >>"$out/gitolite.rc.default" 133 cat <<END >>"$out/gitolite.rc" 134 # This file is managed by NixOS. 135 # Use services.gitolite options to control it. 136 137 END 138 cat "$out/gitolite.rc.default" >>"$out/gitolite.rc" 139 '' 140 + lib.optionalString (cfg.extraGitoliteRc != "") '' 141 echo -n ${lib.escapeShellArg '' 142 143 # Added by NixOS: 144 ${lib.removeSuffix "\n" cfg.extraGitoliteRc} 145 146 # per perl rules, this should be the last line in such a file: 147 1; 148 ''} >>"$out/gitolite.rc" 149 ''; 150 in 151 { 152 services.gitolite.extraGitoliteRc = lib.optionalString cfg.enableGitAnnex '' 153 # Enable git-annex support: 154 push( @{$RC{ENABLE}}, 'git-annex-shell ua'); 155 ''; 156 157 users.users.${cfg.user} = { 158 description = cfg.description; 159 home = cfg.dataDir; 160 uid = config.ids.uids.gitolite; 161 group = cfg.group; 162 useDefaultShell = true; 163 }; 164 users.groups.${cfg.group}.gid = config.ids.gids.gitolite; 165 166 systemd.services.gitolite-init = { 167 description = "Gitolite initialization"; 168 wantedBy = [ "multi-user.target" ]; 169 unitConfig.RequiresMountsFor = cfg.dataDir; 170 171 environment = { 172 GITOLITE_RC = ".gitolite.rc"; 173 GITOLITE_RC_DEFAULT = "${rcDir}/gitolite.rc.default"; 174 }; 175 176 serviceConfig = lib.mkMerge [ 177 (lib.mkIf (cfg.dataDir == "/var/lib/gitolite") { 178 StateDirectory = "gitolite gitolite/.gitolite gitolite/.gitolite/logs"; 179 StateDirectoryMode = "0750"; 180 }) 181 { 182 Type = "oneshot"; 183 User = cfg.user; 184 Group = cfg.group; 185 WorkingDirectory = "~"; 186 RemainAfterExit = true; 187 } 188 ]; 189 190 path = [ 191 pkgs.gitolite 192 pkgs.git 193 pkgs.perl 194 pkgs.bash 195 pkgs.diffutils 196 config.programs.ssh.package 197 ]; 198 script = 199 let 200 rcSetupScriptIfCustomFile = 201 if manageGitoliteRc then 202 '' 203 cat <<END 204 <3>ERROR: NixOS can't apply declarative configuration 205 <3>to your .gitolite.rc file, because it seems to be 206 <3>already customized manually. 207 <3>See the services.gitolite.extraGitoliteRc option 208 <3>in "man configuration.nix" for more information. 209 END 210 # Not sure if the line below addresses the issue directly or just 211 # adds a delay, but without it our error message often doesn't 212 # show up in `systemctl status gitolite-init`. 213 journalctl --flush 214 exit 1 215 '' 216 else 217 '' 218 : 219 ''; 220 rcSetupScriptIfDefaultFileOrStoreSymlink = 221 if manageGitoliteRc then 222 '' 223 ln -sf "${rcDir}/gitolite.rc" "$GITOLITE_RC" 224 '' 225 else 226 '' 227 [[ -L "$GITOLITE_RC" ]] && rm -f "$GITOLITE_RC" 228 ''; 229 in 230 '' 231 if ( [[ ! -e "$GITOLITE_RC" ]] && [[ ! -L "$GITOLITE_RC" ]] ) || 232 ( [[ -f "$GITOLITE_RC" ]] && diff -q "$GITOLITE_RC" "$GITOLITE_RC_DEFAULT" >/dev/null ) || 233 ( [[ -L "$GITOLITE_RC" ]] && [[ "$(readlink "$GITOLITE_RC")" =~ ^/nix/store/ ]] ) 234 then 235 '' 236 + rcSetupScriptIfDefaultFileOrStoreSymlink 237 + '' 238 else 239 '' 240 + rcSetupScriptIfCustomFile 241 + '' 242 fi 243 244 if [ ! -d repositories ]; then 245 gitolite setup -pk ${pubkeyFile} 246 fi 247 if [ -n "${hooks}" ]; then 248 cp -f ${hooks} .gitolite/hooks/common/ 249 chmod +x .gitolite/hooks/common/* 250 fi 251 gitolite setup # Upgrade if needed 252 ''; 253 }; 254 255 environment.systemPackages = [ 256 pkgs.gitolite 257 pkgs.git 258 ] 259 ++ lib.optional cfg.enableGitAnnex pkgs.git-annex; 260 } 261 ); 262}