1
0
Fork 0
mirror of https://github.com/nix-community/home-manager.git synced 2025-11-08 19:46:05 +01:00

accounts.email: add option to disable an account

Allow a user to disable an email account by setting
`accounts.email.accounts.<name>.enable = false`.  This is useful if
someone wants to configure email accounts globally but only use them in
certain circumstances.

Everywhere email account configuration is used, check if the account is
enabled before checking any attributes of the account.
This commit is contained in:
Adam Dinwoodie 2025-08-05 11:04:49 +01:00 committed by Austin Horstman
parent 07b994baed
commit dbfcd3292d
22 changed files with 164 additions and 26 deletions

View file

@ -9,6 +9,7 @@ let
;
cfg = config.accounts.email;
enabledAccounts = lib.filterAttrs (n: v: v.enable) cfg.accounts;
gpgModule = types.submodule {
options = {
@ -353,6 +354,16 @@ let
'';
};
enable = mkOption {
type = types.bool;
default = true;
description = ''
Whether this account is enabled. Potentially useful to allow
setting email configuration globally then enabling or disabling on
specific systems.
'';
};
flavor = mkOption {
type = types.enum [
"davmail"
@ -669,11 +680,11 @@ in
};
};
config = mkIf (cfg.accounts != { }) {
config = mkIf (enabledAccounts != { }) {
assertions = [
(
let
primaries = lib.catAttrs "name" (lib.filter (a: a.primary) (lib.attrValues cfg.accounts));
primaries = lib.catAttrs "name" (lib.filter (a: a.primary) (lib.attrValues enabledAccounts));
in
{
assertion = lib.length primaries == 1;

View file

@ -4,7 +4,9 @@
time = "2024-12-08T17:22:13+00:00";
condition =
let
usingMbsync = lib.any (a: a.mbsync.enable) (lib.attrValues config.accounts.email.accounts);
usingMbsync = lib.any (a: a.enable && a.mbsync.enable) (
lib.attrValues config.accounts.email.accounts
);
in
usingMbsync;
message = ''

View file

@ -47,7 +47,9 @@ let
;
};
aerc-accounts = attrsets.filterAttrs (_: v: v.aerc.enable) config.accounts.email.accounts;
aerc-accounts = attrsets.filterAttrs (
_: v: v.enable && v.aerc.enable
) config.accounts.email.accounts;
configDir =
if (pkgs.stdenv.isDarwin && !config.xdg.enable) then

View file

@ -17,7 +17,9 @@ let
cfg = config.programs.alot;
enabledAccounts = lib.filter (a: a.notmuch.enable) (lib.attrValues config.accounts.email.accounts);
enabledAccounts = lib.filter (a: a.enable && a.notmuch.enable) (
lib.attrValues config.accounts.email.accounts
);
# sorted: primary first
alotAccounts = lib.sort (a: b: !(a.primary -> b.primary)) enabledAccounts;

View file

@ -11,7 +11,9 @@ let
jsonFormat = pkgs.formats.json { };
astroidAccounts = lib.filterAttrs (n: v: v.astroid.enable) config.accounts.email.accounts;
astroidAccounts = lib.filterAttrs (
n: v: v.enable && v.astroid.enable
) config.accounts.email.accounts;
boolOpt = b: if b then "true" else "false";

View file

@ -5,7 +5,9 @@
...
}:
let
accounts = lib.filter (a: a.getmail.enable) (lib.attrValues config.accounts.email.accounts);
accounts = lib.filter (a: a.enable && a.getmail.enable) (
lib.attrValues config.accounts.email.accounts
);
renderAccountConfig =
account:

View file

@ -566,7 +566,7 @@ in
{
programs.git.iniContent =
let
hasSmtp = name: account: account.smtp != null;
hasSmtp = name: account: account.enable && account.smtp != null;
genIdentity =
name: account:

View file

@ -34,7 +34,7 @@ let
mkAccountConfig =
_: account:
let
notmuchEnabled = account.notmuch.enable;
notmuchEnabled = account.enable && account.notmuch.enable;
imapEnabled = !isNull account.imap && !notmuchEnabled;
maildirEnabled = !isNull account.maildir && !imapEnabled && !notmuchEnabled;
@ -165,7 +165,7 @@ in
configFile."himalaya/config.toml".source =
let
enabledAccounts = lib.filterAttrs (
_: account: account.himalaya.enable
_: account: account.enable && account.himalaya.enable
) config.accounts.email.accounts;
accountsConfig = lib.mapAttrs mkAccountConfig enabledAccounts;
globalConfig = compactAttrs himalaya.settings;

View file

@ -15,7 +15,9 @@ let
cfg = config.programs.lieer;
lieerAccounts = lib.filter (a: a.lieer.enable) (lib.attrValues config.accounts.email.accounts);
lieerAccounts = lib.filter (a: a.enable && a.lieer.enable) (
lib.attrValues config.accounts.email.accounts
);
nonGmailAccounts = map (a: a.name) (lib.filter (a: a.flavor != "gmail.com") lieerAccounts);

View file

@ -20,7 +20,9 @@ let
cfg = config.programs.mbsync;
# Accounts for which mbsync is enabled.
mbsyncAccounts = lib.filter (a: a.mbsync.enable) (lib.attrValues config.accounts.email.accounts);
mbsyncAccounts = lib.filter (a: a.enable && a.mbsync.enable) (
lib.attrValues config.accounts.email.accounts
);
# Given a SINGLE group's channels attribute set, return true if ANY of the channel's
# patterns use the invalidOption attribute set value name.

View file

@ -18,7 +18,7 @@ let
tomlFormat = pkgs.formats.toml { };
enabledAccounts = lib.attrsets.filterAttrs (
name: value: value.meli.enable or false
name: value: value.enable && (value.meli.enable or false)
) config.accounts.email.accounts;
meliAccounts = (lib.attrsets.mapAttrs (name: value: (mkMeliAccounts name value)) enabledAccounts);

View file

@ -9,7 +9,9 @@ let
cfg = config.programs.msmtp;
msmtpAccounts = lib.filter (a: a.msmtp.enable) (lib.attrValues config.accounts.email.accounts);
msmtpAccounts = lib.filter (a: a.enable && a.msmtp.enable) (
lib.attrValues config.accounts.email.accounts
);
onOff = p: if p then "on" else "off";

View file

@ -14,7 +14,9 @@ let
sortedAddresses =
let
# Set of email account sets where mu.enable = true.
muAccounts = lib.filter (a: a.mu.enable) (lib.attrValues config.accounts.email.accounts);
muAccounts = lib.filter (a: a.enable && a.mu.enable) (
lib.attrValues config.accounts.email.accounts
);
addrs = map (a: a.address) muAccounts;
# Construct list of lists containing email aliases, and flatten
aliases = map (alias: alias.address or alias) (lib.flatten (map (a: a.aliases) muAccounts));

View file

@ -9,7 +9,9 @@ let
cfg = config.programs.mujmap;
mujmapAccounts = lib.filter (a: a.mujmap.enable) (lib.attrValues config.accounts.email.accounts);
mujmapAccounts = lib.filter (a: a.enable && a.mujmap.enable) (
lib.attrValues config.accounts.email.accounts
);
missingNotmuchAccounts = map (a: a.name) (
lib.filter (a: !a.notmuch.enable && a.mujmap.notmuchSetupWarning) mujmapAccounts

View file

@ -20,7 +20,9 @@ let
cfg = config.programs.neomutt;
neomuttAccountsCfg = filterAttrs (n: a: a.neomutt.enable) config.accounts.email.accounts;
neomuttAccountsCfg = filterAttrs (
n: a: a.enable && a.neomutt.enable
) config.accounts.email.accounts;
neomuttAccounts = attrValues neomuttAccountsCfg;
accountCommandNeeded = lib.any (

View file

@ -47,7 +47,7 @@ let
user =
let
accounts = filter (a: a.notmuch.enable) (lib.attrValues config.accounts.email.accounts);
accounts = filter (a: a.enable && a.notmuch.enable) (lib.attrValues config.accounts.email.accounts);
primary = filter (a: a.primary) accounts;
secondaries = filter (a: !a.primary) accounts;
in

View file

@ -9,7 +9,9 @@ let
cfg = config.programs.offlineimap;
accounts = lib.filter (a: a.offlineimap.enable) (lib.attrValues config.accounts.email.accounts);
accounts = lib.filter (a: a.enable && a.offlineimap.enable) (
lib.attrValues config.accounts.email.accounts
);
toIni = lib.generators.toINI {
mkKeyValue =

View file

@ -32,16 +32,21 @@ let
moduleName = "programs.thunderbird";
filterEnabled = accounts: attrValues (lib.filterAttrs (_: a: a.thunderbird.enable) accounts);
addId = map (a: a // { id = builtins.hashString "sha256" a.name; });
enabledEmailAccounts = filterEnabled config.accounts.email.accounts;
enabledEmailAccounts = filter (a: a.enable && a.thunderbird.enable) (
attrValues config.accounts.email.accounts
);
enabledEmailAccountsWithId = addId enabledEmailAccounts;
enabledCalendarAccounts = filterEnabled config.accounts.calendar.accounts;
enabledCalendarAccounts = filter (a: a.thunderbird.enable) (
attrValues config.accounts.calendar.accounts
);
enabledCalendarAccountsWithId = addId enabledCalendarAccounts;
enabledContactAccounts = filterEnabled config.accounts.contact.accounts;
enabledContactAccounts = filter (a: a.thunderbird.enable) (
attrValues config.accounts.contact.accounts
);
enabledContactAccountsWithId = addId enabledContactAccounts;
thunderbirdConfigPath = if isDarwin then "Library/Thunderbird" else ".thunderbird";

View file

@ -8,7 +8,9 @@ let
cfg = config.services.getmail;
accounts = lib.filter (a: a.getmail.enable) (lib.attrValues config.accounts.email.accounts);
accounts = lib.filter (a: a.enable && a.getmail.enable) (
lib.attrValues config.accounts.email.accounts
);
# Note: The getmail service does not expect a path, but just the filename!
renderConfigFilepath = a: if a.primary then "getmailrc" else "getmail${a.name}";

View file

@ -18,7 +18,7 @@ let
configName = account: "imapnotify-${safeName account.name}-config.json";
imapnotifyAccounts = lib.filter (a: a.imapnotify.enable) (
imapnotifyAccounts = lib.filter (a: a.enable && a.imapnotify.enable) (
lib.attrValues config.accounts.email.accounts
);

View file

@ -7,7 +7,7 @@
let
cfg = config.services.lieer;
syncAccounts = lib.filter (a: a.lieer.enable && a.lieer.sync.enable) (
syncAccounts = lib.filter (a: a.enable && a.lieer.enable && a.lieer.sync.enable) (
lib.attrValues config.accounts.email.accounts
);

View file

@ -1,3 +1,4 @@
{ lib, options, ... }:
{
accounts.email = {
maildirBasePath = "Mail";
@ -22,6 +23,101 @@
smtp.host = "smtp.example.org";
smtp.tls.useStartTls = true;
};
# Account that throws an error if any interesting account attribute is
# accessed other than the `enable` attribute. This is a bit awkward as
# we can't throw just for accessing some submodules, as some get accessed
# just as part of merging config, but it ensures a disabled account is
# genuinely disabled.
disabled-account =
let
# This is intended for use in generating documentation, but it's
# useful here as a way to get a list of attributes that might be
# defined.
accountAttrOptions = options.accounts.email.accounts.type.nestedTypes.elemType.getSubOptions [ ];
throwOnAttrAccess =
baseName: builtins.mapAttrs (n: v: throw "Unexpected access of ${baseName}.${n}");
# Don't want to do anything with these account attributes.
ignoredAttrNames = [
"_module"
"enable"
];
# These are submodules, which means the config attribute will be
# accessed even if subattributes aren't. This means we can't throw
# an error as soon as one of these is accessed, and instead need to
# throw errors if an attribute of this submodule is accessed.
submoduleAttrNames = [
"aerc"
"alot"
"astroid"
"getmail"
"himalaya"
"imapnotify"
"lieer"
"mbsync"
"meli"
"msmtp"
"mu"
"mujmap"
"neomutt"
"notmuch"
"offlineimap"
"thunderbird"
];
# Other attributes should never be accessed if the account is
# disabled, so throw an error if they are.
baseAttrThrows = throwOnAttrAccess "accounts.email.accounts.disabled-account" (
removeAttrs accountAttrOptions (ignoredAttrNames ++ submoduleAttrNames)
);
submoduleToAttrThrows =
name:
let
submoduleAttrOptions = builtins.getAttr name accountAttrOptions;
# Some submodules have sub-submodules, and they need the same
# special handling.
#
# Ideally this would be some recursive function to avoid
# repeating the code, potentially using introspection to workout
# which options are submodules, but that's complicated and
# unnecessary for now.
subSubmoduleAttrNames =
if name == "lieer" then
[ "sync" ]
else if name == "mbsync" then
[ "extraConfig" ]
else if name == "msmtp" then
[ "tls" ]
else if name == "notmuch" then
[ "neomutt" ]
else if name == "offlineimap" then
[ "extraConfig" ]
else
[ ];
subSubmoduleThrows = lib.genAttrs subSubmoduleAttrNames (
n:
throwOnAttrAccess "accounts.email.accounts.disabled-account.${name}.${n}" (
builtins.getAttr n submoduleAttrOptions
)
);
baseThrows = throwOnAttrAccess "accounts.email.accounts.disabled-account.${name}" (
removeAttrs submoduleAttrOptions subSubmoduleAttrNames
);
in
baseThrows // subSubmoduleThrows;
submoduleAttrThrows = lib.genAttrs submoduleAttrNames submoduleToAttrThrows;
in
lib.mergeAttrsList [
baseAttrThrows
submoduleAttrThrows
{ enable = false; }
];
};
};
}