|
1
|
{ |
|
2
|
description = "a git server that your cow will love"; |
|
3
|
inputs.nixpkgs.url = "github:NixOS/nixpkgs"; |
|
4
|
outputs = { self, nixpkgs }: |
|
5
|
let |
|
6
|
systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; |
|
7
|
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); |
|
8
|
in |
|
9
|
{ |
|
10
|
packages = forAllSystems (pkgs: |
|
11
|
let version = self.rev or "dev"; |
|
12
|
in { |
|
13
|
default = self.packages.${pkgs.stdenv.hostPlatform.system}.mugit; |
|
14
|
mugit = pkgs.buildGo126Module { |
|
15
|
pname = "mugit"; |
|
16
|
version = version; |
|
17
|
src = ./.; |
|
18
|
vendorHash = "sha256-LQE7pyxzUHJpTN51HjiamGYg25MX72I/jSPzmjKfTtI="; |
|
19
|
ldflags = [ "-s" "-w" "-X main.version=${version}" ]; |
|
20
|
meta = with pkgs.lib; { |
|
21
|
homepage = "https://git.olexsmir.xyz/mugit"; |
|
22
|
license = licenses.mit; |
|
23
|
}; |
|
24
|
}; |
|
25
|
}); |
|
26
|
|
|
27
|
nixosModules.default = { config, lib, pkgs, ... }: |
|
28
|
with lib; |
|
29
|
let |
|
30
|
cfg = config.services.mugit; |
|
31
|
format = pkgs.formats.yaml { }; |
|
32
|
configFile = format.generate "config.yaml" cfg.config; |
|
33
|
in |
|
34
|
{ |
|
35
|
options.services.mugit = { |
|
36
|
enable = mkEnableOption "mugit service"; |
|
37
|
|
|
38
|
package = mkOption { |
|
39
|
type = types.package; |
|
40
|
default = self.packages.${pkgs.stdenv.hostPlatform.system}.mugit; |
|
41
|
defaultText = literalExpression "self.packages.\${pkgs.stdenv.hostPlatform.system}.mugit"; |
|
42
|
description = "The mugit package to use."; |
|
43
|
}; |
|
44
|
|
|
45
|
openFirewall = mkOption { |
|
46
|
type = types.bool; |
|
47
|
default = false; |
|
48
|
description = "Whether to open the firewall for mugit. Can only be used with `config`, not `configFile`."; |
|
49
|
}; |
|
50
|
|
|
51
|
exposeCli = mkOption { |
|
52
|
type = types.bool; |
|
53
|
default = false; |
|
54
|
description = "Whether to expose a mugit CLI wrapper to all system users, runs as the mugit user/group."; |
|
55
|
}; |
|
56
|
|
|
57
|
configFile = mkOption { |
|
58
|
type = types.nullOr types.path; |
|
59
|
default = null; |
|
60
|
description = "Path to an existing mugit configuration file. Mutually exclusive with `config`."; |
|
61
|
}; |
|
62
|
|
|
63
|
user = mkOption { |
|
64
|
type = types.str; |
|
65
|
default = "mugit"; |
|
66
|
description = "User account under which mugit runs."; |
|
67
|
}; |
|
68
|
|
|
69
|
group = mkOption { |
|
70
|
type = types.str; |
|
71
|
default = "mugit"; |
|
72
|
description = "Group under which mugit runs."; |
|
73
|
}; |
|
74
|
|
|
75
|
config = mkOption { |
|
76
|
default = {}; |
|
77
|
description = '' |
|
78
|
The primary mugit configuration. |
|
79
|
See [docs](https://github.com/olexsmir/mugit) for possible values. |
|
80
|
''; |
|
81
|
example = literalExpression '' |
|
82
|
{ |
|
83
|
meta.host = "git.example.org"; |
|
84
|
repo.dir = "/var/lib/mugit"; |
|
85
|
ssh = { |
|
86
|
enable = true; |
|
87
|
host_key = "/var/lib/mugit/key"; |
|
88
|
}; |
|
89
|
} |
|
90
|
''; |
|
91
|
type = types.submodule { |
|
92
|
options.meta = { |
|
93
|
title = mkOption { |
|
94
|
type = types.str; |
|
95
|
default = "mugit"; |
|
96
|
description = "Website title"; |
|
97
|
}; |
|
98
|
description = mkOption { |
|
99
|
type = types.str; |
|
100
|
default = ""; |
|
101
|
description = "Website description"; |
|
102
|
}; |
|
103
|
host = mkOption { |
|
104
|
type = types.str; |
|
105
|
default = ""; |
|
106
|
description = "Website CNAME (required)"; |
|
107
|
}; |
|
108
|
}; |
|
109
|
options.server = { |
|
110
|
host = mkOption { |
|
111
|
type = types.str; |
|
112
|
default = ""; |
|
113
|
description = "Host address"; |
|
114
|
}; |
|
115
|
port = mkOption { |
|
116
|
type = types.port; |
|
117
|
default = 8080; |
|
118
|
description = "Website port"; |
|
119
|
}; |
|
120
|
}; |
|
121
|
options.repo = { |
|
122
|
dir = mkOption { |
|
123
|
type = types.str; |
|
124
|
default = ""; |
|
125
|
description = "Directory which mugit will scan for repositories (required)"; |
|
126
|
}; |
|
127
|
masters = mkOption { |
|
128
|
type = types.listOf types.str; |
|
129
|
default = ["master" "main"]; |
|
130
|
description = "Master branch to look for"; |
|
131
|
}; |
|
132
|
readmes = mkOption { |
|
133
|
type = types.listOf types.str; |
|
134
|
default = ["README.md" "readme.md" "README.html" "readme.html" "README.txt" "readme.txt" "readme"]; |
|
135
|
description = "Readme files to look for"; |
|
136
|
}; |
|
137
|
}; |
|
138
|
options.ssh = { |
|
139
|
enable = mkOption { |
|
140
|
type = types.bool; |
|
141
|
default = false; |
|
142
|
description = "Wharever to run ssh server"; |
|
143
|
}; |
|
144
|
user = mkOption { |
|
145
|
type = types.str; |
|
146
|
default = "git"; |
|
147
|
description = "User used for git access"; |
|
148
|
}; |
|
149
|
port = mkOption { |
|
150
|
type = types.port; |
|
151
|
default = 2222; |
|
152
|
description = "Website port"; |
|
153
|
}; |
|
154
|
host_key = mkOption { |
|
155
|
type = types.str; |
|
156
|
default = ""; |
|
157
|
description = "Path to ssh private key (required if ssh enabled)"; |
|
158
|
}; |
|
159
|
keys = mkOption { |
|
160
|
type = types.listOf types.str; |
|
161
|
default = []; |
|
162
|
description = "List of public ssh keys which are allows to do git pushes, and access private repositories"; |
|
163
|
}; |
|
164
|
}; |
|
165
|
options.mirror = { |
|
166
|
enable = mkOption { |
|
167
|
type = types.bool; |
|
168
|
default = false; |
|
169
|
description = "Wharever to run mirroring worker"; |
|
170
|
}; |
|
171
|
interval = mkOption { |
|
172
|
type = types.str; |
|
173
|
default = "8h"; |
|
174
|
description = "Interval in which mirroring will happen"; |
|
175
|
}; |
|
176
|
github_token = mkOption { |
|
177
|
type = types.str; |
|
178
|
default = ""; |
|
179
|
description = "Github token for pulling from github repos"; |
|
180
|
}; |
|
181
|
}; |
|
182
|
options.cache = { |
|
183
|
home_page = mkOption { |
|
184
|
type = types.str; |
|
185
|
default = "5m"; |
|
186
|
description = "For how long index page is cached"; |
|
187
|
}; |
|
188
|
readme = mkOption { |
|
189
|
type = types.str; |
|
190
|
default = "1m"; |
|
191
|
description = "For how long repos readme is cached"; |
|
192
|
}; |
|
193
|
}; |
|
194
|
}; |
|
195
|
}; |
|
196
|
}; |
|
197
|
|
|
198
|
|
|
199
|
config = mkIf cfg.enable { |
|
200
|
networking.firewall = mkIf cfg.openFirewall { |
|
201
|
allowedTCPPorts = |
|
202
|
let |
|
203
|
serverPort = cfg.config.server.port or 8080; |
|
204
|
sshPort = cfg.config.ssh.port or 2222; |
|
205
|
sshEnabled = cfg.config.ssh.enable or false; |
|
206
|
in |
|
207
|
[ serverPort ] ++ lib.optional sshEnabled sshPort; |
|
208
|
}; |
|
209
|
|
|
210
|
users.users.${cfg.user} = { |
|
211
|
isSystemUser = true; |
|
212
|
group = cfg.group; |
|
213
|
home = cfg.config.repo.dir; |
|
214
|
createHome = true; |
|
215
|
description = "mugit service user"; |
|
216
|
}; |
|
217
|
|
|
218
|
users.groups.${cfg.group} = { }; |
|
219
|
|
|
220
|
environment.systemPackages = lib.mkIf cfg.exposeCli [ |
|
221
|
(pkgs.runCommandLocal "mugit-completions" {} '' |
|
222
|
mkdir -p $out/share/bash-completion/completions |
|
223
|
mkdir -p $out/share/zsh/site-functions |
|
224
|
mkdir -p $out/share/fish/vendor_completions.d |
|
225
|
${cfg.package}/bin/mugit completion bash > $out/share/bash-completion/completions/mugit |
|
226
|
${cfg.package}/bin/mugit completion zsh > $out/share/zsh/site-functions/_mugit |
|
227
|
${cfg.package}/bin/mugit completion fish > $out/share/fish/vendor_completions.d/mugit.fish |
|
228
|
'') |
|
229
|
]; |
|
230
|
|
|
231
|
security.wrappers = lib.mkIf cfg.exposeCli { |
|
232
|
mugit = { |
|
233
|
source = |
|
234
|
let |
|
235
|
resolvedConfig = if cfg.configFile != null then cfg.configFile else configFile; |
|
236
|
mugitWrapped = pkgs.writeScriptBin "mugit" '' |
|
237
|
#!${pkgs.bash}/bin/bash |
|
238
|
exec ${cfg.package}/bin/mugit --config ${resolvedConfig} "$@" |
|
239
|
''; |
|
240
|
in |
|
241
|
"${mugitWrapped}/bin/mugit"; |
|
242
|
owner = cfg.user; |
|
243
|
group = cfg.group; |
|
244
|
setuid = true; |
|
245
|
setgid = true; |
|
246
|
permissions = "u+rx,g+rx,o+rx"; |
|
247
|
}; |
|
248
|
}; |
|
249
|
|
|
250
|
systemd.services.mugit = { |
|
251
|
description = "mugit service"; |
|
252
|
wantedBy = [ "multi-user.target" ]; |
|
253
|
after = [ "network.target" ]; |
|
254
|
path = [ pkgs.git ]; |
|
255
|
|
|
256
|
serviceConfig = |
|
257
|
let |
|
258
|
serverPort = cfg.config.server.port or 8080; |
|
259
|
sshPort = cfg.config.ssh.port or 2222; |
|
260
|
sshEnabled = cfg.config.ssh.enable or false; |
|
261
|
needsPrivPort = serverPort < 1024 || (sshEnabled && sshPort < 1024); |
|
262
|
in |
|
263
|
{ |
|
264
|
Type = "simple"; |
|
265
|
User = cfg.user; |
|
266
|
Group = cfg.group; |
|
267
|
WorkingDirectory = cfg.config.repo.dir; |
|
268
|
StateDirectory = "mugit"; |
|
269
|
ExecStart = "${cfg.package}/bin/mugit serve --config ${configFile}"; |
|
270
|
Restart = "on-failure"; |
|
271
|
RestartSec = "5s"; |
|
272
|
NoNewPrivileges = true; |
|
273
|
PrivateTmp = true; |
|
274
|
ProtectSystem = "strict"; |
|
275
|
ProtectHome = true; |
|
276
|
ReadWritePaths = [ cfg.config.repo.dir ]; |
|
277
|
ProtectKernelTunables = true; |
|
278
|
ProtectKernelModules = true; |
|
279
|
ProtectControlGroups = true; |
|
280
|
} |
|
281
|
// lib.optionalAttrs needsPrivPort { |
|
282
|
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; |
|
283
|
}; |
|
284
|
}; |
|
285
|
}; |
|
286
|
}; |
|
287
|
}; |
|
288
|
} |