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