feat(accounts): add emails workflow settings

This commit is contained in:
Yorick Barbanneau 2025-07-15 21:36:05 +02:00
parent e4dcb39fc5
commit 4ba9224348
7 changed files with 402 additions and 15 deletions

41
flake.lock generated
View file

@ -56,6 +56,26 @@
"type": "github" "type": "github"
} }
}, },
"nix-private": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1754265461,
"narHash": "sha256-qVA40ypUxsQR+kpzgqrsS2JrdwS6CI7JQcvpLjc+nlg=",
"ref": "main",
"rev": "7ce0411fc0c480211951ef58b263c5eb72579681",
"shallow": true,
"type": "git",
"url": "ssh://git@git.epha.se:24422/ephase/nix-private.git"
},
"original": {
"ref": "main",
"shallow": true,
"type": "git",
"url": "ssh://git@git.epha.se:24422/ephase/nix-private.git"
}
},
"nixgl": { "nixgl": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
@ -78,6 +98,20 @@
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": {
"lastModified": 1753722563,
"narHash": "sha256-FK8iq76wlacriq3u0kFCehsRYTAqjA9nfprpiSWRWIc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "648f70160c03151bc2121d179291337ad6bc564b",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1748693115, "lastModified": 1748693115,
"narHash": "sha256-StSrWhklmDuXT93yc3GrTlb0cKSS0agTAxMGjLKAsY8=", "narHash": "sha256-StSrWhklmDuXT93yc3GrTlb0cKSS0agTAxMGjLKAsY8=",
@ -92,7 +126,7 @@
"type": "indirect" "type": "indirect"
} }
}, },
"nixpkgs_2": { "nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1748693115, "lastModified": 1748693115,
"narHash": "sha256-StSrWhklmDuXT93yc3GrTlb0cKSS0agTAxMGjLKAsY8=", "narHash": "sha256-StSrWhklmDuXT93yc3GrTlb0cKSS0agTAxMGjLKAsY8=",
@ -111,7 +145,7 @@
"nur": { "nur": {
"inputs": { "inputs": {
"flake-parts": "flake-parts", "flake-parts": "flake-parts",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs_3",
"treefmt-nix": "treefmt-nix" "treefmt-nix": "treefmt-nix"
}, },
"locked": { "locked": {
@ -131,8 +165,9 @@
"root": { "root": {
"inputs": { "inputs": {
"home-manager": "home-manager", "home-manager": "home-manager",
"nix-private": "nix-private",
"nixgl": "nixgl", "nixgl": "nixgl",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs_2",
"nur": "nur", "nur": "nur",
"sops-nix": "sops-nix" "sops-nix": "sops-nix"
} }

View file

@ -15,20 +15,23 @@
url = "github:Mic92/sops-nix"; url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
nix-private = {
url = "git+ssh://git@git.epha.se:24422/ephase/nix-private.git?shallow=1&ref=main";
};
}; };
outputs = { self, nixpkgs, home-manager, nur, nixgl, sops-nix, ... }@inputs: outputs = { self, nixpkgs, home-manager, nur, nixgl, sops-nix, ... }@inputs:
let let
stateVersion = "23.11"; stateVersion = "23.11";
allSystems = [ allSystems = [
"x86_64-linux" # 64bit AMD/Intel x86 "x86_64-linux" # 64bit AMD/Intel x86
"aarch64-linux" # 64bit ARM Linux "aarch64-linux" # 64bit ARM Linux
]; ];
forAllSystems = fn: forAllSystems = fn:
nixpkgs.lib.genAttrs allSystems nixpkgs.lib.genAttrs allSystems
(system: fn { pkgs = import nixpkgs { inherit system; }; }); (system: fn { pkgs = import nixpkgs { inherit system; }; });
createNixosSystem = { system, hostname, username ? "ephase" }: nixpkgs.lib.nixosSystem { createNixosSystem = { system, hostname, username ? "ephase" }: nixpkgs.lib.nixosSystem {
system = system; system = system;
specialArgs = { specialArgs = {
@ -40,7 +43,7 @@
./nixos/default.nix ./nixos/default.nix
]; ];
}; };
createHomeConfiguration = { system ? "x86_64-linux", hostname, username ? "ephase" }: createHomeConfiguration = { system ? "x86_64-linux", hostname, username ? "ephase" }:
home-manager.lib.homeManagerConfiguration { home-manager.lib.homeManagerConfiguration {
pkgs = import nixpkgs { pkgs = import nixpkgs {
@ -79,13 +82,13 @@
luci = createNixosSystem { system = "x86_64-linux"; hostname = "luci"; }; luci = createNixosSystem { system = "x86_64-linux"; hostname = "luci"; };
}; };
homeConfigurations = { homeConfigurations = {
"rick" = createHomeConfiguration { system = "aarch64-linux"; hostname = "rick";}; "rick" = createHomeConfiguration { system = "aarch64-linux"; hostname = "rick";};
"luci" = createHomeConfiguration { system = "x86_64-linux"; hostname = "luci";}; "luci" = createHomeConfiguration { system = "x86_64-linux"; hostname = "luci";};
"morty" = createHomeConfiguration { system = "x86_64-linux"; hostname = "morty";}; "morty" = createHomeConfiguration { system = "x86_64-linux"; hostname = "morty";};
"mrmeeseeks" = createHomeConfiguration { system = "x86_64-linux"; hostname = "mrmeeseeks";}; "mrmeeseeks" = createHomeConfiguration { system = "x86_64-linux"; hostname = "mrmeeseeks";};
"work" = createHomeConfiguration { "work" = createHomeConfiguration {
system = "x86_64-linux"; system = "x86_64-linux";
hostname = "work"; hostname = "work";
username = "yorick-barbanneau"; username = "yorick-barbanneau";
}; };
}; };

View file

@ -1,5 +1,13 @@
{ ... }: { { inputs, ... }: {
config.modules = { config.modules = {
email = {
enable = true;
accountConfigs = {
ubordeaux = inputs.nix-private.mail.ubordeaux;
xiemeart = inputs.nix-private.mail.xiemeart;
};
primary = "xiemeart";
};
application = { application = {
gnupg = { gnupg = {
enable = true; enable = true;

View file

@ -1,6 +1,6 @@
{ ... }: { ... }:
{ {
home.file.".config/xkb/symbols/gpdwinmax".text = home.file.".config/xkb/symbols/gpdwinmax".text =
'' ''
default partial alphanumeric_keys default partial alphanumeric_keys
xkb_symbols "us-intl-winmax" { xkb_symbols "us-intl-winmax" {
@ -30,4 +30,5 @@
scale = "1.3"; scale = "1.3";
}; };
}; };
sops.age.keyFile = "/home/ephase/.config/sops/age/keys.txt";
} }

View file

@ -0,0 +1,222 @@
{ lib, config, pkgs, inputs, ... }:
with lib;
let
cfg = config.modules.email;
secretsDirectory = "${(builtins.toString inputs.nix-private)}/secrets";
in
{
options.modules.email = {
enable = mkEnableOption "Enable email accounts configuration";
accountConfigs = mkOption {
type = types.attrsOf types.attrs;
default = false;
description = "List of account variables used to create accounts";
};
primary = mkOption {
type = types.str;
default = false;
description = "name of primary account";
};
};
config = mkIf cfg.enable {
sops = let
secretList = lib.mapAttrs' ( name: value:
nameValuePair ( value.secret.key ) ({
sopsFile = "${secretsDirectory}/${value.secret.file}";
})
) cfg.accountConfigs;
in {
secrets = secretList;
};
accounts.email = let
defaultSetting = {
mbsync = {
enable = true;
create = "maildir";
expunge = "both";
};
msmtp = {
enable = true;
};
notmuch = {
enable = true;
};
neomutt = {
enable = true;
showDefaultMailbox = false;
};
};
accountsList = lib.mapAttrs ( name: value: lib.recursiveUpdate defaultSetting value.config ) cfg.accountConfigs;
in {
maildirBasePath = "mail";
accounts = lib.recursiveUpdate accountsList { "${cfg.primary}".primary = true; };
};
programs.afew = let
mailMoverRules = lib.mergeAttrsList (
lib.attrsets.mapAttrsToList (
n: v:
if lib.hasAttrByPath ["afew" "mailMover"] v then
v.afew.mailMover
else {}
) cfg.accountConfigs);
in {
enable = true;
extraConfig = ''
[FolderNameFilter]
folder_explicit_list = archives
folder_transforms = archives:archive
maildir_separator = /
[MailMover]
folders = ${lib.concatStringsSep " " (lib.unique (lib.mapAttrsToList (n: v: "${n}") mailMoverRules))}
rename = True
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "${n} = ${toString v}") mailMoverRules)}
[SpamFilter]
[KillThreadsFilter]
[ArchiveSentMailsFilter]
sent_tag = sent
[InboxFilter]
'';
};
programs.neomutt = let
accountMacros = lib.imap1 (
i:
elem: elem // {
map = ["index" "pager"];
key = "<F${(toString (i + 1))}>";
}) (
lib.attrsets.mapAttrsToList (
n: _:
{ "action" = "<sync-mailbox><refresh><enter-command>source ~/.config/neomutt/${n}<enter><change-folder>=Inbox<enter><change-vfolder>Unread<enter>";}
) inputs.nix-private.mail);
in {
enable = true;
unmailboxes = true;
editor = "nvim +/^$/ +':nohl'";
settings = {
sleep_time = "1";
mbox_type = "Maildir";
header_color_partial = "yes";
duplicate_threads = "yes";
index_format = "'%4C| %24.24?GS?%F %GS &%F? %?GR?%GR &%?GU?%GU & ??%?GA?%GA & ?%?GE?%GE & ?%?M?󰘕 %s&%s? %* %?g? %g? %<[y?%<[m?%<[d?%9[%H:%M ]&%9[%a %d ]>&%9[%b %d ]>&%9[%m/%y ]>'";
sort_aux = "last-date-sent";
mail_check = "120";
hidden_tags = "inbox,unread,draft,flagged,passed,replied,signed,encrypted,attachment,sent";
markers = "no";
wrap = "90";
smart_wrap = "yes";
reflow_text = "yes";
reflow_wrap = "90";
text_flowed = "yes";
search_context = "3";
pager_context = "5";
pager_index_lines = "10";
rfc2047_parameters = "yes";
edit_headers = "yes";
send_charset = "utf-8";
envelope_from = "yes";
my_status = "' %o/%m | %l 󰉉 | %f %* Sort: %s-%S Pos: %P '";
my_pager = "' %F | %s %* Pos: %P '";
compose_format = "' COMPOSE %a | 󰉉 %l'";
query_command = "'${pkgs.khard}/bin/khard email --parsable --search-in-source-files %s'";
};
extraConfig = ''
${(builtins.readFile ./files/theme.muttrc)}
charset-hook ^iso-8859-1$ cp1252
ignore *
unignore from date subject to cc bcc tags user-agent x-mailer
auto_view text/x-vcard text/html text/enriched text/calendar
alternative_order text/html text/enriched text/plain text/*
tag-transforms "attachment" "󰁦" \
"encrypted" "󱧈" \
"signed" "󱅞" \
"unread" "" \
"replied" ""
tag-formats "attachment" "GA" \
"encrypted" "GE" \
"signed" "GS" \
"unread" "GU" \
"replied" "GR"
'';
binds = [
{ map = [ "attach" "browser" "index" "pager" ]; key = "g"; action = "noop"; }
{ map = [ "attach" "browser" "index" "pager" ]; key = "G"; action = "noop"; }
{ map = [ "index" ]; key = "q"; action = "noop";}
{ map = [ "pager" ]; key = "Q"; action = "noop";}
{ map = [ "attach" "browser" "index" ]; key = "gg"; action = "first-entry";}
{ map = [ "attach" "browser" "index" ]; key = "G"; action = "last-entry";}
{ map = [ "pager" ]; key = "gg"; action = "top"; }
{ map = [ "pager" ]; key = "G"; action = "bottom"; }
{ map = [ "pager" ]; key = "k"; action = "previous-line"; }
{ map = [ "pager" ]; key = "j"; action = "next-line"; }
# Scrolling
{ map = [ "attach" "browser" "pager" "index" ]; key = "\\CF"; action = "next-page";}
{ map = [ "attach" "browser" "pager" "index" ]; key = "\\CB"; action = "previous-page";}
{ map = [ "attach" "browser" "pager" "index" ]; key = "\\Cu"; action = "half-up";}
{ map = [ "attach" "browser" "pager" "index" ]; key = "\\Cd"; action = "half-down";}
{ map = [ "browser" "pager" ]; key = "\\Ce"; action = "next-line";}
{ map = [ "browser" "pager" ]; key = "\\Cy"; action = "previous-line";}
{ map = [ "index" ]; key = "\\Ce"; action = "next-line";}
{ map = [ "index" ]; key = "\\Cy"; action = "previous-line";}
# Delete
{ map = [ "pager" "index" ]; key = "d "; action = "noop";}
{ map = [ "pager" "index" ]; key = "dd "; action = "delete-message";}
# Reply
{ map =[ "pager" "index" ]; key = "R "; action = "group-reply";}
# sidebar
{ map = [ "index" "pager" ]; key = "<f12>"; action = "sidebar-toggle-visible";}
{ map = [ "index" "pager" ]; key = "{"; action = "sidebar-prev";}
{ map = [ "index" "pager" ]; key = "}"; action = "sidebar-next";}
{ map = [ "index" "pager" ]; key = "|"; action = "sidebar-open";}
# open virtual folder
{ map = [ "index" "pager" ]; key = "X"; action = "noop";}
{ map = [ "index" "pager" ]; key = "X"; action = "change-vfolder";}
# read entire thread of the current message
{ map = [ "index" "pager" ]; key = "+"; action = "entire-thread";}
# generate virtual folder from query
{ map = [ "index" "pager" ]; key = "\\eX"; action = "vfolder-from-query";}
# generate virtual folder from query with time window
{ map = [ "index" ]; key = "<"; action = "vfolder-window-backward";}
{ map = [ "index" ]; key = ">"; action = "vfolder-window-forward";}
{ map = [ "index" "pager" ]; key = "\\CD"; action = "modify-tags";}
# Editor
{ map = [ "editor" ]; key = "<Tab>"; action = "complete-query";}
{ map = [ "editor" ]; key = "^T "; action = "complete";}
];
macros = [
#Define some macros here
] ++ accountMacros;
};
programs.mbsync = {
enable = true;
};
services.mbsync = {
enable = true;
postExec = "${pkgs.notmuch}/bin/notmuch new";
};
programs.notmuch = {
enable = true;
new.tags = [ "new" ];
hooks.postNew = ''
${pkgs.afew}/bin/afew --tag --new
${pkgs.afew}/bin/afew --move --all
'';
};
programs.msmtp = {
enable = true;
};
};
}

View file

@ -0,0 +1,117 @@
# vi: ft=muttrc
# base16-mutt: base16-shell support for mutt
## Base
color normal color20 default # softer, bold
## Weak
color tilde color08 default # `~` padding at the end of pager
color attachment color08 default
color tree color01 default # arrow in threads
color signature color08 default
color markers color08 default # `+` wrap indicator in pager
color underline color21 default
color error color01 default
color message color04 default # informational messages
color search color08 color03
color status color20 color18
color indicator color21 color19 # inverse, brighter
color status color00 color06 ' Pos: ([[:alnum:]]|%)+ '
color status color20 color19 ' Sort: ([[:alpha:]]|-)+ '
# Message Index ----------------------------------------------------------------
## Index parts
color index_number color20 default
color index_author color20 default
color index_date color20 default
color index_flags color20 default
color index_tag color20 default
color index_tags color20 default
color index_author italic color06 default "~g !~V"
color index_author color04 default "~g ~V"
color index_subject color04 default "~G"
color index_date color08 default
color index_number color08 default
color index_subject bold color07 default "(~U|~N|~O)"
color index_author italic color00 color06 "~T"
color index_subject italic color00 color06 "~T"
color index_date italic color00 color06 "~T"
color index_tags italic color00 color06 "~T"
color index_tag italic color00 color06 "~T"
color index_subject italic bold color00 default "~T (~U|~N|~O)"
## Weak
color index color01 default "~v~(~F)" # collapsed thread with flagged inside
# Selection
color index italic color00 color06 "~T" # tagged messages
color index color01 color18 "~D" # deleted messages
### Message Headers ----------------------------------------------------
# Base
set header_color_partial
hdr_order From Date: From: To: Cc: Subject:
color header color04 default '^[^[:blank:]:]*:'
color hdrdefault color20 default
color header color17 default "^[Date:] (.*)"
color header color04 default "^From:"
color header color05 default "^Tags:"
color header color16 default "^(To|Cc|CC|BCC):"
color header brightcolor07 default "^Subject: (.*)"
color header color06 color00 "((@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]),)*@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]):)?[0-9a-z_.+%$-]+@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\])"
### Message Body -------------------------------------------------------
# When possible, these regular expressions attempt to match http://spec.commonmark.org/
## Weak
# ~~~ Horizontal rules ~~~
color body color08 color00 "([[:space:]]*[-+=#*~_]){3,}[[:space:]]*"
## Strong
# *Bold* span
color body brightcolor03 color00 "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)"
# _Underline_ span
color body color05 color00 "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)"
# /Italic/ span (Sometimes gets directory names)
color body color05 color00 "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)"
# ATX headers
color body color04 color00 "^[[:space:]]{0,3}#+[[:space:]].*$"
## Highlight
# `Code` span
color body color02 color00 "(^|[[:space:][:punct:]])\`[^\`]+\`([[:space:][:punct:]]|$)"
# Indented code block
color body color02 color00 "^[[:space:]]{4,}.*$"
# URLs
color body italic color04 color00 "([a-z][a-z0-9+-]*://(((([a-z0-9_.!~*'();:&=+$,-]|%[0-9a-f][0-9a-f])*@)?((([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)(:[0-9]+)?)|([a-z0-9_.!~*'()$,;:@&=+-]|%[0-9a-f][0-9a-f])+)(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?(#([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?|(www|ftp)\\.(([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?(:[0-9]+)?(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?(#([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?)[^].,:;!)? \t\r\n<>\"]"
# Email addresses
color body color06 color00 "((@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]),)*@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]):)?[0-9a-z_.+%$-]+@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\])"
# Emoticons ;-P
color body black yellow "[;:][-o]?[})>{(<|P]"
# PGP
color body color02 default "(Good signature)"
color body color01 default "(Bad signature)"
color body color16 default "(Problem signature)"
color body color04 color00 "^gpg: "
## Quotation blocks
color quoted color06 color00
color quoted1 color02 color00
color quoted2 color03 color00
color quoted3 color16 color00
color quoted4 color01 color00
color quoted5 color17 color00
color quoted6 color05 color00
color quoted7 color04 color00

View file

@ -25,5 +25,6 @@
./web/qutebrowser/default.nix ./web/qutebrowser/default.nix
./web/webcord ./web/webcord
./accounts/dav ./accounts/dav
./accounts/email
]; ];
} }