at 18.09-beta 12 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.gitea; 7 gitea = cfg.package; 8 pg = config.services.postgresql; 9 usePostgresql = cfg.database.type == "postgres"; 10 configFile = pkgs.writeText "app.ini" '' 11 APP_NAME = ${cfg.appName} 12 RUN_USER = ${cfg.user} 13 RUN_MODE = prod 14 15 [database] 16 DB_TYPE = ${cfg.database.type} 17 HOST = ${cfg.database.host}:${toString cfg.database.port} 18 NAME = ${cfg.database.name} 19 USER = ${cfg.database.user} 20 PASSWD = #dbpass# 21 PATH = ${cfg.database.path} 22 ${optionalString usePostgresql '' 23 SSL_MODE = disable 24 ''} 25 26 [repository] 27 ROOT = ${cfg.repositoryRoot} 28 29 [server] 30 DOMAIN = ${cfg.domain} 31 HTTP_ADDR = ${cfg.httpAddress} 32 HTTP_PORT = ${toString cfg.httpPort} 33 ROOT_URL = ${cfg.rootUrl} 34 STATIC_ROOT_PATH = ${cfg.staticRootPath} 35 36 [session] 37 COOKIE_NAME = session 38 COOKIE_SECURE = ${boolToString cfg.cookieSecure} 39 40 [security] 41 SECRET_KEY = #secretkey# 42 INSTALL_LOCK = true 43 44 [log] 45 ROOT_PATH = ${cfg.log.rootPath} 46 LEVEL = ${cfg.log.level} 47 48 ${cfg.extraConfig} 49 ''; 50in 51 52{ 53 options = { 54 services.gitea = { 55 enable = mkOption { 56 default = false; 57 type = types.bool; 58 description = "Enable Gitea Service."; 59 }; 60 61 package = mkOption { 62 default = pkgs.gitea; 63 type = types.package; 64 defaultText = "pkgs.gitea"; 65 description = "gitea derivation to use"; 66 }; 67 68 useWizard = mkOption { 69 default = false; 70 type = types.bool; 71 description = "Do not generate a configuration and use gitea' installation wizard instead. The first registered user will be administrator."; 72 }; 73 74 stateDir = mkOption { 75 default = "/var/lib/gitea"; 76 type = types.str; 77 description = "gitea data directory."; 78 }; 79 80 log = { 81 rootPath = mkOption { 82 default = "${cfg.stateDir}/log"; 83 type = types.str; 84 description = "Root path for log files."; 85 }; 86 level = mkOption { 87 default = "Trace"; 88 type = types.enum [ "Trace" "Debug" "Info" "Warn" "Error" "Critical" ]; 89 description = "General log level."; 90 }; 91 }; 92 93 user = mkOption { 94 type = types.str; 95 default = "gitea"; 96 description = "User account under which gitea runs."; 97 }; 98 99 database = { 100 type = mkOption { 101 type = types.enum [ "sqlite3" "mysql" "postgres" ]; 102 example = "mysql"; 103 default = "sqlite3"; 104 description = "Database engine to use."; 105 }; 106 107 host = mkOption { 108 type = types.str; 109 default = "127.0.0.1"; 110 description = "Database host address."; 111 }; 112 113 port = mkOption { 114 type = types.int; 115 default = (if !usePostgresql then 3306 else pg.port); 116 description = "Database host port."; 117 }; 118 119 name = mkOption { 120 type = types.str; 121 default = "gitea"; 122 description = "Database name."; 123 }; 124 125 user = mkOption { 126 type = types.str; 127 default = "gitea"; 128 description = "Database user."; 129 }; 130 131 password = mkOption { 132 type = types.str; 133 default = ""; 134 description = '' 135 The password corresponding to <option>database.user</option>. 136 Warning: this is stored in cleartext in the Nix store! 137 Use <option>database.passwordFile</option> instead. 138 ''; 139 }; 140 141 passwordFile = mkOption { 142 type = types.nullOr types.path; 143 default = null; 144 example = "/run/keys/gitea-dbpassword"; 145 description = '' 146 A file containing the password corresponding to 147 <option>database.user</option>. 148 ''; 149 }; 150 151 path = mkOption { 152 type = types.str; 153 default = "${cfg.stateDir}/data/gitea.db"; 154 description = "Path to the sqlite3 database file."; 155 }; 156 157 createDatabase = mkOption { 158 type = types.bool; 159 default = true; 160 description = '' 161 Whether to create a local postgresql database automatically. 162 This only applies if database type "postgres" is selected. 163 ''; 164 }; 165 }; 166 167 dump = { 168 enable = mkOption { 169 type = types.bool; 170 default = false; 171 description = '' 172 Enable a timer that runs gitea dump to generate backup-files of the 173 current gitea database and repositories. 174 ''; 175 }; 176 177 interval = mkOption { 178 type = types.str; 179 default = "04:31"; 180 example = "hourly"; 181 description = '' 182 Run a gitea dump at this interval. Runs by default at 04:31 every day. 183 184 The format is described in 185 <citerefentry><refentrytitle>systemd.time</refentrytitle> 186 <manvolnum>7</manvolnum></citerefentry>. 187 ''; 188 }; 189 }; 190 191 appName = mkOption { 192 type = types.str; 193 default = "gitea: Gitea Service"; 194 description = "Application name."; 195 }; 196 197 repositoryRoot = mkOption { 198 type = types.str; 199 default = "${cfg.stateDir}/repositories"; 200 description = "Path to the git repositories."; 201 }; 202 203 domain = mkOption { 204 type = types.str; 205 default = "localhost"; 206 description = "Domain name of your server."; 207 }; 208 209 rootUrl = mkOption { 210 type = types.str; 211 default = "http://localhost:3000/"; 212 description = "Full public URL of gitea server."; 213 }; 214 215 httpAddress = mkOption { 216 type = types.str; 217 default = "0.0.0.0"; 218 description = "HTTP listen address."; 219 }; 220 221 httpPort = mkOption { 222 type = types.int; 223 default = 3000; 224 description = "HTTP listen port."; 225 }; 226 227 cookieSecure = mkOption { 228 type = types.bool; 229 default = false; 230 description = '' 231 Marks session cookies as "secure" as a hint for browsers to only send 232 them via HTTPS. This option is recommend, if gitea is being served over HTTPS. 233 ''; 234 }; 235 236 staticRootPath = mkOption { 237 type = types.str; 238 default = "${gitea.data}"; 239 example = "/var/lib/gitea/data"; 240 description = "Upper level of template and static files path."; 241 }; 242 243 extraConfig = mkOption { 244 type = types.str; 245 default = ""; 246 description = "Configuration lines appended to the generated gitea configuration file."; 247 }; 248 }; 249 }; 250 251 config = mkIf cfg.enable { 252 services.postgresql.enable = mkIf usePostgresql (mkDefault true); 253 254 systemd.services.gitea = { 255 description = "gitea"; 256 after = [ "network.target" "postgresql.service" ]; 257 wantedBy = [ "multi-user.target" ]; 258 path = [ gitea.bin ]; 259 260 preStart = let 261 runConfig = "${cfg.stateDir}/custom/conf/app.ini"; 262 secretKey = "${cfg.stateDir}/custom/conf/secret_key"; 263 in '' 264 # Make sure that the stateDir exists, as well as the conf dir in there 265 mkdir -p ${cfg.stateDir}/conf 266 267 # copy custom configuration and generate a random secret key if needed 268 ${optionalString (cfg.useWizard == false) '' 269 mkdir -p ${cfg.stateDir}/custom/conf 270 cp -f ${configFile} ${runConfig} 271 272 if [ ! -e ${secretKey} ]; then 273 head -c 16 /dev/urandom | base64 > ${secretKey} 274 fi 275 276 KEY=$(head -n1 ${secretKey}) 277 DBPASS=$(head -n1 ${cfg.database.passwordFile}) 278 sed -e "s,#secretkey#,$KEY,g" \ 279 -e "s,#dbpass#,$DBPASS,g" \ 280 -i ${runConfig} 281 chmod 640 ${runConfig} ${secretKey} 282 ''} 283 284 mkdir -p ${cfg.repositoryRoot} 285 # update all hooks' binary paths 286 HOOKS=$(find ${cfg.repositoryRoot} -mindepth 4 -maxdepth 6 -type f -wholename "*git/hooks/*") 287 if [ "$HOOKS" ] 288 then 289 sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gitea,${gitea.bin}/bin/gitea,g' $HOOKS 290 sed -ri 's,/nix/store/[a-z0-9.-]+/bin/env,${pkgs.coreutils}/bin/env,g' $HOOKS 291 sed -ri 's,/nix/store/[a-z0-9.-]+/bin/bash,${pkgs.bash}/bin/bash,g' $HOOKS 292 sed -ri 's,/nix/store/[a-z0-9.-]+/bin/perl,${pkgs.perl}/bin/perl,g' $HOOKS 293 fi 294 # If we have a folder or symlink with gitea locales, remove it 295 if [ -e ${cfg.stateDir}/conf/locale ] 296 then 297 rm -r ${cfg.stateDir}/conf/locale 298 fi 299 # And symlink the current gitea locales in place 300 ln -s ${gitea.out}/locale ${cfg.stateDir}/conf/locale 301 # update command option in authorized_keys 302 if [ -r ${cfg.stateDir}/.ssh/authorized_keys ] 303 then 304 sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gitea,${gitea.bin}/bin/gitea,g' ${cfg.stateDir}/.ssh/authorized_keys 305 fi 306 '' + optionalString (usePostgresql && cfg.database.createDatabase) '' 307 if ! test -e "${cfg.stateDir}/db-created"; then 308 echo "CREATE ROLE ${cfg.database.user} 309 WITH ENCRYPTED PASSWORD '$(head -n1 ${cfg.database.passwordFile})' 310 NOCREATEDB NOCREATEROLE LOGIN" | 311 ${pkgs.sudo}/bin/sudo -u ${pg.superUser} ${pg.package}/bin/psql 312 ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \ 313 ${pg.package}/bin/createdb \ 314 --owner=${cfg.database.user} \ 315 --encoding=UTF8 \ 316 --lc-collate=C \ 317 --lc-ctype=C \ 318 --template=template0 \ 319 ${cfg.database.name} 320 touch "${cfg.stateDir}/db-created" 321 fi 322 '' + '' 323 chown ${cfg.user} -R ${cfg.stateDir} 324 ''; 325 326 serviceConfig = { 327 Type = "simple"; 328 User = cfg.user; 329 WorkingDirectory = cfg.stateDir; 330 PermissionsStartOnly = true; 331 ExecStart = "${gitea.bin}/bin/gitea web"; 332 Restart = "always"; 333 }; 334 335 environment = { 336 USER = cfg.user; 337 HOME = cfg.stateDir; 338 GITEA_WORK_DIR = cfg.stateDir; 339 }; 340 }; 341 342 users = mkIf (cfg.user == "gitea") { 343 users.gitea = { 344 description = "Gitea Service"; 345 home = cfg.stateDir; 346 createHome = true; 347 useDefaultShell = true; 348 }; 349 }; 350 351 warnings = optional (cfg.database.password != "") 352 ''config.services.gitea.database.password will be stored as plaintext 353 in the Nix store. Use database.passwordFile instead.''; 354 355 # Create database passwordFile default when password is configured. 356 services.gitea.database.passwordFile = 357 (mkDefault (toString (pkgs.writeTextFile { 358 name = "gitea-database-password"; 359 text = cfg.database.password; 360 }))); 361 362 systemd.services.gitea-dump = mkIf cfg.dump.enable { 363 description = "gitea dump"; 364 after = [ "gitea.service" ]; 365 wantedBy = [ "default.target" ]; 366 path = [ gitea.bin ]; 367 368 environment = { 369 USER = cfg.user; 370 HOME = cfg.stateDir; 371 GITEA_WORK_DIR = cfg.stateDir; 372 }; 373 374 serviceConfig = { 375 Type = "oneshot"; 376 User = cfg.user; 377 ExecStart = "${gitea.bin}/bin/gitea dump"; 378 WorkingDirectory = cfg.stateDir; 379 }; 380 }; 381 382 systemd.timers.gitea-dump = mkIf cfg.dump.enable { 383 description = "Update timer for gitea-dump"; 384 partOf = [ "gitea-dump.service" ]; 385 wantedBy = [ "timers.target" ]; 386 timerConfig.OnCalendar = cfg.dump.interval; 387 }; 388 }; 389}