forked from mirrors/nixpkgs
2e751c0772
the conversion procedure is simple: - find all things that look like options, ie calls to either `mkOption` or `lib.mkOption` that take an attrset. remember the attrset as the option - for all options, find a `description` attribute who's value is not a call to `mdDoc` or `lib.mdDoc` - textually convert the entire value of the attribute to MD with a few simple regexes (the set from mdize-module.sh) - if the change produced a change in the manual output, discard - if the change kept the manual unchanged, add some text to the description to make sure we've actually found an option. if the manual changes this time, keep the converted description this procedure converts 80% of nixos options to markdown. around 2000 options remain to be inspected, but most of those fail the "does not change the manual output check": currently the MD conversion process does not faithfully convert docbook tags like <code> and <package>, so any option using such tags will not be converted at all.
580 lines
23 KiB
Nix
580 lines
23 KiB
Nix
{ lib, pkgs, config, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.services.public-inbox;
|
|
stateDir = "/var/lib/public-inbox";
|
|
|
|
manref = name: vol: "<citerefentry><refentrytitle>${name}</refentrytitle><manvolnum>${toString vol}</manvolnum></citerefentry>";
|
|
|
|
gitIni = pkgs.formats.gitIni { listsAsDuplicateKeys = true; };
|
|
iniAtom = elemAt gitIni.type/*attrsOf*/.functor.wrapped/*attrsOf*/.functor.wrapped/*either*/.functor.wrapped 0;
|
|
|
|
useSpamAssassin = cfg.settings.publicinboxmda.spamcheck == "spamc" ||
|
|
cfg.settings.publicinboxwatch.spamcheck == "spamc";
|
|
|
|
publicInboxDaemonOptions = proto: defaultPort: {
|
|
args = mkOption {
|
|
type = with types; listOf str;
|
|
default = [];
|
|
description = "Command-line arguments to pass to ${manref "public-inbox-${proto}d" 1}.";
|
|
};
|
|
port = mkOption {
|
|
type = with types; nullOr (either str port);
|
|
default = defaultPort;
|
|
description = ''
|
|
Listening port.
|
|
Beware that public-inbox uses well-known ports number to decide whether to enable TLS or not.
|
|
Set to null and use <code>systemd.sockets.public-inbox-${proto}d.listenStreams</code>
|
|
if you need a more advanced listening.
|
|
'';
|
|
};
|
|
cert = mkOption {
|
|
type = with types; nullOr str;
|
|
default = null;
|
|
example = "/path/to/fullchain.pem";
|
|
description = "Path to TLS certificate to use for connections to ${manref "public-inbox-${proto}d" 1}.";
|
|
};
|
|
key = mkOption {
|
|
type = with types; nullOr str;
|
|
default = null;
|
|
example = "/path/to/key.pem";
|
|
description = "Path to TLS key to use for connections to ${manref "public-inbox-${proto}d" 1}.";
|
|
};
|
|
};
|
|
|
|
serviceConfig = srv:
|
|
let proto = removeSuffix "d" srv;
|
|
needNetwork = builtins.hasAttr proto cfg && cfg.${proto}.port == null;
|
|
in {
|
|
serviceConfig = {
|
|
# Enable JIT-compiled C (via Inline::C)
|
|
Environment = [ "PERL_INLINE_DIRECTORY=/run/public-inbox-${srv}/perl-inline" ];
|
|
# NonBlocking is REQUIRED to avoid a race condition
|
|
# if running simultaneous services.
|
|
NonBlocking = true;
|
|
#LimitNOFILE = 30000;
|
|
User = config.users.users."public-inbox".name;
|
|
Group = config.users.groups."public-inbox".name;
|
|
RuntimeDirectory = [
|
|
"public-inbox-${srv}/perl-inline"
|
|
];
|
|
RuntimeDirectoryMode = "700";
|
|
# This is for BindPaths= and BindReadOnlyPaths=
|
|
# to allow traversal of directories they create inside RootDirectory=
|
|
UMask = "0066";
|
|
StateDirectory = ["public-inbox"];
|
|
StateDirectoryMode = "0750";
|
|
WorkingDirectory = stateDir;
|
|
BindReadOnlyPaths = [
|
|
"/etc"
|
|
"/run/systemd"
|
|
"${config.i18n.glibcLocales}"
|
|
] ++
|
|
mapAttrsToList (name: inbox: inbox.description) cfg.inboxes ++
|
|
# Without confinement the whole Nix store
|
|
# is made available to the service
|
|
optionals (!config.systemd.services."public-inbox-${srv}".confinement.enable) [
|
|
"${pkgs.dash}/bin/dash:/bin/sh"
|
|
builtins.storeDir
|
|
];
|
|
# The following options are only for optimizing:
|
|
# systemd-analyze security public-inbox-'*'
|
|
AmbientCapabilities = "";
|
|
CapabilityBoundingSet = "";
|
|
# ProtectClock= adds DeviceAllow=char-rtc r
|
|
DeviceAllow = "";
|
|
LockPersonality = true;
|
|
MemoryDenyWriteExecute = true;
|
|
NoNewPrivileges = true;
|
|
PrivateNetwork = mkDefault (!needNetwork);
|
|
ProcSubset = "pid";
|
|
ProtectClock = true;
|
|
ProtectHome = mkDefault true;
|
|
ProtectHostname = true;
|
|
ProtectKernelLogs = true;
|
|
ProtectProc = "invisible";
|
|
#ProtectSystem = "strict";
|
|
RemoveIPC = true;
|
|
RestrictAddressFamilies = [ "AF_UNIX" ] ++
|
|
optionals needNetwork [ "AF_INET" "AF_INET6" ];
|
|
RestrictNamespaces = true;
|
|
RestrictRealtime = true;
|
|
RestrictSUIDSGID = true;
|
|
SystemCallFilter = [
|
|
"@system-service"
|
|
"~@aio" "~@chown" "~@keyring" "~@memlock" "~@resources"
|
|
# Not removing @setuid and @privileged because Inline::C needs them.
|
|
# Not removing @timer because git upload-pack needs it.
|
|
];
|
|
SystemCallArchitectures = "native";
|
|
|
|
# The following options are redundant when confinement is enabled
|
|
RootDirectory = "/var/empty";
|
|
TemporaryFileSystem = "/";
|
|
PrivateMounts = true;
|
|
MountAPIVFS = true;
|
|
PrivateDevices = true;
|
|
PrivateTmp = true;
|
|
PrivateUsers = true;
|
|
ProtectControlGroups = true;
|
|
ProtectKernelModules = true;
|
|
ProtectKernelTunables = true;
|
|
};
|
|
confinement = {
|
|
# Until we agree upon doing it directly here in NixOS
|
|
# https://github.com/NixOS/nixpkgs/pull/104457#issuecomment-1115768447
|
|
# let the user choose to enable the confinement with:
|
|
# systemd.services.public-inbox-httpd.confinement.enable = true;
|
|
# systemd.services.public-inbox-imapd.confinement.enable = true;
|
|
# systemd.services.public-inbox-init.confinement.enable = true;
|
|
# systemd.services.public-inbox-nntpd.confinement.enable = true;
|
|
#enable = true;
|
|
mode = "full-apivfs";
|
|
# Inline::C needs a /bin/sh, and dash is enough
|
|
binSh = "${pkgs.dash}/bin/dash";
|
|
packages = [
|
|
pkgs.iana-etc
|
|
(getLib pkgs.nss)
|
|
pkgs.tzdata
|
|
];
|
|
};
|
|
};
|
|
in
|
|
|
|
{
|
|
options.services.public-inbox = {
|
|
enable = mkEnableOption "the public-inbox mail archiver";
|
|
package = mkOption {
|
|
type = types.package;
|
|
default = pkgs.public-inbox;
|
|
defaultText = literalExpression "pkgs.public-inbox";
|
|
description = lib.mdDoc "public-inbox package to use.";
|
|
};
|
|
path = mkOption {
|
|
type = with types; listOf package;
|
|
default = [];
|
|
example = literalExpression "with pkgs; [ spamassassin ]";
|
|
description = lib.mdDoc ''
|
|
Additional packages to place in the path of public-inbox-mda,
|
|
public-inbox-watch, etc.
|
|
'';
|
|
};
|
|
inboxes = mkOption {
|
|
description = lib.mdDoc ''
|
|
Inboxes to configure, where attribute names are inbox names.
|
|
'';
|
|
default = {};
|
|
type = types.attrsOf (types.submodule ({name, ...}: {
|
|
freeformType = types.attrsOf iniAtom;
|
|
options.inboxdir = mkOption {
|
|
type = types.str;
|
|
default = "${stateDir}/inboxes/${name}";
|
|
description = lib.mdDoc "The absolute path to the directory which hosts the public-inbox.";
|
|
};
|
|
options.address = mkOption {
|
|
type = with types; listOf str;
|
|
example = "example-discuss@example.org";
|
|
description = lib.mdDoc "The email addresses of the public-inbox.";
|
|
};
|
|
options.url = mkOption {
|
|
type = with types; nullOr str;
|
|
default = null;
|
|
example = "https://example.org/lists/example-discuss";
|
|
description = lib.mdDoc "URL where this inbox can be accessed over HTTP.";
|
|
};
|
|
options.description = mkOption {
|
|
type = types.str;
|
|
example = "user/dev discussion of public-inbox itself";
|
|
description = lib.mdDoc "User-visible description for the repository.";
|
|
apply = pkgs.writeText "public-inbox-description-${name}";
|
|
};
|
|
options.newsgroup = mkOption {
|
|
type = with types; nullOr str;
|
|
default = null;
|
|
description = lib.mdDoc "NNTP group name for the inbox.";
|
|
};
|
|
options.watch = mkOption {
|
|
type = with types; listOf str;
|
|
default = [];
|
|
description = "Paths for ${manref "public-inbox-watch" 1} to monitor for new mail.";
|
|
example = [ "maildir:/path/to/test.example.com.git" ];
|
|
};
|
|
options.watchheader = mkOption {
|
|
type = with types; nullOr str;
|
|
default = null;
|
|
example = "List-Id:<test@example.com>";
|
|
description = ''
|
|
If specified, ${manref "public-inbox-watch" 1} will only process
|
|
mail containing a matching header.
|
|
'';
|
|
};
|
|
options.coderepo = mkOption {
|
|
type = (types.listOf (types.enum (attrNames cfg.settings.coderepo))) // {
|
|
description = "list of coderepo names";
|
|
};
|
|
default = [];
|
|
description = lib.mdDoc "Nicknames of a 'coderepo' section associated with the inbox.";
|
|
};
|
|
}));
|
|
};
|
|
imap = {
|
|
enable = mkEnableOption "the public-inbox IMAP server";
|
|
} // publicInboxDaemonOptions "imap" 993;
|
|
http = {
|
|
enable = mkEnableOption "the public-inbox HTTP server";
|
|
mounts = mkOption {
|
|
type = with types; listOf str;
|
|
default = [ "/" ];
|
|
example = [ "/lists/archives" ];
|
|
description = lib.mdDoc ''
|
|
Root paths or URLs that public-inbox will be served on.
|
|
If domain parts are present, only requests to those
|
|
domains will be accepted.
|
|
'';
|
|
};
|
|
args = (publicInboxDaemonOptions "http" 80).args;
|
|
port = mkOption {
|
|
type = with types; nullOr (either str port);
|
|
default = 80;
|
|
example = "/run/public-inbox-httpd.sock";
|
|
description = ''
|
|
Listening port or systemd's ListenStream= entry
|
|
to be used as a reverse proxy, eg. in nginx:
|
|
<code>locations."/inbox".proxyPass = "http://unix:''${config.services.public-inbox.http.port}:/inbox";</code>
|
|
Set to null and use <code>systemd.sockets.public-inbox-httpd.listenStreams</code>
|
|
if you need a more advanced listening.
|
|
'';
|
|
};
|
|
};
|
|
mda = {
|
|
enable = mkEnableOption "the public-inbox Mail Delivery Agent";
|
|
args = mkOption {
|
|
type = with types; listOf str;
|
|
default = [];
|
|
description = "Command-line arguments to pass to ${manref "public-inbox-mda" 1}.";
|
|
};
|
|
};
|
|
postfix.enable = mkEnableOption "the integration into Postfix";
|
|
nntp = {
|
|
enable = mkEnableOption "the public-inbox NNTP server";
|
|
} // publicInboxDaemonOptions "nntp" 563;
|
|
spamAssassinRules = mkOption {
|
|
type = with types; nullOr path;
|
|
default = "${cfg.package.sa_config}/user/.spamassassin/user_prefs";
|
|
defaultText = literalExpression "\${cfg.package.sa_config}/user/.spamassassin/user_prefs";
|
|
description = lib.mdDoc "SpamAssassin configuration specific to public-inbox.";
|
|
};
|
|
settings = mkOption {
|
|
description = lib.mdDoc ''
|
|
Settings for the [public-inbox config file](https://public-inbox.org/public-inbox-config.html).
|
|
'';
|
|
default = {};
|
|
type = types.submodule {
|
|
freeformType = gitIni.type;
|
|
options.publicinbox = mkOption {
|
|
default = {};
|
|
description = lib.mdDoc "public inboxes";
|
|
type = types.submodule {
|
|
freeformType = with types; /*inbox name*/attrsOf (/*inbox option name*/attrsOf /*inbox option value*/iniAtom);
|
|
options.css = mkOption {
|
|
type = with types; listOf str;
|
|
default = [];
|
|
description = lib.mdDoc "The local path name of a CSS file for the PSGI web interface.";
|
|
};
|
|
options.nntpserver = mkOption {
|
|
type = with types; listOf str;
|
|
default = [];
|
|
example = [ "nntp://news.public-inbox.org" "nntps://news.public-inbox.org" ];
|
|
description = lib.mdDoc "NNTP URLs to this public-inbox instance";
|
|
};
|
|
options.wwwlisting = mkOption {
|
|
type = with types; enum [ "all" "404" "match=domain" ];
|
|
default = "404";
|
|
description = lib.mdDoc ''
|
|
Controls which lists (if any) are listed for when the root
|
|
public-inbox URL is accessed over HTTP.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
options.publicinboxmda.spamcheck = mkOption {
|
|
type = with types; enum [ "spamc" "none" ];
|
|
default = "none";
|
|
description = ''
|
|
If set to spamc, ${manref "public-inbox-watch" 1} will filter spam
|
|
using SpamAssassin.
|
|
'';
|
|
};
|
|
options.publicinboxwatch.spamcheck = mkOption {
|
|
type = with types; enum [ "spamc" "none" ];
|
|
default = "none";
|
|
description = ''
|
|
If set to spamc, ${manref "public-inbox-watch" 1} will filter spam
|
|
using SpamAssassin.
|
|
'';
|
|
};
|
|
options.publicinboxwatch.watchspam = mkOption {
|
|
type = with types; nullOr str;
|
|
default = null;
|
|
example = "maildir:/path/to/spam";
|
|
description = lib.mdDoc ''
|
|
If set, mail in this maildir will be trained as spam and
|
|
deleted from all watched inboxes
|
|
'';
|
|
};
|
|
options.coderepo = mkOption {
|
|
default = {};
|
|
description = lib.mdDoc "code repositories";
|
|
type = types.attrsOf (types.submodule {
|
|
freeformType = types.attrsOf iniAtom;
|
|
options.cgitUrl = mkOption {
|
|
type = types.str;
|
|
description = lib.mdDoc "URL of a cgit instance";
|
|
};
|
|
options.dir = mkOption {
|
|
type = types.str;
|
|
description = lib.mdDoc "Path to a git repository";
|
|
};
|
|
});
|
|
};
|
|
};
|
|
};
|
|
openFirewall = mkEnableOption "opening the firewall when using a port option";
|
|
};
|
|
config = mkIf cfg.enable {
|
|
assertions = [
|
|
{ assertion = config.services.spamassassin.enable || !useSpamAssassin;
|
|
message = ''
|
|
public-inbox is configured to use SpamAssassin, but
|
|
services.spamassassin.enable is false. If you don't need
|
|
spam checking, set `services.public-inbox.settings.publicinboxmda.spamcheck' and
|
|
`services.public-inbox.settings.publicinboxwatch.spamcheck' to null.
|
|
'';
|
|
}
|
|
{ assertion = cfg.path != [] || !useSpamAssassin;
|
|
message = ''
|
|
public-inbox is configured to use SpamAssassin, but there is
|
|
no spamc executable in services.public-inbox.path. If you
|
|
don't need spam checking, set
|
|
`services.public-inbox.settings.publicinboxmda.spamcheck' and
|
|
`services.public-inbox.settings.publicinboxwatch.spamcheck' to null.
|
|
'';
|
|
}
|
|
];
|
|
services.public-inbox.settings =
|
|
filterAttrsRecursive (n: v: v != null) {
|
|
publicinbox = mapAttrs (n: filterAttrs (n: v: n != "description")) cfg.inboxes;
|
|
};
|
|
users = {
|
|
users.public-inbox = {
|
|
home = stateDir;
|
|
group = "public-inbox";
|
|
isSystemUser = true;
|
|
};
|
|
groups.public-inbox = {};
|
|
};
|
|
networking.firewall = mkIf cfg.openFirewall
|
|
{ allowedTCPPorts = mkMerge
|
|
(map (proto: (mkIf (cfg.${proto}.enable && types.port.check cfg.${proto}.port) [ cfg.${proto}.port ]))
|
|
["imap" "http" "nntp"]);
|
|
};
|
|
services.postfix = mkIf (cfg.postfix.enable && cfg.mda.enable) {
|
|
# Not sure limiting to 1 is necessary, but better safe than sorry.
|
|
config.public-inbox_destination_recipient_limit = "1";
|
|
|
|
# Register the addresses as existing
|
|
virtual =
|
|
concatStringsSep "\n" (mapAttrsToList (_: inbox:
|
|
concatMapStringsSep "\n" (address:
|
|
"${address} ${address}"
|
|
) inbox.address
|
|
) cfg.inboxes);
|
|
|
|
# Deliver the addresses with the public-inbox transport
|
|
transport =
|
|
concatStringsSep "\n" (mapAttrsToList (_: inbox:
|
|
concatMapStringsSep "\n" (address:
|
|
"${address} public-inbox:${address}"
|
|
) inbox.address
|
|
) cfg.inboxes);
|
|
|
|
# The public-inbox transport
|
|
masterConfig.public-inbox = {
|
|
type = "unix";
|
|
privileged = true; # Required for user=
|
|
command = "pipe";
|
|
args = [
|
|
"flags=X" # Report as a final delivery
|
|
"user=${with config.users; users."public-inbox".name + ":" + groups."public-inbox".name}"
|
|
# Specifying a nexthop when using the transport
|
|
# (eg. test public-inbox:test) allows to
|
|
# receive mails with an extension (eg. test+foo).
|
|
"argv=${pkgs.writeShellScript "public-inbox-transport" ''
|
|
export HOME="${stateDir}"
|
|
export ORIGINAL_RECIPIENT="''${2:-1}"
|
|
export PATH="${makeBinPath cfg.path}:$PATH"
|
|
exec ${cfg.package}/bin/public-inbox-mda ${escapeShellArgs cfg.mda.args}
|
|
''} \${original_recipient} \${nexthop}"
|
|
];
|
|
};
|
|
};
|
|
systemd.sockets = mkMerge (map (proto:
|
|
mkIf (cfg.${proto}.enable && cfg.${proto}.port != null)
|
|
{ "public-inbox-${proto}d" = {
|
|
listenStreams = [ (toString cfg.${proto}.port) ];
|
|
wantedBy = [ "sockets.target" ];
|
|
};
|
|
}
|
|
) [ "imap" "http" "nntp" ]);
|
|
systemd.services = mkMerge [
|
|
(mkIf cfg.imap.enable
|
|
{ public-inbox-imapd = mkMerge [(serviceConfig "imapd") {
|
|
after = [ "public-inbox-init.service" "public-inbox-watch.service" ];
|
|
requires = [ "public-inbox-init.service" ];
|
|
serviceConfig = {
|
|
ExecStart = escapeShellArgs (
|
|
[ "${cfg.package}/bin/public-inbox-imapd" ] ++
|
|
cfg.imap.args ++
|
|
optionals (cfg.imap.cert != null) [ "--cert" cfg.imap.cert ] ++
|
|
optionals (cfg.imap.key != null) [ "--key" cfg.imap.key ]
|
|
);
|
|
};
|
|
}];
|
|
})
|
|
(mkIf cfg.http.enable
|
|
{ public-inbox-httpd = mkMerge [(serviceConfig "httpd") {
|
|
after = [ "public-inbox-init.service" "public-inbox-watch.service" ];
|
|
requires = [ "public-inbox-init.service" ];
|
|
serviceConfig = {
|
|
ExecStart = escapeShellArgs (
|
|
[ "${cfg.package}/bin/public-inbox-httpd" ] ++
|
|
cfg.http.args ++
|
|
# See https://public-inbox.org/public-inbox.git/tree/examples/public-inbox.psgi
|
|
# for upstream's example.
|
|
[ (pkgs.writeText "public-inbox.psgi" ''
|
|
#!${cfg.package.fullperl} -w
|
|
use strict;
|
|
use warnings;
|
|
use Plack::Builder;
|
|
use PublicInbox::WWW;
|
|
|
|
my $www = PublicInbox::WWW->new;
|
|
$www->preload;
|
|
|
|
builder {
|
|
# If reached through a reverse proxy,
|
|
# make it transparent by resetting some HTTP headers
|
|
# used by public-inbox to generate URIs.
|
|
enable 'ReverseProxy';
|
|
|
|
# No need to send a response body if it's an HTTP HEAD requests.
|
|
enable 'Head';
|
|
|
|
# Route according to configured domains and root paths.
|
|
${concatMapStrings (path: ''
|
|
mount q(${path}) => sub { $www->call(@_); };
|
|
'') cfg.http.mounts}
|
|
}
|
|
'') ]
|
|
);
|
|
};
|
|
}];
|
|
})
|
|
(mkIf cfg.nntp.enable
|
|
{ public-inbox-nntpd = mkMerge [(serviceConfig "nntpd") {
|
|
after = [ "public-inbox-init.service" "public-inbox-watch.service" ];
|
|
requires = [ "public-inbox-init.service" ];
|
|
serviceConfig = {
|
|
ExecStart = escapeShellArgs (
|
|
[ "${cfg.package}/bin/public-inbox-nntpd" ] ++
|
|
cfg.nntp.args ++
|
|
optionals (cfg.nntp.cert != null) [ "--cert" cfg.nntp.cert ] ++
|
|
optionals (cfg.nntp.key != null) [ "--key" cfg.nntp.key ]
|
|
);
|
|
};
|
|
}];
|
|
})
|
|
(mkIf (any (inbox: inbox.watch != []) (attrValues cfg.inboxes)
|
|
|| cfg.settings.publicinboxwatch.watchspam != null)
|
|
{ public-inbox-watch = mkMerge [(serviceConfig "watch") {
|
|
inherit (cfg) path;
|
|
wants = [ "public-inbox-init.service" ];
|
|
requires = [ "public-inbox-init.service" ] ++
|
|
optional (cfg.settings.publicinboxwatch.spamcheck == "spamc") "spamassassin.service";
|
|
wantedBy = [ "multi-user.target" ];
|
|
serviceConfig = {
|
|
ExecStart = "${cfg.package}/bin/public-inbox-watch";
|
|
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
|
};
|
|
}];
|
|
})
|
|
({ public-inbox-init = let
|
|
PI_CONFIG = gitIni.generate "public-inbox.ini"
|
|
(filterAttrsRecursive (n: v: v != null) cfg.settings);
|
|
in mkMerge [(serviceConfig "init") {
|
|
wantedBy = [ "multi-user.target" ];
|
|
restartIfChanged = true;
|
|
restartTriggers = [ PI_CONFIG ];
|
|
script = ''
|
|
set -ux
|
|
install -D -p ${PI_CONFIG} ${stateDir}/.public-inbox/config
|
|
'' + optionalString useSpamAssassin ''
|
|
install -m 0700 -o spamd -d ${stateDir}/.spamassassin
|
|
${optionalString (cfg.spamAssassinRules != null) ''
|
|
ln -sf ${cfg.spamAssassinRules} ${stateDir}/.spamassassin/user_prefs
|
|
''}
|
|
'' + concatStrings (mapAttrsToList (name: inbox: ''
|
|
if [ ! -e ${stateDir}/inboxes/${escapeShellArg name} ]; then
|
|
# public-inbox-init creates an inbox and adds it to a config file.
|
|
# It tries to atomically write the config file by creating
|
|
# another file in the same directory, and renaming it.
|
|
# This has the sad consequence that we can't use
|
|
# /dev/null, or it would try to create a file in /dev.
|
|
conf_dir="$(mktemp -d)"
|
|
|
|
PI_CONFIG=$conf_dir/conf \
|
|
${cfg.package}/bin/public-inbox-init -V2 \
|
|
${escapeShellArgs ([ name "${stateDir}/inboxes/${name}" inbox.url ] ++ inbox.address)}
|
|
|
|
rm -rf $conf_dir
|
|
fi
|
|
|
|
ln -sf ${inbox.description} \
|
|
${stateDir}/inboxes/${escapeShellArg name}/description
|
|
|
|
export GIT_DIR=${stateDir}/inboxes/${escapeShellArg name}/all.git
|
|
if test -d "$GIT_DIR"; then
|
|
# Config is inherited by each epoch repository,
|
|
# so just needs to be set for all.git.
|
|
${pkgs.git}/bin/git config core.sharedRepository 0640
|
|
fi
|
|
'') cfg.inboxes
|
|
) + ''
|
|
shopt -s nullglob
|
|
for inbox in ${stateDir}/inboxes/*/; do
|
|
# This should be idempotent, but only do it for new
|
|
# inboxes anyway because it's only needed once, and could
|
|
# be slow for large pre-existing inboxes.
|
|
ls -1 "$inbox" | grep -q '^xap' ||
|
|
${cfg.package}/bin/public-inbox-index "$inbox"
|
|
done
|
|
'';
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
StateDirectory = [
|
|
"public-inbox/.public-inbox"
|
|
"public-inbox/.public-inbox/emergency"
|
|
"public-inbox/inboxes"
|
|
];
|
|
};
|
|
}];
|
|
})
|
|
];
|
|
environment.systemPackages = with pkgs; [ cfg.package ];
|
|
};
|
|
meta.maintainers = with lib.maintainers; [ julm qyliss ];
|
|
}
|