commit 1de3fc7316426d1fee00bf583da083b2634f5bc2
parent 13d213e83a53b3823af22a0b0470c68cdceee1ef
Author: Katja (zaphyra) <git@ctu.cx>
Date: Fri, 23 May 2025 11:29:19 +0200
parent 13d213e83a53b3823af22a0b0470c68cdceee1ef
Author: Katja (zaphyra) <git@ctu.cx>
Date: Fri, 23 May 2025 11:29:19 +0200
config/nixos/modules/services: add `gitolite`
1 file changed, 236 insertions(+), 0 deletions(-)
A
|
236
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/config/nixos/modules/services/gitolite.nix b/config/nixos/modules/services/gitolite.nix @@ -0,0 +1,236 @@ +{ + povSelf, + config, + pkgs, + lib, + ... +}: +let + inherit (lib) types; + cfg = lib.getAttrFromPath povSelf config; + +in +{ + + options = { + enable = { + type = types.bool; + default = false; + }; + + dataDir = { + type = types.str; + default = "/var/lib/gitolite"; + }; + + adminPubkey = { + type = types.str; + }; + + commonHooks = { + type = types.attrsOf types.lines; + default = { }; + }; + + triggers = { + type = types.attrsOf types.lines; + default = { }; + }; + + commands = { + type = types.attrsOf types.lines; + default = { }; + }; + + extraGitoliteRc = { + type = types.lines; + default = ""; + }; + + user = { + type = types.str; + default = "gitolite"; + }; + + group = { + type = types.str; + default = "gitolite"; + }; + }; + + config = lib.mkIf cfg.enable ( + let + # Use writeTextDir to not leak Nix store hash into file name + pubkeyFile = (pkgs.writeTextDir "gitolite-admin.pub" cfg.adminPubkey) + "/gitolite-admin.pub"; + hooks = lib.mapAttrs ( + name: script: + (pkgs.writeShellScript name ( + if name == "post-receive" then + '' + read oldrev newrev ref + [ -t 0 ] || cat >/dev/null + [ -z "$GL_REPO" ] && die GL_REPO not set + + '' + + script + else + script + )) + ) cfg.commonHooks; + triggers = lib.mapAttrs (name: script: (pkgs.writeShellScript name script)) cfg.triggers; + commands = lib.mapAttrs (name: script: (pkgs.writeShellScript name script)) cfg.commands; + manageGitoliteRc = cfg.extraGitoliteRc != ""; + rcDir = pkgs.runCommand "gitolite-rc" { preferLocalBuild = true; } rcDirScript; + rcDirScript = + '' + mkdir "$out" + export HOME=temp-home + mkdir -p "$HOME/.gitolite/logs" # gitolite can't run without it + '${pkgs.gitolite}'/bin/gitolite print-default-rc >>"$out/gitolite.rc.default" + cat <<END >>"$out/gitolite.rc" + # This file is managed by NixOS. + # Use services.gitolite options to control it. + + END + cat "$out/gitolite.rc.default" >>"$out/gitolite.rc" + '' + + lib.optionalString (cfg.extraGitoliteRc != "") '' + echo -n ${lib.escapeShellArg '' + + # Added by NixOS: + ${lib.removeSuffix "\n" cfg.extraGitoliteRc} + + # per perl rules, this should be the last line in such a file: + 1; + ''} >>"$out/gitolite.rc" + ''; + + in + { + modules.services.gitolite.extraGitoliteRc = '' + $RC{LOCAL_CODE} = "$ENV{HOME}/.gitolite/local"; + ''; + + users.users.${cfg.user} = { + home = cfg.dataDir; + uid = config.ids.uids.gitolite; + group = cfg.group; + extraGroups = [ "ssh" ]; + useDefaultShell = true; + }; + + users.groups.${cfg.group}.gid = config.ids.gids.gitolite; + + systemd.services.gitolite-init = { + description = "Gitolite initialization"; + wantedBy = [ "multi-user.target" ]; + unitConfig.RequiresMountsFor = cfg.dataDir; + + environment = { + GITOLITE_RC = ".gitolite.rc"; + GITOLITE_RC_DEFAULT = "${rcDir}/gitolite.rc.default"; + }; + + serviceConfig = lib.mkMerge [ + (lib.mkIf (cfg.dataDir == "/var/lib/gitolite") { + StateDirectory = "gitolite gitolite/.gitolite gitolite/.gitolite/logs"; + StateDirectoryMode = "0750"; + }) + { + Type = "oneshot"; + User = cfg.user; + Group = cfg.group; + WorkingDirectory = "~"; + RemainAfterExit = true; + } + ]; + + path = [ + pkgs.gitolite + pkgs.git + pkgs.perl + pkgs.bash + pkgs.diffutils + config.programs.ssh.package + ]; + script = + let + rcSetupScriptIfCustomFile = + if manageGitoliteRc then + '' + cat <<END + <3>ERROR: NixOS can't apply declarative configuration + <3>to your .gitolite.rc file, because it seems to be + <3>already customized manually. + <3>See the services.gitolite.extraGitoliteRc option + <3>in "man configuration.nix" for more information. + END + # Not sure if the line below addresses the issue directly or just + # adds a delay, but without it our error message often doesn't + # show up in `systemctl status gitolite-init`. + journalctl --flush + exit 1 + '' + else + '' + : + ''; + rcSetupScriptIfDefaultFileOrStoreSymlink = + if manageGitoliteRc then + '' + ln -sf "${rcDir}/gitolite.rc" "$GITOLITE_RC" + '' + else + '' + [[ -L "$GITOLITE_RC" ]] && rm -f "$GITOLITE_RC" + ''; + in + '' + if ( [[ ! -e "$GITOLITE_RC" ]] && [[ ! -L "$GITOLITE_RC" ]] ) || + ( [[ -f "$GITOLITE_RC" ]] && diff -q "$GITOLITE_RC" "$GITOLITE_RC_DEFAULT" >/dev/null ) || + ( [[ -L "$GITOLITE_RC" ]] && [[ "$(readlink "$GITOLITE_RC")" =~ ^/nix/store/ ]] ) + then + '' + + rcSetupScriptIfDefaultFileOrStoreSymlink + + '' + else + '' + + rcSetupScriptIfCustomFile + + '' + fi + + if [ ! -d repositories ]; then + gitolite setup -pk ${pubkeyFile} + fi + + rm -rf .gitolite/local/hooks/common/ + mkdir -p .gitolite/local/hooks/common/ + ${lib.concatStringsSep "\n" ( + lib.mapAttrsToList (name: script: "ln -s ${script} .gitolite/local/hooks/common/${name}") hooks + )} + + rm -rf .gitolite/local/triggers/ + mkdir -p .gitolite/local/triggers/ + ${lib.concatStringsSep "\n" ( + lib.mapAttrsToList (name: script: "ln -s ${script} .gitolite/local/triggers/${name}") triggers + )} + + rm -rf .gitolite/local/commands/ + mkdir -p .gitolite/local/commands/ + ${lib.concatStringsSep "\n" ( + lib.mapAttrsToList (name: script: "ln -s ${script} .gitolite/local/commands/${name}") commands + )} + + gitolite setup # Upgrade if needed + ''; + }; + + environment.systemPackages = [ + pkgs.gitolite + pkgs.git + pkgs.perl + ]; + } + ); + +}