2 files changed,
277 insertions(+),
0 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-02-06 20:55:33 +0200
Authored at:
2026-02-05 22:54:01 +0200
Change ID:
mvuqnrvkmsvyqttszplmwnrrztqooxvk
Parent:
a4a6674
jump to
| A | flake.lock |
| A | flake.nix |
A
flake.lock
路路路 1 +{ 2 + "nodes": { 3 + "flake-utils": { 4 + "inputs": { 5 + "systems": "systems" 6 + }, 7 + "locked": { 8 + "lastModified": 1731533236, 9 + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 + "owner": "numtide", 11 + "repo": "flake-utils", 12 + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 + "type": "github" 14 + }, 15 + "original": { 16 + "owner": "numtide", 17 + "repo": "flake-utils", 18 + "type": "github" 19 + } 20 + }, 21 + "nixpkgs": { 22 + "locked": { 23 + "lastModified": 1770325241, 24 + "narHash": "sha256-4CPgINJ1do3wjYEFvWvvXgdYFHiM0QGJthAvd5Q/5SY=", 25 + "owner": "NixOS", 26 + "repo": "nixpkgs", 27 + "rev": "c23d7dfb4309052f29c0869f2eb6cfc1136c5091", 28 + "type": "github" 29 + }, 30 + "original": { 31 + "owner": "NixOS", 32 + "repo": "nixpkgs", 33 + "type": "github" 34 + } 35 + }, 36 + "root": { 37 + "inputs": { 38 + "flake-utils": "flake-utils", 39 + "nixpkgs": "nixpkgs" 40 + } 41 + }, 42 + "systems": { 43 + "locked": { 44 + "lastModified": 1681028828, 45 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 46 + "owner": "nix-systems", 47 + "repo": "default", 48 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 49 + "type": "github" 50 + }, 51 + "original": { 52 + "owner": "nix-systems", 53 + "repo": "default", 54 + "type": "github" 55 + } 56 + } 57 + }, 58 + "root": "root", 59 + "version": 7 60 +}
A
flake.nix
路路路 1 +{ 2 + description = "a git server that your cow will love"; 3 + inputs = { 4 + nixpkgs.url = "github:NixOS/nixpkgs"; 5 + flake-utils.url = "github:numtide/flake-utils"; 6 + }; 7 + outputs = 8 + { 9 + self, 10 + nixpkgs, 11 + flake-utils, 12 + }: 13 + flake-utils.lib.eachDefaultSystem ( 14 + system: 15 + let 16 + pkgs = nixpkgs.legacyPackages.${system}; 17 + version = self.rev or "dev"; 18 + in 19 + { 20 + packages = { 21 + default = self.packages.${system}.mugit; 22 + mugit = pkgs.buildGoModule { 23 + pname = "mugit"; 24 + version = version; 25 + src = ./.; 26 + vendorHash = "sha256-VcNnosr9Co+MFEA36s4BIDmg/bx+/mTIHdgOaGJKhbc="; 27 + ldflags = [ 28 + "-s" 29 + "-w" 30 + "-X main.version=${version}" 31 + ]; 32 + meta = with pkgs.lib; { 33 + homepage = "https://github.com/olexsmir/mugit"; 34 + license = licenses.mit; 35 + }; 36 + }; 37 + }; 38 + } 39 + ) 40 + // { 41 + nixosModules.default = 42 + { 43 + config, 44 + lib, 45 + pkgs, 46 + ... 47 + }: 48 + with lib; 49 + let 50 + cfg = config.services.mugit; 51 + format = pkgs.formats.yaml { }; 52 + configFile = 53 + if cfg.configFile != null then cfg.configFile else format.generate "config.yaml" cfg.config; 54 + 55 + mugitWrapper = pkgs.symlinkJoin { 56 + name = "mugit"; 57 + paths = [ cfg.package ]; 58 + buildInputs = [ pkgs.makeWrapper ]; 59 + postBuild = '' 60 + wrapProgram $out/bin/mugit \ 61 + --add-flags "--config ${configFile}" 62 + ''; 63 + }; 64 + 65 + mugitWithCompletions = pkgs.stdenv.mkDerivation { 66 + pname = "mugit"; 67 + version = cfg.package.version; 68 + src = mugitWrapper; 69 + nativeBuildInputs = [ pkgs.installShellFiles ]; 70 + installPhase = '' 71 + mkdir -p $out/bin 72 + cp -r $src/bin/* $out/bin/ 73 + 74 + installShellCompletion --cmd mugit \ 75 + --bash <($out/bin/mugit completion bash) \ 76 + --zsh <($out/bin/mugit completion zsh) \ 77 + --fish <($out/bin/mugit completion fish) 78 + ''; 79 + }; 80 + in 81 + { 82 + options.services.mugit = { 83 + enable = mkEnableOption "mugit service"; 84 + 85 + package = mkOption { 86 + type = types.package; 87 + default = self.packages.${pkgs.system}.mugit; 88 + defaultText = literalExpression "self.packages.\${pkgs.system}.mugit"; 89 + description = "The mugit package to use."; 90 + }; 91 + 92 + openFirewall = mkOption { 93 + type = types.bool; 94 + default = false; 95 + description = "Whether to open the firewall for mugit. Can only be used with `config`, not `configFile`."; 96 + }; 97 + 98 + exposeCli = mkOption { 99 + type = types.bool; 100 + default = true; 101 + description = "Whether to expose the mugit CLI to all users with the service configuration."; 102 + }; 103 + 104 + dataDir = mkOption { 105 + type = types.path; 106 + default = "/var/lib/mugit"; 107 + description = "Directory where mugit stores its data."; 108 + }; 109 + 110 + configFile = mkOption { 111 + type = types.nullOr types.path; 112 + default = null; 113 + description = "Path to an existing mugit configuration file. Mutually exclusive with `config`."; 114 + }; 115 + 116 + config = mkOption { 117 + type = format.type; 118 + default = { }; 119 + description = '' 120 + Configuration for mugit. See documentation for available options. 121 + https://github.com/olexsmir/mugit/blob/main/docs.md 122 + ''; 123 + example = literalExpression '' 124 + { 125 + server.port = 8080; 126 + repo.dir = "/var/lib/mugit"; 127 + } 128 + ''; 129 + }; 130 + 131 + user = mkOption { 132 + type = types.str; 133 + default = "mugit"; 134 + description = "User account under which mugit runs."; 135 + }; 136 + 137 + group = mkOption { 138 + type = types.str; 139 + default = "mugit"; 140 + description = "Group under which mugit runs."; 141 + }; 142 + 143 + }; 144 + 145 + config = mkIf cfg.enable { 146 + assertions = [ 147 + { 148 + assertion = !(cfg.config != { } && cfg.configFile != null); 149 + message = "services.mugit: `config` and `configFile` are mutually exclusive. Only one can be set."; 150 + } 151 + { 152 + assertion = !(cfg.openFirewall && cfg.configFile != null); 153 + message = "services.mugit: `openFirewall` cannot be used with `configFile`. Set firewall rules manually or use `config` instead."; 154 + } 155 + ]; 156 + 157 + environment.systemPackages = mkIf cfg.exposeCli [ mugitWithCompletions ]; 158 + 159 + networking.firewall = mkIf cfg.openFirewall { 160 + allowedTCPPorts = 161 + let 162 + serverPort = cfg.config.server.port or 8080; 163 + sshPort = cfg.config.ssh.port or 2222; 164 + sshEnabled = cfg.config.ssh.enable or false; 165 + in 166 + [ serverPort ] ++ lib.optional sshEnabled sshPort; 167 + }; 168 + 169 + users.users.${cfg.user} = { 170 + isSystemUser = true; 171 + group = cfg.group; 172 + home = cfg.dataDir; 173 + createHome = true; 174 + description = "mugit service user"; 175 + }; 176 + 177 + users.groups.${cfg.group} = { }; 178 + 179 + systemd.services.mugit = { 180 + description = "mugit service"; 181 + wantedBy = [ "multi-user.target" ]; 182 + after = [ "network.target" ]; 183 + path = [ pkgs.git ]; 184 + 185 + serviceConfig = 186 + let 187 + serverPort = cfg.config.server.port or 8080; 188 + sshPort = cfg.config.ssh.port or 2222; 189 + sshEnabled = cfg.config.ssh.enable or false; 190 + needsPrivPort = serverPort < 1024 || (sshEnabled && sshPort < 1024); 191 + in 192 + { 193 + Type = "simple"; 194 + User = cfg.user; 195 + Group = cfg.group; 196 + WorkingDirectory = cfg.dataDir; 197 + StateDirectory = "mugit"; 198 + ExecStart = "${cfg.package}/bin/mugit serve --config ${configFile}"; 199 + Restart = "on-failure"; 200 + RestartSec = "5s"; 201 + NoNewPrivileges = true; 202 + PrivateTmp = true; 203 + ProtectSystem = "strict"; 204 + ProtectHome = true; 205 + ReadWritePaths = [ cfg.dataDir ]; 206 + ProtectKernelTunables = true; 207 + ProtectKernelModules = true; 208 + ProtectControlGroups = true; 209 + } 210 + // lib.optionalAttrs needsPrivPort { 211 + AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; 212 + }; 213 + }; 214 + }; 215 + }; 216 + }; 217 +}