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