{ description = "Forgejo"; inputs = { nixpkgs = { url = "nixpkgs/nixos-unstable"; }; flake-utils = { url = "github:numtide/flake-utils"; }; }; outputs = { nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem ( system: let pkgs = import nixpkgs { inherit system; }; in { packages = { forgejo = let pamSupport = true; sqliteSupport = true; pname = "forgejo"; version = "7.0.2"; rev = "ad0b97c7c26072d3ddbf76c55c3edaab2f070a2b"; hash = "sha256-YY5dHXWMqlCIPfqsDtHZLHjEdYmrFnh4yc0hfTUESww="; npmDepsHash = "sha256-BffoEbIzTU61bw3ECEm5eDHcav4S27MB5jQKsMprkcw="; vendorHash = "sha256-UcjaMi/4XYLdaJhi2j3UWqHqkpTbZBo6EwNXxdRIKLw="; src = pkgs.fetchFromGitea { inherit rev hash; domain = "codeberg.org"; owner = "forgejo"; repo = "forgejo"; }; frontend = pkgs.buildNpmPackage { pname = "forgejo-frontend"; inherit src version npmDepsHash; patches = [ ./package-json-npm-build-frontend.patch ]; # override npmInstallHook installPhase = '' mkdir $out cp -R ./public $out/ ''; }; # data-compressed = pkgs.runCommand # "forgejo-data-compressed" # { # nativeBuildInputs = [ pkgs.brotli pkgs.xorg.lndir ]; # } # '' # mkdir $out # lndir ${forgejo.data}/ $out/ # # Create static gzip and brotli files # find -L $out -type f -regextype posix-extended -iregex '.*\.(css|html|js|svg|ttf|txt)' \ # -exec gzip --best --keep --force {} ';' \ # -exec brotli --best --keep --no-copy-stat {} ';' # ''; tags = pkgs.lib.optional pamSupport "pam" ++ pkgs.lib.optionals sqliteSupport ["sqlite" "sqlite_unlock_notify"]; in pkgs.buildGo122Module { inherit pname src version vendorHash; subPackages = ["."]; outputs = ["out" "data" "compressed"]; nativeBuildInputs = [pkgs.makeWrapper pkgs.brotli pkgs.xorg.lndir]; buildInputs = pkgs.lib.optional pamSupport pkgs.pam; patches = [ ./static-root-path.patch ]; postPatch = '' substituteInPlace modules/setting/setting.go --subst-var data ''; ldflags = [ "-s" "-w" "-X main.Version=${version}-${rev}" "-X 'main.Tags=${pkgs.lib.concatStringsSep " " tags}'" ]; preBuild = '' # go run build/merge-forgejo-locales.go ''; postInstall = '' mkdir $data cp -R ./{templates,options} ${frontend}/public $data mkdir $compressed lndir $data/ $compressed/ # Create static gzip and brotli files find -L $out -type f -regextype posix-extended -iregex '.*\.(css|html|js|svg|ttf|txt)' \ -exec gzip --best --keep --force {} ';' \ -exec brotli --best --keep --no-copy-stat {} ';' mkdir -p $out cp -R ./options/locale $out/locale wrapProgram $out/bin/gitea \ --prefix PATH : ${pkgs.lib.makeBinPath [pkgs.bash pkgs.git pkgs.gzip pkgs.openssh]} ''; # $data is not available in goModules.drv and preBuild isn't needed overrideModAttrs = _: { postPatch = null; preBuild = null; }; passthru = { # allow nix-update to handle npmDepsHash # inherit (frontend) npmDeps; # data-compressed = pkgs.runCommand "forgejo-data-compressed" # { # nativeBuildInputs = [ pkgs.brotli pkgs.xorg.lndir ]; # } '' # mkdir $out # lndir ${forgejo.data}/ $out/ # # Create static gzip and brotli files # find -L $out -type f -regextype posix-extended -iregex '.*\.(css|html|js|svg|ttf|txt)' \ # -exec gzip --best --keep --force {} ';' \ # -exec brotli --best --keep --no-copy-stat {} ';' # ''; # tests = pkgs.nixosTests.forgejo; # updateScript = pkgs.nix-update-script { }; }; meta = { description = "A self-hosted lightweight software forge"; homepage = "https://forgejo.org"; changelog = "https://codeberg.org/forgejo/forgejo/releases/tag/${src.rev}"; license = pkgs.lib.licenses.mit; maintainers = with pkgs.lib.maintainers; [emilylange urandom bendlas adamcstephens]; broken = pkgs.stdenv.isDarwin; mainProgram = "gitea"; }; }; }; } ); # // { # nixosModules.forgejo = { self, config, options, lib, pkgs, name, ... }: # let # cfg = config.jcollie.forgejo; # opt = options.jcollie.forgejo; # format = pkgs.formats.ini { }; # exe = lib.getExe self.packages.${pkgs.system}.forgejo; # useMysql = cfg.database.type == "mysql"; # usePostgresql = cfg.database.type == "postgres"; # useSqlite = cfg.database.type == "sqlite3"; # in # { # imports = [ # (mkRenamedOptionModule [ "jcollie" "forgejo" "appName" ] [ "jcollie" "forgejo" "settings" "DEFAULT" "APP_NAME" ]) # (mkRemovedOptionModule [ "jcollie" "forgejo" "extraConfig" ] "jcollie.forgejo.extraConfig has been removed. Please use the freeform jcollie.forgejo.settings option instead") # (mkRemovedOptionModule [ "jcollie" "forgejo" "database" "jcollie" ] "services.forgejo.database.password has been removed. Please use jcollie.forgejo.database.passwordFile instead") # # copied from services.gitea; remove at some point # (mkRenamedOptionModule [ "jcollie" "forgejo" "cookieSecure" ] [ "jcollie" "forgejo" "settings" "session" "COOKIE_SECURE" ]) # (mkRenamedOptionModule [ "jcollie" "forgejo" "disableRegistration" ] [ "jcollie" "forgejo" "settings" "service" "DISABLE_REGISTRATION" ]) # (mkRenamedOptionModule [ "jcollie" "forgejo" "domain" ] [ "jcollie" "forgejo" "settings" "server" "DOMAIN" ]) # (mkRenamedOptionModule [ "jcollie" "forgejo" "httpAddress" ] [ "jcollie" "forgejo" "settings" "server" "HTTP_ADDR" ]) # (mkRenamedOptionModule [ "jcollie" "forgejo" "httpPort" ] [ "jcollie" "forgejo" "settings" "server" "HTTP_PORT" ]) # (mkRenamedOptionModule [ "jcollie" "forgejo" "log" "level" ] [ "jcollie" "forgejo" "settings" "log" "LEVEL" ]) # (mkRenamedOptionModule [ "jcollie" "forgejo" "log" "rootPath" ] [ "jcollie" "forgejo" "settings" "log" "ROOT_PATH" ]) # (mkRenamedOptionModule [ "jcollie" "forgejo" "rootUrl" ] [ "jcollie" "forgejo" "settings" "server" "ROOT_URL" ]) # (mkRenamedOptionModule [ "jcollie" "forgejo" "ssh" "clonePort" ] [ "jcollie" "forgejo" "settings" "server" "SSH_PORT" ]) # (mkRenamedOptionModule [ "jcollie" "forgejo" "staticRootPath" ] [ "jcollie" "forgejo" "settings" "server" "STATIC_ROOT_PATH" ]) # (mkChangedOptionModule [ "jcollie" "forgejo" "enableUnixSocket" ] [ "jcollie" "forgejo" "settings" "server" "PROTOCOL" ] ( # config: if config.jcollie.forgejo.enableUnixSocket then "http+unix" else "http" # )) # (mkRemovedOptionModule [ "jcollie" "forgejo" "ssh" "enable" ] "jcollie.forgejo.ssh.enable has been migrated into freeform setting jcollie.forgejo.settings.server.DISABLE_SSH. Keep in mind that the setting is inverted") # ]; # options = { # services.forgejo = { # enable = mkEnableOption (mdDoc "Forgejo"); # useWizard = mkOption { # default = false; # type = types.bool; # description = mdDoc '' # Whether to use the built-in installation wizard instead of # declaratively managing the {file}`app.ini` config file in nix. # ''; # }; # stateDir = mkOption { # default = "/var/lib/forgejo"; # type = types.str; # description = mdDoc "Forgejo data directory."; # }; # customDir = mkOption { # default = "${cfg.stateDir}/custom"; # defaultText = literalExpression ''"''${config.${opt.stateDir}}/custom"''; # type = types.str; # description = mdDoc '' # Base directory for custom templates and other options. # If {option}`${opt.useWizard}` is disabled (default), this directory will also # hold secrets and the resulting {file}`app.ini` config at runtime. # ''; # }; # user = mkOption { # type = types.str; # default = "git"; # description = mdDoc "User account under which Forgejo runs."; # }; # group = mkOption { # type = types.str; # default = "git"; # description = mdDoc "Group under which Forgejo runs."; # }; # database = { # type = mkOption { # type = types.enum [ "sqlite3" "mysql" "postgres" ]; # example = "mysql"; # default = "sqlite3"; # description = mdDoc "Database engine to use."; # }; # host = mkOption { # type = types.str; # default = "127.0.0.1"; # description = mdDoc "Database host address."; # }; # port = mkOption { # type = types.port; # default = if !usePostgresql then 3306 else pg.port; # defaultText = literalExpression '' # if config.${opt.database.type} != "postgresql" # then 3306 # else config.${options.services.postgresql.port} # ''; # description = mdDoc "Database host port."; # }; # name = mkOption { # type = types.str; # default = "forgejo"; # description = mdDoc "Database name."; # }; # user = mkOption { # type = types.str; # default = "forgejo"; # description = mdDoc "Database user."; # }; # passwordFile = mkOption { # type = types.nullOr types.path; # default = null; # example = "/run/keys/forgejo-dbpassword"; # description = mdDoc '' # A file containing the password corresponding to # {option}`${opt.database.user}`. # ''; # }; # socket = mkOption { # type = types.nullOr types.path; # default = if usePostgresql then "/run/postgresql" else if useMysql then "/run/mysqld/mysqld.sock" else null; # defaultText = literalExpression "null"; # example = "/run/mysqld/mysqld.sock"; # description = mdDoc "Path to the unix socket file to use for authentication."; # }; # path = mkOption { # type = types.str; # default = "${cfg.stateDir}/data/forgejo.db"; # defaultText = literalExpression ''"''${config.${opt.stateDir}}/data/forgejo.db"''; # description = mdDoc "Path to the sqlite3 database file."; # }; # }; # dump = { # enable = mkEnableOption (mdDoc "periodic dumps via the [built-in {command}`dump` command](https://forgejo.org/docs/latest/admin/command-line/#dump)"); # interval = mkOption { # type = types.str; # default = "04:31"; # example = "hourly"; # description = mdDoc '' # Run a Forgejo dump at this interval. Runs by default at 04:31 every day. # The format is described in # {manpage}`systemd.time(7)`. # ''; # }; # backupDir = mkOption { # type = types.str; # default = "${cfg.stateDir}/dump"; # defaultText = literalExpression ''"''${config.${opt.stateDir}}/dump"''; # description = mdDoc "Path to the directory where the dump archives will be stored."; # }; # type = mkOption { # type = types.enum [ "zip" "tar" "tar.sz" "tar.gz" "tar.xz" "tar.bz2" "tar.br" "tar.lz4" "tar.zst" ]; # default = "zip"; # description = mdDoc "Archive format used to store the dump file."; # }; # file = mkOption { # type = types.nullOr types.str; # default = null; # description = mdDoc "Filename to be used for the dump. If `null` a default name is chosen by forgejo."; # example = "forgejo-dump"; # }; # }; # lfs = { # enable = mkOption { # type = types.bool; # default = false; # description = mdDoc "Enables git-lfs support."; # }; # contentDir = mkOption { # type = types.str; # default = "${cfg.stateDir}/data/lfs"; # defaultText = literalExpression ''"''${config.${opt.stateDir}}/data/lfs"''; # description = mdDoc "Where to store LFS files."; # }; # }; # repositoryRoot = mkOption { # type = types.str; # default = "${cfg.stateDir}/repositories"; # defaultText = literalExpression ''"''${config.${opt.stateDir}}/repositories"''; # description = mdDoc "Path to the git repositories."; # }; # mailerPasswordFile = mkOption { # type = types.nullOr types.str; # default = null; # example = "/run/keys/forgejo-mailpw"; # description = mdDoc "Path to a file containing the SMTP password."; # }; # settings = mkOption { # default = { }; # description = mdDoc '' # Free-form settings written directly to the `app.ini` configfile file. # Refer to for supported values. # ''; # example = literalExpression '' # { # DEFAULT = { # RUN_MODE = "dev"; # }; # "cron.sync_external_users" = { # RUN_AT_START = true; # SCHEDULE = "@every 24h"; # UPDATE_EXISTING = true; # }; # mailer = { # ENABLED = true; # MAILER_TYPE = "sendmail"; # FROM = "do-not-reply@example.org"; # SENDMAIL_PATH = "''${pkgs.system-sendmail}/bin/sendmail"; # }; # other = { # SHOW_FOOTER_VERSION = false; # }; # } # ''; # type = types.submodule { # freeformType = format.type; # options = { # log = { # ROOT_PATH = mkOption { # default = "${cfg.stateDir}/log"; # defaultText = literalExpression ''"''${config.${opt.stateDir}}/log"''; # type = types.str; # description = mdDoc "Root path for log files."; # }; # LEVEL = mkOption { # default = "Info"; # type = types.enum [ "Trace" "Debug" "Info" "Warn" "Error" "Critical" ]; # description = mdDoc "General log level."; # }; # }; # server = { # PROTOCOL = mkOption { # type = types.enum [ "http" "https" "fcgi" "http+unix" "fcgi+unix" ]; # default = "http"; # description = mdDoc ''Listen protocol. `+unix` means "over unix", not "in addition to."''; # }; # HTTP_ADDR = mkOption { # type = types.either types.str types.path; # default = if lib.hasSuffix "+unix" cfg.settings.server.PROTOCOL then "/run/forgejo/forgejo.sock" else "0.0.0.0"; # defaultText = literalExpression ''if lib.hasSuffix "+unix" cfg.settings.server.PROTOCOL then "/run/forgejo/forgejo.sock" else "0.0.0.0"''; # description = mdDoc "Listen address. Must be a path when using a unix socket."; # }; # HTTP_PORT = mkOption { # type = types.port; # default = 3000; # description = mdDoc "Listen port. Ignored when using a unix socket."; # }; # DOMAIN = mkOption { # type = types.str; # default = "localhost"; # description = mdDoc "Domain name of your server."; # }; # ROOT_URL = mkOption { # type = types.str; # default = "http://${cfg.settings.server.DOMAIN}:${toString cfg.settings.server.HTTP_PORT}/"; # defaultText = literalExpression ''"http://''${config.services.forgejo.settings.server.DOMAIN}:''${toString config.services.forgejo.settings.server.HTTP_PORT}/"''; # description = mdDoc "Full public URL of Forgejo server."; # }; # STATIC_ROOT_PATH = mkOption { # type = types.either types.str types.path; # default = cfg.package.data; # defaultText = literalExpression "config.${opt.package}.data"; # example = "/var/lib/forgejo/data"; # description = mdDoc "Upper level of template and static files path."; # }; # DISABLE_SSH = mkOption { # type = types.bool; # default = false; # description = mdDoc "Disable external SSH feature."; # }; # SSH_PORT = mkOption { # type = types.port; # default = 22; # example = 2222; # description = mdDoc '' # SSH port displayed in clone URL. # The option is required to configure a service when the external visible port # differs from the local listening port i.e. if port forwarding is used. # ''; # }; # }; # session = { # COOKIE_SECURE = mkOption { # type = types.bool; # default = false; # description = mdDoc '' # Marks session cookies as "secure" as a hint for browsers to only send # them via HTTPS. This option is recommend, if Forgejo is being served over HTTPS. # ''; # }; # }; # }; # }; # }; # }; # }; # config = mkIf cfg.enable { # assertions = [ # ]; # jcollie.forgejo.settings = { # DEFAULT = { # RUN_MODE = mkDefault "prod"; # RUN_USER = mkDefault cfg.user; # WORK_PATH = mkDefault cfg.stateDir; # }; # database = mkMerge [ # { # DB_TYPE = cfg.database.type; # } # (mkIf (useMysql || usePostgresql) { # HOST = if cfg.database.socket != null then cfg.database.socket else cfg.database.host + ":" + toString cfg.database.port; # NAME = cfg.database.name; # USER = cfg.database.user; # PASSWD = "#dbpass#"; # }) # (mkIf useSqlite { # PATH = cfg.database.path; # }) # (mkIf usePostgresql { # SSL_MODE = "disable"; # }) # ]; # repository = { # ROOT = cfg.repositoryRoot; # }; # server = mkIf cfg.lfs.enable { # LFS_START_SERVER = true; # LFS_JWT_SECRET = "#lfsjwtsecret#"; # }; # session = { # COOKIE_NAME = mkDefault "session"; # }; # security = { # SECRET_KEY = "#secretkey#"; # INTERNAL_TOKEN = "#internaltoken#"; # INSTALL_LOCK = true; # }; # mailer = mkIf (cfg.mailerPasswordFile != null) { # PASSWD = "#mailerpass#"; # }; # oauth2 = { # JWT_SECRET = "#oauth2jwtsecret#"; # }; # lfs = mkIf cfg.lfs.enable { # PATH = cfg.lfs.contentDir; # }; # }; # systemd.tmpfiles.rules = [ # "d '${cfg.dump.backupDir}' 0750 ${cfg.user} ${cfg.group} - -" # "z '${cfg.dump.backupDir}' 0750 ${cfg.user} ${cfg.group} - -" # "d '${cfg.repositoryRoot}' 0750 ${cfg.user} ${cfg.group} - -" # "z '${cfg.repositoryRoot}' 0750 ${cfg.user} ${cfg.group} - -" # "d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -" # "d '${cfg.stateDir}/conf' 0750 ${cfg.user} ${cfg.group} - -" # "d '${cfg.customDir}' 0750 ${cfg.user} ${cfg.group} - -" # "d '${cfg.customDir}/conf' 0750 ${cfg.user} ${cfg.group} - -" # "d '${cfg.stateDir}/data' 0750 ${cfg.user} ${cfg.group} - -" # "d '${cfg.stateDir}/log' 0750 ${cfg.user} ${cfg.group} - -" # "z '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -" # "z '${cfg.stateDir}/.ssh' 0700 ${cfg.user} ${cfg.group} - -" # "z '${cfg.stateDir}/conf' 0750 ${cfg.user} ${cfg.group} - -" # "z '${cfg.customDir}' 0750 ${cfg.user} ${cfg.group} - -" # "z '${cfg.customDir}/conf' 0750 ${cfg.user} ${cfg.group} - -" # "z '${cfg.stateDir}/data' 0750 ${cfg.user} ${cfg.group} - -" # "z '${cfg.stateDir}/log' 0750 ${cfg.user} ${cfg.group} - -" # # If we have a folder or symlink with Forgejo locales, remove it # # And symlink the current Forgejo locales in place # "L+ '${cfg.stateDir}/conf/locale' - - - - ${cfg.package.out}/locale" # ] ++ optionals cfg.lfs.enable [ # "d '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -" # "z '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -" # ]; # environment.systemPackages = # let # forgejo-cli = pkgs.writeScriptBin "forgejo-cli" '' # export GITEA_WORK_DIR=${cfg.stateDir} # export GITEA_CUSTOM=${cfg.stateDir}/custom # export HOME="${cfg.stateDir}"; # exec ${pkgs.util-linux}/bin/runuser --user ${cfg.user} -- ${exe} forgejo-cli "$@" # ''; # in # with pkgs; [ # forgejo-cli # ]; # systemd.services.forgejo = { # description = "Forgejo (Beyond coding. We forge.)"; # after = [ # "network.target" # ]; # wantedBy = [ "multi-user.target" ]; # path = [ cfg.package pkgs.git pkgs.gnupg ]; # # In older versions the secret naming for JWT was kind of confusing. # # The file jwt_secret hold the value for LFS_JWT_SECRET and JWT_SECRET # # wasn't persistent at all. # # To fix that, there is now the file oauth2_jwt_secret containing the # # values for JWT_SECRET and the file jwt_secret gets renamed to # # lfs_jwt_secret. # # We have to consider this to stay compatible with older installations. # preStart = # let # runConfig = "${cfg.customDir}/conf/app.ini"; # secretKey = "${cfg.customDir}/conf/secret_key"; # oauth2JwtSecret = "${cfg.customDir}/conf/oauth2_jwt_secret"; # oldLfsJwtSecret = "${cfg.customDir}/conf/jwt_secret"; # old file for LFS_JWT_SECRET # lfsJwtSecret = "${cfg.customDir}/conf/lfs_jwt_secret"; # new file for LFS_JWT_SECRET # internalToken = "${cfg.customDir}/conf/internal_token"; # replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret"; # in # '' # # copy custom configuration and generate random secrets if needed # ${lib.optionalString (!cfg.useWizard) '' # function forgejo_setup { # cp -f '${format.generate "app.ini" cfg.settings}' '${runConfig}' # if [ ! -s '${secretKey}' ]; then # ${exe} generate secret SECRET_KEY > '${secretKey}' # fi # # Migrate LFS_JWT_SECRET filename # if [[ -s '${oldLfsJwtSecret}' && ! -s '${lfsJwtSecret}' ]]; then # mv '${oldLfsJwtSecret}' '${lfsJwtSecret}' # fi # if [ ! -s '${oauth2JwtSecret}' ]; then # ${exe} generate secret JWT_SECRET > '${oauth2JwtSecret}' # fi # ${optionalString cfg.lfs.enable '' # if [ ! -s '${lfsJwtSecret}' ]; then # ${exe} generate secret LFS_JWT_SECRET > '${lfsJwtSecret}' # fi # ''} # if [ ! -s '${internalToken}' ]; then # ${exe} generate secret INTERNAL_TOKEN > '${internalToken}' # fi # chmod u+w '${runConfig}' # ${replaceSecretBin} '#secretkey#' '${secretKey}' '${runConfig}' # ${replaceSecretBin} '#oauth2jwtsecret#' '${oauth2JwtSecret}' '${runConfig}' # ${replaceSecretBin} '#internaltoken#' '${internalToken}' '${runConfig}' # ${optionalString cfg.lfs.enable '' # ${replaceSecretBin} '#lfsjwtsecret#' '${lfsJwtSecret}' '${runConfig}' # ''} # ${optionalString (cfg.database.passwordFile != null) '' # ${replaceSecretBin} '#dbpass#' '${cfg.database.passwordFile}' '${runConfig}' # ''} # ${optionalString (cfg.mailerPasswordFile != null) '' # ${replaceSecretBin} '#mailerpass#' '${cfg.mailerPasswordFile}' '${runConfig}' # ''} # chmod u-w '${runConfig}' # } # (umask 027; forgejo_setup) # ''} # # run migrations/init the database # ${exe} migrate # # update all hooks' binary paths # ${exe} admin regenerate hooks # # update command option in authorized_keys # if [ -r ${cfg.stateDir}/.ssh/authorized_keys ] # then # ${exe} admin regenerate keys # fi # ''; # serviceConfig = { # Type = "simple"; # User = cfg.user; # Group = cfg.group; # WorkingDirectory = cfg.stateDir; # ExecStart = "${exe} web --pid /run/forgejo/forgejo.pid"; # Restart = "always"; # # Runtime directory and mode # RuntimeDirectory = "forgejo"; # RuntimeDirectoryMode = "0755"; # # Proc filesystem # ProcSubset = "pid"; # ProtectProc = "invisible"; # # Access write directories # ReadWritePaths = [ # cfg.customDir # cfg.dump.backupDir # cfg.repositoryRoot # cfg.stateDir # cfg.lfs.contentDir # ]; # UMask = "0027"; # # Capabilities # CapabilityBoundingSet = ""; # # Security # NoNewPrivileges = true; # # Sandboxing # ProtectSystem = "strict"; # ProtectHome = true; # PrivateTmp = true; # PrivateDevices = true; # PrivateUsers = true; # ProtectHostname = true; # ProtectClock = true; # ProtectKernelTunables = true; # ProtectKernelModules = true; # ProtectKernelLogs = true; # ProtectControlGroups = true; # RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; # RestrictNamespaces = true; # LockPersonality = true; # MemoryDenyWriteExecute = true; # RestrictRealtime = true; # RestrictSUIDSGID = true; # RemoveIPC = true; # PrivateMounts = true; # # System Call Filtering # SystemCallArchitectures = "native"; # SystemCallFilter = [ # "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid" # "setrlimit" # ]; # }; # environment = { # USER = cfg.user; # HOME = cfg.stateDir; # # `GITEA_` prefix until https://codeberg.org/forgejo/forgejo/issues/497 # # is resolved. # GITEA_WORK_DIR = cfg.stateDir; # GITEA_CUSTOM = cfg.customDir; # }; # }; # users.users = mkIf (cfg.user == "git") { # git = { # description = "Forgejo"; # home = cfg.stateDir; # useDefaultShell = true; # group = cfg.group; # isSystemUser = true; # }; # }; # users.groups = mkIf (cfg.group == "git") { # git = { }; # }; # }; # }; # }; }