{ config, inputs, lib, libNginx, libPlausible, pkgs, ... }: with lib; let cfg = config.nixfiles.modules.git; in { options.nixfiles.modules.git = { client.enable = mkEnableOption "Git client"; server = { enable = mkEnableOption "Git server"; domain = mkOption { description = "Domain name sans protocol scheme."; type = with types; nullOr str; default = "git.${config.networking.domain}"; }; package = mkOption { description = "Package."; type = types.package; default = pkgs.cgit; }; }; }; config = mkMerge [ (mkIf cfg.client.enable { secrets = { glab-cli-config = { file = "${inputs.self}/secrets/glab-cli-config"; path = "${config.dirs.config}/glab-cli/config.yml"; owner = my.username; }; gh-hosts = { file = "${inputs.self}/secrets/gh-hosts"; path = "${config.dirs.config}/gh/hosts.yml"; owner = my.username; }; hut = { file = "${inputs.self}/secrets/hut"; path = "${config.dirs.config}/hut/config"; owner = my.username; }; }; nixfiles.modules.common.shell.aliases = { gl = "glab"; ht = "hut"; }; hm = { home.packages = with pkgs; [ git-extras glab hut ]; programs = { git = { enable = true; package = pkgs.git.override { doInstallCheck = false; pythonSupport = false; sendEmailSupport = true; withLibsecret = false; withSsh = true; }; userName = my.fullname; userEmail = my.email; signing = { inherit (my.pgp) key; signByDefault = true; }; extraConfig = { color.ui = true; core.whitespace = "trailing-space"; init.defaultBranch = "master"; status.submoduleSummary = true; commit.verbose = true; push.autoSetupRemote = true; pull.rebase = true; rebase = { autoStash = true; autoSquash = true; }; rerere.enabled = true; branch.sort = "-committerdate"; diff = { mnemonicPrefix = true; renames = "copies"; submodule = "log"; }; submodule.recurse = true; sendemail = rec { smtpServer = my.domain.shire; smtpUser = "${my.username}@${smtpServer}"; smtpEncryption = "ssl"; smtpServerPort = 465; annotate = true; confirm = "always"; }; column.ui = "auto"; github.user = my.username; gitlab.user = my.username; } // mapAttrs' (name: value: nameValuePair ''url "git@${value}:"'' { insteadOf = "${name}:"; }) { "bitbucket" = "bitbucket.com"; "codeberg" = "codeberg.org"; "github" = "github.com"; "gitlab" = "gitlab.com"; "sourcehut" = "git.sr.ht"; } // mapAttrs' (name: values: nameValuePair ''url "https://${values}/"'' { insteadOf = "${name}:"; }) { "alpine" = "gitlab.alpinelinux.org"; "debian" = "salsa.debian.org"; "freedesktop" = "gitlab.freedesktop.org"; "gnome" = "gitlab.gnome.org"; "haskell" = "gitlab.haskell.org"; "homotopic" = "gitlab.homotopic.tech"; "horizon" = "gitlab.horizon-haskell.net"; "kde" = "invent.kde.org"; "nixca" = "gitlab.nixca.dev"; "notabug" = "notabug.org"; "opencode" = "opencode.net"; "torproject" = "gitlab.torproject.org"; "videolan" = "code.videolan.org"; }; aliases = let git = getExe config.hm.programs.git.package; curl = getExe pkgs.curl; in { amend = "commit --amend"; cat = "cat-file -p"; fast = "clone --depth=1"; fixup = "commit --fixup"; fuck = "!${git} reset --hard && ${git} clean --force -dx"; get = "pull --all --recurse-submodules --autostash"; gud = ''commit -m "git gud"''; refresh = "clean --force -dx"; tree = "log --graph --date=relative --pretty=tformat:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%an %ad)%Creset'"; uncommit = "reset --soft HEAD~1"; untrack = "rm --cache --"; wtc = "!${curl} -sq whatthecommit.com/index.txt | ${git} commit -F -"; }; # All helper tools/editor generated files should go here. This must be # kept void of any project-specific or residual files. ignores = [ "*~" ".DS_Store" ".cache/clangd/" ".ccls-cache/" ".gdb_history" ".netrwhist" ".projectile" "[._]*.s[a-v][a-z]" "[._]*.sw[a-p]" "[._]s[a-rt-v][a-z]" "[._]ss[a-gi-z]" "[._]sw[a-p]" "\#*\#" "compile_commands*.json" "cscope.*" "vgcore.*" ]; }; gh = { enable = true; settings.git_protocol = "ssh"; }; }; }; }) (mkIf cfg.server.enable { ark.directories = [ config.services.gitolite.dataDir ]; nixfiles.modules.nginx = { enable = true; virtualHosts.${cfg.server.domain} = { locations = { "/".extraConfig = let cgitrc = pkgs.writeText "cgitrc" '' root-title=github sux (⩺_⩹) root-desc=https://github.com/azahi clone-url=https://${cfg.server.domain}/$CGIT_REPO_URL logo=/cgit-custom-logo.gif favicon=/cgit-custom-favicon.gif css=/cgit-custom-style.css about-filter=${cfg.server.package}/lib/cgit/filters/about-formatting.sh source-filter=${cfg.server.package}/lib/cgit/filters/syntax-highlighting.py commit-filter=${cfg.server.package}/lib/cgit/filters/commit-links.sh enable-git-config=1 enable-gitweb-owner=1 remove-suffix=1 readme=:README readme=:README.md readme=:README.org readme=:README.txt readme=:readme readme=:readme.md readme=:readme.org readme=:readme.txt scan-path=${config.services.gitolite.dataDir}/repositories ''; in '' include ${config.services.nginx.package}/conf/fastcgi_params; fastcgi_split_path_info ^(/?)(.+)$; fastcgi_pass unix:${config.services.fcgiwrap.socketAddress}; fastcgi_param SCRIPT_FILENAME ${cfg.server.package}/cgit/cgit.cgi; fastcgi_param CGIT_CONFIG ${cgitrc}; fastcgi_param PATH_INFO $uri; fastcgi_param QUERY_STRING $args; fastcgi_param HTTP_HOST $server_name; ${libNginx.config.appendHead [ '''' (libPlausible.htmlPlausibleScript { inherit (cfg.server) domain; }) ]} ''; "~* ^.+(cgit.css|robots.txt)$".extraConfig = '' root ${cfg.server.package}/cgit; ''; "~* ^.+cgit-custom-logo.gif$".extraConfig = '' alias ${./logo.gif}; ''; "~* ^.+cgit-custom-favicon.gif$".extraConfig = '' alias ${./favicon.ico}; ''; "~* ^.+cgit-custom-style.css$".extraConfig = let css = pkgs.writeText "custom.css" '' @import url("cgit.css"); div#cgit { font-family: monospace; -moz-tab-size: 4; tab-size: 4; } ''; in '' alias ${css}; ''; }; }; }; services = let user = "git"; group = "git"; in { gitolite = { enable = true; inherit user group; adminPubkey = my.ssh.key; extraGitoliteRc = '' # This allows hiding repositories via "cgit.ignore"[1]. # # [1]: https://www.omarpolo.com/post/cgit-gitolite.html $RC{GIT_CONFIG_KEYS} = '.*'; ''; }; fcgiwrap = { enable = true; inherit user group; }; }; }) ]; }