diff --git a/modules/lib/maintainers.nix b/modules/lib/maintainers.nix
index 19fa96b1..b91ba715 100644
--- a/modules/lib/maintainers.nix
+++ b/modules/lib/maintainers.nix
@@ -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";
diff --git a/modules/programs/himalaya.nix b/modules/programs/himalaya.nix
index 92328153..54f8d1ec 100644
--- a/modules/programs/himalaya.nix
+++ b/modules/programs/himalaya.nix
@@ -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 himalaya 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 himalaya configuration values.
+ Himalaya global configuration.
+ See 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 himalaya will fetch, store,
- etc. mail.
- '';
- };
-
- sender = mkOption {
- type = types.nullOr (types.enum [ "smtp" "sendmail" ]);
- description = ''
- The method for which himalaya will send mail.
- '';
- };
-
- settings = mkOption {
- type = tomlFormat.type;
- default = { };
- example = lib.literalExpression ''
- {
- default-page-size = 50;
- }
- '';
- description = ''
- Extra settings to add to this himalaya
- 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 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.");
};
}
diff --git a/tests/modules/programs/himalaya/himalaya-expected.toml b/tests/modules/programs/himalaya/basic-expected.toml
similarity index 54%
rename from tests/modules/programs/himalaya/himalaya-expected.toml
rename to tests/modules/programs/himalaya/basic-expected.toml
index ca7ada66..abda4a13 100644
--- a/tests/modules/programs/himalaya/himalaya-expected.toml
+++ b/tests/modules/programs/himalaya/basic-expected.toml
@@ -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"
diff --git a/tests/modules/programs/himalaya/basic.nix b/tests/modules/programs/himalaya/basic.nix
new file mode 100644
index 00000000..e2a8fa83
--- /dev/null
+++ b/tests/modules/programs/himalaya/basic.nix
@@ -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
+ }
+ '';
+}
diff --git a/tests/modules/programs/himalaya/default.nix b/tests/modules/programs/himalaya/default.nix
index 54c3978d..e5473344 100644
--- a/tests/modules/programs/himalaya/default.nix
+++ b/tests/modules/programs/himalaya/default.nix
@@ -1 +1,5 @@
-{ himalaya = ./himalaya.nix; }
+{
+ himalaya-basic = ./basic.nix;
+ himalaya-imap-smtp = ./imap-smtp.nix;
+ himalaya-maildir-sendmail = ./maildir-sendmail.nix;
+}
diff --git a/tests/modules/programs/himalaya/imap-smtp-expected.toml b/tests/modules/programs/himalaya/imap-smtp-expected.toml
new file mode 100644
index 00000000..bcfee047
--- /dev/null
+++ b/tests/modules/programs/himalaya/imap-smtp-expected.toml
@@ -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"
diff --git a/tests/modules/programs/himalaya/imap-smtp.nix b/tests/modules/programs/himalaya/imap-smtp.nix
new file mode 100644
index 00000000..c1347d23
--- /dev/null
+++ b/tests/modules/programs/himalaya/imap-smtp.nix
@@ -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
+ }
+ '';
+}
diff --git a/tests/modules/programs/himalaya/maildir-sendmail-expected.toml b/tests/modules/programs/himalaya/maildir-sendmail-expected.toml
new file mode 100644
index 00000000..61ae940d
--- /dev/null
+++ b/tests/modules/programs/himalaya/maildir-sendmail-expected.toml
@@ -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"
diff --git a/tests/modules/programs/himalaya/himalaya.nix b/tests/modules/programs/himalaya/maildir-sendmail.nix
similarity index 50%
rename from tests/modules/programs/himalaya/himalaya.nix
rename to tests/modules/programs/himalaya/maildir-sendmail.nix
index 4d556ff8..a9d15d22 100644
--- a/tests/modules/programs/himalaya/himalaya.nix
+++ b/tests/modules/programs/himalaya/maildir-sendmail.nix
@@ -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
}
'';
}