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