nixos/sourcehut: reformat expressions using nixpkgs-fmt
This commit is contained in:
parent
e35216204b
commit
087c83f45c
@ -3,10 +3,10 @@ srv:
|
|||||||
, srvsrht ? "${srv}srht" # Because "buildsrht" does not follow that pattern (missing an "s").
|
, srvsrht ? "${srv}srht" # Because "buildsrht" does not follow that pattern (missing an "s").
|
||||||
, iniKey ? "${srv}.sr.ht"
|
, iniKey ? "${srv}.sr.ht"
|
||||||
, webhooks ? false
|
, webhooks ? false
|
||||||
, extraTimers ? {}
|
, extraTimers ? { }
|
||||||
, mainService ? {}
|
, mainService ? { }
|
||||||
, extraServices ? {}
|
, extraServices ? { }
|
||||||
, extraConfig ? {}
|
, extraConfig ? { }
|
||||||
, port
|
, port
|
||||||
}:
|
}:
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
@ -19,101 +19,112 @@ let
|
|||||||
cfg = config.services.sourcehut;
|
cfg = config.services.sourcehut;
|
||||||
configIni = configIniOfService srv;
|
configIni = configIniOfService srv;
|
||||||
srvCfg = cfg.${srv};
|
srvCfg = cfg.${srv};
|
||||||
baseService = serviceName: { allowStripe ? false }: extraService: let
|
baseService = serviceName: { allowStripe ? false }: extraService:
|
||||||
runDir = "/run/sourcehut/${serviceName}";
|
let
|
||||||
rootDir = "/run/sourcehut/chroots/${serviceName}";
|
runDir = "/run/sourcehut/${serviceName}";
|
||||||
|
rootDir = "/run/sourcehut/chroots/${serviceName}";
|
||||||
in
|
in
|
||||||
mkMerge [ extraService {
|
mkMerge [
|
||||||
after = [ "network.target" ] ++
|
extraService
|
||||||
optional cfg.postgresql.enable "postgresql.service" ++
|
{
|
||||||
optional cfg.redis.enable "redis-sourcehut-${srvsrht}.service";
|
after = [ "network.target" ] ++
|
||||||
requires =
|
optional cfg.postgresql.enable "postgresql.service" ++
|
||||||
optional cfg.postgresql.enable "postgresql.service" ++
|
optional cfg.redis.enable "redis-sourcehut-${srvsrht}.service";
|
||||||
optional cfg.redis.enable "redis-sourcehut-${srvsrht}.service";
|
requires =
|
||||||
path = [ pkgs.gawk ];
|
optional cfg.postgresql.enable "postgresql.service" ++
|
||||||
environment.HOME = runDir;
|
optional cfg.redis.enable "redis-sourcehut-${srvsrht}.service";
|
||||||
serviceConfig = {
|
path = [ pkgs.gawk ];
|
||||||
User = mkDefault srvCfg.user;
|
environment.HOME = runDir;
|
||||||
Group = mkDefault srvCfg.group;
|
serviceConfig = {
|
||||||
RuntimeDirectory = [
|
User = mkDefault srvCfg.user;
|
||||||
"sourcehut/${serviceName}"
|
Group = mkDefault srvCfg.group;
|
||||||
# Used by *srht-keys which reads ../config.ini
|
RuntimeDirectory = [
|
||||||
"sourcehut/${serviceName}/subdir"
|
"sourcehut/${serviceName}"
|
||||||
"sourcehut/chroots/${serviceName}"
|
# Used by *srht-keys which reads ../config.ini
|
||||||
];
|
"sourcehut/${serviceName}/subdir"
|
||||||
RuntimeDirectoryMode = "2750";
|
"sourcehut/chroots/${serviceName}"
|
||||||
# No need for the chroot path once inside the chroot
|
];
|
||||||
InaccessiblePaths = [ "-+${rootDir}" ];
|
RuntimeDirectoryMode = "2750";
|
||||||
# g+rx is for group members (eg. fcgiwrap or nginx)
|
# No need for the chroot path once inside the chroot
|
||||||
# to read Git/Mercurial repositories, buildlogs, etc.
|
InaccessiblePaths = [ "-+${rootDir}" ];
|
||||||
# o+x is for intermediate directories created by BindPaths= and like,
|
# g+rx is for group members (eg. fcgiwrap or nginx)
|
||||||
# as they're owned by root:root.
|
# to read Git/Mercurial repositories, buildlogs, etc.
|
||||||
UMask = "0026";
|
# o+x is for intermediate directories created by BindPaths= and like,
|
||||||
RootDirectory = rootDir;
|
# as they're owned by root:root.
|
||||||
RootDirectoryStartOnly = true;
|
UMask = "0026";
|
||||||
PrivateTmp = true;
|
RootDirectory = rootDir;
|
||||||
MountAPIVFS = true;
|
RootDirectoryStartOnly = true;
|
||||||
# config.ini is looked up in there, before /etc/srht/config.ini
|
PrivateTmp = true;
|
||||||
# Note that it fails to be set in ExecStartPre=
|
MountAPIVFS = true;
|
||||||
WorkingDirectory = mkDefault ("-"+runDir);
|
# config.ini is looked up in there, before /etc/srht/config.ini
|
||||||
BindReadOnlyPaths = [
|
# Note that it fails to be set in ExecStartPre=
|
||||||
builtins.storeDir
|
WorkingDirectory = mkDefault ("-" + runDir);
|
||||||
"/etc"
|
BindReadOnlyPaths = [
|
||||||
"/run/booted-system"
|
builtins.storeDir
|
||||||
"/run/current-system"
|
"/etc"
|
||||||
"/run/systemd"
|
"/run/booted-system"
|
||||||
] ++
|
"/run/current-system"
|
||||||
optional cfg.postgresql.enable "/run/postgresql" ++
|
"/run/systemd"
|
||||||
optional cfg.redis.enable "/run/redis-sourcehut-${srvsrht}";
|
] ++
|
||||||
# LoadCredential= are unfortunately not available in ExecStartPre=
|
optional cfg.postgresql.enable "/run/postgresql" ++
|
||||||
# Hence this one is run as root (the +) with RootDirectoryStartOnly=
|
optional cfg.redis.enable "/run/redis-sourcehut-${srvsrht}";
|
||||||
# to reach credentials wherever they are.
|
# LoadCredential= are unfortunately not available in ExecStartPre=
|
||||||
# Note that each systemd service gets its own ${runDir}/config.ini file.
|
# Hence this one is run as root (the +) with RootDirectoryStartOnly=
|
||||||
ExecStartPre = mkBefore [("+"+pkgs.writeShellScript "${serviceName}-credentials" ''
|
# to reach credentials wherever they are.
|
||||||
set -x
|
# Note that each systemd service gets its own ${runDir}/config.ini file.
|
||||||
# Replace values beginning with a '<' by the content of the file whose name is after.
|
ExecStartPre = mkBefore [
|
||||||
gawk '{ if (match($0,/^([^=]+=)<(.+)/,m)) { getline f < m[2]; print m[1] f } else print $0 }' ${configIni} |
|
("+" + pkgs.writeShellScript "${serviceName}-credentials" ''
|
||||||
${optionalString (!allowStripe) "gawk '!/^stripe-secret-key=/' |"}
|
set -x
|
||||||
install -o ${srvCfg.user} -g root -m 400 /dev/stdin ${runDir}/config.ini
|
# Replace values beginning 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} |
|
||||||
# The following options are only for optimizing:
|
${optionalString (!allowStripe) "gawk '!/^stripe-secret-key=/' |"}
|
||||||
# systemd-analyze security
|
install -o ${srvCfg.user} -g root -m 400 /dev/stdin ${runDir}/config.ini
|
||||||
AmbientCapabilities = "";
|
'')
|
||||||
CapabilityBoundingSet = "";
|
];
|
||||||
# ProtectClock= adds DeviceAllow=char-rtc r
|
# The following options are only for optimizing:
|
||||||
DeviceAllow = "";
|
# systemd-analyze security
|
||||||
LockPersonality = true;
|
AmbientCapabilities = "";
|
||||||
MemoryDenyWriteExecute = true;
|
CapabilityBoundingSet = "";
|
||||||
NoNewPrivileges = true;
|
# ProtectClock= adds DeviceAllow=char-rtc r
|
||||||
PrivateDevices = true;
|
DeviceAllow = "";
|
||||||
PrivateMounts = true;
|
LockPersonality = true;
|
||||||
PrivateNetwork = mkDefault false;
|
MemoryDenyWriteExecute = true;
|
||||||
PrivateUsers = true;
|
NoNewPrivileges = true;
|
||||||
ProcSubset = "pid";
|
PrivateDevices = true;
|
||||||
ProtectClock = true;
|
PrivateMounts = true;
|
||||||
ProtectControlGroups = true;
|
PrivateNetwork = mkDefault false;
|
||||||
ProtectHome = true;
|
PrivateUsers = true;
|
||||||
ProtectHostname = true;
|
ProcSubset = "pid";
|
||||||
ProtectKernelLogs = true;
|
ProtectClock = true;
|
||||||
ProtectKernelModules = true;
|
ProtectControlGroups = true;
|
||||||
ProtectKernelTunables = true;
|
ProtectHome = true;
|
||||||
ProtectProc = "invisible";
|
ProtectHostname = true;
|
||||||
ProtectSystem = "strict";
|
ProtectKernelLogs = true;
|
||||||
RemoveIPC = true;
|
ProtectKernelModules = true;
|
||||||
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
|
ProtectKernelTunables = true;
|
||||||
RestrictNamespaces = true;
|
ProtectProc = "invisible";
|
||||||
RestrictRealtime = true;
|
ProtectSystem = "strict";
|
||||||
RestrictSUIDSGID = true;
|
RemoveIPC = true;
|
||||||
#SocketBindAllow = [ "tcp:${toString srvCfg.port}" "tcp:${toString srvCfg.prometheusPort}" ];
|
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
|
||||||
#SocketBindDeny = "any";
|
RestrictNamespaces = true;
|
||||||
SystemCallFilter = [
|
RestrictRealtime = true;
|
||||||
"@system-service"
|
RestrictSUIDSGID = true;
|
||||||
"~@aio" "~@keyring" "~@memlock" "~@privileged" "~@timer"
|
#SocketBindAllow = [ "tcp:${toString srvCfg.port}" "tcp:${toString srvCfg.prometheusPort}" ];
|
||||||
"@chown" "@setuid"
|
#SocketBindDeny = "any";
|
||||||
];
|
SystemCallFilter = [
|
||||||
SystemCallArchitectures = "native";
|
"@system-service"
|
||||||
};
|
"~@aio"
|
||||||
} ];
|
"~@keyring"
|
||||||
|
"~@memlock"
|
||||||
|
"~@privileged"
|
||||||
|
"~@timer"
|
||||||
|
"@chown"
|
||||||
|
"@setuid"
|
||||||
|
];
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.services.sourcehut.${srv} = {
|
options.services.sourcehut.${srv} = {
|
||||||
@ -173,7 +184,7 @@ in
|
|||||||
gunicorn = {
|
gunicorn = {
|
||||||
extraArgs = mkOption {
|
extraArgs = mkOption {
|
||||||
type = with types; listOf str;
|
type = with types; listOf str;
|
||||||
default = ["--timeout 120" "--workers 1" "--log-level=info"];
|
default = [ "--timeout 120" "--workers 1" "--log-level=info" ];
|
||||||
description = lib.mdDoc "Extra arguments passed to Gunicorn.";
|
description = lib.mdDoc "Extra arguments passed to Gunicorn.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -181,7 +192,7 @@ in
|
|||||||
webhooks = {
|
webhooks = {
|
||||||
extraArgs = mkOption {
|
extraArgs = mkOption {
|
||||||
type = with types; listOf str;
|
type = with types; listOf str;
|
||||||
default = ["--loglevel DEBUG" "--pool eventlet" "--without-heartbeat"];
|
default = [ "--loglevel DEBUG" "--pool eventlet" "--without-heartbeat" ];
|
||||||
description = lib.mdDoc "Extra arguments passed to the Celery responsible for webhooks.";
|
description = lib.mdDoc "Extra arguments passed to the Celery responsible for webhooks.";
|
||||||
};
|
};
|
||||||
celeryConfig = mkOption {
|
celeryConfig = mkOption {
|
||||||
@ -192,216 +203,237 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf (cfg.enable && srvCfg.enable) (mkMerge [ extraConfig {
|
config = lib.mkIf (cfg.enable && srvCfg.enable) (mkMerge [
|
||||||
users = {
|
extraConfig
|
||||||
|
{
|
||||||
users = {
|
users = {
|
||||||
"${srvCfg.user}" = {
|
users = {
|
||||||
isSystemUser = true;
|
"${srvCfg.user}" = {
|
||||||
group = mkDefault srvCfg.group;
|
isSystemUser = true;
|
||||||
description = mkDefault "sourcehut user for ${srv}.sr.ht";
|
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 ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx = mkIf cfg.nginx.enable {
|
|
||||||
virtualHosts."${srv}.${cfg.settings."sr.ht".global-domain}" = mkMerge [ {
|
|
||||||
forceSSL = mkDefault true;
|
|
||||||
locations."/".proxyPass = "http://${cfg.listenAddress}:${toString srvCfg.port}";
|
|
||||||
locations."/static" = {
|
|
||||||
root = "${pkgs.sourcehut.${srvsrht}}/${pkgs.sourcehut.python.sitePackages}/${srvsrht}";
|
|
||||||
extraConfig = mkDefault ''
|
|
||||||
expires 30d;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
locations."/query" = mkIf (cfg.settings.${iniKey} ? api-origin) {
|
|
||||||
proxyPass = cfg.settings.${iniKey}.api-origin;
|
|
||||||
extraConfig = ''
|
|
||||||
add_header 'Access-Control-Allow-Origin' '*';
|
|
||||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
|
||||||
add_header 'Access-Control-Allow-Headers' 'User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
|
||||||
|
|
||||||
if ($request_method = 'OPTIONS') {
|
|
||||||
add_header 'Access-Control-Max-Age' 1728000;
|
|
||||||
add_header 'Content-Type' 'text/plain; charset=utf-8';
|
|
||||||
add_header 'Content-Length' 0;
|
|
||||||
return 204;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
} 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;
|
|
||||||
# We don't use it because we have a special default database name with dots.
|
|
||||||
# TODO(for maintainers of sourcehut): migrate away from custom preStart script.
|
|
||||||
ensureDBOwnership = false;
|
|
||||||
}) [srvCfg.user];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
groups = {
|
||||||
stateDir = "/var/lib/sourcehut/${srvsrht}";
|
"${srvCfg.group}" = { };
|
||||||
in mkBefore ''
|
} // optionalAttrs
|
||||||
set -x
|
(cfg.postgresql.enable
|
||||||
# Use the /run/sourcehut/${srvsrht}/config.ini
|
&& hasSuffix "0" (postgresql.settings.unix_socket_permissions or ""))
|
||||||
# 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";
|
"postgres".members = [ srvCfg.user ];
|
||||||
after = [ "${srvsrht}.service" ];
|
} // optionalAttrs
|
||||||
wantedBy = [ "${srvsrht}.service" ];
|
(cfg.redis.enable
|
||||||
partOf = [ "${srvsrht}.service" ];
|
&& hasSuffix "0" (redis.settings.unixsocketperm or ""))
|
||||||
preStart = ''
|
{
|
||||||
cp ${pkgs.writeText "${srvsrht}-webhooks-celeryconfig.py" srvCfg.webhooks.celeryConfig} \
|
"redis-sourcehut-${srvsrht}".members = [ srvCfg.user ];
|
||||||
/run/sourcehut/${srvsrht}-webhooks/celeryconfig.py
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nginx = mkIf cfg.nginx.enable {
|
||||||
|
virtualHosts."${srv}.${cfg.settings."sr.ht".global-domain}" = mkMerge [{
|
||||||
|
forceSSL = mkDefault true;
|
||||||
|
locations."/".proxyPass = "http://${cfg.listenAddress}:${toString srvCfg.port}";
|
||||||
|
locations."/static" = {
|
||||||
|
root = "${pkgs.sourcehut.${srvsrht}}/${pkgs.sourcehut.python.sitePackages}/${srvsrht}";
|
||||||
|
extraConfig = mkDefault ''
|
||||||
|
expires 30d;
|
||||||
'';
|
'';
|
||||||
serviceConfig = {
|
};
|
||||||
Type = "simple";
|
locations."/query" = mkIf (cfg.settings.${iniKey} ? api-origin) {
|
||||||
Restart = "always";
|
proxyPass = cfg.settings.${iniKey}.api-origin;
|
||||||
ExecStart = "${cfg.python}/bin/celery --app ${srvsrht}.webhooks worker --hostname ${srvsrht}-webhooks@%%h " + concatStringsSep " " srvCfg.webhooks.extraArgs;
|
extraConfig = ''
|
||||||
# Avoid crashing: os.getloadavg()
|
add_header 'Access-Control-Allow-Origin' '*';
|
||||||
ProcSubset = mkForce "all";
|
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||||
|
add_header 'Access-Control-Allow-Headers' 'User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
||||||
|
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
add_header 'Access-Control-Max-Age' 1728000;
|
||||||
|
add_header 'Content-Type' 'text/plain; charset=utf-8';
|
||||||
|
add_header 'Content-Length' 0;
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
# We don't use it because we have a special default database name with dots.
|
||||||
|
# TODO(for maintainers of sourcehut): migrate away from custom preStart script.
|
||||||
|
ensureDBOwnership = false;
|
||||||
|
}) [ srvCfg.user ];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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 [
|
(mapAttrs
|
||||||
{
|
(timerName: timer: (baseService timerName { } (mkMerge [
|
||||||
description = "sourcehut ${timerName} service";
|
{
|
||||||
after = [ "network.target" "${srvsrht}.service" ];
|
description = "sourcehut ${timerName} service";
|
||||||
serviceConfig = {
|
after = [ "network.target" "${srvsrht}.service" ];
|
||||||
Type = "oneshot";
|
serviceConfig = {
|
||||||
ExecStart = "${cfg.python}/bin/${timerName}";
|
Type = "oneshot";
|
||||||
};
|
ExecStart = "${cfg.python}/bin/${timerName}";
|
||||||
}
|
};
|
||||||
(timer.service or {})
|
}
|
||||||
]))) extraTimers)
|
(timer.service or { })
|
||||||
|
])))
|
||||||
|
extraTimers)
|
||||||
|
|
||||||
(mapAttrs (serviceName: extraService: baseService serviceName {} (mkMerge [
|
(mapAttrs
|
||||||
{
|
(serviceName: extraService: baseService serviceName { } (mkMerge [
|
||||||
description = "sourcehut ${serviceName} service";
|
{
|
||||||
# So that extraServices have the PostgreSQL database initialized.
|
description = "sourcehut ${serviceName} service";
|
||||||
after = [ "${srvsrht}.service" ];
|
# So that extraServices have the PostgreSQL database initialized.
|
||||||
wantedBy = [ "${srvsrht}.service" ];
|
after = [ "${srvsrht}.service" ];
|
||||||
partOf = [ "${srvsrht}.service" ];
|
wantedBy = [ "${srvsrht}.service" ];
|
||||||
serviceConfig = {
|
partOf = [ "${srvsrht}.service" ];
|
||||||
Type = "simple";
|
serviceConfig = {
|
||||||
Restart = mkDefault "always";
|
Type = "simple";
|
||||||
};
|
Restart = mkDefault "always";
|
||||||
}
|
};
|
||||||
extraService
|
}
|
||||||
])) extraServices)
|
extraService
|
||||||
|
]))
|
||||||
|
extraServices)
|
||||||
|
|
||||||
# Work around 'pq: permission denied for schema public' with postgres v15.
|
# Work around 'pq: permission denied for schema public' with postgres v15.
|
||||||
# See https://github.com/NixOS/nixpkgs/issues/216989
|
# See https://github.com/NixOS/nixpkgs/issues/216989
|
||||||
# Workaround taken from nixos/forgejo: https://github.com/NixOS/nixpkgs/pull/262741
|
# Workaround taken from nixos/forgejo: https://github.com/NixOS/nixpkgs/pull/262741
|
||||||
# TODO(to maintainers of sourcehut): please migrate away from this workaround
|
# TODO(to maintainers of sourcehut): please migrate away from this workaround
|
||||||
# by migrating away from database name defaults with dots.
|
# by migrating away from database name defaults with dots.
|
||||||
(lib.mkIf (
|
(lib.mkIf
|
||||||
cfg.postgresql.enable
|
(
|
||||||
&& lib.strings.versionAtLeast config.services.postgresql.package.version "15.0"
|
cfg.postgresql.enable
|
||||||
) {
|
&& lib.strings.versionAtLeast config.services.postgresql.package.version "15.0"
|
||||||
postgresql.postStart = (lib.mkAfter ''
|
)
|
||||||
$PSQL -tAc 'ALTER DATABASE "${srvCfg.postgresql.database}" OWNER TO "${srvCfg.user}";'
|
{
|
||||||
'');
|
postgresql.postStart = (lib.mkAfter ''
|
||||||
}
|
$PSQL -tAc 'ALTER DATABASE "${srvCfg.postgresql.database}" OWNER TO "${srvCfg.user}";'
|
||||||
)
|
'');
|
||||||
];
|
}
|
||||||
|
)
|
||||||
|
];
|
||||||
|
|
||||||
systemd.timers = mapAttrs (timerName: timer:
|
systemd.timers = mapAttrs
|
||||||
{
|
(timerName: timer:
|
||||||
description = "sourcehut timer for ${timerName}";
|
{
|
||||||
wantedBy = [ "timers.target" ];
|
description = "sourcehut timer for ${timerName}";
|
||||||
inherit (timer) timerConfig;
|
wantedBy = [ "timers.target" ];
|
||||||
}) extraTimers;
|
inherit (timer) timerConfig;
|
||||||
} ]);
|
})
|
||||||
|
extraTimers;
|
||||||
|
}
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user