stagit.nix (5807B)
1 { 2 config, 3 lib, 4 pkgs, 5 configData, 6 haravara, 7 ... 8 }: let 9 cfg = config.services.haravara.stagit; 10 11 gitoliteUser = config.services.gitolite.user; 12 reposDir = "${config.services.gitolite.dataDir}/repositories"; 13 assets = "${pkgs.stagit}/share/doc/stagit"; 14 15 excludeNames = ["gitolite-admin"] ++ cfg.excludeRepos; 16 excludeDirPattern = lib.concatMapStringsSep "|" (r: "${r}.git") excludeNames; 17 excludeNamePattern = lib.concatStringsSep "|" excludeNames; 18 19 applyRepoMeta = lib.concatStrings (lib.mapAttrsToList (name: meta: '' 20 if [ -d "${reposDir}/${name}.git" ]; then 21 ${lib.optionalString (meta.description != null) '' 22 printf '%s' ${lib.escapeShellArg meta.description} > "${reposDir}/${name}.git/description" 23 ''} 24 ${lib.optionalString (meta.owner != null) '' 25 git -C "${reposDir}/${name}.git" config gitweb.owner ${lib.escapeShellArg meta.owner} 26 ''} 27 fi 28 '') 29 cfg.repos); 30 31 preamble = '' 32 export LC_CTYPE="C.UTF-8" 33 umask 022 34 35 reposdir="${reposDir}" 36 htmldir="${cfg.htmlDir}" 37 cloneurl="ssh://${gitoliteUser}@${cfg.domain}:${toString cfg.sshPort}" 38 39 mkdir -p "$htmldir" 40 install -m644 "${assets}/style.css" "$htmldir/style.css" 41 # Hide owner field as i am the only user 42 echo '#index tr > *:nth-child(3) { display: none; }' >> "$htmldir/style.css" 43 install -m644 "${../mixins/stagit/fred.png}" "$htmldir/logo.png" 44 install -m644 "${../mixins/stagit/fred.png}" "$htmldir/favicon.png" 45 46 set -- 47 for dir in "$reposdir"/*.git; do 48 [ -d "$dir" ] || continue 49 case "$(basename "$dir")" in ${excludeDirPattern}) continue ;; esac 50 set -- "$@" "$dir" 51 done 52 ''; 53 54 render_repo = '' 55 render_repo() { 56 dir="$1" 57 d=$(basename "$dir" .git) 58 mkdir -p "$htmldir/$d" 59 ( 60 cd "$htmldir/$d" || exit 1 61 stagit -c .cache -u "$cloneurl/$d" "$dir" 62 ln -sf log.html index.html 63 ln -sf ../style.css style.css 64 ln -sf ../logo.png logo.png 65 ln -sf ../favicon.png favicon.png 66 ) || echo "stagit: failed to render $d" >&2 67 } 68 ''; 69 70 generate = pkgs.writeShellApplication { 71 name = "stagit-generate"; 72 runtimeInputs = [pkgs.stagit pkgs.git pkgs.coreutils]; 73 text = '' 74 ${preamble} 75 ${applyRepoMeta} 76 ${render_repo} 77 78 stagit-index "$@" > "$htmldir/index.html" 79 for dir in "$@"; do 80 render_repo "$dir" 81 done 82 ''; 83 }; 84 85 postReceive = pkgs.writeShellApplication { 86 name = "post-receive"; 87 runtimeInputs = [pkgs.stagit pkgs.git pkgs.coreutils]; 88 text = '' 89 ${preamble} 90 ${applyRepoMeta} 91 ${render_repo} 92 93 name=$(basename "$(pwd)") 94 d=$(basename "$name" .git) 95 case "$d" in ${excludeNamePattern}) exit 0 ;; esac 96 97 # Detect a force-push so the commit cache can be rebuilt from scratch. 98 force=0 99 while read -r old new _ref; do 100 [ "$old" = "0000000000000000000000000000000000000000" ] && continue 101 [ "$new" = "0000000000000000000000000000000000000000" ] && continue 102 if [ -n "$(git rev-list "$old" "^$new" 2>/dev/null | sed 1q)" ]; then 103 force=1 104 break 105 fi 106 done 107 108 if [ "$force" = "1" ]; then 109 rm -f "$htmldir/$d/.cache" 110 rm -rf "$htmldir/$d/commit" 111 fi 112 113 render_repo "$reposdir/$name" 114 stagit-index "$@" > "$htmldir/index.html" 115 ''; 116 }; 117 in { 118 options.services.haravara.stagit = { 119 enable = lib.mkEnableOption "stagit static git pages backed by gitolite"; 120 121 domain = lib.mkOption { 122 type = lib.types.str; 123 }; 124 125 htmlDir = lib.mkOption { 126 type = lib.types.str; 127 default = "/var/lib/stagit"; 128 }; 129 130 sshPort = lib.mkOption { 131 type = lib.types.port; 132 default = 22; 133 }; 134 135 authLabel = haravara.authLabelOption; 136 137 excludeRepos = lib.mkOption { 138 type = lib.types.listOf lib.types.str; 139 default = []; 140 example = ["admin" "internal"]; 141 }; 142 143 repos = lib.mkOption { 144 default = {}; 145 type = lib.types.attrsOf (lib.types.submodule { 146 options = { 147 description = lib.mkOption { 148 type = lib.types.nullOr lib.types.str; 149 default = null; 150 }; 151 owner = lib.mkOption { 152 type = lib.types.nullOr lib.types.str; 153 default = null; 154 }; 155 }; 156 }); 157 }; 158 159 adminPubkey = lib.mkOption { 160 type = lib.types.str; 161 default = builtins.head configData.users.${configData.defaultUser}.authorizedKeys; 162 }; 163 }; 164 165 config = lib.mkIf cfg.enable { 166 services = { 167 gitolite = { 168 enable = true; 169 inherit (cfg) adminPubkey; 170 commonHooks = ["${postReceive}/bin/post-receive"]; 171 }; 172 173 openssh.settings.AllowUsers = [gitoliteUser]; 174 175 caddy.virtualHosts."${cfg.domain}".extraConfig = '' 176 ${haravara.mkAuthImport cfg.authLabel} 177 root * ${cfg.htmlDir} 178 encode gzip 179 file_server 180 ''; 181 }; 182 183 systemd = { 184 tmpfiles.rules = [ 185 "d ${cfg.htmlDir} 0755 ${gitoliteUser} ${config.services.gitolite.group} -" 186 ]; 187 188 services.stagit-generate = { 189 description = "Generate stagit static pages for all gitolite repos"; 190 after = ["gitolite-init.service"]; 191 wants = ["gitolite-init.service"]; 192 wantedBy = ["multi-user.target"]; 193 serviceConfig = { 194 Type = "oneshot"; 195 User = gitoliteUser; 196 Group = config.services.gitolite.group; 197 ExecStart = "${generate}/bin/stagit-generate"; 198 }; 199 }; 200 201 timers.stagit-generate = { 202 description = "Periodic stagit regeneration"; 203 wantedBy = ["timers.target"]; 204 timerConfig = { 205 OnCalendar = "*:0/30"; 206 Persistent = true; 207 }; 208 }; 209 }; 210 }; 211 }