nix

configuration files that power my machines
Log | Files | Refs | README | LICENSE

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 }