treewide: implement auto importing for modules
Reduce maintenance burden and increase efficiency by automatically importing modules following a specific convention. Co-authored-by: awwpotato <awwpotato@voidq.com> Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
This commit is contained in:
parent
fefeb0e928
commit
4fca600cb1
461 changed files with 72 additions and 474 deletions
301
modules/programs/aerc/accounts.nix
Normal file
301
modules/programs/aerc/accounts.nix
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
confSections,
|
||||
confSection,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) literalExpression mkOption types;
|
||||
|
||||
mapAttrNames =
|
||||
f: attr:
|
||||
lib.listToAttrs (
|
||||
lib.attrValues (
|
||||
lib.mapAttrs (k: v: {
|
||||
name = f k;
|
||||
value = v;
|
||||
}) attr
|
||||
)
|
||||
);
|
||||
|
||||
addAccountName = name: k: "${k}:account=${name}";
|
||||
|
||||
oauth2Params = mkOption {
|
||||
type =
|
||||
with types;
|
||||
nullOr (submodule {
|
||||
options = {
|
||||
token_endpoint = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
description = "The OAuth2 token endpoint.";
|
||||
};
|
||||
client_id = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
description = "The OAuth2 client identifier.";
|
||||
};
|
||||
client_secret = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
description = "The OAuth2 client secret.";
|
||||
};
|
||||
scope = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
description = "The OAuth2 requested scope.";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = null;
|
||||
example = {
|
||||
token_endpoint = "<token_endpoint>";
|
||||
};
|
||||
description = ''
|
||||
Sets the oauth2 params if authentication mechanism oauthbearer or
|
||||
xoauth2 is used.
|
||||
See {manpage}`aerc-imap(5)`.
|
||||
'';
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
type = mkOption {
|
||||
type = types.attrsOf (
|
||||
types.submodule {
|
||||
options.aerc = {
|
||||
enable = lib.mkEnableOption "aerc";
|
||||
extraAccounts = mkOption {
|
||||
type = confSection;
|
||||
default = { };
|
||||
example = literalExpression ''{ source = "maildir://~/Maildir/example"; }'';
|
||||
description = ''
|
||||
Extra config added to the configuration section for this account in
|
||||
{file}`$HOME/.config/aerc/accounts.conf`.
|
||||
See {manpage}`aerc-accounts(5)`.
|
||||
'';
|
||||
};
|
||||
|
||||
extraBinds = mkOption {
|
||||
type = confSections;
|
||||
default = { };
|
||||
example = literalExpression ''{ messages = { d = ":move ''${folder.trash}<Enter>"; }; }'';
|
||||
description = ''
|
||||
Extra bindings specific to this account, added to
|
||||
{file}`$HOME/.config/aerc/binds.conf`.
|
||||
See {manpage}`aerc-binds(5)`.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = confSections;
|
||||
default = { };
|
||||
example = literalExpression "{ ui = { sidebar-width = 25; }; }";
|
||||
description = ''
|
||||
Config specific to this account, added to {file}`$HOME/.config/aerc/aerc.conf`.
|
||||
Aerc only supports per-account UI configuration.
|
||||
For other sections of {file}`$HOME/.config/aerc/aerc.conf`,
|
||||
use `programs.aerc.extraConfig`.
|
||||
See {manpage}`aerc-config(5)`.
|
||||
'';
|
||||
};
|
||||
|
||||
imapAuth = mkOption {
|
||||
type =
|
||||
with types;
|
||||
nullOr (enum [
|
||||
"oauthbearer"
|
||||
"xoauth2"
|
||||
]);
|
||||
default = null;
|
||||
example = "auth";
|
||||
description = ''
|
||||
Sets the authentication mechanism if imap is used as the incoming
|
||||
method.
|
||||
See {manpage}`aerc-imap(5)`.
|
||||
'';
|
||||
};
|
||||
|
||||
imapOauth2Params = oauth2Params;
|
||||
|
||||
smtpAuth = mkOption {
|
||||
type =
|
||||
with types;
|
||||
nullOr (enum [
|
||||
"none"
|
||||
"plain"
|
||||
"login"
|
||||
"oauthbearer"
|
||||
"xoauth2"
|
||||
]);
|
||||
default = "plain";
|
||||
example = "auth";
|
||||
description = ''
|
||||
Sets the authentication mechanism if smtp is used as the outgoing
|
||||
method.
|
||||
See {manpage}`aerc-smtp(5)`.
|
||||
'';
|
||||
};
|
||||
|
||||
smtpOauth2Params = oauth2Params;
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
mkAccount =
|
||||
name: account:
|
||||
let
|
||||
nullOrMap = f: v: if v == null then v else f v;
|
||||
|
||||
optPort = port: if port != null then ":${toString port}" else "";
|
||||
|
||||
optAttr = k: v: if v != null && v != [ ] && v != "" then { ${k} = v; } else { };
|
||||
|
||||
optPwCmd = k: p: optAttr "${k}-cred-cmd" (nullOrMap (lib.concatStringsSep " ") p);
|
||||
|
||||
useOauth =
|
||||
auth:
|
||||
builtins.elem auth [
|
||||
"oauthbearer"
|
||||
"xoauth2"
|
||||
];
|
||||
|
||||
oauthParams =
|
||||
{ auth, params }:
|
||||
if useOauth auth && params != null && params != { } then
|
||||
"?"
|
||||
+ builtins.concatStringsSep "&" (
|
||||
lib.attrsets.mapAttrsToList (k: v: k + "=" + lib.strings.escapeURL v) (
|
||||
lib.attrsets.filterAttrs (k: v: v != null) params
|
||||
)
|
||||
)
|
||||
else
|
||||
"";
|
||||
|
||||
mkConfig = {
|
||||
maildir = cfg: {
|
||||
source = "maildir://${config.accounts.email.maildirBasePath}/${cfg.maildir.path}";
|
||||
};
|
||||
|
||||
maildirpp = cfg: {
|
||||
source = "maildirpp://${config.accounts.email.maildirBasePath}/${cfg.maildir.path}/Inbox";
|
||||
};
|
||||
|
||||
imap =
|
||||
{
|
||||
userName,
|
||||
imap,
|
||||
passwordCommand,
|
||||
aerc,
|
||||
...
|
||||
}@cfg:
|
||||
let
|
||||
loginMethod' = if cfg.aerc.imapAuth != null then "+${cfg.aerc.imapAuth}" else "";
|
||||
|
||||
oauthParams' = oauthParams {
|
||||
auth = cfg.aerc.imapAuth;
|
||||
params = cfg.aerc.imapOauth2Params;
|
||||
};
|
||||
|
||||
protocol =
|
||||
if imap.tls.enable then
|
||||
if imap.tls.useStartTls then "imap" else "imaps${loginMethod'}"
|
||||
else
|
||||
"imap+insecure";
|
||||
|
||||
port' = optPort imap.port;
|
||||
|
||||
in
|
||||
{
|
||||
source = "${protocol}://${userName}@${imap.host}${port'}${oauthParams'}";
|
||||
}
|
||||
// optPwCmd "source" passwordCommand;
|
||||
|
||||
smtp =
|
||||
{
|
||||
userName,
|
||||
smtp,
|
||||
passwordCommand,
|
||||
...
|
||||
}@cfg:
|
||||
let
|
||||
loginMethod' = if cfg.aerc.smtpAuth != null then "+${cfg.aerc.smtpAuth}" else "";
|
||||
|
||||
oauthParams' = oauthParams {
|
||||
auth = cfg.aerc.smtpAuth;
|
||||
params = cfg.aerc.smtpOauth2Params;
|
||||
};
|
||||
|
||||
protocol =
|
||||
if smtp.tls.enable then
|
||||
if smtp.tls.useStartTls then "smtp${loginMethod'}" else "smtps${loginMethod'}"
|
||||
else
|
||||
"smtp+insecure${loginMethod'}";
|
||||
|
||||
port' = optPort smtp.port;
|
||||
|
||||
in
|
||||
{
|
||||
outgoing = "${protocol}://${userName}@${smtp.host}${port'}${oauthParams'}";
|
||||
}
|
||||
// optPwCmd "outgoing" passwordCommand;
|
||||
|
||||
msmtp = cfg: {
|
||||
outgoing = "msmtpq --read-envelope-from --read-recipients";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
basicCfg =
|
||||
account:
|
||||
{
|
||||
from = "${account.realName} <${account.address}>";
|
||||
}
|
||||
// (optAttr "copy-to" account.folders.sent)
|
||||
// (optAttr "default" account.folders.inbox)
|
||||
// (optAttr "postpone" account.folders.drafts)
|
||||
// (optAttr "aliases" account.aliases);
|
||||
|
||||
sourceCfg =
|
||||
account:
|
||||
if
|
||||
account.mbsync.enable && account.mbsync.flatten == null && account.mbsync.subFolders == "Maildir++"
|
||||
then
|
||||
mkConfig.maildirpp account
|
||||
else if account.mbsync.enable || account.offlineimap.enable then
|
||||
mkConfig.maildir account
|
||||
else if account.imap != null then
|
||||
mkConfig.imap account
|
||||
else
|
||||
{ };
|
||||
|
||||
outgoingCfg =
|
||||
account:
|
||||
if account.msmtp.enable then
|
||||
mkConfig.msmtp account
|
||||
else if account.smtp != null then
|
||||
mkConfig.smtp account
|
||||
else
|
||||
{ };
|
||||
|
||||
gpgCfg =
|
||||
account:
|
||||
lib.optionalAttrs (account.gpg != null) {
|
||||
pgp-key-id = account.gpg.key;
|
||||
pgp-auto-sign = account.gpg.signByDefault;
|
||||
pgp-opportunistic-encrypt = account.gpg.encryptByDefault;
|
||||
};
|
||||
|
||||
in
|
||||
(basicCfg account)
|
||||
// (sourceCfg account)
|
||||
// (outgoingCfg account)
|
||||
// (gpgCfg account)
|
||||
// account.aerc.extraAccounts;
|
||||
|
||||
mkAccountConfig = name: account: mapAttrNames (addAccountName name) account.aerc.extraConfig;
|
||||
|
||||
mkAccountBinds = name: account: mapAttrNames (addAccountName name) account.aerc.extraBinds;
|
||||
}
|
||||
278
modules/programs/aerc/default.nix
Normal file
278
modules/programs/aerc/default.nix
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
attrsets
|
||||
generators
|
||||
literalExpression
|
||||
mapAttrs
|
||||
mkIf
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
|
||||
cfg = config.programs.aerc;
|
||||
|
||||
primitive =
|
||||
with types;
|
||||
((type: either type (listOf type)) (
|
||||
nullOr (oneOf [
|
||||
str
|
||||
int
|
||||
bool
|
||||
float
|
||||
])
|
||||
))
|
||||
// {
|
||||
description = "values (null, bool, int, string, or float) or a list of values, that will be joined with a comma";
|
||||
};
|
||||
|
||||
confSection = types.attrsOf primitive;
|
||||
|
||||
confSections = types.attrsOf confSection;
|
||||
|
||||
sectionsOrLines = types.either types.lines confSections;
|
||||
|
||||
accounts = import ./accounts.nix {
|
||||
inherit
|
||||
config
|
||||
pkgs
|
||||
lib
|
||||
confSection
|
||||
confSections
|
||||
;
|
||||
};
|
||||
|
||||
aerc-accounts = attrsets.filterAttrs (_: v: v.aerc.enable) config.accounts.email.accounts;
|
||||
|
||||
configDir =
|
||||
if (pkgs.stdenv.isDarwin && !config.xdg.enable) then
|
||||
"Library/Preferences/aerc"
|
||||
else
|
||||
"${config.xdg.configHome}/aerc";
|
||||
|
||||
in
|
||||
{
|
||||
meta.maintainers = with lib.hm.maintainers; [ lukasngl ];
|
||||
|
||||
options.accounts.email.accounts = accounts.type;
|
||||
|
||||
options.programs.aerc = {
|
||||
|
||||
enable = lib.mkEnableOption "aerc";
|
||||
|
||||
package = lib.mkPackageOption pkgs "aerc" { nullable = true; };
|
||||
|
||||
extraAccounts = mkOption {
|
||||
type = sectionsOrLines;
|
||||
default = { };
|
||||
example = literalExpression ''{ Work = { source = "maildir://~/Maildir/work"; }; }'';
|
||||
description = ''
|
||||
Extra lines added to {file}`$HOME/.config/aerc/accounts.conf`.
|
||||
|
||||
See {manpage}`aerc-config(5)`.
|
||||
'';
|
||||
};
|
||||
|
||||
extraBinds = mkOption {
|
||||
type = sectionsOrLines;
|
||||
default = { };
|
||||
example = literalExpression ''{ messages = { q = ":quit<Enter>"; }; }'';
|
||||
description = ''
|
||||
Extra lines added to {file}`$HOME/.config/aerc/binds.conf`.
|
||||
Global keybindings can be set in the `global` section.
|
||||
|
||||
See {manpage}`aerc-config(5)`.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = sectionsOrLines;
|
||||
default = { };
|
||||
example = literalExpression ''{ ui = { sort = "-r date"; }; }'';
|
||||
description = ''
|
||||
Extra lines added to {file}`$HOME/.config/aerc/aerc.conf`.
|
||||
|
||||
See {manpage}`aerc-config(5)`.
|
||||
'';
|
||||
};
|
||||
|
||||
stylesets = mkOption {
|
||||
type = with types; attrsOf (either confSection lines);
|
||||
default = { };
|
||||
example = literalExpression ''
|
||||
{ default = { ui = { "tab.selected.reverse" = toggle; }; }; };
|
||||
'';
|
||||
description = ''
|
||||
Stylesets added to {file}`$HOME/.config/aerc/stylesets/`.
|
||||
|
||||
See {manpage}`aerc-stylesets(7)`.
|
||||
'';
|
||||
};
|
||||
|
||||
templates = mkOption {
|
||||
type = with types; attrsOf lines;
|
||||
default = { };
|
||||
example = literalExpression ''
|
||||
{ new_message = "Hello!"; };
|
||||
'';
|
||||
description = ''
|
||||
Templates added to {file}`$HOME/.config/aerc/templates/`.
|
||||
|
||||
See {manpage}`aerc-templates(7)`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config =
|
||||
let
|
||||
joinCfg = cfgs: lib.concatStringsSep "\n" (lib.filter (v: v != "") cfgs);
|
||||
|
||||
toINI =
|
||||
conf: # quirk: global section is prepended w/o section heading
|
||||
let
|
||||
global = conf.global or { };
|
||||
local = removeAttrs conf [ "global" ];
|
||||
mkValueString =
|
||||
v:
|
||||
if lib.isList v then # join with comma
|
||||
lib.concatStringsSep "," (map (generators.mkValueStringDefault { }) v)
|
||||
else
|
||||
generators.mkValueStringDefault { } v;
|
||||
mkKeyValue = generators.mkKeyValueDefault { inherit mkValueString; } " = ";
|
||||
in
|
||||
joinCfg [
|
||||
(generators.toKeyValue { inherit mkKeyValue; } global)
|
||||
(generators.toINI { inherit mkKeyValue; } local)
|
||||
];
|
||||
|
||||
mkINI = conf: if lib.isString conf then conf else toINI conf;
|
||||
|
||||
mkStyleset = attrsets.mapAttrs' (
|
||||
k: v:
|
||||
let
|
||||
value = if lib.isString v then v else toINI { global = v; };
|
||||
in
|
||||
{
|
||||
name = "${configDir}/stylesets/${k}";
|
||||
value.text = joinCfg [
|
||||
header
|
||||
value
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
mkTemplates = attrsets.mapAttrs' (
|
||||
k: v: {
|
||||
name = "${configDir}/templates/${k}";
|
||||
value.text = v;
|
||||
}
|
||||
);
|
||||
|
||||
primaryAccount = attrsets.filterAttrs (_: v: v.primary) aerc-accounts;
|
||||
otherAccounts = attrsets.filterAttrs (_: v: !v.primary) aerc-accounts;
|
||||
|
||||
primaryAccountAccounts = mapAttrs accounts.mkAccount primaryAccount;
|
||||
|
||||
accountsExtraAccounts = mapAttrs accounts.mkAccount otherAccounts;
|
||||
|
||||
accountsExtraConfig = mapAttrs accounts.mkAccountConfig aerc-accounts;
|
||||
|
||||
accountsExtraBinds = mapAttrs accounts.mkAccountBinds aerc-accounts;
|
||||
|
||||
joinContextual = contextual: joinCfg (map mkINI (lib.attrValues contextual));
|
||||
|
||||
isRecursivelyEmpty =
|
||||
x:
|
||||
if lib.isAttrs x then lib.all (x: x == { } || isRecursivelyEmpty x) (lib.attrValues x) else false;
|
||||
|
||||
genAccountsConf = (
|
||||
(cfg.extraAccounts != "" && cfg.extraAccounts != { })
|
||||
|| !(isRecursivelyEmpty accountsExtraAccounts)
|
||||
|| !(isRecursivelyEmpty primaryAccountAccounts)
|
||||
);
|
||||
|
||||
genAercConf = (
|
||||
(cfg.extraConfig != "" && cfg.extraConfig != { }) || !(isRecursivelyEmpty accountsExtraConfig)
|
||||
);
|
||||
|
||||
genBindsConf = (
|
||||
(cfg.extraBinds != "" && cfg.extraBinds != { }) || !(isRecursivelyEmpty accountsExtraBinds)
|
||||
);
|
||||
|
||||
header = ''
|
||||
# Generated by Home Manager.
|
||||
'';
|
||||
|
||||
in
|
||||
mkIf cfg.enable {
|
||||
warnings =
|
||||
if genAccountsConf && (cfg.extraConfig.general.unsafe-accounts-conf or false) == false then
|
||||
[
|
||||
''
|
||||
aerc: `programs.aerc.enable` is set, but `...extraConfig.general.unsafe-accounts-conf` is set to false or unset.
|
||||
This will prevent aerc from starting; see `unsafe-accounts-conf` in the man page aerc-config(5):
|
||||
> By default, the file permissions of accounts.conf must be restrictive and only allow reading by the file owner (0600).
|
||||
> Set this option to true to ignore this permission check. Use this with care as it may expose your credentials.
|
||||
These permissions are not possible with home-manager, since the generated file is in the nix-store (permissions 0444).
|
||||
Therefore, please set `programs.aerc.extraConfig.general.unsafe-accounts-conf = true`.
|
||||
This option is safe; if `passwordCommand` is properly set, no credentials will be written to the nix store.
|
||||
''
|
||||
]
|
||||
else
|
||||
[ ];
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion =
|
||||
let
|
||||
extraConfigSections = (
|
||||
lib.unique (lib.flatten (lib.mapAttrsToList (_: v: lib.attrNames v.aerc.extraConfig) aerc-accounts))
|
||||
);
|
||||
in
|
||||
extraConfigSections == [ ] || extraConfigSections == [ "ui" ];
|
||||
message = ''
|
||||
Only the ui section of $XDG_CONFIG_HOME/aerc.conf supports contextual (per-account) configuration.
|
||||
Please configure it with accounts.email.accounts._.aerc.extraConfig.ui and move any other
|
||||
configuration to programs.aerc.extraConfig.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
|
||||
|
||||
home.file =
|
||||
{
|
||||
"${configDir}/accounts.conf" = mkIf genAccountsConf {
|
||||
text = joinCfg [
|
||||
header
|
||||
(mkINI cfg.extraAccounts)
|
||||
(mkINI primaryAccountAccounts)
|
||||
(mkINI accountsExtraAccounts)
|
||||
];
|
||||
};
|
||||
|
||||
"${configDir}/aerc.conf" = mkIf genAercConf {
|
||||
text = joinCfg [
|
||||
header
|
||||
(mkINI cfg.extraConfig)
|
||||
(joinContextual accountsExtraConfig)
|
||||
];
|
||||
};
|
||||
|
||||
"${configDir}/binds.conf" = mkIf genBindsConf {
|
||||
text = joinCfg [
|
||||
header
|
||||
(mkINI cfg.extraBinds)
|
||||
(joinContextual accountsExtraBinds)
|
||||
];
|
||||
};
|
||||
}
|
||||
// (mkStyleset cfg.stylesets)
|
||||
// (mkTemplates cfg.templates);
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue