1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.gogs;
7 configFile = pkgs.writeText "app.ini" ''
8 APP_NAME = ${cfg.appName}
9 RUN_USER = ${cfg.user}
10 RUN_MODE = prod
11
12 [database]
13 DB_TYPE = ${cfg.database.type}
14 HOST = ${cfg.database.host}:${toString cfg.database.port}
15 NAME = ${cfg.database.name}
16 USER = ${cfg.database.user}
17 PASSWD = #dbpass#
18 PATH = ${cfg.database.path}
19
20 [repository]
21 ROOT = ${cfg.repositoryRoot}
22
23 [server]
24 DOMAIN = ${cfg.domain}
25 HTTP_ADDR = ${cfg.httpAddress}
26 HTTP_PORT = ${toString cfg.httpPort}
27 ROOT_URL = ${cfg.rootUrl}
28
29 [session]
30 COOKIE_NAME = session
31 COOKIE_SECURE = ${boolToString cfg.cookieSecure}
32
33 [security]
34 SECRET_KEY = #secretkey#
35 INSTALL_LOCK = true
36
37 ${cfg.extraConfig}
38 '';
39in
40
41{
42 options = {
43 services.gogs = {
44 enable = mkOption {
45 default = false;
46 type = types.bool;
47 description = "Enable Go Git Service.";
48 };
49
50 useWizard = mkOption {
51 default = false;
52 type = types.bool;
53 description = "Do not generate a configuration and use Gogs' installation wizard instead. The first registered user will be administrator.";
54 };
55
56 stateDir = mkOption {
57 default = "/var/lib/gogs";
58 type = types.str;
59 description = "Gogs data directory.";
60 };
61
62 user = mkOption {
63 type = types.str;
64 default = "gogs";
65 description = "User account under which Gogs runs.";
66 };
67
68 group = mkOption {
69 type = types.str;
70 default = "gogs";
71 description = "Group account under which Gogs runs.";
72 };
73
74 database = {
75 type = mkOption {
76 type = types.enum [ "sqlite3" "mysql" "postgres" ];
77 example = "mysql";
78 default = "sqlite3";
79 description = "Database engine to use.";
80 };
81
82 host = mkOption {
83 type = types.str;
84 default = "127.0.0.1";
85 description = "Database host address.";
86 };
87
88 port = mkOption {
89 type = types.int;
90 default = 3306;
91 description = "Database host port.";
92 };
93
94 name = mkOption {
95 type = types.str;
96 default = "gogs";
97 description = "Database name.";
98 };
99
100 user = mkOption {
101 type = types.str;
102 default = "gogs";
103 description = "Database user.";
104 };
105
106 password = mkOption {
107 type = types.str;
108 default = "";
109 description = ''
110 The password corresponding to <option>database.user</option>.
111 Warning: this is stored in cleartext in the Nix store!
112 Use <option>database.passwordFile</option> instead.
113 '';
114 };
115
116 passwordFile = mkOption {
117 type = types.nullOr types.path;
118 default = null;
119 example = "/run/keys/gogs-dbpassword";
120 description = ''
121 A file containing the password corresponding to
122 <option>database.user</option>.
123 '';
124 };
125
126 path = mkOption {
127 type = types.str;
128 default = "${cfg.stateDir}/data/gogs.db";
129 description = "Path to the sqlite3 database file.";
130 };
131 };
132
133 appName = mkOption {
134 type = types.str;
135 default = "Gogs: Go Git Service";
136 description = "Application name.";
137 };
138
139 repositoryRoot = mkOption {
140 type = types.str;
141 default = "${cfg.stateDir}/repositories";
142 description = "Path to the git repositories.";
143 };
144
145 domain = mkOption {
146 type = types.str;
147 default = "localhost";
148 description = "Domain name of your server.";
149 };
150
151 rootUrl = mkOption {
152 type = types.str;
153 default = "http://localhost:3000/";
154 description = "Full public URL of Gogs server.";
155 };
156
157 httpAddress = mkOption {
158 type = types.str;
159 default = "0.0.0.0";
160 description = "HTTP listen address.";
161 };
162
163 httpPort = mkOption {
164 type = types.int;
165 default = 3000;
166 description = "HTTP listen port.";
167 };
168
169 cookieSecure = mkOption {
170 type = types.bool;
171 default = false;
172 description = ''
173 Marks session cookies as "secure" as a hint for browsers to only send
174 them via HTTPS. This option is recommend, if Gogs is being served over HTTPS.
175 '';
176 };
177
178 extraConfig = mkOption {
179 type = types.str;
180 default = "";
181 description = "Configuration lines appended to the generated Gogs configuration file.";
182 };
183 };
184 };
185
186 config = mkIf cfg.enable {
187
188 systemd.services.gogs = {
189 description = "Gogs (Go Git Service)";
190 after = [ "network.target" ];
191 wantedBy = [ "multi-user.target" ];
192 path = [ pkgs.gogs.bin ];
193
194 preStart = let
195 runConfig = "${cfg.stateDir}/custom/conf/app.ini";
196 secretKey = "${cfg.stateDir}/custom/conf/secret_key";
197 in ''
198 # copy custom configuration and generate a random secret key if needed
199 ${optionalString (cfg.useWizard == false) ''
200 mkdir -p ${cfg.stateDir}/custom/conf
201 cp -f ${configFile} ${runConfig}
202
203 if [ ! -e ${secretKey} ]; then
204 head -c 16 /dev/urandom | base64 > ${secretKey}
205 fi
206
207 KEY=$(head -n1 ${secretKey})
208 DBPASS=$(head -n1 ${cfg.database.passwordFile})
209 sed -e "s,#secretkey#,$KEY,g" \
210 -e "s,#dbpass#,$DBPASS,g" \
211 -i ${runConfig}
212 chmod 440 ${runConfig} ${secretKey}
213 ''}
214
215 mkdir -p ${cfg.repositoryRoot}
216 # update all hooks' binary paths
217 HOOKS=$(find ${cfg.repositoryRoot} -mindepth 4 -maxdepth 4 -type f -wholename "*git/hooks/*")
218 if [ "$HOOKS" ]
219 then
220 sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gogs,${pkgs.gogs.bin}/bin/gogs,g' $HOOKS
221 sed -ri 's,/nix/store/[a-z0-9.-]+/bin/env,${pkgs.coreutils}/bin/env,g' $HOOKS
222 sed -ri 's,/nix/store/[a-z0-9.-]+/bin/bash,${pkgs.bash}/bin/bash,g' $HOOKS
223 sed -ri 's,/nix/store/[a-z0-9.-]+/bin/perl,${pkgs.perl}/bin/perl,g' $HOOKS
224 fi
225 '';
226
227 serviceConfig = {
228 Type = "simple";
229 User = cfg.user;
230 Group = cfg.group;
231 WorkingDirectory = cfg.stateDir;
232 ExecStart = "${pkgs.gogs.bin}/bin/gogs web";
233 Restart = "always";
234 };
235
236 environment = {
237 USER = cfg.user;
238 HOME = cfg.stateDir;
239 GOGS_WORK_DIR = cfg.stateDir;
240 };
241 };
242
243 users = {
244 extraUsers.gogs = {
245 description = "Go Git Service";
246 uid = config.ids.uids.gogs;
247 group = "gogs";
248 home = cfg.stateDir;
249 createHome = true;
250 shell = pkgs.bash;
251 };
252 extraGroups.gogs.gid = config.ids.gids.gogs;
253 };
254
255 warnings = optional (cfg.database.password != "")
256 ''config.services.gogs.database.password will be stored as plaintext
257 in the Nix store. Use database.passwordFile instead.'';
258
259 # Create database passwordFile default when password is configured.
260 services.gogs.database.passwordFile = mkIf (cfg.database.password != "")
261 (mkDefault (toString (pkgs.writeTextFile {
262 name = "gogs-database-password";
263 text = cfg.database.password;
264 })));
265 };
266}