# Forgejo Module # Provides: Self-hosted Git service (Fork of Gitea) # # Usage: # myModules.forgejo = { # enable = true; # domain = "git.example.com"; # }; { config, lib, pkgs, ... }: let cfg = config.myModules.forgejo; in { options.myModules.forgejo = { enable = lib.mkEnableOption "Forgejo Git service"; port = lib.mkOption { type = lib.types.port; default = 3002; description = "Internal port to run Forgejo on"; }; domain = lib.mkOption { type = lib.types.str; example = "git.example.com"; description = "Public domain name for Forgejo"; }; disableRegistration = lib.mkOption { type = lib.types.bool; default = true; description = "Disable public user registration"; }; runner = { enable = lib.mkEnableOption "Forgejo Actions Runner"; name = lib.mkOption { type = lib.types.str; default = config.networking.hostName; description = "Name of the runner"; }; tokenFile = lib.mkOption { type = lib.types.path; description = "Path to the token file (containing TOKEN=...)"; }; labels = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "native:host" "ubuntu-latest:docker://node:20-bullseye" "debian-latest:docker://node:20-bullseye" ]; description = "Labels for the runner"; }; }; }; config = lib.mkIf cfg.enable { services.forgejo = { enable = true; database.type = "postgres"; customDir = "/var/lib/forgejo/custom"; settings = { server = { DOMAIN = cfg.domain; ROOT_URL = "https://${cfg.domain}/"; HTTP_ADDR = "127.0.0.1"; HTTP_PORT = cfg.port; SSH_PORT = 2222; START_SSH_SERVER = true; SSH_LISTEN_ADDR = "0.0.0.0"; SSH_SERVER_KEY_EXCHANGES = "sntrup761x25519-sha512,curve25519-sha256,curve25519-sha256@libssh.org"; SSH_SERVER_CIPHERS = "chacha20-poly1305@openssh.com,aes256-gcm@openssh.com"; SSH_SERVER_MACS = "hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com"; }; service = { DISABLE_REGISTRATION = cfg.disableRegistration; }; session = { COOKIE_SECURE = true; }; security = { PASSWORD_COMPLEXITY = "lower,upper,digit,spec"; MIN_PASSWORD_LENGTH = 12; }; "ui.meta" = { AUTHOR = "Penal Colony"; DESCRIPTION = "The apparatus inscribes your code. Every commit is judged."; }; "ui" = { DEFAULT_THEME = "forgejo-auto"; APP_NAME = "The Harrow"; }; }; }; myModules.nginx.domains."${cfg.domain}" = { port = cfg.port; extraConfig = '' client_max_body_size 512M; ''; contentSecurityPolicy = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self' wss://${cfg.domain}; frame-ancestors 'self'"; }; networking.firewall.allowedTCPPorts = [ 2222 ]; myModules.backup.paths = [ config.services.forgejo.stateDir ]; # Copy branding assets to custom directory (Forgejo serves from /assets/img/) systemd.tmpfiles.rules = [ "d /var/lib/forgejo/custom/public/assets/img 0755 forgejo forgejo -" "C+ /var/lib/forgejo/custom/public/assets/img/logo.svg - - - - ${toString ../custom/public/assets/img/logo.svg}" "C+ /var/lib/forgejo/custom/public/assets/img/favicon.svg - - - - ${toString ../custom/public/assets/img/favicon.svg}" ]; services.gitea-actions-runner = lib.mkIf cfg.runner.enable { package = pkgs.forgejo-runner; instances.default = { enable = true; name = cfg.runner.name; url = "https://${cfg.domain}"; tokenFile = cfg.runner.tokenFile; labels = cfg.runner.labels; settings = { container = { network = "bridge"; }; }; }; }; }; }