{ config , lib , pkgs , ... }: with lib; let cfg = config.services.gogs; iniFormat = pkgs.formats.ini {}; defaultConfig = lib.recursiveUpdate { server.RUN_USER = cfg.user; security.INSTALL_LOCK = true; database.TYPE = "sqlite3"; database.PATH = "${cfg.stateDir}/data/gogs.db"; } cfg.settings; configFile = iniFormat.generate "gogs.ini" defaultConfig; themeVariants = { dark = "kristuff.gogs.dark.min.css"; dark-blue = "kristuff.gogs.dark-accent-blue.min.css"; dark-green = "kristuff.gogs.dark-accent-green.min.css"; dark-magenta = "kristuff.gogs.dark-accent-magenta.min.css"; dark-orange = "kristuff.gogs.dark-accent-orange.min.css"; dark-red = "kristuff.gogs.dark-accent-red.min.css"; dark-yellow = "kristuff.gogs.dark-accent-yellow.min.css"; }; themeFile = if cfg.theme == null then null else if builtins.hasAttr cfg.theme themeVariants then "${pkgs.gogs-themes}/dist/${themeVariants.${cfg.theme}}" else cfg.theme; themeSetupScript = pkgs.writeShellScript "gogs-theme-setup" '' mkdir -p ${cfg.stateDir}/custom/public/css ${cfg.stateDir}/custom/templates/inject cp -f ${themeFile} ${cfg.stateDir}/custom/public/css/theme.css cat > ${cfg.stateDir}/custom/templates/inject/head.tmpl << 'EOF' EOF ''; in { options.services.gogs = { enable = lib.mkEnableOption "Gogs Git service"; package = lib.mkPackageOption pkgs "gogs" { }; user = lib.mkOption { type = lib.types.str; default = "git"; description = "User account under which Gogs runs."; }; group = lib.mkOption { type = lib.types.str; default = "git"; description = "Group under which Gogs runs."; }; stateDir = lib.mkOption { type = lib.types.str; default = "/var/lib/gogs"; description = "Persistent data directory."; }; environmentFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; description = '' File containing environment variables to pass to the Gogs service, formatted as VARIABLE=VALUE per line. Values set here are merged into the service's environment and can be used to pass secrets (e.g. database passwords) without putting them in the Nix store. ''; }; theme = lib.mkOption { type = lib.types.nullOr ( lib.types.either (lib.types.enum [ "dark" "dark-blue" "dark-green" "dark-magenta" "dark-orange" "dark-red" "dark-yellow" ]) lib.types.path ); default = null; example = "dark-blue"; description = '' Dark theme for Gogs. Can be: - A string selecting a built-in variant from pkgs.gogs-themes: dark, dark-blue, dark-green, dark-magenta, dark-orange, dark-red, dark-yellow. - A path or package pointing to a custom CSS file. Set to null to disable theming. ''; }; adminUser = lib.mkOption { type = lib.types.nullOr (lib.types.submodule { options = { name = lib.mkOption { type = lib.types.str; description = "Admin username."; }; email = lib.mkOption { type = lib.types.str; description = "Admin email address."; }; passwordFile = lib.mkOption { type = lib.types.path; description = '' File containing the admin password. Must be readable by root. Recommended to use an age secret managed by agenix. ''; }; }; }); default = null; example = { email = "admin@example.com"; passwordFile = "/run/secrets/gogs-admin-password"; }; description = '' Admin user to create on first startup. Uses gogs admin create-user via ExecStartPost. Idempotent — silently skips if the user already exists. Set to null to skip. ''; }; settings = lib.mkOption { type = iniFormat.type; default = { }; example = lib.literalExpression '' { server = { DOMAIN = "git.example.com"; ROOT_URL = "https://git.example.com/"; HTTP_PORT = 3000; }; database = { TYPE = "sqlite3"; PATH = "/var/lib/gogs/data/gogs.db"; }; } ''; description = '' Settings written to app.ini. See: https://gogs.io/docs/advanced/configuration_cheat_sheet ''; }; }; config = mkIf cfg.enable { users.users.${cfg.user} = { isSystemUser = true; group = cfg.group; home = cfg.stateDir; createHome = true; }; users.groups.${cfg.group} = { }; systemd.tmpfiles.rules = [ "d ${cfg.stateDir} 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.stateDir}/repositories 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.stateDir}/data 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.stateDir}/log 0750 ${cfg.user} ${cfg.group} -" "d ${cfg.stateDir}/custom/public/css 0755 ${cfg.user} ${cfg.group} -" "d ${cfg.stateDir}/custom/templates/inject 0755 ${cfg.user} ${cfg.group} -" ]; systemd.services.gogs = { description = "Gogs Git Service"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; environment = { GOGS_WORK_DIR = cfg.stateDir; GOGS_CUSTOM = "${cfg.stateDir}/custom"; }; serviceConfig = { Type = "simple"; User = cfg.user; Group = cfg.group; EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile; StateDirectory = "gogs"; StateDirectoryMode = "0750"; WorkingDirectory = cfg.stateDir; ExecStart = "${getExe cfg.package} web --config ${configFile}"; ExecStartPre = lib.mkIf (themeFile != null) [ "+${themeSetupScript}" ]; Restart = "on-failure"; NoNewPrivileges = true; PrivateTmp = true; ProtectSystem = "strict"; ProtectHome = true; }; postStart = lib.mkIf (cfg.adminUser != null) '' ${lib.getExe cfg.package} admin create-user \ --name ${lib.escapeShellArg cfg.adminUser.name} \ --password "$(cat ${cfg.adminUser.passwordFile})" \ --email ${lib.escapeShellArg cfg.adminUser.email} \ --admin \ --config ${configFile} \ >/dev/null 2>&1 || true ''; }; }; }