himalaya: improve derivation for v0.7.X (#3664)

* himalaya: add soywod to maintainers

* himalaya: make the config safer

Also added two services and more tests.

* himalaya: fix doc + typos

* himalaya: use freeform

* himalaya: run ./format

* himalaya: make use of mkPackageOption
This commit is contained in:
Clément DOUIN 2023-05-04 12:28:08 +02:00 committed by GitHub
parent 514c0a71f4
commit 6abb775e75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 386 additions and 135 deletions

View file

@ -363,6 +363,15 @@
github = "lukasngl";
githubId = 69244516;
};
soywod = {
name = "Clément DOUIN";
email = "clement.douin@posteo.net";
matrix = "@soywod:matrix.org";
github = "soywod";
githubId = 10437171;
keys =
[{ fingerprint = "75F0 AB7C FE01 D077 AEE6 CAFD 353E 4A18 EE0F AB72"; }];
};
toastal = {
email = "toastal+nix@posteo.net";
matrix = "@toastal:matrix.org";

View file

@ -1,140 +1,253 @@
{ config, lib, pkgs, ... }:
let
cfg = config.programs.himalaya;
enabledAccounts =
lib.filterAttrs (_: a: a.himalaya.enable) (config.accounts.email.accounts);
# aliases
inherit (config.programs) himalaya;
tomlFormat = pkgs.formats.toml { };
himalayaConfig = let
toHimalayaConfig = account:
{
# attrs util that removes entries containing a null value
compactAttrs = lib.filterAttrs (_: val: !isNull val);
# make a himalaya config from a home-manager email account config
mkAccountConfig = _: account:
let
globalConfig = {
email = account.address;
display-name = account.realName;
default = account.primary;
mailboxes = {
folder-aliases = {
inbox = account.folders.inbox;
sent = account.folders.sent;
draft = account.folders.drafts;
# NOTE: himalaya does not support configuring the name of the trash folder
drafts = account.folders.drafts;
trash = account.folders.trash;
};
} // (lib.optionalAttrs (account.signature.showSignature == "append") {
# FIXME: signature cannot be attached
signature = account.signature.text;
signature-delim = account.signature.delimiter;
}) // (if account.himalaya.backend == null then {
backend = "none";
} else if account.himalaya.backend == "imap" then {
# FIXME: does not support disabling TLS altogether
# NOTE: does not accept sequence of strings for password commands
backend = account.himalaya.backend;
imap-login = account.userName;
imap-passwd-cmd = lib.escapeShellArgs account.passwordCommand;
};
signatureConfig =
lib.optionalAttrs (account.signature.showSignature == "append") {
# TODO: signature cannot be attached yet
# https://todo.sr.ht/~soywod/himalaya/27
signature = account.signature.text;
signature-delim = account.signature.delimiter;
};
imapConfig = lib.optionalAttrs (!isNull account.imap) (compactAttrs {
backend = "imap";
imap-host = account.imap.host;
imap-port = account.imap.port;
imap-ssl = account.imap.tls.enable;
imap-starttls = account.imap.tls.useStartTls;
} else if account.himalaya.backend == "maildir" then {
backend = account.himalaya.backend;
maildir-root-dir = account.maildirBasePath;
} else
throw "Unsupported backend: ${account.himalaya.backend}")
// (if account.himalaya.sender == null then {
sender = "none";
} else if account.himalaya.sender == "smtp" then {
sender = account.himalaya.sender;
smtp-login = account.userName;
smtp-passwd-cmd = lib.escapeShellArgs account.passwordCommand;
imap-login = account.userName;
imap-passwd-cmd = builtins.concatStringsSep " " account.passwordCommand;
});
maildirConfig =
lib.optionalAttrs (isNull account.imap && !isNull account.maildir)
(compactAttrs {
backend = "maildir";
maildir-root-dir = account.maildir.absPath;
});
smtpConfig = lib.optionalAttrs (!isNull account.smtp) (compactAttrs {
sender = "smtp";
smtp-host = account.smtp.host;
smtp-port = account.smtp.port;
smtp-ssl = account.smtp.tls.enable;
smtp-starttls = account.smtp.tls.useStartTls;
} else if account.himalaya.sender == "sendmail" then {
sender = account.himalaya.sender;
} else
throw "Unsupported sender: ${account.himalaya.sender}")
// account.himalaya.settings;
in {
# NOTE: will not start without this configured, but each account overrides it
display-name = "";
} // cfg.settings // (lib.mapAttrs (_: toHimalayaConfig) enabledAccounts);
in {
meta.maintainers = with lib.hm.maintainers; [ toastal ];
smtp-login = account.userName;
smtp-passwd-cmd = builtins.concatStringsSep " " account.passwordCommand;
});
options = with lib; {
programs.himalaya = {
enable = mkEnableOption "himalaya mail client";
sendmailConfig =
lib.optionalAttrs (isNull account.smtp) { sender = "sendmail"; };
package = mkOption {
type = types.package;
default = pkgs.himalaya;
defaultText = literalExpression "pkgs.himalaya";
description = ''
Package providing the <command>himalaya</command> mail client.
'';
config = globalConfig // signatureConfig // imapConfig // maildirConfig
// smtpConfig // sendmailConfig;
in lib.recursiveUpdate config account.himalaya.settings;
# make a systemd service config from a name and a description
mkServiceConfig = name: desc:
let
inherit (config.services."himalaya-${name}") enable environment settings;
optionalArg = key:
if (key ? settings && !isNull settings."${key}") then
[ "--${key} ${settings."${key}"}" ]
else
[ ];
in {
"himalaya-${name}" = lib.mkIf enable {
Unit = {
Description = desc;
After = [ "network.target" ];
};
Install = { WantedBy = [ "default.target" ]; };
Service = {
ExecStart = lib.concatStringsSep " "
([ "${himalaya.package}/bin/himalaya" ] ++ optionalArg "account"
++ [ name ] ++ optionalArg "keepalive");
ExecSearchPath = "/bin";
Environment =
lib.mapAttrsToList (key: val: "${key}=${val}") environment;
Restart = "always";
RestartSec = 10;
};
};
};
settings = mkOption {
type = tomlFormat.type;
in {
meta.maintainers = with lib.hm.maintainers; [ soywod toastal ];
options = {
programs.himalaya = {
enable = lib.mkEnableOption "Enable the Himalaya email client.";
package = lib.mkPackageOption pkgs "himalaya" { };
settings = lib.mkOption {
type = lib.types.submodule { freeformType = tomlFormat.type; };
default = { };
example = lib.literalExpression ''
{
email-listing-page-size = 50;
watch-cmds = [ "mbsync -a" ]
}
'';
description = ''
Global <command>himalaya</command> configuration values.
Himalaya global configuration.
See <link xlink:href="https://pimalaya.org/himalaya/cli/configuration/global.html"/> for supported values.
'';
};
};
accounts.email.accounts = mkOption {
type = with types;
attrsOf (submodule {
options.himalaya = {
enable = mkEnableOption ''
the himalaya mail client for this account
services = {
himalaya-notify = {
enable =
lib.mkEnableOption "Enable the Himalaya new emails notifier service.";
environment = lib.mkOption {
type = with lib.types; attrsOf str;
default = { };
example = lib.literalExpression ''
{
"PASSWORD_STORE_DIR" = "~/.password-store";
}
'';
description = ''
Extra environment variables to be exported in the service.
'';
};
settings = {
account = lib.mkOption {
type = with lib.types; nullOr str;
default = null;
example = "gmail";
description = ''
Name of the account the notifier should be started for. If
no account is given, the default one is used.
'';
backend = mkOption {
# TODO: “notmuch” (requires compile flag for himalaya, libnotmuch)
type = types.nullOr (types.enum [ "imap" "maildir" ]);
description = ''
The method for which <command>himalaya</command> will fetch, store,
etc. mail.
'';
};
sender = mkOption {
type = types.nullOr (types.enum [ "smtp" "sendmail" ]);
description = ''
The method for which <command>himalaya</command> will send mail.
'';
};
settings = mkOption {
type = tomlFormat.type;
default = { };
example = lib.literalExpression ''
{
default-page-size = 50;
}
'';
description = ''
Extra settings to add to this <command>himalaya</command>
account configuration.
'';
};
};
});
keepalive = lib.mkOption {
type = with lib.types; nullOr int;
default = null;
example = "500";
description = ''
Notifier lifetime of the IDLE session (in seconds).
'';
};
};
};
himalaya-watch = {
enable = lib.mkEnableOption
"Enable the Himalaya folder changes watcher service.";
environment = lib.mkOption {
type = with lib.types; attrsOf str;
default = { };
example = lib.literalExpression ''
{
"PASSWORD_STORE_DIR" = "~/.password-store";
}
'';
description = ''
Extra environment variables to be exported in the service.
'';
};
settings = {
account = lib.mkOption {
type = with lib.types; nullOr str;
default = null;
example = "gmail";
description = ''
Name of the account the watcher should be started for. If
no account is given, the default one is used.
'';
};
keepalive = lib.mkOption {
type = with lib.types; nullOr int;
default = null;
example = "500";
description = ''
Watcher lifetime of the IDLE session (in seconds).
'';
};
};
};
};
accounts.email.accounts = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule {
options.himalaya = {
enable = lib.mkEnableOption "Enable Himalaya for this email account.";
# TODO: remove me for the next release
backend = lib.mkOption {
type = with lib.types; nullOr str;
default = null;
description = ''
Specifying 'accounts.email.accounts.*.himalaya.backend' is deprecated,
set 'accounts.email.accounts.*.himalaya.settings.backend' instead.
'';
};
# TODO: remove me for the next release
sender = lib.mkOption {
type = with lib.types; nullOr str;
description = ''
Specifying 'accounts.email.accounts.*.himalaya.sender' is deprecated,
set 'accounts.email.accounts.*.himalaya.settings.sender' instead.
'';
};
settings = lib.mkOption {
type = lib.types.submodule { freeformType = tomlFormat.type; };
default = { };
description = ''
Himalaya configuration for this email account.
See <link xlink:href="https://pimalaya.org/himalaya/cli/configuration/account.html"/> for supported values.
'';
};
};
});
};
};
config = lib.mkIf cfg.enable {
home.packages = [ cfg.package ];
config = lib.mkIf himalaya.enable {
home.packages = [ himalaya.package ];
xdg.configFile."himalaya/config.toml".source =
tomlFormat.generate "himalaya-config.toml" himalayaConfig;
xdg.configFile."himalaya/config.toml".source = let
enabledAccounts = lib.filterAttrs (_: account: account.himalaya.enable)
config.accounts.email.accounts;
accountsConfig = lib.mapAttrs mkAccountConfig enabledAccounts;
globalConfig = compactAttrs himalaya.settings;
allConfig = globalConfig // accountsConfig;
in tomlFormat.generate "himalaya-config.toml" allConfig;
systemd.user.services = { }
// mkServiceConfig "notify" "Himalaya new emails notifier service"
// mkServiceConfig "watch" "Himalaya folder changes watcher service";
# TODO: remove me for the next release
warnings = (lib.optional ("backend" ? himalaya && !isNull himalaya.backend)
"Specifying 'accounts.email.accounts.*.himalaya.backend' is deprecated, set 'accounts.email.accounts.*.himalaya.settings.backend' instead")
++ (lib.optional ("sender" ? himalaya && !isNull himalaya.sender)
"Specifying 'accounts.email.accounts.*.himalaya.sender' is deprecated, set 'accounts.email.accounts.*.himalaya.settings.sender' instead.");
};
}

View file

@ -1,25 +1,24 @@
display-name = ""
downloads-dir = "/data/download"
["hm@example.com"]
backend = "imap"
default = true
display-name = "H. M. Test"
email = "hm@example.com"
email-listing-page-size = 50
imap-host = "imap.example.com"
imap-login = "home.manager"
imap-passwd-cmd = "'password-command'"
imap-port = 995
imap-passwd-cmd = "password-command"
imap-port = 993
imap-ssl = true
imap-starttls = false
sender = "smtp"
smtp-host = "smtp.example.com"
smtp-login = "home.manager"
smtp-passwd-cmd = "'password-command'"
smtp-passwd-cmd = "password-command"
smtp-port = 465
smtp-ssl = true
smtp-starttls = false
["hm@example.com".mailboxes]
draft = "Drafts"
inbox = "In"
sent = "Out"
["hm@example.com".folder-aliases]
drafts = "Drafts"
inbox = "Inbox"
sent = "Sent"
trash = "Trash"

View file

@ -0,0 +1,28 @@
{ config, lib, pkgs, ... }:
with lib;
{
imports = [ ../../accounts/email-test-accounts.nix ];
accounts.email.accounts = {
"hm@example.com" = {
imap.port = 993;
smtp.port = 465;
himalaya.enable = true;
himalaya.backend = "deprecated";
himalaya.sender = "deprecated";
};
};
programs.himalaya = { enable = true; };
test.stubs.himalaya = { };
nmt.script = ''
assertFileExists home-files/.config/himalaya/config.toml
assertFileContent home-files/.config/himalaya/config.toml ${
./basic-expected.toml
}
'';
}

View file

@ -1 +1,5 @@
{ himalaya = ./himalaya.nix; }
{
himalaya-basic = ./basic.nix;
himalaya-imap-smtp = ./imap-smtp.nix;
himalaya-maildir-sendmail = ./maildir-sendmail.nix;
}

View file

@ -0,0 +1,29 @@
email-listing-page-size = 40
["hm@example.com"]
backend = "imap"
default = true
display-name = "H. M. Test"
email = "hm@example.com"
email-listing-page-size = 50
folder-listing-page-size = 50
imap-host = "imap.example.com"
imap-login = "home.manager"
imap-passwd-cmd = "password-command"
imap-port = 143
imap-ssl = false
imap-starttls = false
sender = "smtp"
smtp-host = "smtp.example.com"
smtp-login = "home.manager"
smtp-passwd-cmd = "password-command"
smtp-port = 465
smtp-ssl = true
smtp-starttls = true
["hm@example.com".folder-aliases]
custom = "Custom"
drafts = "D"
inbox = "In2"
sent = "Out"
trash = "Trash"

View file

@ -0,0 +1,58 @@
{ config, lib, pkgs, ... }:
with lib;
{
accounts.email.accounts = {
"hm@example.com" = {
primary = true;
address = "hm@example.com";
userName = "home.manager";
realName = "H. M. Test";
passwordCommand = "password-command";
imap = {
host = "imap.example.com";
port = 143;
tls = { enable = false; };
};
smtp = {
host = "smtp.example.com";
port = 465;
tls = {
enable = true;
useStartTls = true;
};
};
folders = {
inbox = "In";
sent = "Out";
drafts = "D";
};
himalaya = {
enable = true;
settings = {
folder-listing-page-size = 50;
email-listing-page-size = 50;
folder-aliases = {
inbox = "In2";
custom = "Custom";
};
};
};
};
};
programs.himalaya = {
enable = true;
settings = { email-listing-page-size = 40; };
};
test.stubs.himalaya = { };
nmt.script = ''
assertFileExists home-files/.config/himalaya/config.toml
assertFileContent home-files/.config/himalaya/config.toml ${
./imap-smtp-expected.toml
}
'';
}

View file

@ -0,0 +1,16 @@
email-listing-page-size = 50
["hm@example.com"]
backend = "maildir"
default = true
display-name = "H. M. Test"
email = "hm@example.com"
maildir-root-dir = "/home/hm-user/Maildir/hm@example.com"
sender = "sendmail"
sendmail-cmd = "msmtp"
["hm@example.com".folder-aliases]
drafts = "Drafts"
inbox = "Inbox"
sent = "Sent"
trash = "Deleted"

View file

@ -3,32 +3,27 @@
with lib;
{
imports = [ ../../accounts/email-test-accounts.nix ];
accounts.email.accounts = {
"hm@example.com" = {
primary = true;
address = "hm@example.com";
userName = "home.manager";
realName = "H. M. Test";
passwordCommand = "password-command";
folders = { trash = "Deleted"; };
himalaya = {
enable = true;
backend = "imap";
sender = "smtp";
settings = { email-listing-page-size = 50; };
settings = {
sender = "sendmail";
sendmail-cmd = "msmtp";
};
};
folders = {
inbox = "In";
sent = "Out";
drafts = "Drafts";
};
imap.port = 995;
smtp.port = 465;
};
};
programs.himalaya = {
enable = true;
settings = { downloads-dir = "/data/download"; };
settings = { email-listing-page-size = 50; };
};
test.stubs.himalaya = { };
@ -36,7 +31,7 @@ with lib;
nmt.script = ''
assertFileExists home-files/.config/himalaya/config.toml
assertFileContent home-files/.config/himalaya/config.toml ${
./himalaya-expected.toml
./maildir-sendmail-expected.toml
}
'';
}