diff --git a/nixos/lib/make-options-doc/options-to-docbook.xsl b/nixos/lib/make-options-doc/options-to-docbook.xsl
index 18d19fddaca..304698a51ad 100644
--- a/nixos/lib/make-options-doc/options-to-docbook.xsl
+++ b/nixos/lib/make-options-doc/options-to-docbook.xsl
@@ -20,7 +20,7 @@
Configuration Options
-
+
diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
index 8873f6d00e0..e14bb89c75c 100644
--- a/nixos/modules/services/databases/redis.nix
+++ b/nixos/modules/services/databases/redis.nix
@@ -5,17 +5,18 @@ with lib;
let
cfg = config.services.redis;
- ulimitNofile = cfg.maxclients + 32;
-
mkValueString = value:
if value == true then "yes"
else if value == false then "no"
else generators.mkValueStringDefault { } value;
- redisConfig = pkgs.writeText "redis.conf" (generators.toKeyValue {
+ redisConfig = settings: pkgs.writeText "redis.conf" (generators.toKeyValue {
listsAsDuplicateKeys = true;
mkKeyValue = generators.mkKeyValueDefault { inherit mkValueString; } " ";
- } cfg.settings);
+ } settings);
+
+ redisName = name: "redis" + optionalString (name != "") ("-"+name);
+ enabledServers = filterAttrs (name: conf: conf.enable) config.services.redis.servers;
in {
imports = [
@@ -25,6 +26,27 @@ in {
(mkRemovedOptionModule [ "services" "redis" "appendOnlyFilename" ] "This option was never used.")
(mkRemovedOptionModule [ "services" "redis" "pidFile" ] "This option was removed.")
(mkRemovedOptionModule [ "services" "redis" "extraConfig" ] "Use services.redis.settings instead.")
+ (mkRenamedOptionModule [ "services" "redis" "enable"] [ "services" "redis" "servers" "" "enable" ])
+ (mkRenamedOptionModule [ "services" "redis" "port"] [ "services" "redis" "servers" "" "port" ])
+ (mkRenamedOptionModule [ "services" "redis" "openFirewall"] [ "services" "redis" "servers" "" "openFirewall" ])
+ (mkRenamedOptionModule [ "services" "redis" "bind"] [ "services" "redis" "servers" "" "bind" ])
+ (mkRenamedOptionModule [ "services" "redis" "unixSocket"] [ "services" "redis" "servers" "" "unixSocket" ])
+ (mkRenamedOptionModule [ "services" "redis" "unixSocketPerm"] [ "services" "redis" "servers" "" "unixSocketPerm" ])
+ (mkRenamedOptionModule [ "services" "redis" "logLevel"] [ "services" "redis" "servers" "" "logLevel" ])
+ (mkRenamedOptionModule [ "services" "redis" "logfile"] [ "services" "redis" "servers" "" "logfile" ])
+ (mkRenamedOptionModule [ "services" "redis" "syslog"] [ "services" "redis" "servers" "" "syslog" ])
+ (mkRenamedOptionModule [ "services" "redis" "databases"] [ "services" "redis" "servers" "" "databases" ])
+ (mkRenamedOptionModule [ "services" "redis" "maxclients"] [ "services" "redis" "servers" "" "maxclients" ])
+ (mkRenamedOptionModule [ "services" "redis" "save"] [ "services" "redis" "servers" "" "save" ])
+ (mkRenamedOptionModule [ "services" "redis" "slaveOf"] [ "services" "redis" "servers" "" "slaveOf" ])
+ (mkRenamedOptionModule [ "services" "redis" "masterAuth"] [ "services" "redis" "servers" "" "masterAuth" ])
+ (mkRenamedOptionModule [ "services" "redis" "requirePass"] [ "services" "redis" "servers" "" "requirePass" ])
+ (mkRenamedOptionModule [ "services" "redis" "requirePassFile"] [ "services" "redis" "servers" "" "requirePassFile" ])
+ (mkRenamedOptionModule [ "services" "redis" "appendOnly"] [ "services" "redis" "servers" "" "appendOnly" ])
+ (mkRenamedOptionModule [ "services" "redis" "appendFsync"] [ "services" "redis" "servers" "" "appendFsync" ])
+ (mkRenamedOptionModule [ "services" "redis" "slowLogLogSlowerThan"] [ "services" "redis" "servers" "" "slowLogLogSlowerThan" ])
+ (mkRenamedOptionModule [ "services" "redis" "slowLogMaxLen"] [ "services" "redis" "servers" "" "slowLogMaxLen" ])
+ (mkRenamedOptionModule [ "services" "redis" "settings"] [ "services" "redis" "servers" "" "settings" ])
];
###### interface
@@ -32,18 +54,6 @@ in {
options = {
services.redis = {
-
- enable = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Whether to enable the Redis server. Note that the NixOS module for
- Redis disables kernel support for Transparent Huge Pages (THP),
- because this features causes major performance problems for Redis,
- e.g. (https://redis.io/topics/latency).
- '';
- };
-
package = mkOption {
type = types.package;
default = pkgs.redis;
@@ -51,177 +61,227 @@ in {
description = "Which Redis derivation to use.";
};
- port = mkOption {
- type = types.port;
- default = 6379;
- description = "The port for Redis to listen to.";
- };
+ vmOverCommit = mkEnableOption ''
+ setting of vm.overcommit_memory to 1
+ (Suggested for Background Saving: http://redis.io/topics/faq)
+ '';
- vmOverCommit = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Set vm.overcommit_memory to 1 (Suggested for Background Saving: http://redis.io/topics/faq)
- '';
- };
-
- openFirewall = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Whether to open ports in the firewall for the server.
- '';
- };
-
- bind = mkOption {
- type = with types; nullOr str;
- default = "127.0.0.1";
- description = ''
- The IP interface to bind to.
- null means "all interfaces".
- '';
- example = "192.0.2.1";
- };
-
- unixSocket = mkOption {
- type = with types; nullOr path;
- default = null;
- description = "The path to the socket to bind to.";
- example = "/run/redis/redis.sock";
- };
-
- unixSocketPerm = mkOption {
- type = types.int;
- default = 750;
- description = "Change permissions for the socket";
- example = 700;
- };
-
- logLevel = mkOption {
- type = types.str;
- default = "notice"; # debug, verbose, notice, warning
- example = "debug";
- description = "Specify the server verbosity level, options: debug, verbose, notice, warning.";
- };
-
- logfile = mkOption {
- type = types.str;
- default = "/dev/null";
- description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output.";
- example = "/var/log/redis.log";
- };
-
- syslog = mkOption {
- type = types.bool;
- default = true;
- description = "Enable logging to the system logger.";
- };
-
- databases = mkOption {
- type = types.int;
- default = 16;
- description = "Set the number of databases.";
- };
-
- maxclients = mkOption {
- type = types.int;
- default = 10000;
- description = "Set the max number of connected clients at the same time.";
- };
-
- save = mkOption {
- type = with types; listOf (listOf int);
- default = [ [900 1] [300 10] [60 10000] ];
- description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.";
- example = [ [900 1] [300 10] [60 10000] ];
- };
-
- slaveOf = mkOption {
- type = with types; nullOr (submodule ({ ... }: {
+ servers = mkOption {
+ type = with types; attrsOf (submodule ({config, name, ...}@args: {
options = {
- ip = mkOption {
- type = str;
- description = "IP of the Redis master";
- example = "192.168.1.100";
+ enable = mkEnableOption ''
+ Redis server.
+
+ Note that the NixOS module for Redis disables kernel support
+ for Transparent Huge Pages (THP),
+ because this features causes major performance problems for Redis,
+ e.g. (https://redis.io/topics/latency).
+ '';
+
+ user = mkOption {
+ type = types.str;
+ default = redisName name;
+ defaultText = "\"redis\" or \"redis-\${name}\" if name != \"\"";
+ description = "The username and groupname for redis-server.";
};
port = mkOption {
- type = port;
- description = "port of the Redis master";
+ type = types.port;
default = 6379;
+ description = "The port for Redis to listen to.";
+ };
+
+ openFirewall = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to open ports in the firewall for the server.
+ '';
+ };
+
+ bind = mkOption {
+ type = with types; nullOr str;
+ default = if name == "" then "127.0.0.1" else null;
+ defaultText = "127.0.0.1 or null if name != \"\"";
+ description = ''
+ The IP interface to bind to.
+ null means "all interfaces".
+ '';
+ example = "192.0.2.1";
+ };
+
+ unixSocket = mkOption {
+ type = with types; nullOr path;
+ default = "/run/${redisName name}/redis.sock";
+ defaultText = "\"/run/redis/redis.sock\" or \"/run/redis-\${name}/redis.sock\" if name != \"\"";
+ description = "The path to the socket to bind to.";
+ };
+
+ unixSocketPerm = mkOption {
+ type = types.int;
+ default = 660;
+ description = "Change permissions for the socket";
+ example = 600;
+ };
+
+ logLevel = mkOption {
+ type = types.str;
+ default = "notice"; # debug, verbose, notice, warning
+ example = "debug";
+ description = "Specify the server verbosity level, options: debug, verbose, notice, warning.";
+ };
+
+ logfile = mkOption {
+ type = types.str;
+ default = "/dev/null";
+ description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output.";
+ example = "/var/log/redis.log";
+ };
+
+ syslog = mkOption {
+ type = types.bool;
+ default = true;
+ description = "Enable logging to the system logger.";
+ };
+
+ databases = mkOption {
+ type = types.int;
+ default = 16;
+ description = "Set the number of databases.";
+ };
+
+ maxclients = mkOption {
+ type = types.int;
+ default = 10000;
+ description = "Set the max number of connected clients at the same time.";
+ };
+
+ save = mkOption {
+ type = with types; listOf (listOf int);
+ default = [ [900 1] [300 10] [60 10000] ];
+ description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.";
+ example = [ [900 1] [300 10] [60 10000] ];
+ };
+
+ slaveOf = mkOption {
+ type = with types; nullOr (submodule ({ ... }: {
+ options = {
+ ip = mkOption {
+ type = str;
+ description = "IP of the Redis master";
+ example = "192.168.1.100";
+ };
+
+ port = mkOption {
+ type = port;
+ description = "port of the Redis master";
+ default = 6379;
+ };
+ };
+ }));
+
+ default = null;
+ description = "IP and port to which this redis instance acts as a slave.";
+ example = { ip = "192.168.1.100"; port = 6379; };
+ };
+
+ masterAuth = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = ''If the master is password protected (using the requirePass configuration)
+ it is possible to tell the slave to authenticate before starting the replication synchronization
+ process, otherwise the master will refuse the slave request.
+ (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
+ };
+
+ requirePass = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = ''
+ Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE).
+ Use requirePassFile to store it outside of the nix store in a dedicated file.
+ '';
+ example = "letmein!";
+ };
+
+ requirePassFile = mkOption {
+ type = with types; nullOr path;
+ default = null;
+ description = "File with password for the database.";
+ example = "/run/keys/redis-password";
+ };
+
+ appendOnly = mkOption {
+ type = types.bool;
+ default = false;
+ description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence.";
+ };
+
+ appendFsync = mkOption {
+ type = types.str;
+ default = "everysec"; # no, always, everysec
+ description = "How often to fsync the append-only log, options: no, always, everysec.";
+ };
+
+ slowLogLogSlowerThan = mkOption {
+ type = types.int;
+ default = 10000;
+ description = "Log queries whose execution take longer than X in milliseconds.";
+ example = 1000;
+ };
+
+ slowLogMaxLen = mkOption {
+ type = types.int;
+ default = 128;
+ description = "Maximum number of items to keep in slow log.";
+ };
+
+ settings = mkOption {
+ # TODO: this should be converted to freeformType
+ type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
+ default = {};
+ description = ''
+ Redis configuration. Refer to
+
+ for details on supported values.
+ '';
+ example = literalExample ''
+ {
+ loadmodule = [ "/path/to/my_module.so" "/path/to/other_module.so" ];
+ }
+ '';
};
};
+ config.settings = mkMerge [
+ {
+ port = config.port;
+ daemonize = false;
+ supervised = "systemd";
+ loglevel = config.logLevel;
+ logfile = config.logfile;
+ syslog-enabled = config.syslog;
+ databases = config.databases;
+ maxclients = config.maxclients;
+ save = map (d: "${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}") config.save;
+ dbfilename = "dump.rdb";
+ dir = "/var/lib/${redisName name}";
+ appendOnly = config.appendOnly;
+ appendfsync = config.appendFsync;
+ slowlog-log-slower-than = config.slowLogLogSlowerThan;
+ slowlog-max-len = config.slowLogMaxLen;
+ }
+ (mkIf (config.bind != null) { bind = config.bind; })
+ (mkIf (config.unixSocket != null) {
+ unixsocket = config.unixSocket;
+ unixsocketperm = toString config.unixSocketPerm;
+ })
+ (mkIf (config.slaveOf != null) { slaveof = "${config.slaveOf.ip} ${toString config.slaveOf.port}"; })
+ (mkIf (config.masterAuth != null) { masterauth = config.masterAuth; })
+ (mkIf (config.requirePass != null) { requirepass = config.requirePass; })
+ ];
}));
-
- default = null;
- description = "IP and port to which this redis instance acts as a slave.";
- example = { ip = "192.168.1.100"; port = 6379; };
- };
-
- masterAuth = mkOption {
- type = with types; nullOr str;
- default = null;
- description = ''If the master is password protected (using the requirePass configuration)
- it is possible to tell the slave to authenticate before starting the replication synchronization
- process, otherwise the master will refuse the slave request.
- (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
- };
-
- requirePass = mkOption {
- type = with types; nullOr str;
- default = null;
- description = ''
- Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE).
- Use requirePassFile to store it outside of the nix store in a dedicated file.
- '';
- example = "letmein!";
- };
-
- requirePassFile = mkOption {
- type = with types; nullOr path;
- default = null;
- description = "File with password for the database.";
- example = "/run/keys/redis-password";
- };
-
- appendOnly = mkOption {
- type = types.bool;
- default = false;
- description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence.";
- };
-
- appendFsync = mkOption {
- type = types.str;
- default = "everysec"; # no, always, everysec
- description = "How often to fsync the append-only log, options: no, always, everysec.";
- };
-
- slowLogLogSlowerThan = mkOption {
- type = types.int;
- default = 10000;
- description = "Log queries whose execution take longer than X in milliseconds.";
- example = 1000;
- };
-
- slowLogMaxLen = mkOption {
- type = types.int;
- default = 128;
- description = "Maximum number of items to keep in slow log.";
- };
-
- settings = mkOption {
- type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
+ description = "Configuration of multiple redis-server instances.";
default = {};
- description = ''
- Redis configuration. Refer to
-
- for details on supported values.
- '';
- example = literalExample ''
- {
- loadmodule = [ "/path/to/my_module.so" "/path/to/other_module.so" ];
- }
- '';
};
};
@@ -230,77 +290,60 @@ in {
###### implementation
- config = mkIf config.services.redis.enable {
- assertions = [{
- assertion = cfg.requirePass != null -> cfg.requirePassFile == null;
- message = "You can only set one services.redis.requirePass or services.redis.requirePassFile";
- }];
- boot.kernel.sysctl = (mkMerge [
+ config = mkIf (enabledServers != {}) {
+
+ assertions = attrValues (mapAttrs (name: conf: {
+ assertion = conf.requirePass != null -> conf.requirePassFile == null;
+ message = ''
+ You can only set one services.redis.servers.${name}.requirePass
+ or services.redis.servers.${name}.requirePassFile
+ '';
+ }) enabledServers);
+
+ boot.kernel.sysctl = mkMerge [
{ "vm.nr_hugepages" = "0"; }
( mkIf cfg.vmOverCommit { "vm.overcommit_memory" = "1"; } )
- ]);
+ ];
- networking.firewall = mkIf cfg.openFirewall {
- allowedTCPPorts = [ cfg.port ];
- };
-
- users.users.redis = {
- description = "Redis database user";
- isSystemUser = true;
- };
- users.groups.redis = {};
+ networking.firewall.allowedTCPPorts = concatMap (conf:
+ optional conf.openFirewall conf.port
+ ) (attrValues enabledServers);
environment.systemPackages = [ cfg.package ];
- services.redis.settings = mkMerge [
- {
- port = cfg.port;
- daemonize = false;
- supervised = "systemd";
- loglevel = cfg.logLevel;
- logfile = cfg.logfile;
- syslog-enabled = cfg.syslog;
- databases = cfg.databases;
- maxclients = cfg.maxclients;
- save = map (d: "${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}") cfg.save;
- dbfilename = "dump.rdb";
- dir = "/var/lib/redis";
- appendOnly = cfg.appendOnly;
- appendfsync = cfg.appendFsync;
- slowlog-log-slower-than = cfg.slowLogLogSlowerThan;
- slowlog-max-len = cfg.slowLogMaxLen;
- }
- (mkIf (cfg.bind != null) { bind = cfg.bind; })
- (mkIf (cfg.unixSocket != null) { unixsocket = cfg.unixSocket; unixsocketperm = "${toString cfg.unixSocketPerm}"; })
- (mkIf (cfg.slaveOf != null) { slaveof = "${cfg.slaveOf.ip} ${toString cfg.slaveOf.port}"; })
- (mkIf (cfg.masterAuth != null) { masterauth = cfg.masterAuth; })
- (mkIf (cfg.requirePass != null) { requirepass = cfg.requirePass; })
- ];
+ users.users = mapAttrs' (name: conf: nameValuePair (redisName name) {
+ description = "System user for the redis-server instance ${name}";
+ isSystemUser = true;
+ }) enabledServers;
+ users.groups = mapAttrs' (name: conf: nameValuePair (redisName name) {
+ }) enabledServers;
- systemd.services.redis = {
- description = "Redis Server";
+ systemd.services = mapAttrs' (name: conf: nameValuePair (redisName name) {
+ description = "Redis Server - ${redisName name}";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
- preStart = ''
- install -m 600 ${redisConfig} /run/redis/redis.conf
- '' + optionalString (cfg.requirePassFile != null) ''
- password=$(cat ${escapeShellArg cfg.requirePassFile})
- echo "requirePass $password" >> /run/redis/redis.conf
- '';
-
serviceConfig = {
- ExecStart = "${cfg.package}/bin/redis-server /run/redis/redis.conf";
+ ExecStart = "${cfg.package}/bin/redis-server /run/${redisName name}/redis.conf";
+ ExecStartPre = [("+"+pkgs.writeShellScript "${redisName name}-credentials" (''
+ install -o '${conf.user}' -m 600 ${redisConfig conf.settings} /run/${redisName name}/redis.conf
+ '' + optionalString (conf.requirePassFile != null) ''
+ {
+ printf requirePass' '
+ cat ${escapeShellArg conf.requirePassFile}
+ } >>/run/${redisName name}/redis.conf
+ '')
+ )];
Type = "notify";
# User and group
- User = "redis";
- Group = "redis";
+ User = conf.user;
+ Group = conf.user;
# Runtime directory and mode
- RuntimeDirectory = "redis";
+ RuntimeDirectory = redisName name;
RuntimeDirectoryMode = "0750";
# State directory and mode
- StateDirectory = "redis";
+ StateDirectory = redisName name;
StateDirectoryMode = "0700";
# Access write directories
UMask = "0077";
@@ -309,7 +352,7 @@ in {
# Security
NoNewPrivileges = true;
# Process Properties
- LimitNOFILE = "${toString ulimitNofile}";
+ LimitNOFILE = mkDefault "${toString (conf.maxclients + 32)}";
# Sandboxing
ProtectSystem = "strict";
ProtectHome = true;
@@ -322,7 +365,9 @@ in {
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;
- RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+ RestrictAddressFamilies =
+ optionals (conf.bind != null) ["AF_INET" "AF_INET6"] ++
+ optional (conf.unixSocket != null) "AF_UNIX";
RestrictNamespaces = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
@@ -333,6 +378,7 @@ in {
SystemCallArchitectures = "native";
SystemCallFilter = "~@cpu-emulation @debug @keyring @memlock @mount @obsolete @privileged @resources @setuid";
};
- };
+ }) enabledServers;
+
};
}
diff --git a/nixos/modules/services/misc/sourcehut/builds.nix b/nixos/modules/services/misc/sourcehut/builds.nix
deleted file mode 100644
index e446f08284f..00000000000
--- a/nixos/modules/services/misc/sourcehut/builds.nix
+++ /dev/null
@@ -1,234 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
- cfg = config.services.sourcehut;
- scfg = cfg.builds;
- rcfg = config.services.redis;
- iniKey = "builds.sr.ht";
-
- drv = pkgs.sourcehut.buildsrht;
-in
-{
- options.services.sourcehut.builds = {
- user = mkOption {
- type = types.str;
- default = "buildsrht";
- description = ''
- User for builds.sr.ht.
- '';
- };
-
- port = mkOption {
- type = types.port;
- default = 5002;
- description = ''
- Port on which the "builds" module should listen.
- '';
- };
-
- database = mkOption {
- type = types.str;
- default = "builds.sr.ht";
- description = ''
- PostgreSQL database name for builds.sr.ht.
- '';
- };
-
- statePath = mkOption {
- type = types.path;
- default = "${cfg.statePath}/buildsrht";
- description = ''
- State path for builds.sr.ht.
- '';
- };
-
- enableWorker = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Run workers for builds.sr.ht.
- '';
- };
-
- images = mkOption {
- type = types.attrsOf (types.attrsOf (types.attrsOf types.package));
- default = { };
- example = lib.literalExample ''(let
- # Pinning unstable to allow usage with flakes and limit rebuilds.
- pkgs_unstable = builtins.fetchGit {
- url = "https://github.com/NixOS/nixpkgs";
- rev = "ff96a0fa5635770390b184ae74debea75c3fd534";
- ref = "nixos-unstable";
- };
- image_from_nixpkgs = pkgs_unstable: (import ("${pkgs.sourcehut.buildsrht}/lib/images/nixos/image.nix") {
- pkgs = (import pkgs_unstable {});
- });
- in
- {
- nixos.unstable.x86_64 = image_from_nixpkgs pkgs_unstable;
- }
- )'';
- description = ''
- Images for builds.sr.ht. Each package should be distro.release.arch and point to a /nix/store/package/root.img.qcow2.
- '';
- };
-
- };
-
- config = with scfg; let
- image_dirs = lib.lists.flatten (
- lib.attrsets.mapAttrsToList
- (distro: revs:
- lib.attrsets.mapAttrsToList
- (rev: archs:
- lib.attrsets.mapAttrsToList
- (arch: image:
- pkgs.runCommand "buildsrht-images" { } ''
- mkdir -p $out/${distro}/${rev}/${arch}
- ln -s ${image}/*.qcow2 $out/${distro}/${rev}/${arch}/root.img.qcow2
- '')
- archs)
- revs)
- scfg.images);
- image_dir_pre = pkgs.symlinkJoin {
- name = "builds.sr.ht-worker-images-pre";
- paths = image_dirs ++ [
- "${pkgs.sourcehut.buildsrht}/lib/images"
- ];
- };
- image_dir = pkgs.runCommand "builds.sr.ht-worker-images" { } ''
- mkdir -p $out/images
- cp -Lr ${image_dir_pre}/* $out/images
- '';
- in
- lib.mkIf (cfg.enable && elem "builds" cfg.services) {
- users = {
- users = {
- "${user}" = {
- isSystemUser = true;
- group = user;
- extraGroups = lib.optionals cfg.builds.enableWorker [ "docker" ];
- description = "builds.sr.ht user";
- };
- };
-
- groups = {
- "${user}" = { };
- };
- };
-
- services.postgresql = {
- authentication = ''
- local ${database} ${user} trust
- '';
- ensureDatabases = [ database ];
- ensureUsers = [
- {
- name = user;
- ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
- }
- ];
- };
-
- systemd = {
- tmpfiles.rules = [
- "d ${statePath} 0755 ${user} ${user} -"
- ] ++ (lib.optionals cfg.builds.enableWorker
- [ "d ${statePath}/logs 0775 ${user} ${user} - -" ]
- );
-
- services = {
- buildsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey
- {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "builds.sr.ht website service";
-
- serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
-
- # Hack to bypass this hack: https://git.sr.ht/~sircmpwn/core.sr.ht/tree/master/item/srht-update-profiles#L6
- } // { preStart = " "; };
-
- buildsrht-worker = {
- enable = scfg.enableWorker;
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
- partOf = [ "buildsrht.service" ];
- description = "builds.sr.ht worker service";
- path = [ pkgs.openssh pkgs.docker ];
- preStart = let qemuPackage = pkgs.qemu_kvm;
- in ''
- if [[ "$(docker images -q qemu:latest 2> /dev/null)" == "" || "$(cat ${statePath}/docker-image-qemu 2> /dev/null || true)" != "${qemuPackage.version}" ]]; then
- # Create and import qemu:latest image for docker
- ${
- pkgs.dockerTools.streamLayeredImage {
- name = "qemu";
- tag = "latest";
- contents = [ qemuPackage ];
- }
- } | docker load
- # Mark down current package version
- printf "%s" "${qemuPackage.version}" > ${statePath}/docker-image-qemu
- fi
- '';
- serviceConfig = {
- Type = "simple";
- User = user;
- Group = "nginx";
- Restart = "always";
- };
- serviceConfig.ExecStart = "${pkgs.sourcehut.buildsrht}/bin/builds.sr.ht-worker";
- };
- };
- };
-
- services.sourcehut.settings = {
- # URL builds.sr.ht is being served at (protocol://domain)
- "builds.sr.ht".origin = mkDefault "http://builds.${cfg.originBase}";
- # Address and port to bind the debug server to
- "builds.sr.ht".debug-host = mkDefault "0.0.0.0";
- "builds.sr.ht".debug-port = mkDefault port;
- # Configures the SQLAlchemy connection string for the database.
- "builds.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
- # Set to "yes" to automatically run migrations on package upgrade.
- "builds.sr.ht".migrate-on-upgrade = mkDefault "yes";
- # builds.sr.ht's OAuth client ID and secret for meta.sr.ht
- # Register your client at meta.example.org/oauth
- "builds.sr.ht".oauth-client-id = mkDefault null;
- "builds.sr.ht".oauth-client-secret = mkDefault null;
- # The redis connection used for the celery worker
- "builds.sr.ht".redis = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/3";
- # The shell used for ssh
- "builds.sr.ht".shell = mkDefault "runner-shell";
- # Register the builds.sr.ht dispatcher
- "git.sr.ht::dispatch".${builtins.unsafeDiscardStringContext "${pkgs.sourcehut.buildsrht}/bin/buildsrht-keys"} = mkDefault "${user}:${user}";
-
- # Location for build logs, images, and control command
- } // lib.attrsets.optionalAttrs scfg.enableWorker {
- # Default worker stores logs that are accessible via this address:port
- "builds.sr.ht::worker".name = mkDefault "127.0.0.1:5020";
- "builds.sr.ht::worker".buildlogs = mkDefault "${scfg.statePath}/logs";
- "builds.sr.ht::worker".images = mkDefault "${image_dir}/images";
- "builds.sr.ht::worker".controlcmd = mkDefault "${image_dir}/images/control";
- "builds.sr.ht::worker".timeout = mkDefault "3m";
- };
-
- services.nginx.virtualHosts."logs.${cfg.originBase}" =
- if scfg.enableWorker then {
- listen = with builtins; let address = split ":" cfg.settings."builds.sr.ht::worker".name;
- in [{ addr = elemAt address 0; port = lib.toInt (elemAt address 2); }];
- locations."/logs".root = "${scfg.statePath}";
- } else { };
-
- services.nginx.virtualHosts."builds.${cfg.originBase}" = {
- forceSSL = true;
- locations."/".proxyPass = "http://${cfg.address}:${toString port}";
- locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
- locations."/static".root = "${pkgs.sourcehut.buildsrht}/${pkgs.sourcehut.python.sitePackages}/buildsrht";
- };
- };
-}
diff --git a/nixos/modules/services/misc/sourcehut/default.nix b/nixos/modules/services/misc/sourcehut/default.nix
index 9c812d6b043..c6c70e6ae2b 100644
--- a/nixos/modules/services/misc/sourcehut/default.nix
+++ b/nixos/modules/services/misc/sourcehut/default.nix
@@ -1,14 +1,90 @@
{ config, pkgs, lib, ... }:
-
with lib;
let
+ inherit (config.services) nginx postfix postgresql redis;
+ inherit (config.users) users groups;
cfg = config.services.sourcehut;
- cfgIni = cfg.settings;
- settingsFormat = pkgs.formats.ini { };
+ domain = cfg.settings."sr.ht".global-domain;
+ settingsFormat = pkgs.formats.ini {
+ listToValue = concatMapStringsSep "," (generators.mkValueStringDefault {});
+ mkKeyValue = k: v:
+ if v == null then ""
+ else generators.mkKeyValueDefault {
+ mkValueString = v:
+ if v == true then "yes"
+ else if v == false then "no"
+ else generators.mkValueStringDefault {} v;
+ } "=" k v;
+ };
+ configIniOfService = srv: settingsFormat.generate "sourcehut-${srv}-config.ini"
+ # Each service needs access to only a subset of sections (and secrets).
+ (filterAttrs (k: v: v != null)
+ (mapAttrs (section: v:
+ let srvMatch = builtins.match "^([a-z]*)\\.sr\\.ht(::.*)?$" section; in
+ if srvMatch == null # Include sections shared by all services
+ || head srvMatch == srv # Include sections for the service being configured
+ then v
+ # Enable Web links and integrations between services.
+ else if tail srvMatch == [ null ] && elem (head srvMatch) cfg.services
+ then {
+ inherit (v) origin;
+ # mansrht crashes without it
+ oauth-client-id = v.oauth-client-id or null;
+ }
+ # Drop sub-sections of other services
+ else null)
+ (recursiveUpdate cfg.settings {
+ # Those paths are mounted using BindPaths= or BindReadOnlyPaths=
+ # for services needing access to them.
+ "builds.sr.ht::worker".buildlogs = "/var/log/sourcehut/buildsrht/logs";
+ "git.sr.ht".post-update-script = "/var/lib/sourcehut/gitsrht/bin/post-update-script";
+ "git.sr.ht".repos = "/var/lib/sourcehut/gitsrht/repos";
+ "hg.sr.ht".changegroup-script = "/var/lib/sourcehut/hgsrht/bin/changegroup-script";
+ "hg.sr.ht".repos = "/var/lib/sourcehut/hgsrht/repos";
+ # Making this a per service option despite being in a global section,
+ # so that it uses the redis-server used by the service.
+ "sr.ht".redis-host = cfg.${srv}.redis.host;
+ })));
+ commonServiceSettings = srv: {
+ origin = mkOption {
+ description = "URL ${srv}.sr.ht is being served at (protocol://domain)";
+ type = types.str;
+ default = "https://${srv}.${domain}";
+ defaultText = "https://${srv}.example.com";
+ };
+ debug-host = mkOption {
+ description = "Address to bind the debug server to.";
+ type = with types; nullOr str;
+ default = null;
+ };
+ debug-port = mkOption {
+ description = "Port to bind the debug server to.";
+ type = with types; nullOr str;
+ default = null;
+ };
+ connection-string = mkOption {
+ description = "SQLAlchemy connection string for the database.";
+ type = types.str;
+ default = "postgresql:///localhost?user=${srv}srht&host=/run/postgresql";
+ };
+ migrate-on-upgrade = mkEnableOption "automatic migrations on package upgrade" // { default = true; };
+ oauth-client-id = mkOption {
+ description = "${srv}.sr.ht's OAuth client id for meta.sr.ht.";
+ type = types.str;
+ };
+ oauth-client-secret = mkOption {
+ description = "${srv}.sr.ht's OAuth client secret for meta.sr.ht.";
+ type = types.path;
+ apply = s: "<" + toString s;
+ };
+ };
# Specialized python containing all the modules
python = pkgs.sourcehut.python.withPackages (ps: with ps; [
gunicorn
+ eventlet
+ # For monitoring Celery: sudo -u listssrht celery --app listssrht.process -b redis+socket:///run/redis-sourcehut/redis.sock?virtual_host=5 flower
+ flower
# Sourcehut services
srht
buildsrht
@@ -19,69 +95,37 @@ let
listssrht
mansrht
metasrht
+ # Not a python package
+ #pagessrht
pastesrht
todosrht
]);
+ mkOptionNullOrStr = description: mkOption {
+ inherit description;
+ type = with types; nullOr str;
+ default = null;
+ };
in
{
- imports =
- [
- ./git.nix
- ./hg.nix
- ./hub.nix
- ./todo.nix
- ./man.nix
- ./meta.nix
- ./paste.nix
- ./builds.nix
- ./lists.nix
- ./dispatch.nix
- (mkRemovedOptionModule [ "services" "sourcehut" "nginx" "enable" ] ''
- The sourcehut module supports `nginx` as a local reverse-proxy by default and doesn't
- support other reverse-proxies officially.
-
- However it's possible to use an alternative reverse-proxy by
-
- * disabling nginx
- * adjusting the relevant settings for server addresses and ports directly
-
- Further details about this can be found in the `Sourcehut`-section of the NixOS-manual.
- '')
- ];
-
options.services.sourcehut = {
- enable = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Enable sourcehut - git hosting, continuous integration, mailing list, ticket tracking,
- task dispatching, wiki and account management services
- '';
- };
+ enable = mkEnableOption ''
+ sourcehut - git hosting, continuous integration, mailing list, ticket tracking,
+ task dispatching, wiki and account management services
+ '';
services = mkOption {
- type = types.nonEmptyListOf (types.enum [ "builds" "dispatch" "git" "hub" "hg" "lists" "man" "meta" "paste" "todo" ]);
- default = [ "man" "meta" "paste" ];
- example = [ "builds" "dispatch" "git" "hub" "hg" "lists" "man" "meta" "paste" "todo" ];
+ type = with types; listOf (enum
+ [ "builds" "dispatch" "git" "hg" "hub" "lists" "man" "meta" "pages" "paste" "todo" ]);
+ defaultText = "locally enabled services";
description = ''
- Services to enable on the sourcehut network.
+ Services that may be displayed as links in the title bar of the Web interface.
'';
};
- originBase = mkOption {
+ listenAddress = mkOption {
type = types.str;
- default = with config.networking; hostName + lib.optionalString (domain != null) ".${domain}";
- description = ''
- Host name used by reverse-proxy and for default settings. Will host services at git."''${originBase}". For example: git.sr.ht
- '';
- };
-
- address = mkOption {
- type = types.str;
- default = "127.0.0.1";
- description = ''
- Address to bind to.
- '';
+ default = "localhost";
+ description = "Address to bind to.";
};
python = mkOption {
@@ -94,105 +138,1222 @@ in
'';
};
- statePath = mkOption {
- type = types.path;
- default = "/var/lib/sourcehut";
- description = ''
- Root state path for the sourcehut network. If left as the default value
- this directory will automatically be created before the sourcehut server
- starts, otherwise the sysadmin is responsible for ensuring the
- directory exists with appropriate ownership and permissions.
- '';
+ minio = {
+ enable = mkEnableOption ''local minio integration'';
+ };
+
+ nginx = {
+ enable = mkEnableOption ''local nginx integration'';
+ virtualHost = mkOption {
+ type = types.attrs;
+ default = {};
+ description = "Virtual-host configuration merged with all Sourcehut's virtual-hosts.";
+ };
+ };
+
+ postfix = {
+ enable = mkEnableOption ''local postfix integration'';
+ };
+
+ postgresql = {
+ enable = mkEnableOption ''local postgresql integration'';
+ };
+
+ redis = {
+ enable = mkEnableOption ''local redis integration in a dedicated redis-server'';
};
settings = mkOption {
type = lib.types.submodule {
freeformType = settingsFormat.type;
+ options."sr.ht" = {
+ global-domain = mkOption {
+ description = "Global domain name.";
+ type = types.str;
+ example = "example.com";
+ };
+ environment = mkOption {
+ description = "Values other than \"production\" adds a banner to each page.";
+ type = types.enum [ "development" "production" ];
+ default = "development";
+ };
+ network-key = mkOption {
+ description = ''
+ An absolute file path (which should be outside the Nix-store)
+ to a secret key to encrypt internal messages with. Use srht-keygen network
to
+ generate this key. It must be consistent between all services and nodes.
+ '';
+ type = types.path;
+ apply = s: "<" + toString s;
+ };
+ owner-email = mkOption {
+ description = "Owner's email.";
+ type = types.str;
+ default = "contact@example.com";
+ };
+ owner-name = mkOption {
+ description = "Owner's name.";
+ type = types.str;
+ default = "John Doe";
+ };
+ site-blurb = mkOption {
+ description = "Blurb for your site.";
+ type = types.str;
+ default = "the hacker's forge";
+ };
+ site-info = mkOption {
+ description = "The top-level info page for your site.";
+ type = types.str;
+ default = "https://sourcehut.org";
+ };
+ service-key = mkOption {
+ description = ''
+ An absolute file path (which should be outside the Nix-store)
+ to a key used for encrypting session cookies. Use srht-keygen service
to
+ generate the service key. This must be shared between each node of the same
+ service (e.g. git1.sr.ht and git2.sr.ht), but different services may use
+ different keys. If you configure all of your services with the same
+ config.ini, you may use the same service-key for all of them.
+ '';
+ type = types.path;
+ apply = s: "<" + toString s;
+ };
+ site-name = mkOption {
+ description = "The name of your network of sr.ht-based sites.";
+ type = types.str;
+ default = "sourcehut";
+ };
+ source-url = mkOption {
+ description = "The source code for your fork of sr.ht.";
+ type = types.str;
+ default = "https://git.sr.ht/~sircmpwn/srht";
+ };
+ };
+ options.mail = {
+ smtp-host = mkOptionNullOrStr "Outgoing SMTP host.";
+ smtp-port = mkOption {
+ description = "Outgoing SMTP port.";
+ type = with types; nullOr port;
+ default = null;
+ };
+ smtp-user = mkOptionNullOrStr "Outgoing SMTP user.";
+ smtp-password = mkOptionNullOrStr "Outgoing SMTP password.";
+ smtp-from = mkOptionNullOrStr "Outgoing SMTP FROM.";
+ error-to = mkOptionNullOrStr "Address receiving application exceptions";
+ error-from = mkOptionNullOrStr "Address sending application exceptions";
+ pgp-privkey = mkOptionNullOrStr ''
+ An absolute file path (which should be outside the Nix-store)
+ to an OpenPGP private key.
+
+ Your PGP key information (DO NOT mix up pub and priv here)
+ You must remove the password from your secret key, if present.
+ You can do this with gpg --edit-key [key-id]
,
+ then use the passwd
command and do not enter a new password.
+ '';
+ pgp-pubkey = mkOptionNullOrStr "OpenPGP public key.";
+ pgp-key-id = mkOptionNullOrStr "OpenPGP key identifier.";
+ };
+ options.objects = {
+ s3-upstream = mkOption {
+ description = "Configure the S3-compatible object storage service.";
+ type = with types; nullOr str;
+ default = null;
+ };
+ s3-access-key = mkOption {
+ description = "Access key to the S3-compatible object storage service";
+ type = with types; nullOr str;
+ default = null;
+ };
+ s3-secret-key = mkOption {
+ description = ''
+ An absolute file path (which should be outside the Nix-store)
+ to the secret key of the S3-compatible object storage service.
+ '';
+ type = with types; nullOr path;
+ default = null;
+ apply = mapNullable (s: "<" + toString s);
+ };
+ };
+ options.webhooks = {
+ private-key = mkOption {
+ description = ''
+ An absolute file path (which should be outside the Nix-store)
+ to a base64-encoded Ed25519 key for signing webhook payloads.
+ This should be consistent for all *.sr.ht sites,
+ as this key will be used to verify signatures
+ from other sites in your network.
+ Use the srht-keygen webhook
command to generate a key.
+ '';
+ type = types.path;
+ apply = s: "<" + toString s;
+ };
+ };
+
+ options."dispatch.sr.ht" = commonServiceSettings "dispatch" // {
+ };
+ options."dispatch.sr.ht::github" = {
+ oauth-client-id = mkOptionNullOrStr "OAuth client id.";
+ oauth-client-secret = mkOptionNullOrStr "OAuth client secret.";
+ };
+ options."dispatch.sr.ht::gitlab" = {
+ enabled = mkEnableOption "GitLab integration";
+ canonical-upstream = mkOption {
+ type = types.str;
+ description = "Canonical upstream.";
+ default = "gitlab.com";
+ };
+ repo-cache = mkOption {
+ type = types.str;
+ description = "Repository cache directory.";
+ default = "./repo-cache";
+ };
+ "gitlab.com" = mkOption {
+ type = with types; nullOr str;
+ description = "GitLab id and secret.";
+ default = null;
+ example = "GitLab:application id:secret";
+ };
+ };
+
+ options."builds.sr.ht" = commonServiceSettings "builds" // {
+ allow-free = mkEnableOption "nonpaying users to submit builds";
+ redis = mkOption {
+ description = "The Redis connection used for the Celery worker.";
+ type = types.str;
+ default = "unix+socket:/run/redis-sourcehut-buildsrht/redis.sock?virtual_host=2";
+ };
+ shell = mkOption {
+ description = ''
+ Scripts used to launch on SSH connection.
+ /usr/bin/master-shell on master,
+ /usr/bin/runner-shell on runner.
+ If master and worker are on the same system
+ set to /usr/bin/runner-shell.
+ '';
+ type = types.enum ["/usr/bin/master-shell" "/usr/bin/runner-shell"];
+ default = "/usr/bin/master-shell";
+ };
+ };
+ options."builds.sr.ht::worker" = {
+ bind-address = mkOption {
+ description = ''
+ HTTP bind address for serving local build information/monitoring.
+ '';
+ type = types.str;
+ default = "localhost:8080";
+ };
+ buildlogs = mkOption {
+ description = "Path to write build logs.";
+ type = types.str;
+ default = "/var/log/sourcehut/buildsrht";
+ };
+ name = mkOption {
+ description = ''
+ Listening address and listening port
+ of the build runner (with HTTP port if not 80).
+ '';
+ type = types.str;
+ default = "localhost:5020";
+ };
+ timeout = mkOption {
+ description = ''
+ Max build duration.
+ See .
+ '';
+ type = types.str;
+ default = "3m";
+ };
+ };
+
+ options."git.sr.ht" = commonServiceSettings "git" // {
+ outgoing-domain = mkOption {
+ description = "Outgoing domain.";
+ type = types.str;
+ default = "https://git.localhost.localdomain";
+ };
+ post-update-script = mkOption {
+ description = ''
+ A post-update script which is installed in every git repo.
+ This setting is propagated to newer and existing repositories.
+ '';
+ type = types.path;
+ default = "${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
+ defaultText = "\${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
+ # Git hooks are run relative to their repository's directory,
+ # but gitsrht-update-hook looks up ../config.ini
+ apply = p: pkgs.writeShellScript "update-hook-wrapper" ''
+ test -e "''${PWD%/*}"/config.ini ||
+ ln -s ${users."sshsrht".home}/../config.ini "''${PWD%/*}"/config.ini
+ exec -a "$0" '${p}' "$@"
+ '';
+ };
+ repos = mkOption {
+ description = ''
+ Path to git repositories on disk.
+ If changing the default, you must ensure that
+ the gitsrht's user as read and write access to it.
+ '';
+ type = types.str;
+ default = "/var/lib/sourcehut/gitsrht/repos";
+ };
+ webhooks = mkOption {
+ description = "The Redis connection used for the webhooks worker.";
+ type = types.str;
+ default = "unix+socket:/run/redis-sourcehut-gitsrht/redis.sock?virtual_host=1";
+ };
+ };
+ options."git.sr.ht::api" = {
+ internal-ipnet = mkOption {
+ description = ''
+ Set of IP subnets which are permitted to utilize internal API
+ authentication. This should be limited to the subnets
+ from which your *.sr.ht services are running.
+ See .
+ '';
+ type = with types; listOf str;
+ default = [ "127.0.0.0/8" "::1/128" ];
+ };
+ };
+
+ options."hg.sr.ht" = commonServiceSettings "hg" // {
+ changegroup-script = mkOption {
+ description = ''
+ A changegroup script which is installed in every mercurial repo.
+ This setting is propagated to newer and existing repositories.
+ '';
+ type = types.str;
+ default = "${cfg.python}/bin/hgsrht-hook-changegroup";
+ defaultText = "\${cfg.python}/bin/hgsrht-hook-changegroup";
+ # Mercurial's changegroup hooks are run relative to their repository's directory,
+ # but hgsrht-hook-changegroup looks up ./config.ini
+ apply = p: pkgs.writeShellScript "hook-changegroup-wrapper" ''
+ test -e "''$PWD"/config.ini ||
+ ln -s ${users."sshsrht".home}/../config.ini "''$PWD"/config.ini
+ exec -a "$0" '${p}' "$@"
+ '';
+ };
+ repos = mkOption {
+ description = ''
+ Path to mercurial repositories on disk.
+ If changing the default, you must ensure that
+ the hgsrht's user as read and write access to it.
+ '';
+ type = types.str;
+ default = "/var/lib/sourcehut/hgsrht/repos";
+ };
+ srhtext = mkOptionNullOrStr ''
+ Path to the srht mercurial extension
+ (defaults to where the hgsrht code is)
+ '';
+ clone_bundle_threshold = mkOption {
+ description = ".hg/store size (in MB) past which the nightly job generates clone bundles.";
+ type = types.ints.unsigned;
+ default = 50;
+ };
+ hg_ssh = mkOption {
+ description = "Path to hg-ssh (if not in $PATH).";
+ type = types.str;
+ default = "${pkgs.mercurial}/bin/hg-ssh";
+ defaultText = "\${pkgs.mercurial}/bin/hg-ssh";
+ };
+ webhooks = mkOption {
+ description = "The Redis connection used for the webhooks worker.";
+ type = types.str;
+ default = "unix+socket:/run/redis-sourcehut-hgsrht/redis.sock?virtual_host=1";
+ };
+ };
+
+ options."hub.sr.ht" = commonServiceSettings "hub" // {
+ };
+
+ options."lists.sr.ht" = commonServiceSettings "lists" // {
+ allow-new-lists = mkEnableOption "Allow creation of new lists.";
+ notify-from = mkOption {
+ description = "Outgoing email for notifications generated by users.";
+ type = types.str;
+ default = "lists-notify@localhost.localdomain";
+ };
+ posting-domain = mkOption {
+ description = "Posting domain.";
+ type = types.str;
+ default = "lists.localhost.localdomain";
+ };
+ redis = mkOption {
+ description = "The Redis connection used for the Celery worker.";
+ type = types.str;
+ default = "unix+socket:/run/redis-sourcehut-listssrht/redis.sock?virtual_host=2";
+ };
+ webhooks = mkOption {
+ description = "The Redis connection used for the webhooks worker.";
+ type = types.str;
+ default = "unix+socket:/run/redis-sourcehut-listssrht/redis.sock?virtual_host=1";
+ };
+ };
+ options."lists.sr.ht::worker" = {
+ reject-mimetypes = mkOption {
+ description = ''
+ Comma-delimited list of Content-Types to reject. Messages with Content-Types
+ included in this list are rejected. Multipart messages are always supported,
+ and each part is checked against this list.
+
+ Uses fnmatch for wildcard expansion.
+ '';
+ type = with types; listOf str;
+ default = ["text/html"];
+ };
+ reject-url = mkOption {
+ description = "Reject URL.";
+ type = types.str;
+ default = "https://man.sr.ht/lists.sr.ht/etiquette.md";
+ };
+ sock = mkOption {
+ description = ''
+ Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
+ Alternatively, specify IP:PORT and an SMTP server will be run instead.
+ '';
+ type = types.str;
+ default = "/tmp/lists.sr.ht-lmtp.sock";
+ };
+ sock-group = mkOption {
+ description = ''
+ The lmtp daemon will make the unix socket group-read/write
+ for users in this group.
+ '';
+ type = types.str;
+ default = "postfix";
+ };
+ };
+
+ options."man.sr.ht" = commonServiceSettings "man" // {
+ };
+
+ options."meta.sr.ht" =
+ removeAttrs (commonServiceSettings "meta")
+ ["oauth-client-id" "oauth-client-secret"] // {
+ api-origin = mkOption {
+ description = "Origin URL for API, 100 more than web.";
+ type = types.str;
+ default = "http://${cfg.listenAddress}:${toString (cfg.meta.port + 100)}";
+ defaultText = ''http://:''${toString ( + 100)}'';
+ };
+ webhooks = mkOption {
+ description = "The Redis connection used for the webhooks worker.";
+ type = types.str;
+ default = "unix+socket:/run/redis-sourcehut-metasrht/redis.sock?virtual_host=1";
+ };
+ welcome-emails = mkEnableOption "sending stock sourcehut welcome emails after signup";
+ };
+ options."meta.sr.ht::api" = {
+ internal-ipnet = mkOption {
+ description = ''
+ Set of IP subnets which are permitted to utilize internal API
+ authentication. This should be limited to the subnets
+ from which your *.sr.ht services are running.
+ See .
+ '';
+ type = with types; listOf str;
+ default = [ "127.0.0.0/8" "::1/128" ];
+ };
+ };
+ options."meta.sr.ht::aliases" = mkOption {
+ description = "Aliases for the client IDs of commonly used OAuth clients.";
+ type = with types; attrsOf int;
+ default = {};
+ example = { "git.sr.ht" = 12345; };
+ };
+ options."meta.sr.ht::billing" = {
+ enabled = mkEnableOption "the billing system";
+ stripe-public-key = mkOptionNullOrStr "Public key for Stripe. Get your keys at https://dashboard.stripe.com/account/apikeys";
+ stripe-secret-key = mkOptionNullOrStr ''
+ An absolute file path (which should be outside the Nix-store)
+ to a secret key for Stripe. Get your keys at https://dashboard.stripe.com/account/apikeys
+ '' // {
+ apply = mapNullable (s: "<" + toString s);
+ };
+ };
+ options."meta.sr.ht::settings" = {
+ registration = mkEnableOption "public registration";
+ onboarding-redirect = mkOption {
+ description = "Where to redirect new users upon registration.";
+ type = types.str;
+ default = "https://meta.localhost.localdomain";
+ };
+ user-invites = mkOption {
+ description = ''
+ How many invites each user is issued upon registration
+ (only applicable if open registration is disabled).
+ '';
+ type = types.ints.unsigned;
+ default = 5;
+ };
+ };
+
+ options."pages.sr.ht" = commonServiceSettings "pages" // {
+ gemini-certs = mkOption {
+ description = ''
+ An absolute file path (which should be outside the Nix-store)
+ to Gemini certificates.
+ '';
+ type = with types; nullOr path;
+ default = null;
+ };
+ max-site-size = mkOption {
+ description = "Maximum size of any given site (post-gunzip), in MiB.";
+ type = types.int;
+ default = 1024;
+ };
+ user-domain = mkOption {
+ description = ''
+ Configures the user domain, if enabled.
+ All users are given <username>.this.domain.
+ '';
+ type = with types; nullOr str;
+ default = null;
+ };
+ };
+ options."pages.sr.ht::api" = {
+ internal-ipnet = mkOption {
+ description = ''
+ Set of IP subnets which are permitted to utilize internal API
+ authentication. This should be limited to the subnets
+ from which your *.sr.ht services are running.
+ See .
+ '';
+ type = with types; listOf str;
+ default = [ "127.0.0.0/8" "::1/128" ];
+ };
+ };
+
+ options."paste.sr.ht" = commonServiceSettings "paste" // {
+ };
+
+ options."todo.sr.ht" = commonServiceSettings "todo" // {
+ notify-from = mkOption {
+ description = "Outgoing email for notifications generated by users.";
+ type = types.str;
+ default = "todo-notify@localhost.localdomain";
+ };
+ webhooks = mkOption {
+ description = "The Redis connection used for the webhooks worker.";
+ type = types.str;
+ default = "unix+socket:/run/redis-sourcehut-todosrht/redis.sock?virtual_host=1";
+ };
+ };
+ options."todo.sr.ht::mail" = {
+ posting-domain = mkOption {
+ description = "Posting domain.";
+ type = types.str;
+ default = "todo.localhost.localdomain";
+ };
+ sock = mkOption {
+ description = ''
+ Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
+ Alternatively, specify IP:PORT and an SMTP server will be run instead.
+ '';
+ type = types.str;
+ default = "/tmp/todo.sr.ht-lmtp.sock";
+ };
+ sock-group = mkOption {
+ description = ''
+ The lmtp daemon will make the unix socket group-read/write
+ for users in this group.
+ '';
+ type = types.str;
+ default = "postfix";
+ };
+ };
};
default = { };
description = ''
The configuration for the sourcehut network.
'';
};
- };
- config = mkIf cfg.enable {
- assertions =
- [
- {
- assertion = with cfgIni.webhooks; private-key != null && stringLength private-key == 44;
- message = "The webhook's private key must be defined and of a 44 byte length.";
- }
+ builds = {
+ enableWorker = mkEnableOption "worker for builds.sr.ht";
- {
- assertion = hasAttrByPath [ "meta.sr.ht" "origin" ] cfgIni && cfgIni."meta.sr.ht".origin != null;
- message = "meta.sr.ht's origin must be defined.";
- }
- ];
+ images = mkOption {
+ type = with types; attrsOf (attrsOf (attrsOf package));
+ default = { };
+ example = lib.literalExample ''(let
+ # Pinning unstable to allow usage with flakes and limit rebuilds.
+ pkgs_unstable = builtins.fetchGit {
+ url = "https://github.com/NixOS/nixpkgs";
+ rev = "ff96a0fa5635770390b184ae74debea75c3fd534";
+ ref = "nixos-unstable";
+ };
+ image_from_nixpkgs = (import ("${pkgs.sourcehut.buildsrht}/lib/images/nixos/image.nix") {
+ pkgs = (import pkgs_unstable {});
+ });
+ in
+ {
+ nixos.unstable.x86_64 = image_from_nixpkgs;
+ }
+ )'';
+ description = ''
+ Images for builds.sr.ht. Each package should be distro.release.arch and point to a /nix/store/package/root.img.qcow2.
+ '';
+ };
+ };
- virtualisation.docker.enable = true;
- environment.etc."sr.ht/config.ini".source =
- settingsFormat.generate "sourcehut-config.ini" (mapAttrsRecursive
- (
- path: v: if v == null then "" else v
- )
- cfg.settings);
+ git = {
+ package = mkOption {
+ type = types.package;
+ default = pkgs.git;
+ example = literalExample "pkgs.gitFull";
+ description = ''
+ Git package for git.sr.ht. This can help silence collisions.
+ '';
+ };
+ fcgiwrap.preforkProcess = mkOption {
+ description = "Number of fcgiwrap processes to prefork.";
+ type = types.int;
+ default = 4;
+ };
+ };
- environment.systemPackages = [ pkgs.sourcehut.coresrht ];
+ hg = {
+ package = mkOption {
+ type = types.package;
+ default = pkgs.mercurial;
+ description = ''
+ Mercurial package for hg.sr.ht. This can help silence collisions.
+ '';
+ };
+ cloneBundles = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Generate clonebundles (which require more disk space but dramatically speed up cloning large repositories).
+ '';
+ };
+ };
- # PostgreSQL server
- services.postgresql.enable = mkOverride 999 true;
- # Mail server
- services.postfix.enable = mkOverride 999 true;
- # Cron daemon
- services.cron.enable = mkOverride 999 true;
- # Redis server
- services.redis.enable = mkOverride 999 true;
- services.redis.bind = mkOverride 999 "127.0.0.1";
-
- services.sourcehut.settings = {
- # The name of your network of sr.ht-based sites
- "sr.ht".site-name = mkDefault "sourcehut";
- # The top-level info page for your site
- "sr.ht".site-info = mkDefault "https://sourcehut.org";
- # {{ site-name }}, {{ site-blurb }}
- "sr.ht".site-blurb = mkDefault "the hacker's forge";
- # If this != production, we add a banner to each page
- "sr.ht".environment = mkDefault "development";
- # Contact information for the site owners
- "sr.ht".owner-name = mkDefault "Drew DeVault";
- "sr.ht".owner-email = mkDefault "sir@cmpwn.com";
- # The source code for your fork of sr.ht
- "sr.ht".source-url = mkDefault "https://git.sr.ht/~sircmpwn/srht";
- # A secret key to encrypt session cookies with
- "sr.ht".secret-key = mkDefault null;
- "sr.ht".global-domain = mkDefault null;
-
- # Outgoing SMTP settings
- mail.smtp-host = mkDefault null;
- mail.smtp-port = mkDefault null;
- mail.smtp-user = mkDefault null;
- mail.smtp-password = mkDefault null;
- mail.smtp-from = mkDefault null;
- # Application exceptions are emailed to this address
- mail.error-to = mkDefault null;
- mail.error-from = mkDefault null;
- # Your PGP key information (DO NOT mix up pub and priv here)
- # You must remove the password from your secret key, if present.
- # You can do this with gpg --edit-key [key-id], then use the passwd
- # command and do not enter a new password.
- mail.pgp-privkey = mkDefault null;
- mail.pgp-pubkey = mkDefault null;
- mail.pgp-key-id = mkDefault null;
-
- # base64-encoded Ed25519 key for signing webhook payloads. This should be
- # consistent for all *.sr.ht sites, as we'll use this key to verify signatures
- # from other sites in your network.
- #
- # Use the srht-webhook-keygen command to generate a key.
- webhooks.private-key = mkDefault null;
+ lists = {
+ process = {
+ extraArgs = mkOption {
+ type = with types; listOf str;
+ default = [ "--loglevel DEBUG" "--pool eventlet" "--without-heartbeat" ];
+ description = "Extra arguments passed to the Celery responsible for processing mails.";
+ };
+ celeryConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Content of the celeryconfig.py used by the Celery of listssrht-process.";
+ };
+ };
};
};
+
+ config = mkIf cfg.enable (mkMerge [
+ {
+ environment.systemPackages = [ pkgs.sourcehut.coresrht ];
+
+ services.sourcehut.settings = {
+ "git.sr.ht".outgoing-domain = mkDefault "https://git.${domain}";
+ "lists.sr.ht".notify-from = mkDefault "lists-notify@${domain}";
+ "lists.sr.ht".posting-domain = mkDefault "lists.${domain}";
+ "meta.sr.ht::settings".onboarding-redirect = mkDefault "https://meta.${domain}";
+ "todo.sr.ht".notify-from = mkDefault "todo-notify@${domain}";
+ "todo.sr.ht::mail".posting-domain = mkDefault "todo.${domain}";
+ };
+ }
+ (mkIf cfg.postgresql.enable {
+ assertions = [
+ { assertion = postgresql.enable;
+ message = "postgresql must be enabled and configured";
+ }
+ ];
+ })
+ (mkIf cfg.postfix.enable {
+ assertions = [
+ { assertion = postfix.enable;
+ message = "postfix must be enabled and configured";
+ }
+ ];
+ # Needed for sharing the LMTP sockets with JoinsNamespaceOf=
+ systemd.services.postfix.serviceConfig.PrivateTmp = true;
+ })
+ (mkIf cfg.redis.enable {
+ services.redis.vmOverCommit = mkDefault true;
+ })
+ (mkIf cfg.nginx.enable {
+ assertions = [
+ { assertion = nginx.enable;
+ message = "nginx must be enabled and configured";
+ }
+ ];
+ # For proxyPass= in virtual-hosts for Sourcehut services.
+ services.nginx.recommendedProxySettings = mkDefault true;
+ })
+ (mkIf (cfg.builds.enable || cfg.git.enable || cfg.hg.enable) {
+ services.openssh = {
+ # Note that sshd will continue to honor AuthorizedKeysFile
+ # sshsrht-dispatch needs to read ${users."sshsrht".home}/../config.ini,
+ # Note that you may want automatically rotate
+ # or link to /dev/null the following log files:
+ # - /var/log/gitsrht-dispatch
+ # - /var/log/{build,git,hg}srht-keys
+ # - /var/log/{git,hg}srht-shell
+ # - /var/log/gitsrht-update-hook
+ authorizedKeysCommand = ''/etc/ssh/srht-dispatch "%u" "%h" "%t" "%k"'';
+ # srht-dispatch will setuid/setgid according to [git.sr.ht::dispatch]
+ authorizedKeysCommandUser = "root";
+ extraConfig = ''
+ PermitUserEnvironment SRHT_*
+ '';
+ };
+ environment.etc."ssh/srht-dispatch" = {
+ # sshd_config(5): The program must be owned by root, not writable by group or others
+ mode = "0755";
+ source = pkgs.writeShellScript "srht-dispatch" ''
+ set -e
+ cd ${users."sshsrht".home}
+ ${cfg.python}/bin/gitsrht-dispatch "$@"
+ '';
+ };
+ systemd.services.sshd = let
+ # TODO: use a filtered config.ini containing only [git.sr.ht::dispatch]
+ configIni = settingsFormat.generate "sourcehut-dispatch-config.ini"
+ # Each service needs access to only a subset of sections (and secrets).
+ (filterAttrs (k: v: k == "git.sr.ht::dispatch")
+ cfg.settings);
+ in {
+ #path = optional cfg.git.enable [ cfg.git.package ];
+ restartTriggers = [ configIni ];
+ serviceConfig = {
+ RuntimeDirectory = [ "sourcehut/sshsrht/subdir" ];
+ BindReadOnlyPaths =
+ # Note that those /usr/bin/* paths are hardcoded in multiple places in *.sr.ht,
+ # for instance to get the user from the [git.sr.ht::dispatch] settings.
+ # *srht-keys needs to:
+ # - access a redis-server in [sr.ht] redis-host,
+ # - access the PostgreSQL server in [*.sr.ht] connection-string,
+ # - query metasrht-api (through the HTTP API).
+ optionals cfg.builds.enable [
+ "${pkgs.writeShellScript "buildsrht-keys-wrapper" ''
+ set -ex
+ cd /run/sourcehut/buildsrht/subdir
+ exec ${pkgs.sourcehut.buildsrht}/bin/buildsrht-keys "$@"
+ ''}:/usr/bin/buildsrht-keys"
+ "${pkgs.sourcehut.buildsrht}/bin/master-shell:/usr/bin/master-shell"
+ "${pkgs.sourcehut.buildsrht}/bin/runner-shell:/usr/bin/runner-shell"
+ ] ++
+ optionals cfg.git.enable [
+ "${pkgs.writeShellScript "gitsrht-keys-wrapper" ''
+ set -ex
+ cd /run/sourcehut/gitsrht/subdir
+ exec ${pkgs.sourcehut.gitsrht}/bin/gitsrht-keys "$@"
+ ''}:/usr/bin/gitsrht-keys"
+ "${pkgs.sourcehut.gitsrht}/bin/gitsrht-shell:/usr/bin/gitsrht-shell"
+ ] ++
+ optionals cfg.hg.enable [
+ "${pkgs.writeShellScript "hgsrht-keys-wrapper" ''
+ set -ex
+ cd /run/sourcehut/hgsrht/subdir
+ exec ${pkgs.sourcehut.hgsrht}/bin/hgsrht-keys "$@"
+ ''}:/usr/bin/hgsrht-keys"
+ "${pkgs.sourcehut.hgsrht}/bin/hgsrht-shell:/usr/bin/hgsrht-shell"
+ ];
+ ExecStartPre = mkBefore [("+"+pkgs.writeShellScript "sshsrht-credentials" ''
+ # Replace values begining with a '<' by the content of the file whose name is after.
+ ${pkgs.gawk}/bin/gawk '{ if (match($0,/^([^=]+=)<(.+)/,m)) { getline f < m[2]; print m[1] f } else print $0 }' ${configIni} |
+ install -o ${users."sshsrht".name} -g ${groups."sshsrht".name} -m 440 \
+ /dev/stdin ${users."sshsrht".home}/../config.ini
+ '')];
+ };
+ };
+ users = {
+ users."sshsrht" = {
+ isSystemUser = true;
+ # srht-dispatch, *srht-keys, and *srht-shell
+ # look up in ../config.ini from this directory;
+ # that config.ini being set in *srht.service's ExecStartPre=
+ home = "/run/sourcehut/sshsrht/subdir";
+ group = groups.nogroup.name;
+ description = "sourcehut user for sshd's AuthorizedKeysCommandUser";
+ };
+ groups."sshsrht" = {};
+ };
+ })
+ ]);
+
+ imports = [
+
+ (import ./service.nix "builds" {
+ inherit configIniOfService;
+ srvsrht = "buildsrht";
+ port = 5002;
+ # TODO: a celery worker on the master and worker are apparently needed
+ extraServices.buildsrht-worker = let
+ qemuPackage = pkgs.qemu_kvm;
+ serviceName = "buildsrht-worker";
+ statePath = "/var/lib/sourcehut/${serviceName}";
+ in mkIf cfg.builds.enableWorker {
+ path = [ pkgs.openssh pkgs.docker ];
+ preStart = ''
+ set -x
+ if test -z "$(docker images -q qemu:latest 2>/dev/null)" \
+ || test "$(cat ${statePath}/docker-image-qemu)" != "${qemuPackage.version}"
+ then
+ # Create and import qemu:latest image for docker
+ ${pkgs.dockerTools.streamLayeredImage {
+ name = "qemu";
+ tag = "latest";
+ contents = [ qemuPackage ];
+ }} | docker load
+ # Mark down current package version
+ echo '${qemuPackage.version}' >${statePath}/docker-image-qemu
+ fi
+ '';
+ serviceConfig = {
+ ExecStart = "${pkgs.sourcehut.buildsrht}/bin/builds.sr.ht-worker";
+ RuntimeDirectory = [ "sourcehut/${serviceName}/subdir" ];
+ # builds.sr.ht-worker looks up ../config.ini
+ LogsDirectory = [ "sourcehut/${serviceName}" ];
+ StateDirectory = [ "sourcehut/${serviceName}" ];
+ WorkingDirectory = "-"+"/run/sourcehut/${serviceName}/subdir";
+ };
+ };
+ extraConfig = let
+ image_dirs = flatten (
+ mapAttrsToList (distro: revs:
+ mapAttrsToList (rev: archs:
+ mapAttrsToList (arch: image:
+ pkgs.runCommand "buildsrht-images" { } ''
+ mkdir -p $out/${distro}/${rev}/${arch}
+ ln -s ${image}/*.qcow2 $out/${distro}/${rev}/${arch}/root.img.qcow2
+ ''
+ ) archs
+ ) revs
+ ) cfg.builds.images
+ );
+ image_dir_pre = pkgs.symlinkJoin {
+ name = "builds.sr.ht-worker-images-pre";
+ paths = image_dirs;
+ # FIXME: not working, apparently because ubuntu/latest is a broken link
+ # ++ [ "${pkgs.sourcehut.buildsrht}/lib/images" ];
+ };
+ image_dir = pkgs.runCommand "builds.sr.ht-worker-images" { } ''
+ mkdir -p $out/images
+ cp -Lr ${image_dir_pre}/* $out/images
+ '';
+ in mkMerge [
+ {
+ users.users.${cfg.builds.user} = {
+ shell = pkgs.bash;
+ # Allow reading of ${users."sshsrht".home}/../config.ini
+ extraGroups = [ groups."sshsrht".name ];
+ };
+
+ virtualisation.docker.enable = true;
+
+ services.sourcehut.settings = mkMerge [
+ { # Note that git.sr.ht::dispatch is not a typo,
+ # gitsrht-dispatch always use this section
+ "git.sr.ht::dispatch"."/usr/bin/buildsrht-keys" =
+ mkDefault "${cfg.builds.user}:${cfg.builds.group}";
+ }
+ (mkIf cfg.builds.enableWorker {
+ "builds.sr.ht::worker".shell = "/usr/bin/runner-shell";
+ "builds.sr.ht::worker".images = mkDefault "${image_dir}/images";
+ "builds.sr.ht::worker".controlcmd = mkDefault "${image_dir}/images/control";
+ })
+ ];
+ }
+ (mkIf cfg.builds.enableWorker {
+ users.groups = {
+ docker.members = [ cfg.builds.user ];
+ };
+ })
+ (mkIf (cfg.builds.enableWorker && cfg.nginx.enable) {
+ # Allow nginx access to buildlogs
+ users.users.${nginx.user}.extraGroups = [ cfg.builds.group ];
+ systemd.services.nginx = {
+ serviceConfig.BindReadOnlyPaths = [ "${cfg.settings."builds.sr.ht::worker".buildlogs}:/var/log/nginx/buildsrht/logs" ];
+ };
+ services.nginx.virtualHosts."logs.${domain}" = mkMerge [ {
+ /* FIXME: is a listen needed?
+ listen = with builtins;
+ # FIXME: not compatible with IPv6
+ let address = split ":" cfg.settings."builds.sr.ht::worker".name; in
+ [{ addr = elemAt address 0; port = lib.toInt (elemAt address 2); }];
+ */
+ locations."/logs/".alias = "/var/log/nginx/buildsrht/logs/";
+ } cfg.nginx.virtualHost ];
+ })
+ ];
+ })
+
+ (import ./service.nix "dispatch" {
+ inherit configIniOfService;
+ port = 5005;
+ })
+
+ (import ./service.nix "git" (let
+ baseService = {
+ path = [ cfg.git.package ];
+ serviceConfig.BindPaths = [ "${cfg.settings."git.sr.ht".repos}:/var/lib/sourcehut/gitsrht/repos" ];
+ serviceConfig.BindReadOnlyPaths = [ "${cfg.settings."git.sr.ht".post-update-script}:/var/lib/sourcehut/gitsrht/bin/post-update-script" ];
+ };
+ in {
+ inherit configIniOfService;
+ mainService = mkMerge [ baseService {
+ serviceConfig.StateDirectory = [ "sourcehut/gitsrht" "sourcehut/gitsrht/repos" ];
+ } ];
+ port = 5001;
+ webhooks = true;
+ extraTimers.gitsrht-periodic = {
+ service = baseService;
+ timerConfig.OnCalendar = ["20min"];
+ };
+ extraConfig = mkMerge [
+ {
+ users.users.${cfg.git.user} = {
+ # https://stackoverflow.com/questions/22314298/git-push-results-in-fatal-protocol-error-bad-line-length-character-this
+ # Probably could use gitsrht-shell if output is restricted to just parameters...
+ shell = pkgs.bash;
+ # Allow reading of ${users."sshsrht".home}/../config.ini
+ extraGroups = [ groups."sshsrht".name ];
+ home = users.sshsrht.home;
+ };
+ services.sourcehut.settings = {
+ "git.sr.ht::dispatch"."/usr/bin/gitsrht-keys" =
+ mkDefault "${cfg.git.user}:${cfg.git.group}";
+ };
+ systemd.services.sshd = baseService;
+ }
+ (mkIf cfg.nginx.enable {
+ services.nginx.virtualHosts."git.${domain}" = {
+ locations."/authorize" = {
+ proxyPass = "http://${cfg.listenAddress}:${toString cfg.git.port}";
+ extraConfig = ''
+ proxy_pass_request_body off;
+ proxy_set_header Content-Length "";
+ proxy_set_header X-Original-URI $request_uri;
+ '';
+ };
+ locations."~ ^/([^/]+)/([^/]+)/(HEAD|info/refs|objects/info/.*|git-upload-pack).*$" = {
+ root = "/var/lib/sourcehut/gitsrht/repos";
+ fastcgiParams = {
+ GIT_HTTP_EXPORT_ALL = "";
+ GIT_PROJECT_ROOT = "$document_root";
+ PATH_INFO = "$uri";
+ SCRIPT_FILENAME = "${cfg.git.package}/bin/git-http-backend";
+ };
+ extraConfig = ''
+ auth_request /authorize;
+ fastcgi_read_timeout 500s;
+ fastcgi_pass unix:/run/gitsrht-fcgiwrap.sock;
+ gzip off;
+ '';
+ };
+ };
+ systemd.sockets.gitsrht-fcgiwrap = {
+ before = [ "nginx.service" ];
+ wantedBy = [ "sockets.target" "gitsrht.service" ];
+ # This path remains accessible to nginx.service, which has no RootDirectory=
+ socketConfig.ListenStream = "/run/gitsrht-fcgiwrap.sock";
+ socketConfig.SocketUser = nginx.user;
+ socketConfig.SocketMode = "600";
+ };
+ })
+ ];
+ extraServices.gitsrht-fcgiwrap = mkIf cfg.nginx.enable {
+ serviceConfig = {
+ # Socket is passed by gitsrht-fcgiwrap.socket
+ ExecStart = "${pkgs.fcgiwrap}/sbin/fcgiwrap -c ${toString cfg.git.fcgiwrap.preforkProcess}";
+ # No need for config.ini
+ ExecStartPre = mkForce [];
+ User = null;
+ DynamicUser = true;
+ BindReadOnlyPaths = [ "${cfg.settings."git.sr.ht".repos}:/var/lib/sourcehut/gitsrht/repos" ];
+ IPAddressDeny = "any";
+ InaccessiblePaths = [ "-+/run/postgresql" "-+/run/redis-sourcehut" ];
+ PrivateNetwork = true;
+ RestrictAddressFamilies = mkForce [ "none" ];
+ SystemCallFilter = mkForce [
+ "@system-service"
+ "~@aio" "~@keyring" "~@memlock" "~@privileged" "~@resources" "~@setuid"
+ # @timer is needed for alarm()
+ ];
+ };
+ };
+ }))
+
+ (import ./service.nix "hg" (let
+ baseService = {
+ path = [ cfg.hg.package ];
+ serviceConfig.BindPaths = [ "${cfg.settings."hg.sr.ht".repos}:/var/lib/sourcehut/hgsrht/repos" ];
+ serviceConfig.BindReadOnlyPaths = [ "${cfg.settings."ht.sr.ht".changegroup-script}:/var/lib/sourcehut/hgsrht/bin/changegroup-script" ];
+ };
+ in {
+ inherit configIniOfService;
+ mainService = mkMerge [ baseService {
+ serviceConfig.StateDirectory = [ "sourcehut/hgsrht" "sourcehut/hgsrht/repos" ];
+ } ];
+ port = 5010;
+ webhooks = true;
+ extraTimers.hgsrht-periodic = {
+ service = baseService;
+ timerConfig.OnCalendar = ["20min"];
+ };
+ extraTimers.hgsrht-clonebundles = mkIf cfg.hg.cloneBundles {
+ service = baseService;
+ timerConfig.OnCalendar = ["daily"];
+ timerConfig.AccuracySec = "1h";
+ };
+ extraConfig = mkMerge [
+ {
+ users.users.${cfg.hg.user} = {
+ shell = pkgs.bash;
+ # Allow reading of ${users."sshsrht".home}/../config.ini
+ extraGroups = [ groups."sshsrht".name ];
+ };
+ services.sourcehut.settings = {
+ # Note that git.sr.ht::dispatch is not a typo,
+ # gitsrht-dispatch always uses this section.
+ "git.sr.ht::dispatch"."/usr/bin/hgsrht-keys" =
+ mkDefault "${cfg.hg.user}:${cfg.hg.group}";
+ };
+ systemd.services.sshd = baseService;
+ }
+ (mkIf cfg.nginx.enable {
+ # Allow nginx access to repositories
+ users.users.${nginx.user}.extraGroups = [ cfg.hg.group ];
+ services.nginx.virtualHosts."hg.${domain}" = {
+ locations."/authorize" = {
+ proxyPass = "http://${cfg.listenAddress}:${toString cfg.hg.port}";
+ extraConfig = ''
+ proxy_pass_request_body off;
+ proxy_set_header Content-Length "";
+ proxy_set_header X-Original-URI $request_uri;
+ '';
+ };
+ # Let clients reach pull bundles. We don't really need to lock this down even for
+ # private repos because the bundles are named after the revision hashes...
+ # so someone would need to know or guess a SHA value to download anything.
+ # TODO: proxyPass to an hg serve service?
+ locations."~ ^/[~^][a-z0-9_]+/[a-zA-Z0-9_.-]+/\\.hg/bundles/.*$" = {
+ root = "/var/lib/nginx/hgsrht/repos";
+ extraConfig = ''
+ auth_request /authorize;
+ gzip off;
+ '';
+ };
+ };
+ systemd.services.nginx = {
+ serviceConfig.BindReadOnlyPaths = [ "${cfg.settings."hg.sr.ht".repos}:/var/lib/nginx/hgsrht/repos" ];
+ };
+ })
+ ];
+ }))
+
+ (import ./service.nix "hub" {
+ inherit configIniOfService;
+ port = 5014;
+ extraConfig = {
+ services.nginx = mkIf cfg.nginx.enable {
+ virtualHosts."hub.${domain}" = mkMerge [ {
+ serverAliases = [ domain ];
+ } cfg.nginx.virtualHost ];
+ };
+ };
+ })
+
+ (import ./service.nix "lists" (let
+ srvsrht = "listssrht";
+ in {
+ inherit configIniOfService;
+ port = 5006;
+ webhooks = true;
+ # Receive the mail from Postfix and enqueue them into Redis and PostgreSQL
+ extraServices.listssrht-lmtp = {
+ wants = [ "postfix.service" ];
+ unitConfig.JoinsNamespaceOf = optional cfg.postfix.enable "postfix.service";
+ serviceConfig.ExecStart = "${cfg.python}/bin/listssrht-lmtp";
+ # Avoid crashing: os.chown(sock, os.getuid(), sock_gid)
+ serviceConfig.PrivateUsers = mkForce false;
+ };
+ # Dequeue the mails from Redis and dispatch them
+ extraServices.listssrht-process = {
+ serviceConfig = {
+ preStart = ''
+ cp ${pkgs.writeText "${srvsrht}-webhooks-celeryconfig.py" cfg.lists.process.celeryConfig} \
+ /run/sourcehut/${srvsrht}-webhooks/celeryconfig.py
+ '';
+ ExecStart = "${cfg.python}/bin/celery --app listssrht.process worker --hostname listssrht-process@%%h " + concatStringsSep " " cfg.lists.process.extraArgs;
+ # Avoid crashing: os.getloadavg()
+ ProcSubset = mkForce "all";
+ };
+ };
+ extraConfig = mkIf cfg.postfix.enable {
+ users.groups.${postfix.group}.members = [ cfg.lists.user ];
+ services.sourcehut.settings."lists.sr.ht::mail".sock-group = postfix.group;
+ services.postfix = {
+ destination = [ "lists.${domain}" ];
+ # FIXME: an accurate recipient list should be queried
+ # from the lists.sr.ht PostgreSQL database to avoid backscattering.
+ # But usernames are unfortunately not in that database but in meta.sr.ht.
+ # Note that two syntaxes are allowed:
+ # - ~username/list-name@lists.${domain}
+ # - u.username.list-name@lists.${domain}
+ localRecipients = [ "@lists.${domain}" ];
+ transport = ''
+ lists.${domain} lmtp:unix:${cfg.settings."lists.sr.ht::worker".sock}
+ '';
+ };
+ };
+ }))
+
+ (import ./service.nix "man" {
+ inherit configIniOfService;
+ port = 5004;
+ })
+
+ (import ./service.nix "meta" {
+ inherit configIniOfService;
+ port = 5000;
+ webhooks = true;
+ extraServices.metasrht-api = {
+ serviceConfig.Restart = "always";
+ serviceConfig.RestartSec = "2s";
+ preStart = "set -x\n" + concatStringsSep "\n\n" (attrValues (mapAttrs (k: s:
+ let srvMatch = builtins.match "^([a-z]*)\\.sr\\.ht$" k;
+ srv = head srvMatch;
+ in
+ # Configure client(s) as "preauthorized"
+ optionalString (srvMatch != null && cfg.${srv}.enable && ((s.oauth-client-id or null) != null)) ''
+ # Configure ${srv}'s OAuth client as "preauthorized"
+ ${postgresql.package}/bin/psql '${cfg.settings."meta.sr.ht".connection-string}' \
+ -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${s.oauth-client-id}'"
+ ''
+ ) cfg.settings));
+ serviceConfig.ExecStart = "${pkgs.sourcehut.metasrht}/bin/metasrht-api -b ${cfg.listenAddress}:${toString (cfg.meta.port + 100)}";
+ };
+ extraTimers.metasrht-daily.timerConfig = {
+ OnCalendar = ["daily"];
+ AccuracySec = "1h";
+ };
+ extraConfig = mkMerge [
+ {
+ assertions = [
+ { assertion = let s = cfg.settings."meta.sr.ht::billing"; in
+ s.enabled == "yes" -> (s.stripe-public-key != null && s.stripe-secret-key != null);
+ message = "If meta.sr.ht::billing is enabled, the keys must be defined.";
+ }
+ ];
+ environment.systemPackages = optional cfg.meta.enable
+ (pkgs.writeShellScriptBin "metasrht-manageuser" ''
+ set -eux
+ if test "$(${pkgs.coreutils}/bin/id -n -u)" != '${cfg.meta.user}'
+ then exec sudo -u '${cfg.meta.user}' "$0" "$@"
+ else
+ # In order to load config.ini
+ if cd /run/sourcehut/metasrht
+ then exec ${cfg.python}/bin/metasrht-manageuser "$@"
+ else cat <${stateDir}/db
+ fi
+
+ ${optionalString cfg.settings.${iniKey}.migrate-on-upgrade ''
+ # Just try all the migrations because they're not linked to the version
+ for sql in ${pkgs.sourcehut.pagessrht}/share/sql/migrations/*.sql; do
+ ${postgresql.package}/bin/psql '${cfg.settings.${iniKey}.connection-string}' -f "$sql" || true
+ done
+ ''}
+
+ # Disable webhook
+ touch ${stateDir}/webhook
+ '';
+ serviceConfig = {
+ ExecStart = mkForce "${pkgs.sourcehut.pagessrht}/bin/pages.sr.ht -b ${cfg.listenAddress}:${toString cfg.pages.port}";
+ };
+ };
+ })
+
+ (import ./service.nix "paste" {
+ inherit configIniOfService;
+ port = 5011;
+ })
+
+ (import ./service.nix "todo" {
+ inherit configIniOfService;
+ port = 5003;
+ webhooks = true;
+ extraServices.todosrht-lmtp = {
+ wants = [ "postfix.service" ];
+ unitConfig.JoinsNamespaceOf = optional cfg.postfix.enable "postfix.service";
+ serviceConfig.ExecStart = "${cfg.python}/bin/todosrht-lmtp";
+ # Avoid crashing: os.chown(sock, os.getuid(), sock_gid)
+ serviceConfig.PrivateUsers = mkForce false;
+ };
+ extraConfig = mkIf cfg.postfix.enable {
+ users.groups.${postfix.group}.members = [ cfg.todo.user ];
+ services.sourcehut.settings."todo.sr.ht::mail".sock-group = postfix.group;
+ services.postfix.transport = ''
+ todo.${domain} lmtp:unix:${cfg.settings."todo.sr.ht::mail".sock}
+ '';
+ };
+ })
+
+ (mkRenamedOptionModule [ "services" "sourcehut" "originBase" ]
+ [ "services" "sourcehut" "settings" "sr.ht" "global-domain" ])
+ (mkRenamedOptionModule [ "services" "sourcehut" "address" ]
+ [ "services" "sourcehut" "listenAddress" ])
+
+ ];
+
meta.doc = ./sourcehut.xml;
- meta.maintainers = with maintainers; [ tomberek ];
+ meta.maintainers = with maintainers; [ julm tomberek ];
}
diff --git a/nixos/modules/services/misc/sourcehut/dispatch.nix b/nixos/modules/services/misc/sourcehut/dispatch.nix
deleted file mode 100644
index a9db17bebe8..00000000000
--- a/nixos/modules/services/misc/sourcehut/dispatch.nix
+++ /dev/null
@@ -1,125 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
- cfg = config.services.sourcehut;
- cfgIni = cfg.settings;
- scfg = cfg.dispatch;
- iniKey = "dispatch.sr.ht";
-
- drv = pkgs.sourcehut.dispatchsrht;
-in
-{
- options.services.sourcehut.dispatch = {
- user = mkOption {
- type = types.str;
- default = "dispatchsrht";
- description = ''
- User for dispatch.sr.ht.
- '';
- };
-
- port = mkOption {
- type = types.port;
- default = 5005;
- description = ''
- Port on which the "dispatch" module should listen.
- '';
- };
-
- database = mkOption {
- type = types.str;
- default = "dispatch.sr.ht";
- description = ''
- PostgreSQL database name for dispatch.sr.ht.
- '';
- };
-
- statePath = mkOption {
- type = types.path;
- default = "${cfg.statePath}/dispatchsrht";
- description = ''
- State path for dispatch.sr.ht.
- '';
- };
- };
-
- config = with scfg; lib.mkIf (cfg.enable && elem "dispatch" cfg.services) {
-
- users = {
- users = {
- "${user}" = {
- isSystemUser = true;
- group = user;
- description = "dispatch.sr.ht user";
- };
- };
-
- groups = {
- "${user}" = { };
- };
- };
-
- services.postgresql = {
- authentication = ''
- local ${database} ${user} trust
- '';
- ensureDatabases = [ database ];
- ensureUsers = [
- {
- name = user;
- ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
- }
- ];
- };
-
- systemd = {
- tmpfiles.rules = [
- "d ${statePath} 0750 ${user} ${user} -"
- ];
-
- services.dispatchsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "dispatch.sr.ht website service";
-
- serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
- };
- };
-
- services.sourcehut.settings = {
- # URL dispatch.sr.ht is being served at (protocol://domain)
- "dispatch.sr.ht".origin = mkDefault "http://dispatch.${cfg.originBase}";
- # Address and port to bind the debug server to
- "dispatch.sr.ht".debug-host = mkDefault "0.0.0.0";
- "dispatch.sr.ht".debug-port = mkDefault port;
- # Configures the SQLAlchemy connection string for the database.
- "dispatch.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
- # Set to "yes" to automatically run migrations on package upgrade.
- "dispatch.sr.ht".migrate-on-upgrade = mkDefault "yes";
- # dispatch.sr.ht's OAuth client ID and secret for meta.sr.ht
- # Register your client at meta.example.org/oauth
- "dispatch.sr.ht".oauth-client-id = mkDefault null;
- "dispatch.sr.ht".oauth-client-secret = mkDefault null;
-
- # Github Integration
- "dispatch.sr.ht::github".oauth-client-id = mkDefault null;
- "dispatch.sr.ht::github".oauth-client-secret = mkDefault null;
-
- # Gitlab Integration
- "dispatch.sr.ht::gitlab".enabled = mkDefault null;
- "dispatch.sr.ht::gitlab".canonical-upstream = mkDefault "gitlab.com";
- "dispatch.sr.ht::gitlab".repo-cache = mkDefault "./repo-cache";
- # "dispatch.sr.ht::gitlab"."gitlab.com" = mkDefault "GitLab:application id:secret";
- };
-
- services.nginx.virtualHosts."dispatch.${cfg.originBase}" = {
- forceSSL = true;
- locations."/".proxyPass = "http://${cfg.address}:${toString port}";
- locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
- locations."/static".root = "${pkgs.sourcehut.dispatchsrht}/${pkgs.sourcehut.python.sitePackages}/dispatchsrht";
- };
- };
-}
diff --git a/nixos/modules/services/misc/sourcehut/git.nix b/nixos/modules/services/misc/sourcehut/git.nix
deleted file mode 100644
index 99b9aec0612..00000000000
--- a/nixos/modules/services/misc/sourcehut/git.nix
+++ /dev/null
@@ -1,214 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
- cfg = config.services.sourcehut;
- scfg = cfg.git;
- iniKey = "git.sr.ht";
-
- rcfg = config.services.redis;
- drv = pkgs.sourcehut.gitsrht;
-in
-{
- options.services.sourcehut.git = {
- user = mkOption {
- type = types.str;
- visible = false;
- internal = true;
- readOnly = true;
- default = "git";
- description = ''
- User for git.sr.ht.
- '';
- };
-
- port = mkOption {
- type = types.port;
- default = 5001;
- description = ''
- Port on which the "git" module should listen.
- '';
- };
-
- database = mkOption {
- type = types.str;
- default = "git.sr.ht";
- description = ''
- PostgreSQL database name for git.sr.ht.
- '';
- };
-
- statePath = mkOption {
- type = types.path;
- default = "${cfg.statePath}/gitsrht";
- description = ''
- State path for git.sr.ht.
- '';
- };
-
- package = mkOption {
- type = types.package;
- default = pkgs.git;
- example = literalExample "pkgs.gitFull";
- description = ''
- Git package for git.sr.ht. This can help silence collisions.
- '';
- };
- };
-
- config = with scfg; lib.mkIf (cfg.enable && elem "git" cfg.services) {
- # sshd refuses to run with `Unsafe AuthorizedKeysCommand ... bad ownership or modes for directory /nix/store`
- environment.etc."ssh/gitsrht-dispatch" = {
- mode = "0755";
- text = ''
- #! ${pkgs.stdenv.shell}
- ${cfg.python}/bin/gitsrht-dispatch "$@"
- '';
- };
-
- # Needs this in the $PATH when sshing into the server
- environment.systemPackages = [ cfg.git.package ];
-
- users = {
- users = {
- "${user}" = {
- isSystemUser = true;
- group = user;
- # https://stackoverflow.com/questions/22314298/git-push-results-in-fatal-protocol-error-bad-line-length-character-this
- # Probably could use gitsrht-shell if output is restricted to just parameters...
- shell = pkgs.bash;
- description = "git.sr.ht user";
- };
- };
-
- groups = {
- "${user}" = { };
- };
- };
-
- services = {
- cron.systemCronJobs = [ "*/20 * * * * ${cfg.python}/bin/gitsrht-periodic" ];
- fcgiwrap.enable = true;
-
- openssh.authorizedKeysCommand = ''/etc/ssh/gitsrht-dispatch "%u" "%h" "%t" "%k"'';
- openssh.authorizedKeysCommandUser = "root";
- openssh.extraConfig = ''
- PermitUserEnvironment SRHT_*
- '';
-
- postgresql = {
- authentication = ''
- local ${database} ${user} trust
- '';
- ensureDatabases = [ database ];
- ensureUsers = [
- {
- name = user;
- ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
- }
- ];
- };
- };
-
- systemd = {
- tmpfiles.rules = [
- # /var/log is owned by root
- "f /var/log/git-srht-shell 0644 ${user} ${user} -"
-
- "d ${statePath} 0750 ${user} ${user} -"
- "d ${cfg.settings."${iniKey}".repos} 2755 ${user} ${user} -"
- ];
-
- services = {
- gitsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
- after = [ "redis.service" "postgresql.service" "network.target" ];
- requires = [ "redis.service" "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- # Needs internally to create repos at the very least
- path = [ pkgs.git ];
- description = "git.sr.ht website service";
-
- serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
- };
-
- gitsrht-webhooks = {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "git.sr.ht webhooks service";
- serviceConfig = {
- Type = "simple";
- User = user;
- Restart = "always";
- };
-
- serviceConfig.ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
- };
- };
- };
-
- services.sourcehut.settings = {
- # URL git.sr.ht is being served at (protocol://domain)
- "git.sr.ht".origin = mkDefault "http://git.${cfg.originBase}";
- # Address and port to bind the debug server to
- "git.sr.ht".debug-host = mkDefault "0.0.0.0";
- "git.sr.ht".debug-port = mkDefault port;
- # Configures the SQLAlchemy connection string for the database.
- "git.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
- # Set to "yes" to automatically run migrations on package upgrade.
- "git.sr.ht".migrate-on-upgrade = mkDefault "yes";
- # The redis connection used for the webhooks worker
- "git.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/1";
-
- # A post-update script which is installed in every git repo.
- "git.sr.ht".post-update-script = mkDefault "${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
-
- # git.sr.ht's OAuth client ID and secret for meta.sr.ht
- # Register your client at meta.example.org/oauth
- "git.sr.ht".oauth-client-id = mkDefault null;
- "git.sr.ht".oauth-client-secret = mkDefault null;
- # Path to git repositories on disk
- "git.sr.ht".repos = mkDefault "/var/lib/git";
-
- "git.sr.ht".outgoing-domain = mkDefault "http://git.${cfg.originBase}";
-
- # The authorized keys hook uses this to dispatch to various handlers
- # The format is a program to exec into as the key, and the user to match as the
- # value. When someone tries to log in as this user, this program is executed
- # and is expected to omit an AuthorizedKeys file.
- #
- # Discard of the string context is in order to allow derivation-derived strings.
- # This is safe if the relevant package is installed which will be the case if the setting is utilized.
- "git.sr.ht::dispatch".${builtins.unsafeDiscardStringContext "${pkgs.sourcehut.gitsrht}/bin/gitsrht-keys"} = mkDefault "${user}:${user}";
- };
-
- services.nginx.virtualHosts."git.${cfg.originBase}" = {
- forceSSL = true;
- locations."/".proxyPass = "http://${cfg.address}:${toString port}";
- locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
- locations."/static".root = "${pkgs.sourcehut.gitsrht}/${pkgs.sourcehut.python.sitePackages}/gitsrht";
- extraConfig = ''
- location = /authorize {
- proxy_pass http://${cfg.address}:${toString port};
- proxy_pass_request_body off;
- proxy_set_header Content-Length "";
- proxy_set_header X-Original-URI $request_uri;
- }
- location ~ ^/([^/]+)/([^/]+)/(HEAD|info/refs|objects/info/.*|git-upload-pack).*$ {
- auth_request /authorize;
- root /var/lib/git;
- fastcgi_pass unix:/run/fcgiwrap.sock;
- fastcgi_param SCRIPT_FILENAME ${pkgs.git}/bin/git-http-backend;
- fastcgi_param PATH_INFO $uri;
- fastcgi_param GIT_PROJECT_ROOT $document_root;
- fastcgi_read_timeout 500s;
- include ${pkgs.nginx}/conf/fastcgi_params;
- gzip off;
- }
- '';
-
- };
- };
-}
diff --git a/nixos/modules/services/misc/sourcehut/hg.nix b/nixos/modules/services/misc/sourcehut/hg.nix
deleted file mode 100644
index 5cd36bb0455..00000000000
--- a/nixos/modules/services/misc/sourcehut/hg.nix
+++ /dev/null
@@ -1,173 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
- cfg = config.services.sourcehut;
- scfg = cfg.hg;
- iniKey = "hg.sr.ht";
-
- rcfg = config.services.redis;
- drv = pkgs.sourcehut.hgsrht;
-in
-{
- options.services.sourcehut.hg = {
- user = mkOption {
- type = types.str;
- internal = true;
- readOnly = true;
- default = "hg";
- description = ''
- User for hg.sr.ht.
- '';
- };
-
- port = mkOption {
- type = types.port;
- default = 5010;
- description = ''
- Port on which the "hg" module should listen.
- '';
- };
-
- database = mkOption {
- type = types.str;
- default = "hg.sr.ht";
- description = ''
- PostgreSQL database name for hg.sr.ht.
- '';
- };
-
- statePath = mkOption {
- type = types.path;
- default = "${cfg.statePath}/hgsrht";
- description = ''
- State path for hg.sr.ht.
- '';
- };
-
- cloneBundles = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Generate clonebundles (which require more disk space but dramatically speed up cloning large repositories).
- '';
- };
- };
-
- config = with scfg; lib.mkIf (cfg.enable && elem "hg" cfg.services) {
- # In case it ever comes into being
- environment.etc."ssh/hgsrht-dispatch" = {
- mode = "0755";
- text = ''
- #! ${pkgs.stdenv.shell}
- ${cfg.python}/bin/gitsrht-dispatch $@
- '';
- };
-
- environment.systemPackages = [ pkgs.mercurial ];
-
- users = {
- users = {
- "${user}" = {
- isSystemUser = true;
- group = user;
- # Assuming hg.sr.ht needs this too
- shell = pkgs.bash;
- description = "hg.sr.ht user";
- };
- };
-
- groups = {
- "${user}" = { };
- };
- };
-
- services = {
- cron.systemCronJobs = [ "*/20 * * * * ${cfg.python}/bin/hgsrht-periodic" ]
- ++ optional cloneBundles "0 * * * * ${cfg.python}/bin/hgsrht-clonebundles";
-
- openssh.authorizedKeysCommand = ''/etc/ssh/hgsrht-dispatch "%u" "%h" "%t" "%k"'';
- openssh.authorizedKeysCommandUser = "root";
- openssh.extraConfig = ''
- PermitUserEnvironment SRHT_*
- '';
-
- postgresql = {
- authentication = ''
- local ${database} ${user} trust
- '';
- ensureDatabases = [ database ];
- ensureUsers = [
- {
- name = user;
- ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
- }
- ];
- };
- };
-
- systemd = {
- tmpfiles.rules = [
- # /var/log is owned by root
- "f /var/log/hg-srht-shell 0644 ${user} ${user} -"
-
- "d ${statePath} 0750 ${user} ${user} -"
- "d ${cfg.settings."${iniKey}".repos} 2755 ${user} ${user} -"
- ];
-
- services.hgsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
- after = [ "redis.service" "postgresql.service" "network.target" ];
- requires = [ "redis.service" "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- path = [ pkgs.mercurial ];
- description = "hg.sr.ht website service";
-
- serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
- };
- };
-
- services.sourcehut.settings = {
- # URL hg.sr.ht is being served at (protocol://domain)
- "hg.sr.ht".origin = mkDefault "http://hg.${cfg.originBase}";
- # Address and port to bind the debug server to
- "hg.sr.ht".debug-host = mkDefault "0.0.0.0";
- "hg.sr.ht".debug-port = mkDefault port;
- # Configures the SQLAlchemy connection string for the database.
- "hg.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
- # The redis connection used for the webhooks worker
- "hg.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/1";
- # A post-update script which is installed in every mercurial repo.
- "hg.sr.ht".changegroup-script = mkDefault "${cfg.python}/bin/hgsrht-hook-changegroup";
- # hg.sr.ht's OAuth client ID and secret for meta.sr.ht
- # Register your client at meta.example.org/oauth
- "hg.sr.ht".oauth-client-id = mkDefault null;
- "hg.sr.ht".oauth-client-secret = mkDefault null;
- # Path to mercurial repositories on disk
- "hg.sr.ht".repos = mkDefault "/var/lib/hg";
- # Path to the srht mercurial extension
- # (defaults to where the hgsrht code is)
- # "hg.sr.ht".srhtext = mkDefault null;
- # .hg/store size (in MB) past which the nightly job generates clone bundles.
- # "hg.sr.ht".clone_bundle_threshold = mkDefault 50;
- # Path to hg-ssh (if not in $PATH)
- # "hg.sr.ht".hg_ssh = mkDefault /path/to/hg-ssh;
-
- # The authorized keys hook uses this to dispatch to various handlers
- # The format is a program to exec into as the key, and the user to match as the
- # value. When someone tries to log in as this user, this program is executed
- # and is expected to omit an AuthorizedKeys file.
- #
- # Uncomment the relevant lines to enable the various sr.ht dispatchers.
- "hg.sr.ht::dispatch"."/run/current-system/sw/bin/hgsrht-keys" = mkDefault "${user}:${user}";
- };
-
- # TODO: requires testing and addition of hg-specific requirements
- services.nginx.virtualHosts."hg.${cfg.originBase}" = {
- forceSSL = true;
- locations."/".proxyPass = "http://${cfg.address}:${toString port}";
- locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
- locations."/static".root = "${pkgs.sourcehut.hgsrht}/${pkgs.sourcehut.python.sitePackages}/hgsrht";
- };
- };
-}
diff --git a/nixos/modules/services/misc/sourcehut/hub.nix b/nixos/modules/services/misc/sourcehut/hub.nix
deleted file mode 100644
index be3ea21011c..00000000000
--- a/nixos/modules/services/misc/sourcehut/hub.nix
+++ /dev/null
@@ -1,118 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
- cfg = config.services.sourcehut;
- cfgIni = cfg.settings;
- scfg = cfg.hub;
- iniKey = "hub.sr.ht";
-
- drv = pkgs.sourcehut.hubsrht;
-in
-{
- options.services.sourcehut.hub = {
- user = mkOption {
- type = types.str;
- default = "hubsrht";
- description = ''
- User for hub.sr.ht.
- '';
- };
-
- port = mkOption {
- type = types.port;
- default = 5014;
- description = ''
- Port on which the "hub" module should listen.
- '';
- };
-
- database = mkOption {
- type = types.str;
- default = "hub.sr.ht";
- description = ''
- PostgreSQL database name for hub.sr.ht.
- '';
- };
-
- statePath = mkOption {
- type = types.path;
- default = "${cfg.statePath}/hubsrht";
- description = ''
- State path for hub.sr.ht.
- '';
- };
- };
-
- config = with scfg; lib.mkIf (cfg.enable && elem "hub" cfg.services) {
- users = {
- users = {
- "${user}" = {
- isSystemUser = true;
- group = user;
- description = "hub.sr.ht user";
- };
- };
-
- groups = {
- "${user}" = { };
- };
- };
-
- services.postgresql = {
- authentication = ''
- local ${database} ${user} trust
- '';
- ensureDatabases = [ database ];
- ensureUsers = [
- {
- name = user;
- ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
- }
- ];
- };
-
- systemd = {
- tmpfiles.rules = [
- "d ${statePath} 0750 ${user} ${user} -"
- ];
-
- services.hubsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "hub.sr.ht website service";
-
- serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
- };
- };
-
- services.sourcehut.settings = {
- # URL hub.sr.ht is being served at (protocol://domain)
- "hub.sr.ht".origin = mkDefault "http://hub.${cfg.originBase}";
- # Address and port to bind the debug server to
- "hub.sr.ht".debug-host = mkDefault "0.0.0.0";
- "hub.sr.ht".debug-port = mkDefault port;
- # Configures the SQLAlchemy connection string for the database.
- "hub.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
- # Set to "yes" to automatically run migrations on package upgrade.
- "hub.sr.ht".migrate-on-upgrade = mkDefault "yes";
- # hub.sr.ht's OAuth client ID and secret for meta.sr.ht
- # Register your client at meta.example.org/oauth
- "hub.sr.ht".oauth-client-id = mkDefault null;
- "hub.sr.ht".oauth-client-secret = mkDefault null;
- };
-
- services.nginx.virtualHosts."${cfg.originBase}" = {
- forceSSL = true;
- locations."/".proxyPass = "http://${cfg.address}:${toString port}";
- locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
- locations."/static".root = "${pkgs.sourcehut.hubsrht}/${pkgs.sourcehut.python.sitePackages}/hubsrht";
- };
- services.nginx.virtualHosts."hub.${cfg.originBase}" = {
- globalRedirect = "${cfg.originBase}";
- forceSSL = true;
- };
- };
-}
diff --git a/nixos/modules/services/misc/sourcehut/lists.nix b/nixos/modules/services/misc/sourcehut/lists.nix
deleted file mode 100644
index 7b1fe9fd463..00000000000
--- a/nixos/modules/services/misc/sourcehut/lists.nix
+++ /dev/null
@@ -1,185 +0,0 @@
-# Email setup is fairly involved, useful references:
-# https://drewdevault.com/2018/08/05/Local-mail-server.html
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
- cfg = config.services.sourcehut;
- cfgIni = cfg.settings;
- scfg = cfg.lists;
- iniKey = "lists.sr.ht";
-
- rcfg = config.services.redis;
- drv = pkgs.sourcehut.listssrht;
-in
-{
- options.services.sourcehut.lists = {
- user = mkOption {
- type = types.str;
- default = "listssrht";
- description = ''
- User for lists.sr.ht.
- '';
- };
-
- port = mkOption {
- type = types.port;
- default = 5006;
- description = ''
- Port on which the "lists" module should listen.
- '';
- };
-
- database = mkOption {
- type = types.str;
- default = "lists.sr.ht";
- description = ''
- PostgreSQL database name for lists.sr.ht.
- '';
- };
-
- statePath = mkOption {
- type = types.path;
- default = "${cfg.statePath}/listssrht";
- description = ''
- State path for lists.sr.ht.
- '';
- };
- };
-
- config = with scfg; lib.mkIf (cfg.enable && elem "lists" cfg.services) {
- users = {
- users = {
- "${user}" = {
- isSystemUser = true;
- group = user;
- extraGroups = [ "postfix" ];
- description = "lists.sr.ht user";
- };
- };
- groups = {
- "${user}" = { };
- };
- };
-
- services.postgresql = {
- authentication = ''
- local ${database} ${user} trust
- '';
- ensureDatabases = [ database ];
- ensureUsers = [
- {
- name = user;
- ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
- }
- ];
- };
-
- systemd = {
- tmpfiles.rules = [
- "d ${statePath} 0750 ${user} ${user} -"
- ];
-
- services = {
- listssrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "lists.sr.ht website service";
-
- serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
- };
-
- listssrht-process = {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "lists.sr.ht process service";
- serviceConfig = {
- Type = "simple";
- User = user;
- Restart = "always";
- ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.process worker --loglevel=info";
- };
- };
-
- listssrht-lmtp = {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "lists.sr.ht process service";
- serviceConfig = {
- Type = "simple";
- User = user;
- Restart = "always";
- ExecStart = "${cfg.python}/bin/listssrht-lmtp";
- };
- };
-
-
- listssrht-webhooks = {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "lists.sr.ht webhooks service";
- serviceConfig = {
- Type = "simple";
- User = user;
- Restart = "always";
- ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
- };
- };
- };
- };
-
- services.sourcehut.settings = {
- # URL lists.sr.ht is being served at (protocol://domain)
- "lists.sr.ht".origin = mkDefault "http://lists.${cfg.originBase}";
- # Address and port to bind the debug server to
- "lists.sr.ht".debug-host = mkDefault "0.0.0.0";
- "lists.sr.ht".debug-port = mkDefault port;
- # Configures the SQLAlchemy connection string for the database.
- "lists.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
- # Set to "yes" to automatically run migrations on package upgrade.
- "lists.sr.ht".migrate-on-upgrade = mkDefault "yes";
- # lists.sr.ht's OAuth client ID and secret for meta.sr.ht
- # Register your client at meta.example.org/oauth
- "lists.sr.ht".oauth-client-id = mkDefault null;
- "lists.sr.ht".oauth-client-secret = mkDefault null;
- # Outgoing email for notifications generated by users
- "lists.sr.ht".notify-from = mkDefault "CHANGEME@example.org";
- # The redis connection used for the webhooks worker
- "lists.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/2";
- # The redis connection used for the celery worker
- "lists.sr.ht".redis = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/4";
- # Network-key
- "lists.sr.ht".network-key = mkDefault null;
- # Allow creation
- "lists.sr.ht".allow-new-lists = mkDefault "no";
- # Posting Domain
- "lists.sr.ht".posting-domain = mkDefault "lists.${cfg.originBase}";
-
- # Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
- # Alternatively, specify IP:PORT and an SMTP server will be run instead.
- "lists.sr.ht::worker".sock = mkDefault "/tmp/lists.sr.ht-lmtp.sock";
- # The lmtp daemon will make the unix socket group-read/write for users in this
- # group.
- "lists.sr.ht::worker".sock-group = mkDefault "postfix";
- "lists.sr.ht::worker".reject-url = mkDefault "https://man.sr.ht/lists.sr.ht/etiquette.md";
- "lists.sr.ht::worker".reject-mimetypes = mkDefault "text/html";
-
- };
-
- services.nginx.virtualHosts."lists.${cfg.originBase}" = {
- forceSSL = true;
- locations."/".proxyPass = "http://${cfg.address}:${toString port}";
- locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
- locations."/static".root = "${pkgs.sourcehut.listssrht}/${pkgs.sourcehut.python.sitePackages}/listssrht";
- };
- };
-}
diff --git a/nixos/modules/services/misc/sourcehut/man.nix b/nixos/modules/services/misc/sourcehut/man.nix
deleted file mode 100644
index 7693396d187..00000000000
--- a/nixos/modules/services/misc/sourcehut/man.nix
+++ /dev/null
@@ -1,122 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
- cfg = config.services.sourcehut;
- cfgIni = cfg.settings;
- scfg = cfg.man;
- iniKey = "man.sr.ht";
-
- drv = pkgs.sourcehut.mansrht;
-in
-{
- options.services.sourcehut.man = {
- user = mkOption {
- type = types.str;
- default = "mansrht";
- description = ''
- User for man.sr.ht.
- '';
- };
-
- port = mkOption {
- type = types.port;
- default = 5004;
- description = ''
- Port on which the "man" module should listen.
- '';
- };
-
- database = mkOption {
- type = types.str;
- default = "man.sr.ht";
- description = ''
- PostgreSQL database name for man.sr.ht.
- '';
- };
-
- statePath = mkOption {
- type = types.path;
- default = "${cfg.statePath}/mansrht";
- description = ''
- State path for man.sr.ht.
- '';
- };
- };
-
- config = with scfg; lib.mkIf (cfg.enable && elem "man" cfg.services) {
- assertions =
- [
- {
- assertion = hasAttrByPath [ "git.sr.ht" "oauth-client-id" ] cfgIni;
- message = "man.sr.ht needs access to git.sr.ht.";
- }
- ];
-
- users = {
- users = {
- "${user}" = {
- isSystemUser = true;
- group = user;
- description = "man.sr.ht user";
- };
- };
-
- groups = {
- "${user}" = { };
- };
- };
-
- services.postgresql = {
- authentication = ''
- local ${database} ${user} trust
- '';
- ensureDatabases = [ database ];
- ensureUsers = [
- {
- name = user;
- ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
- }
- ];
- };
-
- systemd = {
- tmpfiles.rules = [
- "d ${statePath} 0750 ${user} ${user} -"
- ];
-
- services.mansrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "man.sr.ht website service";
-
- serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
- };
- };
-
- services.sourcehut.settings = {
- # URL man.sr.ht is being served at (protocol://domain)
- "man.sr.ht".origin = mkDefault "http://man.${cfg.originBase}";
- # Address and port to bind the debug server to
- "man.sr.ht".debug-host = mkDefault "0.0.0.0";
- "man.sr.ht".debug-port = mkDefault port;
- # Configures the SQLAlchemy connection string for the database.
- "man.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
- # Set to "yes" to automatically run migrations on package upgrade.
- "man.sr.ht".migrate-on-upgrade = mkDefault "yes";
- # man.sr.ht's OAuth client ID and secret for meta.sr.ht
- # Register your client at meta.example.org/oauth
- "man.sr.ht".oauth-client-id = mkDefault null;
- "man.sr.ht".oauth-client-secret = mkDefault null;
- };
-
- services.nginx.virtualHosts."man.${cfg.originBase}" = {
- forceSSL = true;
- locations."/".proxyPass = "http://${cfg.address}:${toString port}";
- locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
- locations."/static".root = "${pkgs.sourcehut.mansrht}/${pkgs.sourcehut.python.sitePackages}/mansrht";
- };
- };
-}
diff --git a/nixos/modules/services/misc/sourcehut/meta.nix b/nixos/modules/services/misc/sourcehut/meta.nix
deleted file mode 100644
index 56127a824eb..00000000000
--- a/nixos/modules/services/misc/sourcehut/meta.nix
+++ /dev/null
@@ -1,211 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
- cfg = config.services.sourcehut;
- cfgIni = cfg.settings;
- scfg = cfg.meta;
- iniKey = "meta.sr.ht";
-
- rcfg = config.services.redis;
- drv = pkgs.sourcehut.metasrht;
-in
-{
- options.services.sourcehut.meta = {
- user = mkOption {
- type = types.str;
- default = "metasrht";
- description = ''
- User for meta.sr.ht.
- '';
- };
-
- port = mkOption {
- type = types.port;
- default = 5000;
- description = ''
- Port on which the "meta" module should listen.
- '';
- };
-
- database = mkOption {
- type = types.str;
- default = "meta.sr.ht";
- description = ''
- PostgreSQL database name for meta.sr.ht.
- '';
- };
-
- statePath = mkOption {
- type = types.path;
- default = "${cfg.statePath}/metasrht";
- description = ''
- State path for meta.sr.ht.
- '';
- };
- };
-
- config = with scfg; lib.mkIf (cfg.enable && elem "meta" cfg.services) {
- assertions =
- [
- {
- assertion = with cfgIni."meta.sr.ht::billing"; enabled == "yes" -> (stripe-public-key != null && stripe-secret-key != null);
- message = "If meta.sr.ht::billing is enabled, the keys should be defined.";
- }
- ];
-
- users = {
- users = {
- ${user} = {
- isSystemUser = true;
- group = user;
- description = "meta.sr.ht user";
- };
- };
-
- groups = {
- "${user}" = { };
- };
- };
-
- services.cron.systemCronJobs = [ "0 0 * * * ${cfg.python}/bin/metasrht-daily" ];
- services.postgresql = {
- authentication = ''
- local ${database} ${user} trust
- '';
- ensureDatabases = [ database ];
- ensureUsers = [
- {
- name = user;
- ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
- }
- ];
- };
-
- systemd = {
- tmpfiles.rules = [
- "d ${statePath} 0750 ${user} ${user} -"
- ];
-
- services = {
- metasrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "meta.sr.ht website service";
-
- preStart = ''
- # Configure client(s) as "preauthorized"
- ${concatMapStringsSep "\n\n"
- (attr: ''
- if ! test -e "${statePath}/${attr}.oauth" || [ "$(cat ${statePath}/${attr}.oauth)" != "${cfgIni."${attr}".oauth-client-id}" ]; then
- # Configure ${attr}'s OAuth client as "preauthorized"
- psql ${database} \
- -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${cfgIni."${attr}".oauth-client-id}'"
-
- printf "%s" "${cfgIni."${attr}".oauth-client-id}" > "${statePath}/${attr}.oauth"
- fi
- '')
- (builtins.attrNames (filterAttrs
- (k: v: !(hasInfix "::" k) && builtins.hasAttr "oauth-client-id" v && v.oauth-client-id != null)
- cfg.settings))}
- '';
-
- serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
- };
-
- metasrht-api = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "meta.sr.ht api service";
-
- preStart = ''
- # Configure client(s) as "preauthorized"
- ${concatMapStringsSep "\n\n"
- (attr: ''
- if ! test -e "${statePath}/${attr}.oauth" || [ "$(cat ${statePath}/${attr}.oauth)" != "${cfgIni."${attr}".oauth-client-id}" ]; then
- # Configure ${attr}'s OAuth client as "preauthorized"
- psql ${database} \
- -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${cfgIni."${attr}".oauth-client-id}'"
-
- printf "%s" "${cfgIni."${attr}".oauth-client-id}" > "${statePath}/${attr}.oauth"
- fi
- '')
- (builtins.attrNames (filterAttrs
- (k: v: !(hasInfix "::" k) && builtins.hasAttr "oauth-client-id" v && v.oauth-client-id != null)
- cfg.settings))}
- '';
-
- serviceConfig.ExecStart = "${pkgs.sourcehut.metasrht}/bin/metasrht-api -b :${toString (port + 100)}";
- };
-
- metasrht-webhooks = {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "meta.sr.ht webhooks service";
- serviceConfig = {
- Type = "simple";
- User = user;
- Restart = "always";
- ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
- };
-
- };
- };
- };
-
- services.sourcehut.settings = {
- # URL meta.sr.ht is being served at (protocol://domain)
- "meta.sr.ht".origin = mkDefault "https://meta.${cfg.originBase}";
- # Address and port to bind the debug server to
- "meta.sr.ht".debug-host = mkDefault "0.0.0.0";
- "meta.sr.ht".debug-port = mkDefault port;
- # Configures the SQLAlchemy connection string for the database.
- "meta.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
- # Set to "yes" to automatically run migrations on package upgrade.
- "meta.sr.ht".migrate-on-upgrade = mkDefault "yes";
- # If "yes", the user will be sent the stock sourcehut welcome emails after
- # signup (requires cron to be configured properly). These are specific to the
- # sr.ht instance so you probably want to patch these before enabling this.
- "meta.sr.ht".welcome-emails = mkDefault "no";
-
- # The redis connection used for the webhooks worker
- "meta.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/6";
-
- # If "no", public registration will not be permitted.
- "meta.sr.ht::settings".registration = mkDefault "no";
- # Where to redirect new users upon registration
- "meta.sr.ht::settings".onboarding-redirect = mkDefault "https://meta.${cfg.originBase}";
- # How many invites each user is issued upon registration (only applicable if
- # open registration is disabled)
- "meta.sr.ht::settings".user-invites = mkDefault 5;
-
- # Origin URL for API, 100 more than web
- "meta.sr.ht".api-origin = mkDefault "http://localhost:5100";
-
- # You can add aliases for the client IDs of commonly used OAuth clients here.
- #
- # Example:
- "meta.sr.ht::aliases" = mkDefault { };
- # "meta.sr.ht::aliases"."git.sr.ht" = 12345;
-
- # "yes" to enable the billing system
- "meta.sr.ht::billing".enabled = mkDefault "no";
- # Get your keys at https://dashboard.stripe.com/account/apikeys
- "meta.sr.ht::billing".stripe-public-key = mkDefault null;
- "meta.sr.ht::billing".stripe-secret-key = mkDefault null;
- };
-
- services.nginx.virtualHosts."meta.${cfg.originBase}" = {
- forceSSL = true;
- locations."/".proxyPass = "http://${cfg.address}:${toString port}";
- locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
- locations."/static".root = "${pkgs.sourcehut.metasrht}/${pkgs.sourcehut.python.sitePackages}/metasrht";
- };
- };
-}
diff --git a/nixos/modules/services/misc/sourcehut/paste.nix b/nixos/modules/services/misc/sourcehut/paste.nix
deleted file mode 100644
index b2d5151969e..00000000000
--- a/nixos/modules/services/misc/sourcehut/paste.nix
+++ /dev/null
@@ -1,133 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
- cfg = config.services.sourcehut;
- cfgIni = cfg.settings;
- scfg = cfg.paste;
- iniKey = "paste.sr.ht";
-
- rcfg = config.services.redis;
- drv = pkgs.sourcehut.pastesrht;
-in
-{
- options.services.sourcehut.paste = {
- user = mkOption {
- type = types.str;
- default = "pastesrht";
- description = ''
- User for paste.sr.ht.
- '';
- };
-
- port = mkOption {
- type = types.port;
- default = 5011;
- description = ''
- Port on which the "paste" module should listen.
- '';
- };
-
- database = mkOption {
- type = types.str;
- default = "paste.sr.ht";
- description = ''
- PostgreSQL database name for paste.sr.ht.
- '';
- };
-
- statePath = mkOption {
- type = types.path;
- default = "${cfg.statePath}/pastesrht";
- description = ''
- State path for pastesrht.sr.ht.
- '';
- };
- };
-
- config = with scfg; lib.mkIf (cfg.enable && elem "paste" cfg.services) {
- users = {
- users = {
- "${user}" = {
- isSystemUser = true;
- group = user;
- description = "paste.sr.ht user";
- };
- };
-
- groups = {
- "${user}" = { };
- };
- };
-
- services.postgresql = {
- authentication = ''
- local ${database} ${user} trust
- '';
- ensureDatabases = [ database ];
- ensureUsers = [
- {
- name = user;
- ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
- }
- ];
- };
-
- systemd = {
- tmpfiles.rules = [
- "d ${statePath} 0750 ${user} ${user} -"
- ];
-
- services = {
- pastesrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "paste.sr.ht website service";
-
- serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
- };
-
- pastesrht-webhooks = {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "paste.sr.ht webhooks service";
- serviceConfig = {
- Type = "simple";
- User = user;
- Restart = "always";
- ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
- };
-
- };
- };
- };
-
- services.sourcehut.settings = {
- # URL paste.sr.ht is being served at (protocol://domain)
- "paste.sr.ht".origin = mkDefault "http://paste.${cfg.originBase}";
- # Address and port to bind the debug server to
- "paste.sr.ht".debug-host = mkDefault "0.0.0.0";
- "paste.sr.ht".debug-port = mkDefault port;
- # Configures the SQLAlchemy connection string for the database.
- "paste.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
- # Set to "yes" to automatically run migrations on package upgrade.
- "paste.sr.ht".migrate-on-upgrade = mkDefault "yes";
- # paste.sr.ht's OAuth client ID and secret for meta.sr.ht
- # Register your client at meta.example.org/oauth
- "paste.sr.ht".oauth-client-id = mkDefault null;
- "paste.sr.ht".oauth-client-secret = mkDefault null;
- "paste.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/5";
- };
-
- services.nginx.virtualHosts."paste.${cfg.originBase}" = {
- forceSSL = true;
- locations."/".proxyPass = "http://${cfg.address}:${toString port}";
- locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
- locations."/static".root = "${pkgs.sourcehut.pastesrht}/${pkgs.sourcehut.python.sitePackages}/pastesrht";
- };
- };
-}
diff --git a/nixos/modules/services/misc/sourcehut/service.nix b/nixos/modules/services/misc/sourcehut/service.nix
index 65b4ad020f9..b3c0efc07dd 100644
--- a/nixos/modules/services/misc/sourcehut/service.nix
+++ b/nixos/modules/services/misc/sourcehut/service.nix
@@ -1,66 +1,375 @@
-{ config, pkgs, lib }:
-serviceCfg: serviceDrv: iniKey: attrs:
+srv:
+{ configIniOfService
+, srvsrht ? "${srv}srht" # Because "buildsrht" does not follow that pattern (missing an "s").
+, iniKey ? "${srv}.sr.ht"
+, webhooks ? false
+, extraTimers ? {}
+, mainService ? {}
+, extraServices ? {}
+, extraConfig ? {}
+, port
+}:
+{ config, lib, pkgs, ... }:
+
+with lib;
let
+ inherit (config.services) postgresql;
+ redis = config.services.redis.servers."sourcehut-${srvsrht}";
+ inherit (config.users) users;
cfg = config.services.sourcehut;
- cfgIni = cfg.settings."${iniKey}";
- pgSuperUser = config.services.postgresql.superUser;
-
- setupDB = pkgs.writeScript "${serviceDrv.pname}-gen-db" ''
- #! ${cfg.python}/bin/python
- from ${serviceDrv.pname}.app import db
- db.create()
- '';
+ configIni = configIniOfService srv;
+ srvCfg = cfg.${srv};
+ baseService = serviceName: { allowStripe ? false }: extraService: let
+ runDir = "/run/sourcehut/${serviceName}";
+ rootDir = "/run/sourcehut/chroots/${serviceName}";
+ in
+ mkMerge [ extraService {
+ after = [ "network.target" ] ++
+ optional cfg.postgresql.enable "postgresql.service" ++
+ optional cfg.redis.enable "redis-sourcehut-${srvsrht}.service";
+ requires =
+ optional cfg.postgresql.enable "postgresql.service" ++
+ optional cfg.redis.enable "redis-sourcehut-${srvsrht}.service";
+ path = [ pkgs.gawk ];
+ environment.HOME = runDir;
+ serviceConfig = {
+ User = mkDefault srvCfg.user;
+ Group = mkDefault srvCfg.group;
+ RuntimeDirectory = [
+ "sourcehut/${serviceName}"
+ # Used by *srht-keys which reads ../config.ini
+ "sourcehut/${serviceName}/subdir"
+ "sourcehut/chroots/${serviceName}"
+ ];
+ RuntimeDirectoryMode = "2750";
+ # No need for the chroot path once inside the chroot
+ InaccessiblePaths = [ "-+${rootDir}" ];
+ # g+rx is for group members (eg. fcgiwrap or nginx)
+ # to read Git/Mercurial repositories, buildlogs, etc.
+ # o+x is for intermediate directories created by BindPaths= and like,
+ # as they're owned by root:root.
+ UMask = "0026";
+ RootDirectory = rootDir;
+ RootDirectoryStartOnly = true;
+ PrivateTmp = true;
+ MountAPIVFS = true;
+ # config.ini is looked up in there, before /etc/srht/config.ini
+ # Note that it fails to be set in ExecStartPre=
+ WorkingDirectory = mkDefault ("-"+runDir);
+ BindReadOnlyPaths = [
+ builtins.storeDir
+ "/etc"
+ "/run/booted-system"
+ "/run/current-system"
+ "/run/systemd"
+ ] ++
+ optional cfg.postgresql.enable "/run/postgresql" ++
+ optional cfg.redis.enable "/run/redis-sourcehut-${srvsrht}";
+ # LoadCredential= are unfortunately not available in ExecStartPre=
+ # Hence this one is run as root (the +) with RootDirectoryStartOnly=
+ # to reach credentials wherever they are.
+ # Note that each systemd service gets its own ${runDir}/config.ini file.
+ ExecStartPre = mkBefore [("+"+pkgs.writeShellScript "${serviceName}-credentials" ''
+ set -x
+ # Replace values begining with a '<' by the content of the file whose name is after.
+ gawk '{ if (match($0,/^([^=]+=)<(.+)/,m)) { getline f < m[2]; print m[1] f } else print $0 }' ${configIni} |
+ ${optionalString (!allowStripe) "gawk '!/^stripe-secret-key=/' |"}
+ install -o ${srvCfg.user} -g root -m 400 /dev/stdin ${runDir}/config.ini
+ '')];
+ # The following options are only for optimizing:
+ # systemd-analyze security
+ AmbientCapabilities = "";
+ CapabilityBoundingSet = "";
+ # ProtectClock= adds DeviceAllow=char-rtc r
+ DeviceAllow = "";
+ LockPersonality = true;
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ PrivateDevices = true;
+ PrivateMounts = true;
+ PrivateNetwork = mkDefault false;
+ PrivateUsers = true;
+ ProcSubset = "pid";
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ ProtectSystem = "strict";
+ RemoveIPC = true;
+ RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ #SocketBindAllow = [ "tcp:${toString srvCfg.port}" "tcp:${toString srvCfg.prometheusPort}" ];
+ #SocketBindDeny = "any";
+ SystemCallFilter = [
+ "@system-service"
+ "~@aio" "~@keyring" "~@memlock" "~@privileged" "~@resources" "~@timer"
+ "@chown" "@setuid"
+ ];
+ SystemCallArchitectures = "native";
+ };
+ } ];
in
-with serviceCfg; with lib; recursiveUpdate
{
- environment.HOME = statePath;
- path = [ config.services.postgresql.package ] ++ (attrs.path or [ ]);
- restartTriggers = [ config.environment.etc."sr.ht/config.ini".source ];
- serviceConfig = {
- Type = "simple";
- User = user;
- Group = user;
- Restart = "always";
- WorkingDirectory = statePath;
- } // (if (cfg.statePath == "/var/lib/sourcehut/${serviceDrv.pname}") then {
- StateDirectory = [ "sourcehut/${serviceDrv.pname}" ];
- } else {})
- ;
+ options.services.sourcehut.${srv} = {
+ enable = mkEnableOption "${srv} service";
- preStart = ''
- if ! test -e ${statePath}/db; then
- # Setup the initial database
- ${setupDB}
+ user = mkOption {
+ type = types.str;
+ default = srvsrht;
+ description = ''
+ User for ${srv}.sr.ht.
+ '';
+ };
- # Set the initial state of the database for future database upgrades
- if test -e ${cfg.python}/bin/${serviceDrv.pname}-migrate; then
- # Run alembic stamp head once to tell alembic the schema is up-to-date
- ${cfg.python}/bin/${serviceDrv.pname}-migrate stamp head
- fi
+ group = mkOption {
+ type = types.str;
+ default = srvsrht;
+ description = ''
+ Group for ${srv}.sr.ht.
+ Membership grants access to the Git/Mercurial repositories by default,
+ but not to the config.ini file (where secrets are).
+ '';
+ };
- printf "%s" "${serviceDrv.version}" > ${statePath}/db
- fi
+ port = mkOption {
+ type = types.port;
+ default = port;
+ description = ''
+ Port on which the "${srv}" backend should listen.
+ '';
+ };
- # Update copy of each users' profile to the latest
- # See https://lists.sr.ht/~sircmpwn/sr.ht-admins/<20190302181207.GA13778%40cirno.my.domain>
- if ! test -e ${statePath}/webhook; then
- # Update ${iniKey}'s users' profile copy to the latest
- ${cfg.python}/bin/srht-update-profiles ${iniKey}
+ redis = {
+ host = mkOption {
+ type = types.str;
+ default = "unix:/run/redis-sourcehut-${srvsrht}/redis.sock?db=0";
+ example = "redis://shared.wireguard:6379/0";
+ description = ''
+ The redis host URL. This is used for caching and temporary storage, and must
+ be shared between nodes (e.g. git1.sr.ht and git2.sr.ht), but need not be
+ shared between services. It may be shared between services, however, with no
+ ill effect, if this better suits your infrastructure.
+ '';
+ };
+ };
- touch ${statePath}/webhook
- fi
+ postgresql = {
+ database = mkOption {
+ type = types.str;
+ default = "${srv}.sr.ht";
+ description = ''
+ PostgreSQL database name for the ${srv}.sr.ht service,
+ used if is true.
+ '';
+ };
+ };
- ${optionalString (builtins.hasAttr "migrate-on-upgrade" cfgIni && cfgIni.migrate-on-upgrade == "yes") ''
- if [ "$(cat ${statePath}/db)" != "${serviceDrv.version}" ]; then
- # Manage schema migrations using alembic
- ${cfg.python}/bin/${serviceDrv.pname}-migrate -a upgrade head
+ gunicorn = {
+ extraArgs = mkOption {
+ type = with types; listOf str;
+ default = ["--timeout 120" "--workers 1" "--log-level=info"];
+ description = "Extra arguments passed to Gunicorn.";
+ };
+ };
+ } // optionalAttrs webhooks {
+ webhooks = {
+ extraArgs = mkOption {
+ type = with types; listOf str;
+ default = ["--loglevel DEBUG" "--pool eventlet" "--without-heartbeat"];
+ description = "Extra arguments passed to the Celery responsible for webhooks.";
+ };
+ celeryConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Content of the celeryconfig.py used by the Celery responsible for webhooks.";
+ };
+ };
+ };
- # Mark down current package version
- printf "%s" "${serviceDrv.version}" > ${statePath}/db
- fi
- ''}
+ config = lib.mkIf (cfg.enable && srvCfg.enable) (mkMerge [ extraConfig {
+ users = {
+ users = {
+ "${srvCfg.user}" = {
+ isSystemUser = true;
+ group = mkDefault srvCfg.group;
+ description = mkDefault "sourcehut user for ${srv}.sr.ht";
+ };
+ };
+ groups = {
+ "${srvCfg.group}" = { };
+ } // optionalAttrs (cfg.postgresql.enable
+ && hasSuffix "0" (postgresql.settings.unix_socket_permissions or "")) {
+ "postgres".members = [ srvCfg.user ];
+ } // optionalAttrs (cfg.redis.enable
+ && hasSuffix "0" (redis.settings.unixsocketperm or "")) {
+ "redis-sourcehut-${srvsrht}".members = [ srvCfg.user ];
+ };
+ };
- ${attrs.preStart or ""}
- '';
+ services.nginx = mkIf cfg.nginx.enable {
+ virtualHosts."${srv}.${cfg.settings."sr.ht".global-domain}" = mkMerge [ {
+ forceSSL = true;
+ locations."/".proxyPass = "http://${cfg.listenAddress}:${toString srvCfg.port}";
+ locations."/static" = {
+ root = "${pkgs.sourcehut.${srvsrht}}/${pkgs.sourcehut.python.sitePackages}/${srvsrht}";
+ extraConfig = mkDefault ''
+ expires 30d;
+ '';
+ };
+ } cfg.nginx.virtualHost ];
+ };
+
+ services.postgresql = mkIf cfg.postgresql.enable {
+ authentication = ''
+ local ${srvCfg.postgresql.database} ${srvCfg.user} trust
+ '';
+ ensureDatabases = [ srvCfg.postgresql.database ];
+ ensureUsers = map (name: {
+ inherit name;
+ ensurePermissions = { "DATABASE \"${srvCfg.postgresql.database}\"" = "ALL PRIVILEGES"; };
+ }) [srvCfg.user];
+ };
+
+ services.sourcehut.services = mkDefault (filter (s: cfg.${s}.enable)
+ [ "builds" "dispatch" "git" "hg" "hub" "lists" "man" "meta" "pages" "paste" "todo" ]);
+
+ services.sourcehut.settings = mkMerge [
+ {
+ "${srv}.sr.ht".origin = mkDefault "https://${srv}.${cfg.settings."sr.ht".global-domain}";
+ }
+
+ (mkIf cfg.postgresql.enable {
+ "${srv}.sr.ht".connection-string = mkDefault "postgresql:///${srvCfg.postgresql.database}?user=${srvCfg.user}&host=/run/postgresql";
+ })
+ ];
+
+ services.redis.servers."sourcehut-${srvsrht}" = mkIf cfg.redis.enable {
+ enable = true;
+ databases = 3;
+ syslog = true;
+ # TODO: set a more informed value
+ save = mkDefault [ [1800 10] [300 100] ];
+ settings = {
+ # TODO: set a more informed value
+ maxmemory = "128MB";
+ maxmemory-policy = "volatile-ttl";
+ };
+ };
+
+ systemd.services = mkMerge [
+ {
+ "${srvsrht}" = baseService srvsrht { allowStripe = srv == "meta"; } (mkMerge [
+ {
+ description = "sourcehut ${srv}.sr.ht website service";
+ before = optional cfg.nginx.enable "nginx.service";
+ wants = optional cfg.nginx.enable "nginx.service";
+ wantedBy = [ "multi-user.target" ];
+ path = optional cfg.postgresql.enable postgresql.package;
+ # Beware: change in credentials' content will not trigger restart.
+ restartTriggers = [ configIni ];
+ serviceConfig = {
+ Type = "simple";
+ Restart = mkDefault "always";
+ #RestartSec = mkDefault "2min";
+ StateDirectory = [ "sourcehut/${srvsrht}" ];
+ StateDirectoryMode = "2750";
+ ExecStart = "${cfg.python}/bin/gunicorn ${srvsrht}.app:app --name ${srvsrht} --bind ${cfg.listenAddress}:${toString srvCfg.port} " + concatStringsSep " " srvCfg.gunicorn.extraArgs;
+ };
+ preStart = let
+ version = pkgs.sourcehut.${srvsrht}.version;
+ stateDir = "/var/lib/sourcehut/${srvsrht}";
+ in mkBefore ''
+ set -x
+ # Use the /run/sourcehut/${srvsrht}/config.ini
+ # installed by a previous ExecStartPre= in baseService
+ cd /run/sourcehut/${srvsrht}
+
+ if test ! -e ${stateDir}/db; then
+ # Setup the initial database.
+ # Note that it stamps the alembic head afterward
+ ${cfg.python}/bin/${srvsrht}-initdb
+ echo ${version} >${stateDir}/db
+ fi
+
+ ${optionalString cfg.settings.${iniKey}.migrate-on-upgrade ''
+ if [ "$(cat ${stateDir}/db)" != "${version}" ]; then
+ # Manage schema migrations using alembic
+ ${cfg.python}/bin/${srvsrht}-migrate -a upgrade head
+ echo ${version} >${stateDir}/db
+ fi
+ ''}
+
+ # Update copy of each users' profile to the latest
+ # See https://lists.sr.ht/~sircmpwn/sr.ht-admins/<20190302181207.GA13778%40cirno.my.domain>
+ if test ! -e ${stateDir}/webhook; then
+ # Update ${iniKey}'s users' profile copy to the latest
+ ${cfg.python}/bin/srht-update-profiles ${iniKey}
+ touch ${stateDir}/webhook
+ fi
+ '';
+ } mainService ]);
+ }
+
+ (mkIf webhooks {
+ "${srvsrht}-webhooks" = baseService "${srvsrht}-webhooks" {}
+ {
+ description = "sourcehut ${srv}.sr.ht webhooks service";
+ after = [ "${srvsrht}.service" ];
+ wantedBy = [ "${srvsrht}.service" ];
+ partOf = [ "${srvsrht}.service" ];
+ preStart = ''
+ cp ${pkgs.writeText "${srvsrht}-webhooks-celeryconfig.py" srvCfg.webhooks.celeryConfig} \
+ /run/sourcehut/${srvsrht}-webhooks/celeryconfig.py
+ '';
+ serviceConfig = {
+ Type = "simple";
+ Restart = "always";
+ ExecStart = "${cfg.python}/bin/celery --app ${srvsrht}.webhooks worker --hostname ${srvsrht}-webhooks@%%h " + concatStringsSep " " srvCfg.webhooks.extraArgs;
+ # Avoid crashing: os.getloadavg()
+ ProcSubset = mkForce "all";
+ };
+ };
+ })
+
+ (mapAttrs (timerName: timer: (baseService timerName {} (mkMerge [
+ {
+ description = "sourcehut ${timerName} service";
+ after = [ "network.target" "${srvsrht}.service" ];
+ serviceConfig = {
+ Type = "oneshot";
+ ExecStart = "${cfg.python}/bin/${timerName}";
+ };
+ }
+ (timer.service or {})
+ ]))) extraTimers)
+
+ (mapAttrs (serviceName: extraService: baseService serviceName {} (mkMerge [
+ {
+ description = "sourcehut ${serviceName} service";
+ # So that extraServices have the PostgreSQL database initialized.
+ after = [ "${srvsrht}.service" ];
+ wantedBy = [ "${srvsrht}.service" ];
+ partOf = [ "${srvsrht}.service" ];
+ serviceConfig = {
+ Type = "simple";
+ Restart = mkDefault "always";
+ };
+ }
+ extraService
+ ])) extraServices)
+ ];
+
+ systemd.timers = mapAttrs (timerName: timer:
+ {
+ description = "sourcehut timer for ${timerName}";
+ wantedBy = [ "timers.target" ];
+ inherit (timer) timerConfig;
+ }) extraTimers;
+ } ]);
}
- (builtins.removeAttrs attrs [ "path" "preStart" ])
diff --git a/nixos/modules/services/misc/sourcehut/sourcehut.xml b/nixos/modules/services/misc/sourcehut/sourcehut.xml
index ab9a8c6cb4b..41094f65a94 100644
--- a/nixos/modules/services/misc/sourcehut/sourcehut.xml
+++ b/nixos/modules/services/misc/sourcehut/sourcehut.xml
@@ -14,13 +14,12 @@
Basic usage
Sourcehut is a Python and Go based set of applications.
- services.sourcehut
- by default will use
+ This NixOS module also provides basic configuration integrating Sourcehut into locally running
services.nginx,
- services.redis,
- services.cron,
+ services.redis.servers.sourcehut,
+ services.postfix
and
- services.postgresql.
+ services.postgresql services.
@@ -42,18 +41,23 @@ in {
services.sourcehut = {
enable = true;
- originBase = fqdn;
- services = [ "meta" "man" "git" ];
+ git.enable = true;
+ man.enable = true;
+ meta.enable = true;
+ nginx.enable = true;
+ postfix.enable = true;
+ postgresql.enable = true;
+ redis.enable = true;
settings = {
"sr.ht" = {
environment = "production";
global-domain = fqdn;
origin = "https://${fqdn}";
# Produce keys with srht-keygen from sourcehut.coresrht.
- network-key = "SECRET";
- service-key = "SECRET";
+ network-key = "/run/keys/path/to/network-key";
+ service-key = "/run/keys/path/to/service-key";
};
- webhooks.private-key= "SECRET";
+ webhooks.private-key= "/run/keys/path/to/webhook-key";
};
};
diff --git a/nixos/modules/services/misc/sourcehut/todo.nix b/nixos/modules/services/misc/sourcehut/todo.nix
deleted file mode 100644
index aec773b0669..00000000000
--- a/nixos/modules/services/misc/sourcehut/todo.nix
+++ /dev/null
@@ -1,161 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
- cfg = config.services.sourcehut;
- cfgIni = cfg.settings;
- scfg = cfg.todo;
- iniKey = "todo.sr.ht";
-
- rcfg = config.services.redis;
- drv = pkgs.sourcehut.todosrht;
-in
-{
- options.services.sourcehut.todo = {
- user = mkOption {
- type = types.str;
- default = "todosrht";
- description = ''
- User for todo.sr.ht.
- '';
- };
-
- port = mkOption {
- type = types.port;
- default = 5003;
- description = ''
- Port on which the "todo" module should listen.
- '';
- };
-
- database = mkOption {
- type = types.str;
- default = "todo.sr.ht";
- description = ''
- PostgreSQL database name for todo.sr.ht.
- '';
- };
-
- statePath = mkOption {
- type = types.path;
- default = "${cfg.statePath}/todosrht";
- description = ''
- State path for todo.sr.ht.
- '';
- };
- };
-
- config = with scfg; lib.mkIf (cfg.enable && elem "todo" cfg.services) {
- users = {
- users = {
- "${user}" = {
- isSystemUser = true;
- group = user;
- extraGroups = [ "postfix" ];
- description = "todo.sr.ht user";
- };
- };
- groups = {
- "${user}" = { };
- };
- };
-
- services.postgresql = {
- authentication = ''
- local ${database} ${user} trust
- '';
- ensureDatabases = [ database ];
- ensureUsers = [
- {
- name = user;
- ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
- }
- ];
- };
-
- systemd = {
- tmpfiles.rules = [
- "d ${statePath} 0750 ${user} ${user} -"
- ];
-
- services = {
- todosrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "todo.sr.ht website service";
-
- serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
- };
-
- todosrht-lmtp = {
- after = [ "postgresql.service" "network.target" ];
- bindsTo = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "todo.sr.ht process service";
- serviceConfig = {
- Type = "simple";
- User = user;
- Restart = "always";
- ExecStart = "${cfg.python}/bin/todosrht-lmtp";
- };
- };
-
- todosrht-webhooks = {
- after = [ "postgresql.service" "network.target" ];
- requires = [ "postgresql.service" ];
- wantedBy = [ "multi-user.target" ];
-
- description = "todo.sr.ht webhooks service";
- serviceConfig = {
- Type = "simple";
- User = user;
- Restart = "always";
- ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
- };
-
- };
- };
- };
-
- services.sourcehut.settings = {
- # URL todo.sr.ht is being served at (protocol://domain)
- "todo.sr.ht".origin = mkDefault "http://todo.${cfg.originBase}";
- # Address and port to bind the debug server to
- "todo.sr.ht".debug-host = mkDefault "0.0.0.0";
- "todo.sr.ht".debug-port = mkDefault port;
- # Configures the SQLAlchemy connection string for the database.
- "todo.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
- # Set to "yes" to automatically run migrations on package upgrade.
- "todo.sr.ht".migrate-on-upgrade = mkDefault "yes";
- # todo.sr.ht's OAuth client ID and secret for meta.sr.ht
- # Register your client at meta.example.org/oauth
- "todo.sr.ht".oauth-client-id = mkDefault null;
- "todo.sr.ht".oauth-client-secret = mkDefault null;
- # Outgoing email for notifications generated by users
- "todo.sr.ht".notify-from = mkDefault "CHANGEME@example.org";
- # The redis connection used for the webhooks worker
- "todo.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/1";
- # Network-key
- "todo.sr.ht".network-key = mkDefault null;
-
- # Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
- # Alternatively, specify IP:PORT and an SMTP server will be run instead.
- "todo.sr.ht::mail".sock = mkDefault "/tmp/todo.sr.ht-lmtp.sock";
- # The lmtp daemon will make the unix socket group-read/write for users in this
- # group.
- "todo.sr.ht::mail".sock-group = mkDefault "postfix";
-
- "todo.sr.ht::mail".posting-domain = mkDefault "todo.${cfg.originBase}";
- };
-
- services.nginx.virtualHosts."todo.${cfg.originBase}" = {
- forceSSL = true;
- locations."/".proxyPass = "http://${cfg.address}:${toString port}";
- locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
- locations."/static".root = "${pkgs.sourcehut.todosrht}/${pkgs.sourcehut.python.sitePackages}/todosrht";
- };
- };
-}
diff --git a/pkgs/applications/version-management/sourcehut/builds.nix b/pkgs/applications/version-management/sourcehut/builds.nix
index c8163caf8ea..374736e80f5 100644
--- a/pkgs/applications/version-management/sourcehut/builds.nix
+++ b/pkgs/applications/version-management/sourcehut/builds.nix
@@ -11,13 +11,13 @@
, python
}:
let
- version = "0.66.7";
+ version = "0.71.0";
buildWorker = src: buildGoModule {
inherit src version;
pname = "builds-sr-ht-worker";
- vendorSha256 = "sha256-giOaldV46aBqXyFH/cQVsbUr6Rb4VMhbBO86o48tRZY=";
+ vendorSha256 = "sha256-ZEarWM/33t+pNXUEIpfd/DkBkhu3UUg17Hh8XXWOepA=";
};
in
buildPythonPackage rec {
@@ -28,7 +28,7 @@ buildPythonPackage rec {
owner = "~sircmpwn";
repo = "builds.sr.ht";
rev = version;
- sha256 = "sha256-2MLs/DOXHjEYarXDVUcPZe3o0fmZbzVxn528SE72lhM=";
+ sha256 = "sha256-S3mMndUdVGi+YxAOI3wSNlSZrH3cwumxatXpErS2yQI=";
};
nativeBuildInputs = srht.nativeBuildInputs;
@@ -56,10 +56,12 @@ buildPythonPackage rec {
cp ${buildWorker "${src}/worker"}/bin/worker $out/bin/builds.sr.ht-worker
'';
+ pythonImportsCheck = [ "buildsrht" ];
+
meta = with lib; {
homepage = "https://git.sr.ht/~sircmpwn/builds.sr.ht";
description = "Continuous integration service for the sr.ht network";
- license = licenses.agpl3;
+ license = licenses.agpl3Only;
maintainers = with maintainers; [ eadwu ];
};
}
diff --git a/pkgs/applications/version-management/sourcehut/core.nix b/pkgs/applications/version-management/sourcehut/core.nix
index d359d524eb2..939d9d7b750 100644
--- a/pkgs/applications/version-management/sourcehut/core.nix
+++ b/pkgs/applications/version-management/sourcehut/core.nix
@@ -25,17 +25,16 @@
, sassc
, nodejs
, redis
-, writeText
}:
buildPythonPackage rec {
pname = "srht";
- version = "0.67.4";
+ version = "0.67.18";
src = fetchgit {
url = "https://git.sr.ht/~sircmpwn/core.sr.ht";
rev = version;
- sha256 = "sha256-XvzFfcBK5Mq8p7xEBAF/eupUE1kkUBh5k+ByM/WA9bc=";
+ sha256 = "sha256-lPPB1DRWHANGBBfWeu0pUn1xBkd37ZhyreozLlxX6cA=";
fetchSubmodules = true;
};
@@ -46,7 +45,12 @@ buildPythonPackage rec {
};
patches = [
- ./disable-npm-install.patch
+ # Disable check for npm
+ patches/disable-npm-install.patch
+ # Fix broken hack: removing dots from "builds.sr.ht" does not produce "buildsrht"
+ patches/srht-update-profiles/0001-fix-disgusting-hack-in-the-case-of-buildsrht.patch
+ # Add Unix socket support for redis-host=
+ patches/redis-socket/core/v2-0001-add-Unix-socket-support-for-redis-host.patch
];
nativeBuildInputs = [
@@ -87,6 +91,7 @@ buildPythonPackage rec {
'';
dontUseSetuptoolsCheck = true;
+ pythonImportsCheck = [ "srht" ];
meta = with lib; {
homepage = "https://git.sr.ht/~sircmpwn/srht";
diff --git a/pkgs/applications/version-management/sourcehut/default.nix b/pkgs/applications/version-management/sourcehut/default.nix
index 401a1437b7d..7dde841b543 100644
--- a/pkgs/applications/version-management/sourcehut/default.nix
+++ b/pkgs/applications/version-management/sourcehut/default.nix
@@ -22,10 +22,12 @@ let
listssrht = self.callPackage ./lists.nix { };
mansrht = self.callPackage ./man.nix { };
metasrht = self.callPackage ./meta.nix { };
+ pagessrht = self.callPackage ./pages.nix { };
pastesrht = self.callPackage ./paste.nix { };
todosrht = self.callPackage ./todo.nix { };
scmsrht = self.callPackage ./scm.nix { };
+ srht-keys = self.scmsrht.srht-keys;
};
};
in
@@ -40,6 +42,8 @@ with python.pkgs; recurseIntoAttrs {
listssrht = toPythonApplication listssrht;
mansrht = toPythonApplication mansrht;
metasrht = toPythonApplication metasrht;
+ pagessrht = pagessrht;
pastesrht = toPythonApplication pastesrht;
todosrht = toPythonApplication todosrht;
+ srht-keys = scmsrht.srht-keys;
}
diff --git a/pkgs/applications/version-management/sourcehut/dispatch.nix b/pkgs/applications/version-management/sourcehut/dispatch.nix
index 637c6f9c1df..9456d0c998c 100644
--- a/pkgs/applications/version-management/sourcehut/dispatch.nix
+++ b/pkgs/applications/version-management/sourcehut/dispatch.nix
@@ -9,13 +9,13 @@
buildPythonPackage rec {
pname = "dispatchsrht";
- version = "0.15.8";
+ version = "0.15.32";
src = fetchFromSourcehut {
owner = "~sircmpwn";
repo = "dispatch.sr.ht";
rev = version;
- sha256 = "sha256-zWCGPjIgMKHXHJUs9aciV7IFgo0rpahon6KXHDwcfss=";
+ sha256 = "sha256-4P4cXhjcZ8IBzpRfmYIJkzl9U4Plo36a48Pf/KjmhFY=";
};
nativeBuildInputs = srht.nativeBuildInputs;
@@ -31,10 +31,12 @@ buildPythonPackage rec {
export SRHT_PATH=${srht}/${python.sitePackages}/srht
'';
+ pythonImportsCheck = [ "dispatchsrht" ];
+
meta = with lib; {
homepage = "https://dispatch.sr.ht/~sircmpwn/dispatch.sr.ht";
description = "Task dispatcher and service integration tool for the sr.ht network";
- license = licenses.agpl3;
+ license = licenses.agpl3Only;
maintainers = with maintainers; [ eadwu ];
};
}
diff --git a/pkgs/applications/version-management/sourcehut/git.nix b/pkgs/applications/version-management/sourcehut/git.nix
index e44fb9cd6c6..0a09530b14b 100644
--- a/pkgs/applications/version-management/sourcehut/git.nix
+++ b/pkgs/applications/version-management/sourcehut/git.nix
@@ -6,42 +6,109 @@
, srht
, pygit2
, scmsrht
+, srht-keys
}:
let
- version = "0.72.8";
+ version = "0.72.44";
src = fetchFromSourcehut {
owner = "~sircmpwn";
repo = "git.sr.ht";
rev = version;
- sha256 = "sha256-AB2uzajO5PtcpJfbOOTfuDFM6is5K39v3AZJ1hShRNc=";
+ sha256 = "sha256-U+hJiQpAJIHBC//M/Lfw82DLhZF46qHhz8zZW1ZvJoo=";
};
- buildShell = src: buildGoModule {
+ gitsrht-shell = buildGoModule {
inherit src version;
+ sourceRoot = "source/gitsrht-shell";
pname = "gitsrht-shell";
vendorSha256 = "sha256-aqUFICp0C2reqb2p6JCPAUIRsxzSv0t9BHoNWrTYfqk=";
};
- buildDispatcher = src: buildGoModule {
+ gitsrht-dispatch = buildGoModule {
inherit src version;
+ sourceRoot = "source/gitsrht-dispatch";
pname = "gitsrht-dispatcher";
vendorSha256 = "sha256-qWXPHo86s6iuRBhRMtmD5jxnAWKdrWHtA/iSUkdw89M=";
+ patches = [
+ # Add support for supplementary groups
+ patches/redis-socket/git/v2-0003-gitsrht-dispatch-add-support-for-supplementary-gr.patch
+ ];
+ patchFlags = "-p2";
};
- buildKeys = src: buildGoModule {
+ gitsrht-keys = buildGoModule rec {
inherit src version;
+ sourceRoot = "source/gitsrht-keys";
pname = "gitsrht-keys";
- vendorSha256 = "1d94cqy7x0q0agwg515xxsbl70b3qrzxbzsyjhn1pbyj532brn7f";
+ vendorSha256 = "sha256-0Rnyo4IRQFhM4LFi0499+xJaboMiKEYOgoR5BumzRE8=";
+
+ # What follows is only to update go-redis
+ # go.{mod,sum} could be patched directly but that would be less resilient
+ # to changes from upstream, and thus harder to maintain the patching
+ # while it hasn't been merged upstream.
+
+ overrideModAttrs = old: {
+ inherit patches patchFlags;
+ preBuild = ''
+ # This is a fixed-output derivation so it is not allowed to reference other derivations,
+ # but here srht-keys will be copied to vendor/ by go mod vendor
+ ln -s ${srht-keys} srht-keys
+ go mod edit -replace git.sr.ht/~sircmpwn/scm.sr.ht/srht-keys=$PWD/srht-keys
+ go get github.com/go-redis/redis/v8
+ go get github.com/go-redis/redis@none
+ go mod tidy
+ '';
+ # Pass updated go.{mod,sum} from go-modules to gitsrht-keys' vendor/go.{mod,sum}
+ postInstall = ''
+ cp --reflink=auto go.* $out/
+ '';
+ };
+
+ patches = [
+ # Update go-redis to support Unix sockets
+ patches/redis-socket/git/v2-0001-gitsrht-keys-update-go-redis-to-support-Unix-sock.patch
+ ];
+ patchFlags = "-p2";
+ postConfigure = ''
+ cp -v vendor/go.{mod,sum} .
+ '';
};
- buildUpdateHook = src: buildGoModule {
+ gitsrht-update-hook = buildGoModule rec {
inherit src version;
+ sourceRoot = "source/gitsrht-update-hook";
pname = "gitsrht-update-hook";
- vendorSha256 = "0fwzqpjv8x5y3w3bfjd0x0cvqjjak23m0zj88hf32jpw49xmjkih";
- };
+ vendorSha256 = "sha256-UoHxGVYEgTDqFzVQ2Dv6BRT4jVt+/QpNqEH3G2UWFjs=";
- updateHook = buildUpdateHook "${src}/gitsrht-update-hook";
+ # What follows is only to update go-redis
+
+ overrideModAttrs = old: {
+ inherit patches patchFlags;
+ preBuild = ''
+ # This is a fixed-output derivation so it is not allowed to reference other derivations,
+ # but here srht-keys will be copied to vendor/ by go mod vendor
+ ln -s ${srht-keys} srht-keys
+ go mod edit -replace git.sr.ht/~sircmpwn/scm.sr.ht/srht-keys=$PWD/srht-keys
+ go get github.com/go-redis/redis/v8
+ go get github.com/go-redis/redis@none
+ go mod tidy
+ '';
+ # Pass updated go.{mod,sum} from go-modules to gitsrht-keys' vendor/go.{mod,sum}
+ postInstall = ''
+ cp --reflink=auto go.* $out/
+ '';
+ };
+
+ patches = [
+ # Update go-redis to support Unix sockets
+ patches/redis-socket/git/v2-0002-gitsrht-update-hook-update-go-redis-to-support-Un.patch
+ ];
+ patchFlags = "-p2";
+ postConfigure = ''
+ cp -v vendor/go.{mod,sum} .
+ '';
+ };
in
buildPythonPackage rec {
@@ -63,19 +130,21 @@ buildPythonPackage rec {
postInstall = ''
mkdir -p $out/bin
- cp ${buildShell "${src}/gitsrht-shell"}/bin/gitsrht-shell $out/bin/gitsrht-shell
- cp ${buildDispatcher "${src}/gitsrht-dispatch"}/bin/gitsrht-dispatch $out/bin/gitsrht-dispatch
- cp ${buildKeys "${src}/gitsrht-keys"}/bin/gitsrht-keys $out/bin/gitsrht-keys
- cp ${updateHook}/bin/gitsrht-update-hook $out/bin/gitsrht-update-hook
+ cp ${gitsrht-shell}/bin/gitsrht-shell $out/bin/gitsrht-shell
+ cp ${gitsrht-dispatch}/bin/gitsrht-dispatch $out/bin/gitsrht-dispatch
+ cp ${gitsrht-keys}/bin/gitsrht-keys $out/bin/gitsrht-keys
+ cp ${gitsrht-update-hook}/bin/gitsrht-update-hook $out/bin/gitsrht-update-hook
'';
passthru = {
- inherit updateHook;
+ inherit gitsrht-shell gitsrht-dispatch gitsrht-keys gitsrht-update-hook;
};
+ pythonImportsCheck = [ "gitsrht" ];
+
meta = with lib; {
homepage = "https://git.sr.ht/~sircmpwn/git.sr.ht";
description = "Git repository hosting service for the sr.ht network";
- license = licenses.agpl3;
+ license = licenses.agpl3Only;
maintainers = with maintainers; [ eadwu ];
};
}
diff --git a/pkgs/applications/version-management/sourcehut/hg.nix b/pkgs/applications/version-management/sourcehut/hg.nix
index cddb76cabf2..1d6062d81cc 100644
--- a/pkgs/applications/version-management/sourcehut/hg.nix
+++ b/pkgs/applications/version-management/sourcehut/hg.nix
@@ -10,12 +10,12 @@
buildPythonPackage rec {
pname = "hgsrht";
- version = "0.27.4";
+ version = "0.27.6";
src = fetchhg {
url = "https://hg.sr.ht/~sircmpwn/hg.sr.ht";
rev = version;
- sha256 = "1c0qfi0gmbfngvds6917fy9ii2iglawn429757rh7b4bvzn7n6mr";
+ sha256 = "ibijvKjS4CiWTYrO6Qdh3RkD0EUE7BY8wjdPwrD6vkA=";
};
nativeBuildInputs = srht.nativeBuildInputs;
@@ -32,10 +32,12 @@ buildPythonPackage rec {
export SRHT_PATH=${srht}/${python.sitePackages}/srht
'';
+ pythonImportsCheck = [ "hgsrht" ];
+
meta = with lib; {
homepage = "https://git.sr.ht/~sircmpwn/hg.sr.ht";
description = "Mercurial repository hosting service for the sr.ht network";
- license = licenses.agpl3;
+ license = licenses.agpl3Only;
maintainers = with maintainers; [ eadwu ];
};
}
diff --git a/pkgs/applications/version-management/sourcehut/hub.nix b/pkgs/applications/version-management/sourcehut/hub.nix
index 17cb3fe4b61..31191cba713 100644
--- a/pkgs/applications/version-management/sourcehut/hub.nix
+++ b/pkgs/applications/version-management/sourcehut/hub.nix
@@ -6,13 +6,13 @@
buildPythonPackage rec {
pname = "hubsrht";
- version = "0.13.1";
+ version = "0.13.8";
src = fetchFromSourcehut {
owner = "~sircmpwn";
repo = "hub.sr.ht";
rev = version;
- sha256 = "sha256-Kqzy4mh5Nn1emzHBco/LVuXro/tW3NX+OYqdEwBSQ/U=";
+ sha256 = "sha256-RsRJxwViEoQLh86o+8kQE5PBlLrOyIFM7hkSGjXhqdg=";
};
nativeBuildInputs = srht.nativeBuildInputs;
@@ -26,11 +26,12 @@ buildPythonPackage rec {
'';
dontUseSetuptoolsCheck = true;
+ pythonImportsCheck = [ "hubsrht" ];
meta = with lib; {
homepage = "https://git.sr.ht/~sircmpwn/hub.sr.ht";
description = "Project hub service for the sr.ht network";
- license = licenses.agpl3;
+ license = licenses.agpl3Only;
maintainers = with maintainers; [ eadwu ];
};
}
diff --git a/pkgs/applications/version-management/sourcehut/lists.nix b/pkgs/applications/version-management/sourcehut/lists.nix
index b419b49f7b5..0882241401a 100644
--- a/pkgs/applications/version-management/sourcehut/lists.nix
+++ b/pkgs/applications/version-management/sourcehut/lists.nix
@@ -12,13 +12,13 @@
buildPythonPackage rec {
pname = "listssrht";
- version = "0.48.19";
+ version = "0.49.3";
src = fetchFromSourcehut {
owner = "~sircmpwn";
repo = "lists.sr.ht";
rev = version;
- sha256 = "sha256-bsakEMyvWaxiE4/SGcAP4mlGG9jkdHfFxpt9H+TJn/8=";
+ sha256 = "sha256-kEzKgB8godIL7hEXMrqaxVte6RJAegjT4keZifXbOq0=";
};
nativeBuildInputs = srht.nativeBuildInputs;
@@ -37,10 +37,12 @@ buildPythonPackage rec {
export SRHT_PATH=${srht}/${python.sitePackages}/srht
'';
+ pythonImportsCheck = [ "listssrht" ];
+
meta = with lib; {
homepage = "https://git.sr.ht/~sircmpwn/lists.sr.ht";
description = "Mailing list service for the sr.ht network";
- license = licenses.agpl3;
+ license = licenses.agpl3Only;
maintainers = with maintainers; [ eadwu ];
};
}
diff --git a/pkgs/applications/version-management/sourcehut/man.nix b/pkgs/applications/version-management/sourcehut/man.nix
index bd331f000a7..47c6bb0ac4f 100644
--- a/pkgs/applications/version-management/sourcehut/man.nix
+++ b/pkgs/applications/version-management/sourcehut/man.nix
@@ -8,13 +8,13 @@
buildPythonPackage rec {
pname = "mansrht";
- version = "0.15.12";
+ version = "0.15.20";
src = fetchFromSourcehut {
owner = "~sircmpwn";
repo = "man.sr.ht";
rev = version;
- sha256 = "sha256-MqH/8K9XRvEg6P7GHE6XXtWnhDP3wT8iGoNaFtYQbio=";
+ sha256 = "sha256-ulwdrVrw2bwdafgc3NrJ1J15evQ5btpHLTaiqsyA58U=";
};
nativeBuildInputs = srht.nativeBuildInputs;
@@ -29,10 +29,12 @@ buildPythonPackage rec {
export SRHT_PATH=${srht}/${python.sitePackages}/srht
'';
+ pythonImportsCheck = [ "mansrht" ];
+
meta = with lib; {
homepage = "https://git.sr.ht/~sircmpwn/man.sr.ht";
description = "Wiki service for the sr.ht network";
- license = licenses.agpl3;
+ license = licenses.agpl3Only;
maintainers = with maintainers; [ eadwu ];
};
}
diff --git a/pkgs/applications/version-management/sourcehut/meta.nix b/pkgs/applications/version-management/sourcehut/meta.nix
index a285d484ed2..dbb0483c5bb 100644
--- a/pkgs/applications/version-management/sourcehut/meta.nix
+++ b/pkgs/applications/version-management/sourcehut/meta.nix
@@ -18,19 +18,19 @@
, python
}:
let
- version = "0.53.14";
+ version = "0.54.4";
src = fetchFromSourcehut {
owner = "~sircmpwn";
repo = "meta.sr.ht";
rev = version;
- sha256 = "sha256-/+r/XLDkcSTW647xPMh5bcJmR2xZNNH74AJ5jemna2k=";
+ sha256 = "sha256-MleyF6aqFWbYtxRdMHXpy7HBgJKL9doBmDcYLLe8bW4=";
};
buildApi = src: buildGoModule {
inherit src version;
pname = "metasrht-api";
- vendorSha256 = "sha256-eZyDrr2VcNMxI++18qUy7LA1Q1YDlWCoRtl00L8lfR4=";
+ vendorSha256 = "sha256-gi+dGQPVzrZI+1s9SSa2M3bdgi8vwpR/ofaLG2ZX4kU=";
};
in
@@ -66,10 +66,12 @@ buildPythonPackage rec {
cp ${buildApi "${src}/api/"}/bin/api $out/bin/metasrht-api
'';
+ pythonImportsCheck = [ "metasrht" ];
+
meta = with lib; {
homepage = "https://git.sr.ht/~sircmpwn/meta.sr.ht";
description = "Account management service for the sr.ht network";
- license = licenses.agpl3;
+ license = licenses.agpl3Only;
maintainers = with maintainers; [ eadwu ];
};
}
diff --git a/pkgs/applications/version-management/sourcehut/pages-fix-syntax-error-in-schema.sql.patch b/pkgs/applications/version-management/sourcehut/pages-fix-syntax-error-in-schema.sql.patch
new file mode 100644
index 00000000000..9b3f6fbc6fb
--- /dev/null
+++ b/pkgs/applications/version-management/sourcehut/pages-fix-syntax-error-in-schema.sql.patch
@@ -0,0 +1,27 @@
+From 3df160ad289b25574322f587095d00d6641f057c Mon Sep 17 00:00:00 2001
+From: Juan Picca
+Date: Wed, 21 Jul 2021 08:26:56 -0300
+Subject: [PATCH] Fix syntax error in schema.sql
+
+---
+ schema.sql | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/schema.sql b/schema.sql
+index 168377f..2e473ea 100644
+--- a/schema.sql
++++ b/schema.sql
+@@ -28,8 +28,8 @@ CREATE TABLE sites (
+ user_id integer NOT NULL references "user"(id),
+ domain varchar NOT NULL,
+ protocol protocol NOT NULL,
+- version varchar NOT NULL
+- UNIQUE (domain, protocol),
++ version varchar NOT NULL,
++ UNIQUE (domain, protocol)
+ );
+
+ COMMIT;
+--
+2.32.0
+
diff --git a/pkgs/applications/version-management/sourcehut/pages.nix b/pkgs/applications/version-management/sourcehut/pages.nix
new file mode 100644
index 00000000000..d04182251a7
--- /dev/null
+++ b/pkgs/applications/version-management/sourcehut/pages.nix
@@ -0,0 +1,34 @@
+{ lib
+, fetchFromSourcehut
+, buildGoModule
+}:
+buildGoModule rec {
+ pname = "pagessrht";
+ version = "0.4.8";
+
+ src = fetchFromSourcehut {
+ owner = "~sircmpwn";
+ repo = "pages.sr.ht";
+ rev = version;
+ sha256 = "sha256-z9w8v5e6LY6VUEczltyD55KEUUH7Gw1vUO00KPmT+D8=";
+ };
+
+ vendorSha256 = "sha256-xOd9i+PNlLxZrw/+z/C9V+AbOLEociW2YHY+x1K+mJI=";
+
+ patches = [
+ # Upstream after 0.4.8
+ ./pages-fix-syntax-error-in-schema.sql.patch
+ ];
+
+ postInstall = ''
+ mkdir -p $out/share/sql/
+ cp -r -t $out/share/sql/ schema.sql migrations
+ '';
+
+ meta = with lib; {
+ homepage = "https://git.sr.ht/~sircmpwn/pages.sr.ht";
+ description = "Web hosting service for the sr.ht network";
+ license = licenses.agpl3Only;
+ maintainers = with maintainers; [ eadwu ];
+ };
+}
diff --git a/pkgs/applications/version-management/sourcehut/paste.nix b/pkgs/applications/version-management/sourcehut/paste.nix
index 0d8c9135493..ecd31c25deb 100644
--- a/pkgs/applications/version-management/sourcehut/paste.nix
+++ b/pkgs/applications/version-management/sourcehut/paste.nix
@@ -8,13 +8,13 @@
buildPythonPackage rec {
pname = "pastesrht";
- version = "0.12.1";
+ version = "0.12.4";
src = fetchFromSourcehut {
owner = "~sircmpwn";
repo = "paste.sr.ht";
rev = version;
- sha256 = "sha256-QQhd2LeH9BLmlHilhsv+9fZ+RPNmEMSmOpFA3dsMBFc=";
+ sha256 = "sha256-hFjWa7L7JiQoG3Hm9NyoP2FNypDiW+nGDmQ2DoZkAIw=";
};
nativeBuildInputs = srht.nativeBuildInputs;
@@ -29,10 +29,12 @@ buildPythonPackage rec {
export SRHT_PATH=${srht}/${python.sitePackages}/srht
'';
+ pythonImportsCheck = [ "pastesrht" ];
+
meta = with lib; {
homepage = "https://git.sr.ht/~sircmpwn/paste.sr.ht";
description = "Ad-hoc text file hosting service for the sr.ht network";
- license = licenses.agpl3;
+ license = licenses.agpl3Only;
maintainers = with maintainers; [ eadwu ];
};
}
diff --git a/pkgs/applications/version-management/sourcehut/disable-npm-install.patch b/pkgs/applications/version-management/sourcehut/patches/disable-npm-install.patch
similarity index 100%
rename from pkgs/applications/version-management/sourcehut/disable-npm-install.patch
rename to pkgs/applications/version-management/sourcehut/patches/disable-npm-install.patch
diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/core/v2-0001-add-Unix-socket-support-for-redis-host.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/core/v2-0001-add-Unix-socket-support-for-redis-host.patch
new file mode 100644
index 00000000000..46241bc847f
--- /dev/null
+++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/core/v2-0001-add-Unix-socket-support-for-redis-host.patch
@@ -0,0 +1,30 @@
+From c0ccc8db051a2f8278edf59b41ed238fa71aa4c0 Mon Sep 17 00:00:00 2001
+From: Julien Moutinho
+Date: Mon, 23 Aug 2021 18:43:18 +0200
+Subject: [PATCH core.sr.ht v2] add Unix socket support for redis-host=
+
+---
+ srht/redis.py | 11 ++---------
+ 1 file changed, 2 insertions(+), 9 deletions(-)
+
+diff --git a/srht/redis.py b/srht/redis.py
+index 8a9347c..2e91c35 100644
+--- a/srht/redis.py
++++ b/srht/redis.py
+@@ -1,11 +1,4 @@
+-from redis import Redis
++from redis import from_url
+ from srht.config import cfg
+-from urllib.parse import urlparse
+
+-url = cfg("sr.ht", "redis-host", "redis://localhost")
+-url = urlparse(url)
+-
+-redis = Redis(host=url.hostname,
+- port=(url.port or 6379),
+- password=url.password,
+- db=int(url.path[1:]) if url.path else 0)
++redis = from_url(cfg("sr.ht", "redis-host", "redis://localhost"))
+--
+2.32.0
+
diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v2-0001-gitsrht-keys-update-go-redis-to-support-Unix-sock.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v2-0001-gitsrht-keys-update-go-redis-to-support-Unix-sock.patch
new file mode 100644
index 00000000000..24fbc26399c
--- /dev/null
+++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v2-0001-gitsrht-keys-update-go-redis-to-support-Unix-sock.patch
@@ -0,0 +1,40 @@
+From 466528eabef3123c715420472dc2cc15e8807bdf Mon Sep 17 00:00:00 2001
+From: Julien Moutinho
+Date: Fri, 27 Aug 2021 15:38:28 +0200
+Subject: [PATCH git.sr.ht v2 1/5] gitsrht-keys: update go-redis to support
+ Unix sockets
+
+---
+ gitsrht-keys/main.go | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/gitsrht-keys/main.go b/gitsrht-keys/main.go
+index 0c1aea1..b4278c3 100644
+--- a/gitsrht-keys/main.go
++++ b/gitsrht-keys/main.go
+@@ -5,7 +5,7 @@ import (
+ "os"
+ "path"
+
+- goredis "github.com/go-redis/redis"
++ goRedis "github.com/go-redis/redis/v8"
+ "github.com/vaughan0/go-ini"
+ "git.sr.ht/~sircmpwn/scm.sr.ht/srht-keys"
+ )
+@@ -53,11 +53,11 @@ func main() {
+ if redisHost == "" {
+ redisHost = "redis://localhost:6379"
+ }
+- ropts, err := goredis.ParseURL(redisHost)
++ ropts, err := goRedis.ParseURL(redisHost)
+ if err != nil {
+ logger.Fatalf("Failed to parse redis host: %v", err)
+ }
+- redis := goredis.NewClient(ropts)
++ redis := goRedis.NewClient(ropts)
+
+ keyType, b64key, prefix, err = srhtkeys.ParseArgs(logger)
+ if err != nil {
+--
+2.32.0
+
diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v2-0002-gitsrht-update-hook-update-go-redis-to-support-Un.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v2-0002-gitsrht-update-hook-update-go-redis-to-support-Un.patch
new file mode 100644
index 00000000000..36566a6f7d9
--- /dev/null
+++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v2-0002-gitsrht-update-hook-update-go-redis-to-support-Un.patch
@@ -0,0 +1,139 @@
+From 4f947b26e42d3bcab6d675718eed28ca2fdf4762 Mon Sep 17 00:00:00 2001
+From: Julien Moutinho
+Date: Fri, 27 Aug 2021 15:39:29 +0200
+Subject: [PATCH git.sr.ht v2 2/5] gitsrht-update-hook: update go-redis to
+ support Unix sockets
+
+---
+ gitsrht-update-hook/options.go | 16 +++++++++-------
+ gitsrht-update-hook/post-update.go | 8 ++++----
+ gitsrht-update-hook/update.go | 8 ++++----
+ 3 files changed, 17 insertions(+), 15 deletions(-)
+
+diff --git a/gitsrht-update-hook/options.go b/gitsrht-update-hook/options.go
+index 8efbb0a..4e9d294 100644
+--- a/gitsrht-update-hook/options.go
++++ b/gitsrht-update-hook/options.go
+@@ -1,15 +1,17 @@
+ package main
+
+ import (
++ "context"
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+
+- goredis "github.com/go-redis/redis"
++ goRedis "github.com/go-redis/redis/v8"
+ )
+
++var ctx = context.Background()
+ var options map[string]string
+
+ func loadOptions() {
+@@ -26,19 +28,19 @@ func loadOptions() {
+ if !ok {
+ redisHost = "redis://localhost:6379"
+ }
+- ropts, err := goredis.ParseURL(redisHost)
++ ropts, err := goRedis.ParseURL(redisHost)
+ if err != nil {
+ logger.Fatalf("Failed to parse redis host: %v", err)
+ }
+- redis := goredis.NewClient(ropts)
++ redis := goRedis.NewClient(ropts)
+
+ var n int
+ if nopts, ok := os.LookupEnv("GIT_PUSH_OPTION_COUNT"); ok {
+ n, _ = strconv.Atoi(nopts)
+- redis.Set(fmt.Sprintf("git.sr.ht.options.%s", uuid),
++ redis.Set(ctx, fmt.Sprintf("git.sr.ht.options.%s", uuid),
+ nopts, 10*time.Minute)
+ } else {
+- nopts, err := redis.Get(fmt.Sprintf(
++ nopts, err := redis.Get(ctx, fmt.Sprintf(
+ "git.sr.ht.options.%s", uuid)).Result()
+ if err != nil {
+ return
+@@ -51,12 +53,12 @@ func loadOptions() {
+ opt, ok := os.LookupEnv(fmt.Sprintf("GIT_PUSH_OPTION_%d", i))
+ optkey := fmt.Sprintf("git.sr.ht.options.%s.%d", uuid, i)
+ if !ok {
+- opt, err = redis.Get(optkey).Result()
++ opt, err = redis.Get(ctx, optkey).Result()
+ if err != nil {
+ return
+ }
+ } else {
+- redis.Set(optkey, opt, 10*time.Minute)
++ redis.Set(ctx, optkey, opt, 10*time.Minute)
+ }
+ parts := strings.SplitN(opt, "=", 2)
+ if len(parts) == 1 {
+diff --git a/gitsrht-update-hook/post-update.go b/gitsrht-update-hook/post-update.go
+index d14d616..37c08b3 100644
+--- a/gitsrht-update-hook/post-update.go
++++ b/gitsrht-update-hook/post-update.go
+@@ -15,7 +15,7 @@ import (
+ "github.com/go-git/go-git/v5/plumbing"
+ "github.com/go-git/go-git/v5/plumbing/object"
+ "github.com/go-git/go-git/v5/plumbing/storer"
+- goredis "github.com/go-redis/redis"
++ goRedis "github.com/go-redis/redis/v8"
+ _ "github.com/lib/pq"
+ )
+
+@@ -210,17 +210,17 @@ func postUpdate() {
+ if !ok {
+ redisHost = "redis://localhost:6379"
+ }
+- ropts, err := goredis.ParseURL(redisHost)
++ ropts, err := goRedis.ParseURL(redisHost)
+ if err != nil {
+ logger.Fatalf("Failed to parse redis host: %v", err)
+ }
+ nbuilds := 0
+- redis := goredis.NewClient(ropts)
++ redis := goRedis.NewClient(ropts)
+ for i, refname := range refs {
+ var oldref, newref string
+ var oldobj, newobj object.Object
+ updateKey := fmt.Sprintf("update.%s.%s", pushUuid, refname)
+- update, err := redis.Get(updateKey).Result()
++ update, err := redis.Get(ctx, updateKey).Result()
+ if update == "" || err != nil {
+ logger.Println("redis.Get: missing key")
+ continue
+diff --git a/gitsrht-update-hook/update.go b/gitsrht-update-hook/update.go
+index 72c661a..0968cfb 100644
+--- a/gitsrht-update-hook/update.go
++++ b/gitsrht-update-hook/update.go
+@@ -5,7 +5,7 @@ import (
+ "os"
+ "time"
+
+- goredis "github.com/go-redis/redis"
++ goRedis "github.com/go-redis/redis/v8"
+ )
+
+ // XXX: This is run once for every single ref that's pushed. If someone pushes
+@@ -26,11 +26,11 @@ func update() {
+ if !ok {
+ redisHost = "redis://localhost:6379"
+ }
+- ropts, err := goredis.ParseURL(redisHost)
++ ropts, err := goRedis.ParseURL(redisHost)
+ if err != nil {
+ logger.Fatalf("Failed to parse redis host: %v", err)
+ }
+- redis := goredis.NewClient(ropts)
+- redis.Set(fmt.Sprintf("update.%s.%s", pushUuid, refname),
++ redis := goRedis.NewClient(ropts)
++ redis.Set(ctx, fmt.Sprintf("update.%s.%s", pushUuid, refname),
+ fmt.Sprintf("%s:%s", oldref, newref), 10*time.Minute)
+ }
+--
+2.32.0
+
diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v2-0003-gitsrht-dispatch-add-support-for-supplementary-gr.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v2-0003-gitsrht-dispatch-add-support-for-supplementary-gr.patch
new file mode 100644
index 00000000000..a96f6430b56
--- /dev/null
+++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v2-0003-gitsrht-dispatch-add-support-for-supplementary-gr.patch
@@ -0,0 +1,57 @@
+From ef02ce68925888b2bca77713c6321cb33023e026 Mon Sep 17 00:00:00 2001
+From: Julien Moutinho
+Date: Fri, 27 Aug 2021 17:42:33 +0200
+Subject: [PATCH git.sr.ht v2 3/5] gitsrht-dispatch: add support for
+ supplementary groups
+
+---
+ gitsrht-dispatch/main.go | 17 ++++++++++++++---
+ 1 file changed, 14 insertions(+), 3 deletions(-)
+
+diff --git a/gitsrht-dispatch/main.go b/gitsrht-dispatch/main.go
+index d7aee14..5f17b75 100644
+--- a/gitsrht-dispatch/main.go
++++ b/gitsrht-dispatch/main.go
+@@ -17,6 +17,7 @@ type Dispatcher struct {
+ cmd string
+ uid int
+ gid int
++ gids []int
+ }
+
+ func main() {
+@@ -70,11 +71,20 @@ AuthorizedKeysUser=root`, os.Args[0])
+ if err != nil {
+ logger.Fatalf("Error looking up group %s: %v", spec[1], err)
+ }
++ groups, err := user.GroupIds()
++ if err != nil {
++ logger.Fatalf("Error looking up supplementary groups of user %s: %v", spec[0], err)
++ }
++ gids := make([]int, len(groups))
++ for i, grp := range groups {
++ sgid, _ := strconv.Atoi(grp)
++ gids[i] = sgid
++ }
+ uid, _ := strconv.Atoi(user.Uid)
+ gid, _ := strconv.Atoi(group.Gid)
+- dispatchers[uid] = Dispatcher{cmd, uid, gid}
+- logger.Printf("Registered dispatcher for %s(%d):%s(%d): %s",
+- spec[0], uid, spec[1], gid, cmd)
++ dispatchers[uid] = Dispatcher{cmd, uid, gid, gids}
++ logger.Printf("Registered dispatcher for %s(%d):%s(%d):(%s): %s",
++ spec[0], uid, spec[1], gid, strings.Join(groups, ","), cmd)
+ }
+
+ var user *osuser.User
+@@ -93,6 +103,7 @@ AuthorizedKeysUser=root`, os.Args[0])
+
+ if dispatcher, ok := dispatchers[uid]; ok {
+ logger.Printf("Dispatching to %s", dispatcher.cmd)
++ syscall.Setgroups(dispatcher.gids)
+ syscall.Setgid(dispatcher.gid)
+ syscall.Setuid(dispatcher.uid)
+ if err := syscall.Exec(dispatcher.cmd, append([]string{
+--
+2.32.0
+
diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/scm/v2-0001-srht-keys-update-go-redis-to-support-Unix-sockets.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/scm/v2-0001-srht-keys-update-go-redis-to-support-Unix-sockets.patch
new file mode 100644
index 00000000000..389cd71751a
--- /dev/null
+++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/scm/v2-0001-srht-keys-update-go-redis-to-support-Unix-sockets.patch
@@ -0,0 +1,74 @@
+From 538ff956141f5b56b77233664d4d4ea5eff8ad08 Mon Sep 17 00:00:00 2001
+From: Julien Moutinho
+Date: Fri, 27 Aug 2021 12:48:56 +0200
+Subject: [PATCH v2 1/2] srht-keys: update go-redis to support Unix sockets
+
+---
+ srht-keys/srhtkeys.go | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/srht-keys/srhtkeys.go b/srht-keys/srhtkeys.go
+index be925ed..4cc144c 100644
+--- a/srht-keys/srhtkeys.go
++++ b/srht-keys/srhtkeys.go
+@@ -1,6 +1,7 @@
+ package srhtkeys
+
+ import (
++ "context"
+ "database/sql"
+ "encoding/json"
+ "errors"
+@@ -12,7 +13,7 @@ import (
+ "path"
+ "time"
+
+- goredis "github.com/go-redis/redis"
++ goRedis "github.com/go-redis/redis/v8"
+ "github.com/google/uuid"
+ _ "github.com/lib/pq"
+ "github.com/vaughan0/go-ini"
+@@ -37,6 +38,8 @@ type MetaSSHKey struct {
+ Owner MetaUser `json:"owner"`
+ }
+
++var ctx = context.Background()
++
+ // Stores the SSH key in the database and returns the user's ID.
+ func storeKey(logger *log.Logger, db *sql.DB, key *MetaSSHKey) (int, error) {
+ logger.Println("Storing meta.sr.ht key in database")
+@@ -84,7 +87,7 @@ func storeKey(logger *log.Logger, db *sql.DB, key *MetaSSHKey) (int, error) {
+ }
+
+ func fetchKeysFromMeta(logger *log.Logger, config ini.File,
+- redis *goredis.Client, service string, b64key string) (string, int) {
++ redis *goRedis.Client, service string, b64key string) (string, int) {
+
+ meta, ok := config.Get("meta.sr.ht", "internal-origin")
+ if !ok {
+@@ -145,7 +148,7 @@ func fetchKeysFromMeta(logger *log.Logger, config ini.File,
+ if err != nil {
+ logger.Printf("Caching SSH key in redis failed: %v", err)
+ } else {
+- redis.Set(cacheKey, cacheBytes, 7*24*time.Hour)
++ redis.Set(ctx, cacheKey, cacheBytes, 7*24*time.Hour)
+ }
+
+ return key.Owner.Username, userId
+@@ -164,11 +167,11 @@ func ParseArgs(logger *log.Logger) (string, string, string, error) {
+ }
+
+ func UserFromKey(logger *log.Logger, config ini.File,
+- redis *goredis.Client, service string, b64key string) (string, int) {
++ redis *goRedis.Client, service string, b64key string) (string, int) {
+
+ cacheKey := fmt.Sprintf("%s.ssh-keys.%s", service, b64key)
+ logger.Printf("Cache key for SSH key lookup: %s", cacheKey)
+- cacheBytes, err := redis.Get(cacheKey).Bytes()
++ cacheBytes, err := redis.Get(ctx, cacheKey).Bytes()
+ var (
+ username string
+ userId int
+--
+2.32.0
+
diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/scm/v2-0002-srht-keys-update-go.-mod-sum-for-go-redis.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/scm/v2-0002-srht-keys-update-go.-mod-sum-for-go-redis.patch
new file mode 100644
index 00000000000..8db7dc674c7
--- /dev/null
+++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/scm/v2-0002-srht-keys-update-go.-mod-sum-for-go-redis.patch
@@ -0,0 +1,155 @@
+From d0862969b1470701edded4772337822ca52c8509 Mon Sep 17 00:00:00 2001
+From: Julien Moutinho
+Date: Fri, 27 Aug 2021 13:06:27 +0200
+Subject: [PATCH v2 2/2] srht-keys: update go.{mod,sum} for go-redis
+
+---
+ srht-keys/go.mod | 2 +-
+ srht-keys/go.sum | 103 ++++++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 99 insertions(+), 6 deletions(-)
+
+diff --git a/srht-keys/go.mod b/srht-keys/go.mod
+index d275913..8d1c10a 100644
+--- a/srht-keys/go.mod
++++ b/srht-keys/go.mod
+@@ -4,7 +4,7 @@ go 1.13
+
+ require (
+ git.sr.ht/~sircmpwn/core-go v0.0.0-20201005173246-a9e49d17a1e6
+- github.com/go-redis/redis v6.15.9+incompatible
++ github.com/go-redis/redis/v8 v8.11.3
+ github.com/google/uuid v1.1.1
+ github.com/lib/pq v1.8.0
+ github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec
+diff --git a/srht-keys/go.sum b/srht-keys/go.sum
+index 974326e..a264a26 100644
+--- a/srht-keys/go.sum
++++ b/srht-keys/go.sum
+@@ -1,26 +1,119 @@
+-git.sr.ht/~sircmpwn/core-go v0.0.0-20200820135923-98806e712f5e h1:TJqf/neVU5peFAS9WcR1aADXcflPOvAd7ABEirmU7m0=
+-git.sr.ht/~sircmpwn/core-go v0.0.0-20200820135923-98806e712f5e/go.mod h1:aXSNgRsGoI3tTFKlwD0xm2htbEzKlR2xUm1osRxfhOM=
+ git.sr.ht/~sircmpwn/core-go v0.0.0-20201005173246-a9e49d17a1e6 h1:Ky6HzcRmbMUxOrWXv04+mb97GkyxO/Nx7v8uJBUdpNk=
+ git.sr.ht/~sircmpwn/core-go v0.0.0-20201005173246-a9e49d17a1e6/go.mod h1:HpPX22ilJUWKOA4NDhrOcIyblQhdiKHPg4oMJFYdh0Y=
++github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
++github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
++github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
++github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
++github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
++github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
++github.com/fernet/fernet-go v0.0.0-20191111064656-eff2850e6001 h1:/UMxx5lGDg30aioUL9e7xJnbJfJeX7vhcm57fa5udaI=
+ github.com/fernet/fernet-go v0.0.0-20191111064656-eff2850e6001/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c=
+-github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
+-github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
++github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
++github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
++github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
++github.com/go-redis/redis/v8 v8.11.3 h1:GCjoYp8c+yQTJfc0n69iwSiHjvuAdruxl7elnZCxgt8=
++github.com/go-redis/redis/v8 v8.11.3/go.mod h1:xNJ9xDG09FsIPwh3bWdk+0oDWHbtF9rPN0F/oD9XeKc=
++github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
++github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
++github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
++github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
++github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
++github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
++github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
++github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
++github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
++github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
++github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
++github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
++github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
++github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
++github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
++github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
++github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+ github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
++github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+ github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
+ github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
++github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
++github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
++github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
++github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
++github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
++github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
++github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
++github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
++github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
++github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU=
++github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
++github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
++github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
++github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+ github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec h1:DGmKwyZwEB8dI7tbLt/I/gQuP559o/0FrAkHKlQM/Ks=
+ github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec/go.mod h1:owBmyHYMLkxyrugmfwE/DLJyW8Ro9mkphwuVErQ0iUw=
++github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+-golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
++golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
++golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
++golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
++golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
++golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
++golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
++golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
++golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
++golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
++golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
++golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
++golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
++golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
++golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
++golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
++golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
++golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
++golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
++golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
++golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
++golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
++golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
++golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
++golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
++golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
++golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
++golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
++golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
++golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
++golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
++golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
++golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
++golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
++golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
++google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
++google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
++google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
++google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
++google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
++google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
++google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
++google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
++google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
++gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
++gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
++gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
++gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
++gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
++gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
++gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
++gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
++gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
++gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+--
+2.32.0
+
diff --git a/pkgs/applications/version-management/sourcehut/patches/srht-update-profiles/0001-fix-disgusting-hack-in-the-case-of-buildsrht.patch b/pkgs/applications/version-management/sourcehut/patches/srht-update-profiles/0001-fix-disgusting-hack-in-the-case-of-buildsrht.patch
new file mode 100644
index 00000000000..9db31c9e554
--- /dev/null
+++ b/pkgs/applications/version-management/sourcehut/patches/srht-update-profiles/0001-fix-disgusting-hack-in-the-case-of-buildsrht.patch
@@ -0,0 +1,25 @@
+From 8bb7c927c815582b26d026f4b2ea72f0245ccab7 Mon Sep 17 00:00:00 2001
+From: Julien Moutinho
+Date: Mon, 23 Aug 2021 18:45:07 +0200
+Subject: [PATCH core.sr.ht] fix "disgusting hack" in the case of buildsrht
+
+---
+ srht-update-profiles | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/srht-update-profiles b/srht-update-profiles
+index 0d9588c..063d6e2 100755
+--- a/srht-update-profiles
++++ b/srht-update-profiles
+@@ -3,7 +3,7 @@ import sys
+ import os
+ sys.path.append(os.getcwd())
+ site = sys.argv[1]
+-app = __import__(site.replace(".", "") + ".app").app.app # disgusting hack
++app = __import__(site.replace(".", "").replace("builds","build") + ".app").app.app # disgusting hack
+ from srht.config import cfg
+ from srht.database import db, DbSession
+ db = DbSession(cfg(site, "connection-string"))
+--
+2.32.0
+
diff --git a/pkgs/applications/version-management/sourcehut/scm.nix b/pkgs/applications/version-management/sourcehut/scm.nix
index 1f385265360..6737251f833 100644
--- a/pkgs/applications/version-management/sourcehut/scm.nix
+++ b/pkgs/applications/version-management/sourcehut/scm.nix
@@ -1,22 +1,59 @@
{ lib
, fetchFromSourcehut
+, buildGoModule
, buildPythonPackage
, srht
, redis
, pyyaml
, buildsrht
-, writeText
+, applyPatches
}:
buildPythonPackage rec {
pname = "scmsrht";
- version = "0.22.9";
+ version = "0.22.13";
src = fetchFromSourcehut {
owner = "~sircmpwn";
repo = "scm.sr.ht";
rev = version;
- sha256 = "sha256-327G6C8FW+iZx+167D7TQsFtV6FGc8MpMVo9L/cUUqU=";
+ sha256 = "sha256-9iRmQBt4Cxr5itTk34b8iDRyXYDHTDfZjV0SIDT/kkM=";
+ };
+
+ passthru = {
+ srht-keys = buildGoModule rec {
+ inherit src version;
+ sourceRoot = "source/srht-keys";
+ pname = "srht-keys";
+ vendorSha256 = "sha256-lQk1dymMCefHMFJhO3yC/muBP/cxI//5Yz991D2YZY4=";
+
+ # What follows is only to update go-redis
+ # go.{mod,sum} could be patched directly but that would be less resilient
+ # to changes from upstream, and thus harder to maintain the patching
+ # while it hasn't been merged upstream.
+
+ overrideModAttrs = old: {
+ inherit patches patchFlags;
+ preBuild = ''
+ go get github.com/go-redis/redis/v8
+ go get github.com/go-redis/redis@none
+ go mod tidy
+ '';
+ # Pass updated go.{mod,sum} from go-modules to srht-keys's vendor/go.{mod,sum}
+ postInstall = ''
+ cp --reflink=auto go.* $out
+ '';
+ };
+
+ patches = [
+ # Update go-redis to support Unix sockets
+ patches/redis-socket/scm/v2-0001-srht-keys-update-go-redis-to-support-Unix-sockets.patch
+ ];
+ patchFlags = "-p2";
+ postInstall = ''
+ cp --reflink=auto *.go vendor/go.* $out
+ '';
+ };
};
nativeBuildInputs = srht.nativeBuildInputs;
@@ -33,11 +70,12 @@ buildPythonPackage rec {
'';
dontUseSetuptoolsCheck = true;
+ pythonImportsCheck = [ "scmsrht" ];
meta = with lib; {
homepage = "https://git.sr.ht/~sircmpwn/git.sr.ht";
description = "Shared support code for sr.ht source control services.";
- license = licenses.agpl3;
+ license = licenses.agpl3Only;
maintainers = with maintainers; [ eadwu ];
};
}
diff --git a/pkgs/applications/version-management/sourcehut/todo.nix b/pkgs/applications/version-management/sourcehut/todo.nix
index 85e1f5637b6..e091341c7dd 100644
--- a/pkgs/applications/version-management/sourcehut/todo.nix
+++ b/pkgs/applications/version-management/sourcehut/todo.nix
@@ -12,13 +12,13 @@
buildPythonPackage rec {
pname = "todosrht";
- version = "0.64.14";
+ version = "0.64.24";
src = fetchFromSourcehut {
owner = "~sircmpwn";
repo = "todo.sr.ht";
rev = version;
- sha256 = "sha256-huIAhn6h1F5w5ST4/yBwr82kAzyYwhLu+gpRuOQgnsE=";
+ sha256 = "sha256-H2XGOxHyurXw3GekZJXSO6RMChRjNbjqxik/mvFVqfY=";
};
nativeBuildInputs = srht.nativeBuildInputs;
@@ -42,11 +42,12 @@ buildPythonPackage rec {
];
dontUseSetuptoolsCheck = true;
+ pythonImportsCheck = [ "todosrht" ];
meta = with lib; {
homepage = "https://todo.sr.ht/~sircmpwn/todo.sr.ht";
description = "Ticket tracking service for the sr.ht network";
- license = licenses.agpl3;
+ license = licenses.agpl3Only;
maintainers = with maintainers; [ eadwu ];
};
}
diff --git a/pkgs/applications/version-management/sourcehut/update.sh b/pkgs/applications/version-management/sourcehut/update.sh
index 156d4cc35e4..6733046d000 100755
--- a/pkgs/applications/version-management/sourcehut/update.sh
+++ b/pkgs/applications/version-management/sourcehut/update.sh
@@ -1,8 +1,11 @@
#! /usr/bin/env nix-shell
#! nix-shell -i bash -p git mercurial common-updater-scripts
+set -x
-cd "$(dirname "${BASH_SOURCE[0]}")"
+cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1
root=../../../..
+tmp=$(mktemp -d)
+trap 'rm -rf "$tmp"' EXIT
default() {
(cd "$root" && nix-instantiate --eval --strict -A "sourcehut.python.pkgs.$1.meta.position" | sed -re 's/^"(.*):[0-9]+"$/\1/')
@@ -13,19 +16,18 @@ version() {
}
src_url() {
- (cd "$root" && nix-instantiate --eval --strict -A "sourcehut.python.pkgs.$1.src.drvAttrs.url" | tr -d '"')
+ nix-instantiate --eval --strict --expr " with import $root {}; let src = sourcehut.python.pkgs.$1.drvAttrs.src; in src.url or src.meta.homepage" | tr -d '"'
}
get_latest_version() {
src="$(src_url "$1")"
- tmp=$(mktemp -d)
-
+ rm -rf "$tmp"
if [ "$1" = "hgsrht" ]; then
- hg clone "$src" "$tmp" &> /dev/null
+ hg clone "$src" "$tmp" >/dev/null
printf "%s" "$(cd "$tmp" && hg log --limit 1 --template '{latesttag}')"
else
- git clone "$src" "$tmp"
- printf "%s" "$(cd "$tmp" && git describe $(git rev-list --tags --max-count=1))"
+ git clone "$src" "$tmp" >/dev/null
+ printf "%s" "$(cd "$tmp" && git describe "$(git rev-list --tags --max-count=1)")"
fi
}
@@ -36,19 +38,33 @@ update_version() {
(cd "$root" && update-source-version "sourcehut.python.pkgs.$1" "$version")
+ # Update vendorSha256 of Go modules
+ nixFile="${1%srht}".nix
+ nixFile="${nixFile/build/builds}"
+ retry=true
+ while "$retry"; do
+ retry=false;
+ exec < <(exec nix -L build -f "$root" sourcehut.python.pkgs."$1" 2>&1)
+ while IFS=' :' read -r origin hash; do
+ case "$origin" in
+ (expected|specified) oldHash="$hash";;
+ (got) sed -i "s|$oldHash|$(nix hash to-sri --type sha256 "$hash")|" "$nixFile"; retry=true; break;;
+ (*) printf >&2 "%s\n" "$origin${hash:+:$hash}"
+ esac
+ done
+ done
+
git add "$default_nix"
- git commit -m "$1: $version_old -> $version"
+ git commit -m "sourcehut.$1: $version_old -> $version"
}
-services=( "srht" "buildsrht" "dispatchsrht" "gitsrht" "hgsrht" "hubsrht" "listssrht" "mansrht"
- "metasrht" "pastesrht" "todosrht" "scmsrht" )
-
-# Whether or not a specific service is requested
-if [ -n "$1" ]; then
- version="$(get_latest_version "$1")"
- (cd "$root" && update-source-version "sourcehut.python.pkgs.$1" "$version")
+if [ $# -gt 0 ]; then
+ services=("$@")
else
- for service in "${services[@]}"; do
- update_version "$service"
- done
+ services=( "srht" "buildsrht" "dispatchsrht" "gitsrht" "hgsrht" "hubsrht" "listssrht" "mansrht"
+ "metasrht" "pagessrht" "pastesrht" "todosrht" "scmsrht" )
fi
+
+for service in "${services[@]}"; do
+ update_version "$service"
+done