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