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"; github = "lukasngl";
githubId = 69244516; 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 = { toastal = {
email = "toastal+nix@posteo.net"; email = "toastal+nix@posteo.net";
matrix = "@toastal:matrix.org"; matrix = "@toastal:matrix.org";

View file

@ -1,140 +1,253 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
cfg = config.programs.himalaya; # aliases
inherit (config.programs) himalaya;
enabledAccounts =
lib.filterAttrs (_: a: a.himalaya.enable) (config.accounts.email.accounts);
tomlFormat = pkgs.formats.toml { }; tomlFormat = pkgs.formats.toml { };
himalayaConfig = let # attrs util that removes entries containing a null value
toHimalayaConfig = account: compactAttrs = lib.filterAttrs (_: val: !isNull val);
{
# make a himalaya config from a home-manager email account config
mkAccountConfig = _: account:
let
globalConfig = {
email = account.address; email = account.address;
display-name = account.realName; display-name = account.realName;
default = account.primary; default = account.primary;
folder-aliases = {
mailboxes = {
inbox = account.folders.inbox; inbox = account.folders.inbox;
sent = account.folders.sent; sent = account.folders.sent;
draft = account.folders.drafts; drafts = account.folders.drafts;
# NOTE: himalaya does not support configuring the name of the trash folder trash = account.folders.trash;
}; };
} // (lib.optionalAttrs (account.signature.showSignature == "append") { };
# FIXME: signature cannot be attached
signature = account.signature.text; signatureConfig =
signature-delim = account.signature.delimiter; lib.optionalAttrs (account.signature.showSignature == "append") {
}) // (if account.himalaya.backend == null then { # TODO: signature cannot be attached yet
backend = "none"; # https://todo.sr.ht/~soywod/himalaya/27
} else if account.himalaya.backend == "imap" then { signature = account.signature.text;
# FIXME: does not support disabling TLS altogether signature-delim = account.signature.delimiter;
# NOTE: does not accept sequence of strings for password commands };
backend = account.himalaya.backend;
imap-login = account.userName; imapConfig = lib.optionalAttrs (!isNull account.imap) (compactAttrs {
imap-passwd-cmd = lib.escapeShellArgs account.passwordCommand; backend = "imap";
imap-host = account.imap.host; imap-host = account.imap.host;
imap-port = account.imap.port; imap-port = account.imap.port;
imap-ssl = account.imap.tls.enable;
imap-starttls = account.imap.tls.useStartTls; imap-starttls = account.imap.tls.useStartTls;
} else if account.himalaya.backend == "maildir" then { imap-login = account.userName;
backend = account.himalaya.backend; imap-passwd-cmd = builtins.concatStringsSep " " account.passwordCommand;
maildir-root-dir = account.maildirBasePath; });
} else
throw "Unsupported backend: ${account.himalaya.backend}") maildirConfig =
// (if account.himalaya.sender == null then { lib.optionalAttrs (isNull account.imap && !isNull account.maildir)
sender = "none"; (compactAttrs {
} else if account.himalaya.sender == "smtp" then { backend = "maildir";
sender = account.himalaya.sender; maildir-root-dir = account.maildir.absPath;
smtp-login = account.userName; });
smtp-passwd-cmd = lib.escapeShellArgs account.passwordCommand;
smtpConfig = lib.optionalAttrs (!isNull account.smtp) (compactAttrs {
sender = "smtp";
smtp-host = account.smtp.host; smtp-host = account.smtp.host;
smtp-port = account.smtp.port; smtp-port = account.smtp.port;
smtp-ssl = account.smtp.tls.enable;
smtp-starttls = account.smtp.tls.useStartTls; smtp-starttls = account.smtp.tls.useStartTls;
} else if account.himalaya.sender == "sendmail" then { smtp-login = account.userName;
sender = account.himalaya.sender; smtp-passwd-cmd = builtins.concatStringsSep " " account.passwordCommand;
} 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 ];
options = with lib; { sendmailConfig =
programs.himalaya = { lib.optionalAttrs (isNull account.smtp) { sender = "sendmail"; };
enable = mkEnableOption "himalaya mail client";
package = mkOption { config = globalConfig // signatureConfig // imapConfig // maildirConfig
type = types.package; // smtpConfig // sendmailConfig;
default = pkgs.himalaya;
defaultText = literalExpression "pkgs.himalaya"; in lib.recursiveUpdate config account.himalaya.settings;
description = ''
Package providing the <command>himalaya</command> mail client. # 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 { in {
type = tomlFormat.type; 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 = { }; default = { };
example = lib.literalExpression ''
{
email-listing-page-size = 50;
watch-cmds = [ "mbsync -a" ]
}
'';
description = '' 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 { services = {
type = with types; himalaya-notify = {
attrsOf (submodule { enable =
options.himalaya = { lib.mkEnableOption "Enable the Himalaya new emails notifier service.";
enable = mkEnableOption ''
the himalaya mail client for this account 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 { config = lib.mkIf himalaya.enable {
home.packages = [ cfg.package ]; home.packages = [ himalaya.package ];
xdg.configFile."himalaya/config.toml".source = xdg.configFile."himalaya/config.toml".source = let
tomlFormat.generate "himalaya-config.toml" himalayaConfig; 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"] ["hm@example.com"]
backend = "imap" backend = "imap"
default = true default = true
display-name = "H. M. Test" display-name = "H. M. Test"
email = "hm@example.com" email = "hm@example.com"
email-listing-page-size = 50
imap-host = "imap.example.com" imap-host = "imap.example.com"
imap-login = "home.manager" imap-login = "home.manager"
imap-passwd-cmd = "'password-command'" imap-passwd-cmd = "password-command"
imap-port = 995 imap-port = 993
imap-ssl = true
imap-starttls = false imap-starttls = false
sender = "smtp" sender = "smtp"
smtp-host = "smtp.example.com" smtp-host = "smtp.example.com"
smtp-login = "home.manager" smtp-login = "home.manager"
smtp-passwd-cmd = "'password-command'" smtp-passwd-cmd = "password-command"
smtp-port = 465 smtp-port = 465
smtp-ssl = true
smtp-starttls = false smtp-starttls = false
["hm@example.com".mailboxes] ["hm@example.com".folder-aliases]
draft = "Drafts" drafts = "Drafts"
inbox = "In" inbox = "Inbox"
sent = "Out" 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; with lib;
{ {
imports = [ ../../accounts/email-test-accounts.nix ];
accounts.email.accounts = { accounts.email.accounts = {
"hm@example.com" = { "hm@example.com" = {
primary = true;
address = "hm@example.com";
userName = "home.manager";
realName = "H. M. Test";
passwordCommand = "password-command";
folders = { trash = "Deleted"; };
himalaya = { himalaya = {
enable = true; enable = true;
settings = {
backend = "imap"; sender = "sendmail";
sender = "smtp"; sendmail-cmd = "msmtp";
settings = { email-listing-page-size = 50; }; };
}; };
folders = {
inbox = "In";
sent = "Out";
drafts = "Drafts";
};
imap.port = 995;
smtp.port = 465;
}; };
}; };
programs.himalaya = { programs.himalaya = {
enable = true; enable = true;
settings = { downloads-dir = "/data/download"; }; settings = { email-listing-page-size = 50; };
}; };
test.stubs.himalaya = { }; test.stubs.himalaya = { };
@ -36,7 +31,7 @@ with lib;
nmt.script = '' nmt.script = ''
assertFileExists home-files/.config/himalaya/config.toml assertFileExists home-files/.config/himalaya/config.toml
assertFileContent home-files/.config/himalaya/config.toml ${ assertFileContent home-files/.config/himalaya/config.toml ${
./himalaya-expected.toml ./maildir-sendmail-expected.toml
} }
''; '';
} }