diff options
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/private/default.example.nix | 11 | ||||
| -rw-r--r-- | modules/private/email.example.nix | 43 | ||||
| -rw-r--r-- | modules/private/git.example.nix | 8 | ||||
| -rw-r--r-- | modules/programs/aerc/assertions.nix | 19 | ||||
| -rw-r--r-- | modules/programs/aerc/default.nix | 58 | ||||
| -rw-r--r-- | modules/programs/default.nix | 3 | ||||
| -rw-r--r-- | modules/programs/email/default.nix | 53 | ||||
| -rw-r--r-- | modules/programs/git/default.nix | 23 | ||||
| -rw-r--r-- | modules/programs/meli/assertions.nix | 27 | ||||
| -rw-r--r-- | modules/programs/meli/default.nix | 46 | ||||
| -rw-r--r-- | modules/scripts/aerc-mail-setup.sh | 332 | ||||
| -rw-r--r-- | modules/scripts/aerc-sync-mail.sh | 14 | ||||
| -rw-r--r-- | modules/scripts/default.nix | 41 | ||||
| -rw-r--r-- | modules/scripts/meli-sync.sh | 5 |
14 files changed, 228 insertions, 455 deletions
diff --git a/modules/private/default.example.nix b/modules/private/default.example.nix new file mode 100644 index 0000000..e5b1a84 --- /dev/null +++ b/modules/private/default.example.nix @@ -0,0 +1,11 @@ +{ config, ... }: + +{ + # This file is a template for modules/private/default.nix + # Copy to modules/private/default.nix and fill in your secrets. + + imports = [ + ./git.nix + ./email.nix + ]; +} diff --git a/modules/private/email.example.nix b/modules/private/email.example.nix new file mode 100644 index 0000000..ae3b475 --- /dev/null +++ b/modules/private/email.example.nix @@ -0,0 +1,43 @@ +{ config, ... }: + +let + mailDir = "${config.home.homeDirectory}/.local/mail"; +in +{ + accounts.email.accounts = { + "example" = { + realName = "Example User"; + address = "user@example.com"; + userName = "user@example.com"; + passwordCommand = "${config.programs.gopass.package}/bin/gopass show -o mail/example"; + flavor = "gmail.com"; + + mbsync = { + enable = true; + createMailbox = true; + extraConfig.channel = { + Patterns = "*"; + SyncState = "*"; + }; + }; + + notmuch.enable = true; + + mu.enable = false; + msmtp.enable = false; + imapnotify.enable = false; + }; + }; + + # Mbsync config + programs.mbsync = { + enable = true; + directory = mailDir; + }; + + # Notmuch config + programs.notmuch = { + enable = true; + database.path = "${mailDir}/.notmuch"; + }; +} diff --git a/modules/private/git.example.nix b/modules/private/git.example.nix new file mode 100644 index 0000000..888c769 --- /dev/null +++ b/modules/private/git.example.nix @@ -0,0 +1,8 @@ +{ config, ... }: + +{ + programs.git.signing = { + key = "EXAMPLE_GPG_KEY_ID"; + signByDefault = true; + }; +} diff --git a/modules/programs/aerc/assertions.nix b/modules/programs/aerc/assertions.nix deleted file mode 100644 index a2b49c1..0000000 --- a/modules/programs/aerc/assertions.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ config, ... }: -{ - assertions = [ - { - assertion = !config.programs.aerc.enable || config.programs.less.enable; - message = '' - programs.aerc is enabled but programs.less.enable is not. - aerc requires less as its pager. Please enable programs.less. - ''; - } - { - assertion = !config.programs.aerc.enable || config.programs.bat.enable; - message = '' - programs.aerc is enabled but programs.bat.enable is not. - aerc requires bat for text/html filtering. Please enable programs.bat. - ''; - } - ]; -} diff --git a/modules/programs/aerc/default.nix b/modules/programs/aerc/default.nix deleted file mode 100644 index 5b4ce78..0000000 --- a/modules/programs/aerc/default.nix +++ /dev/null @@ -1,58 +0,0 @@ -# aerc - Terminal email client -{ - config, - lib, - ... -}: - -{ - imports = [ ./assertions.nix ]; - - config = lib.mkIf config.programs.aerc.enable { - programs.aerc = { - extraConfig = { - general = { - unsafe-accounts-conf = false; - default-save-path = "${config.home.homeDirectory}/Downloads"; - use-terminal-pinentry = false; - }; - - ui = { - index-columns = "flags:4,name<32%,subject,date>="; - column-name = "{{with index .From 0}}{{.Address}}{{if .Name}} ({{.Name}}){{end}}{{end}}"; - timestamp-format = "2006-01-02 15:04"; - this-day-time-format = "15:04"; - this-week-time-format = "Mon 15:04"; - sidebar-width = 24; - }; - - viewer = { - pager = lib.getExe config.programs.less.package; - show-headers = true; - }; - - filters = '' - text/plain = fold -w 100 -s - text/html = fold -w 100 -s - text/* = fold -w 100 -s - message/delivery-status = fold -w 100 -s - ''; - - hooks = { - mail-added = "aerc-sync-mail \"$AERC_ACCOUNT\""; - mail-deleted = "aerc-sync-mail \"$AERC_ACCOUNT\""; - flag-changed = "aerc-sync-mail \"$AERC_ACCOUNT\""; - }; - }; - }; - - home.file.".config/aerc/notmuch-query-map".text = '' - Inbox=tag:inbox and not tag:deleted - Unread=tag:unread and not tag:deleted - Flagged=tag:flagged and not tag:deleted - Sent=folder:sent or folder:Sent or folder:"[Gmail]/Sent Mail" - Archive=not tag:inbox and not tag:deleted - All=* - ''; - }; -} diff --git a/modules/programs/default.nix b/modules/programs/default.nix index b1f620b..1527087 100644 --- a/modules/programs/default.nix +++ b/modules/programs/default.nix @@ -3,7 +3,6 @@ # Tools - Collection of tool configurations (browsers, editors, utilities, etc.) { imports = [ - ./aerc ./aria2 ./bat ./bluetui @@ -25,6 +24,7 @@ ./lf ./lsd ./maim + ./meli ./nixvim ./nvtop ./opencode @@ -33,6 +33,7 @@ ./ouch ./pamixer ./gopass + ./email ./playerctl ./qemu ./slop diff --git a/modules/programs/email/default.nix b/modules/programs/email/default.nix new file mode 100644 index 0000000..36dfc95 --- /dev/null +++ b/modules/programs/email/default.nix @@ -0,0 +1,53 @@ +# email - Mbsync and notmuch mail synchronization +{ + config, + lib, + pkgs, + ... +}: + +let + mailDir = "${config.home.homeDirectory}/.local/mail"; +in +{ + options.programs.email = { + enable = lib.mkEnableOption "Email suite (mbsync + notmuch)"; + }; + + config = lib.mkIf config.programs.email.enable { + home.packages = [ + pkgs.mbsync + pkgs.notmuch + ]; + + home.sessionVariables = { + MAILDIR = mailDir; + NOTMUCH_CONFIG = "${config.xdg.configHome}/notmuch/notmuchrc"; + }; + + xdg.configFile."notmuch/notmuch.config" = { + text = '' + [database] + path=${mailDir}/.notmuch + + [user] + name= + address= + primary_key= + + [new] + tags=unclassified;new; + ignore= + + [search] + exclude_tags=deleted;spam; + + [maildir] + synchronize_flags=true + + [crypto] + gpg_path=${pkgs.gnupg}/bin/gpg + ''; + }; + }; +} diff --git a/modules/programs/git/default.nix b/modules/programs/git/default.nix index 133c063..91498b0 100644 --- a/modules/programs/git/default.nix +++ b/modules/programs/git/default.nix @@ -20,14 +20,24 @@ }; useSSH = lib.mkEnableOption "Use SSH instead of HTTPS for common git platforms."; + + signing = { + key = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "GPG signing key ID."; + }; + signByDefault = lib.mkEnableOption "Sign commits by default"; + }; }; config = lib.mkIf config.programs.git.enable { programs.git = { includes = [ - # An additional config file for global git settings but for more - # private/non-portable configuration options like GPG keys - { path = "${config.xdg.configHome}/git/personal"; } + { + path = "${config.xdg.configHome}/git/personal"; + condition = "hasconfig:remote.*.url:git@*"; + } ]; settings = lib.recursiveUpdate @@ -47,6 +57,13 @@ url."git@git.sr.ht:".insteadOf = "https://git.sr.ht/"; } ); + extraConfig = { + user.signingKey = lib.mkIf ( + config.programs.git.signing.key != null + ) config.programs.git.signing.key; + commit.gpgSign = config.programs.git.signing.signByDefault; + tag.forceSignAnnotated = config.programs.git.signing.signByDefault; + }; }; }; } diff --git a/modules/programs/meli/assertions.nix b/modules/programs/meli/assertions.nix new file mode 100644 index 0000000..d651047 --- /dev/null +++ b/modules/programs/meli/assertions.nix @@ -0,0 +1,27 @@ +{ config, ... }: + +{ + assertions = [ + { + assertion = !config.programs.meli.enable || config.programs.less.enable; + message = '' + programs.meli is enabled but programs.less.enable is not. + meli requires less as its pager. Please enable programs.less. + ''; + } + { + assertion = !config.programs.meli.enable || config.programs.bat.enable; + message = '' + programs.meli is enabled but programs.bat.enable is not. + meli requires bat for text/html filtering. Please enable programs.bat. + ''; + } + { + assertion = !config.programs.meli.enable || config.programs.email.enable; + message = '' + programs.meli is enabled but programs.email.enable is not. + meli requires the email module (mbsync + notmuch) to be enabled. + ''; + } + ]; +} diff --git a/modules/programs/meli/default.nix b/modules/programs/meli/default.nix new file mode 100644 index 0000000..d17a3db --- /dev/null +++ b/modules/programs/meli/default.nix @@ -0,0 +1,46 @@ +# meli - Terminal email client +{ + config, + lib, + pkgs, + ... +}: + +let + mailDir = "${config.home.homeDirectory}/.local/mail"; +in +{ + imports = [ ./assertions.nix ]; + + config = lib.mkIf config.programs.meli.enable { + home.packages = [ pkgs.meli ]; + + xdg.configFile."meli/config.toml" = { + text = '' + [terminal] + themes = "dark" + + [notifications] + enable = false + + [pager] + html_filter = "${pkgs.bat}/bin/bat --language=html" + + [composing] + editor_command = "${pkgs.neovim}/bin/nvim" + + [pgp] + auto_verify_signatures = true + gpg_binary = "${pkgs.gnupg}/bin/gpg" + + [search] + backend = "notmuch" + database_path = "${mailDir}/.notmuch" + ''; + }; + + programs.zsh.initExtra = lib.mkAfter '' + alias meli='meli-sync && meli' + ''; + }; +} diff --git a/modules/scripts/aerc-mail-setup.sh b/modules/scripts/aerc-mail-setup.sh deleted file mode 100644 index cca982e..0000000 --- a/modules/scripts/aerc-mail-setup.sh +++ /dev/null @@ -1,332 +0,0 @@ -set -euo pipefail - -config_home="${XDG_CONFIG_HOME:-$HOME/.config}" -data_home="${XDG_DATA_HOME:-$HOME/.local/share}" -state_home="${XDG_STATE_HOME:-$HOME/.local/state}" -password_store_dir="${PASSWORD_STORE_DIR:-$HOME/.password-store}" - -mail_root="${data_home}/mail" -isync_config="${config_home}/isyncrc" -aerc_accounts="${config_home}/aerc/accounts.conf" -notmuch_config="${config_home}/notmuch/default/config" -query_map="${config_home}/aerc/notmuch-query-map" - -usage() { - cat <<'USAGE' -Usage: - aerc-mail-setup add Add or update a Gmail, Yahoo, or Outlook account - aerc-mail-setup remove Remove generated aerc/isync blocks for an account - aerc-mail-setup list List generated accounts - aerc-mail-setup sync Run mbsync and notmuch indexing - -Secrets are stored through pass(1). Generated private config is written under -~/.config/isyncrc, ~/.config/aerc/accounts.conf, and ~/.config/notmuch/default/config. -Removing an account only removes generated config blocks; it does not delete mail or passwords. -USAGE -} - -prompt() { - local label="$1" - local default="${2:-}" - local value - - if [[ -n "$default" ]]; then - printf '%s [%s]: ' "$label" "$default" >&2 - read -r value - printf '%s\n' "${value:-$default}" - else - printf '%s: ' "$label" >&2 - read -r value - printf '%s\n' "$value" - fi -} - -sanitize_account() { - printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_.-]/-/g' -} - -url_encode_user() { - printf '%s' "$1" | sed 's/%/%25/g; s/@/%40/g; s/+/%2B/g' -} - -ensure_query_map() { - mkdir -p "$(dirname "$query_map")" - if [[ ! -e "$query_map" ]]; then - cat >"$query_map" <<'EOF' -Inbox=tag:inbox and not tag:deleted -Unread=tag:unread and not tag:deleted -Flagged=tag:flagged and not tag:deleted -Sent=folder:sent or folder:Sent or folder:"[Gmail]/Sent Mail" -Archive=not tag:inbox and not tag:deleted -All=* -EOF - fi -} - -write_notmuch_config() { - local name="$1" - local email="$2" - - mkdir -p "$(dirname "$notmuch_config")" "$mail_root" "$state_home/isync" - if [[ ! -e "$notmuch_config" ]]; then - cat >"$notmuch_config" <<EOF -[database] -path=${mail_root} -mail_root=${mail_root} - -[user] -name=${name} -primary_email=${email} - -[new] -tags=unread;inbox; -ignore=.uidvalidity;.mbsyncstate - -[search] -exclude_tags=deleted;spam; - -[maildir] -synchronize_flags=true -EOF - fi -} - -replace_block() { - local file="$1" - local block_name="$2" - local tmp - - mkdir -p "$(dirname "$file")" - tmp="$(mktemp)" - if [[ -e "$file" ]]; then - awk -v start="# BEGIN ${block_name}" -v end="# END ${block_name}" ' - $0 == start { skip = 1; next } - $0 == end { skip = 0; next } - !skip { print } - ' "$file" >"$tmp" - fi - - { - sed '/^[[:space:]]*$/N;/^\n$/D' "$tmp" 2>/dev/null || true - printf '\n# BEGIN %s\n' "$block_name" - cat - printf '# END %s\n' "$block_name" - } >"$file" - rm -f "$tmp" - chmod 600 "$file" -} - -remove_block() { - local file="$1" - local block_name="$2" - local tmp - - [[ -e "$file" ]] || return - tmp="$(mktemp)" - awk -v start="# BEGIN ${block_name}" -v end="# END ${block_name}" ' - $0 == start { skip = 1; next } - $0 == end { skip = 0; next } - !skip { print } - ' "$file" >"$tmp" - cat "$tmp" >"$file" - rm -f "$tmp" - chmod 600 "$file" -} - -pass_insert_if_requested() { - local pass_entry="$1" - - if pass show "$pass_entry" >/dev/null 2>&1; then - return - fi - - printf 'No pass entry found at %s.\n' "$pass_entry" - if [[ ! -f "${password_store_dir}/.gpg-id" ]]; then - cat >&2 <<EOF -pass is not initialized yet. - -Create or choose a GPG key, then initialize pass with: - gpg --list-secret-keys --keyid-format=long - pass init <gpg-key-id-or-email> - -After that, rerun: - aerc-mail-setup add -EOF - exit 1 - fi - - printf 'Store the password there now? [y/N]: ' - read -r answer - case "$answer" in - y|Y|yes|YES) - pass insert "$pass_entry" - ;; - *) - printf 'Create it later with: pass insert %s\n' "$pass_entry" - ;; - esac -} - -provider_defaults() { - local provider="$1" - case "$provider" in - gmail) - imap_host="imap.gmail.com" - imap_port="993" - imap_tls="IMAPS" - imap_auth="LOGIN" - smtp_host="smtp.gmail.com" - smtp_port="587" - smtp_scheme="smtp+login" - ;; - yahoo) - imap_host="imap.mail.yahoo.com" - imap_port="993" - imap_tls="IMAPS" - imap_auth="LOGIN" - smtp_host="smtp.mail.yahoo.com" - smtp_port="587" - smtp_scheme="smtp+login" - ;; - outlook) - imap_host="outlook.office365.com" - imap_port="993" - imap_tls="IMAPS" - imap_auth="LOGIN" - smtp_host="smtp.office365.com" - smtp_port="587" - smtp_scheme="smtp+login" - ;; - *) - printf 'Unsupported provider: %s\n' "$provider" >&2 - exit 1 - ;; - esac -} - -add_account() { - local provider account email name user pass_entry sync_patterns - local imap_host imap_port imap_tls imap_auth smtp_host smtp_port smtp_scheme - local encoded_user - - provider="$(prompt "Provider (gmail/yahoo/outlook)")" - provider="$(printf '%s' "$provider" | tr '[:upper:]' '[:lower:]')" - provider_defaults "$provider" - sync_patterns="*" - - email="$(prompt "Email address")" - account="$(prompt "Account id" "$(sanitize_account "$email")")" - account="$(sanitize_account "$account")" - name="$(prompt "Display name")" - - printf 'Use an app password stored in pass; OAuth is not configured here.\n' - user="$(prompt "IMAP/SMTP username" "$email")" - - imap_host="$(prompt "IMAP host" "$imap_host")" - imap_port="$(prompt "IMAP port" "$imap_port")" - imap_tls="$(prompt "mbsync IMAP TLS mode (IMAPS/STARTTLS/None)" "$imap_tls")" - imap_auth="$(prompt "mbsync IMAP auth mechanism" "$imap_auth")" - smtp_host="$(prompt "SMTP host" "$smtp_host")" - smtp_port="$(prompt "SMTP port" "$smtp_port")" - smtp_scheme="$(prompt "aerc SMTP scheme" "$smtp_scheme")" - - pass_entry="$(prompt "pass entry for this account password" "mail/${account}")" - pass_insert_if_requested "$pass_entry" - - mkdir -p "${mail_root}/${account}/INBOX/cur" "${mail_root}/${account}/INBOX/new" "${mail_root}/${account}/INBOX/tmp" - ensure_query_map - write_notmuch_config "$name" "$email" - - replace_block "$isync_config" "aerc-mail:${account}" <<EOF -IMAPAccount ${account}-remote -Host ${imap_host} -Port ${imap_port} -User ${user} -PassCmd "pass show ${pass_entry}" -TLSType ${imap_tls} -AuthMechs ${imap_auth} - -IMAPStore ${account}-remote -Account ${account}-remote - -MaildirStore ${account}-local -SubFolders Verbatim -Path ${mail_root}/${account}/ -Inbox ${mail_root}/${account}/INBOX - -Channel ${account} -Far :${account}-remote: -Near :${account}-local: -Patterns ${sync_patterns} -Create Both -Remove Both -Expunge Both -Sync Full -SyncState * -EOF - - encoded_user="$(url_encode_user "$user")" - replace_block "$aerc_accounts" "aerc-mail:${account}" <<EOF -[${account}] -source = notmuch://${mail_root} -maildir-store = ${mail_root} -maildir-account-path = ${account} -multi-file-strategy = act-all -query-map = ${query_map} -from = ${name} <${email}> -outgoing = ${smtp_scheme}://${encoded_user}@${smtp_host}:${smtp_port} -outgoing-cred-cmd = pass show ${pass_entry} -check-mail = 5m -check-mail-cmd = aerc-sync-mail ${account} -check-mail-timeout = 5m -default = Inbox -copy-to = Sent -archive = Archive -postpone = Drafts -EOF - - printf 'Configured %s. Run this once for the initial download:\n' "$account" - printf ' aerc-sync-mail %s\n' "$account" -} - -list_accounts() { - if [[ ! -e "$aerc_accounts" ]]; then - printf 'No generated accounts found.\n' - return - fi - sed -n 's/^# BEGIN aerc-mail:\(.*\)$/\1/p' "$aerc_accounts" -} - -remove_account() { - local account - - account="$(prompt "Account id to remove")" - account="$(sanitize_account "$account")" - - remove_block "$isync_config" "aerc-mail:${account}" - remove_block "$aerc_accounts" "aerc-mail:${account}" - printf 'Removed generated config blocks for %s.\n' "$account" -} - -case "${1:-}" in - add) - add_account - ;; - remove) - remove_account - ;; - list) - list_accounts - ;; - sync) - shift - aerc-sync-mail "$@" - ;; - -h|--help|help|"") - usage - ;; - *) - usage >&2 - exit 1 - ;; -esac diff --git a/modules/scripts/aerc-sync-mail.sh b/modules/scripts/aerc-sync-mail.sh deleted file mode 100644 index 7e45cdf..0000000 --- a/modules/scripts/aerc-sync-mail.sh +++ /dev/null @@ -1,14 +0,0 @@ -set -euo pipefail - -target="${1:--a}" -mail_root="${XDG_DATA_HOME:-$HOME/.local/share}/mail" -mkdir -p "$mail_root" - -if [[ "$target" == "all" ]]; then - target="-a" -else - mkdir -p "${mail_root}/${target}/INBOX/cur" "${mail_root}/${target}/INBOX/new" "${mail_root}/${target}/INBOX/tmp" -fi - -mbsync "$target" -notmuch new diff --git a/modules/scripts/default.nix b/modules/scripts/default.nix index 8ee8f11..5e6b88b 100644 --- a/modules/scripts/default.nix +++ b/modules/scripts/default.nix @@ -37,34 +37,6 @@ let # Script definitions: { path, env?, deps?, conditions? } # condition: attrset of { option (string path), value (expected value) } scriptDefs = { - aerc-mail-setup = { - path = ./aerc-mail-setup.sh; - deps = [ - pkgs.gnupg - pkgs.isync - pkgs.notmuch - config.programs.gopass.package - ]; - conditions = [ - { - option = "programs.aerc.enable"; - value = true; - } - ]; - }; - aerc-sync-mail = { - path = ./aerc-sync-mail.sh; - deps = [ - pkgs.isync - pkgs.notmuch - ]; - conditions = [ - { - option = "programs.aerc.enable"; - value = true; - } - ]; - }; aria2-run = { path = ./aria2-run.sh; deps = [ @@ -218,6 +190,19 @@ let } ]; }; + meli-sync = { + path = ./meli-sync.sh; + deps = [ + pkgs.mbsync + pkgs.notmuch + ]; + conditions = [ + { + option = "programs.meli.enable"; + value = true; + } + ]; + }; }; # Check whether all conditions for a script are satisfied diff --git a/modules/scripts/meli-sync.sh b/modules/scripts/meli-sync.sh new file mode 100644 index 0000000..2024562 --- /dev/null +++ b/modules/scripts/meli-sync.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env sh + +# Sync all accounts and immediately tag with notmuch +mbsync -a +notmuch new 2>/dev/null
\ No newline at end of file |
