chore: 2021 -> 2022

This commit is contained in:
Pavel Korytov 2022-02-20 11:59:47 +03:00
parent 7d7a719cee
commit 3b9437dbde
15 changed files with 1 additions and 27542 deletions

View file

@ -10,7 +10,7 @@ staticDir = ["static"]
favicon = "favicon.ico" # path to a .ico to use as favicon
# logo = "logo.svg"
[params.footer]
text = "Pavel Korytov, 2021"
text = "Pavel Korytov, 2022"
mainSections = ['posts']
[permalinks]

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,793 +0,0 @@
+++
title = "Guix"
author = ["Pavel"]
draft = false
+++
[GNU Guix](https://guix.gnu.org/) is (1) a transactional package manager and (2) a GNU/Linux distribution.
My personal selling points are declarative package configuration and transactional upgrades.
References:
- [Official help](https://guix.gnu.org/en/help/)
- [System Crafters wiki](https://wiki.systemcrafters.cc/guix)
- [Pjotr Prins' Guix notes](https://gitlab.com/pjotrp/guix-notes)
- [Davil Wilson's YouTube series](https://www.youtube.com/watch?v=iBaqOK75cho&list=PLEoMzSkcN8oNxnj7jm5V2ZcGc52002pQU)
<div class="ox-hugo-toc toc">
<div></div>
<div class="heading">Table of Contents</div>
- [Profiles](#profiles)
- [Activate profiles](#activate-profiles)
- [Update profiles](#update-profiles)
- [Channels](#channels)
- [Systems](#systems)
- [Base configuration](#base-configuration)
- [indigo](#indigo)
- [eminence](#eminence)
- [azure](#azure)
- [System installation](#system-installation)
- [Preparation](#preparation)
- [Installation](#installation)
- [After installation](#after-installation)
- [Misc software & notes](#misc-software-and-notes)
- [VPN](#vpn)
- [vpn-start](#vpn-start)
- [vpn-stop](#vpn-stop)
- [flatpak](#flatpak)
- [conda](#conda)
- [Slack](#slack)
- [virt-manager](#virt-manager)
- [wakatime-cli](#wakatime-cli)
- [Manifest](#manifest)
</div>
<!--endtoc-->
## Profiles {#profiles}
A profile is a way to group Guix packages. Amongst its advantages, profiles can be defined by manifests, which in turn can be stored in VCS.
References:
- [Guix Profiles in Practice](https://guix.gnu.org/en/cookbook/en/html%5Fnode/Guix-Profiles-in-Practice.html)
### Activate profiles {#activate-profiles}
A script to activate guix profiles. Usage:
```text
activate-profiles [profile1] [profile2] ...
```
Source: [David Wilson's config](https://github.com/daviwil/dotfiles/blob/master/Systems.org#activating-profiles)
```bash
GREEN='\033[1;32m'
RED='\033[1;30m'
NC='\033[0m'
GUIX_EXTRA_PROFILES=$HOME/.guix-extra-profiles
profiles=$*
if [[ $# -eq 0 ]]; then
profiles="$HOME/.config/guix/manifests/*.scm";
fi
for profile in $profiles; do
# Remove the path and file extension, if any
profileName=$(basename $profile)
profileName="${profileName%.*}"
profilePath="$GUIX_EXTRA_PROFILES/$profileName"
manifestPath=$HOME/.config/guix/manifests/$profileName.scm
if [ -f $manifestPath ]; then
echo
echo -e "${GREEN}Activating profile:" $manifestPath "${NC}"
echo
mkdir -p $profilePath
guix package --manifest=$manifestPath --profile="$profilePath/$profileName"
# Source the new profile
GUIX_PROFILE="$profilePath/$profileName"
if [ -f $GUIX_PROFILE/etc/profile ]; then
. "$GUIX_PROFILE"/etc/profile
else
echo -e "${RED}Couldn't find profile:" $GUIX_PROFILE/etc/profile "${NC}"
fi
else
echo "No profile found at path" $profilePath
fi
done
```
### Update profiles {#update-profiles}
A script to update Guix profiles. Usage:
```text
update-profiles [profile1] [profile2] ...
```
Source: once again, [David Wilson's config](https://github.com/daviwil/dotfiles/blob/master/Systems.org#updating-profiles).
```bash
GREEN='\033[1;32m'
NC='\033[0m'
GUIX_EXTRA_PROFILES=$HOME/.guix-extra-profiles
profiles=$*
if [[ $# -eq 0 ]]; then
profiles="$GUIX_EXTRA_PROFILES/*";
fi
for profile in $profiles; do
profileName=$(basename $profile)
profilePath=$GUIX_EXTRA_PROFILES/$profileName
echo
echo -e "${GREEN}Updating profile:" $profilePath "${NC}"
echo
guix package --profile="$profilePath/$profileName" --manifest="$HOME/.config/guix/manifests/$profileName.scm"
done
```
## Channels {#channels}
Specifying additional channels.
[channel-q](https://github.com/SqrtMinusOne/channel-q) is my Guix channel. Don't use it at home.
References:
- [nonguix channel repo](https://gitlab.com/nonguix/nonguix)
- [Guix channels reference](https://guix.gnu.org/manual/en/html%5Fnode/Channels.html)
<!--listend-->
```scheme
(cons*
(channel
(name 'channel-q)
(url "file:///home/pavel/Code/channel-q"))
(channel
(name 'flat)
(url "https://github.com/flatwhatson/guix-channel.git")
(introduction
(make-channel-introduction
"33f86a4b48205c0dc19d7c036c85393f0766f806"
(openpgp-fingerprint
"736A C00E 1254 378B A982 7AF6 9DBE 8265 81B6 4490"))))
(channel
(name 'nonguix)
(url "https://gitlab.com/nonguix/nonguix")
;; (commit "d54973e47b89fe5772a5b6e2d0c0b86acb089e27")
(introduction
(make-channel-introduction
"897c1a470da759236cc11798f4e0a5f7d4d59fbc"
(openpgp-fingerprint
"2A39 3FFF 68F4 EF7A 3D29 12AF 6F51 20A0 22FB B2D5"))))
%default-channels)
```
## Systems {#systems}
Configuring systems with Guix.
Yes, all my machines are named after colors I like.
### Base configuration {#base-configuration}
The base configuration is shared between all the machines.
While it's possible to make a single `.scm` file with base configuration and load it, I noticed that it produces more cryptic error messages whenever there is an error in the base file, so I opt-in for noweb.
`guix system` invocation is as follows:
```text
sudo -E guix system reconfigure ~/.config/guix/systems/[system].scm
```
Common modules:
```scheme
(use-modules (gnu))
(use-modules (gnu system nss))
(use-modules (gnu packages bash))
(use-modules ((gnu packages base) #:select (coreutils glibc)))
(use-modules (gnu packages certs))
(use-modules (gnu packages version-control))
(use-modules (gnu packages vim))
(use-modules (gnu packages gnome))
(use-modules (gnu packages xorg))
(use-modules (gnu packages wm))
(use-modules (gnu packages openbox))
(use-modules (gnu services docker))
(use-modules (gnu services cups))
(use-modules (gnu services virtualization))
(use-modules (srfi srfi-1))
(use-modules (guix channels))
(use-modules (guix inferior))
(use-modules (nongnu packages linux))
(use-modules (nongnu system linux-initrd))
(use-service-modules desktop networking ssh xorg nix)
(use-package-modules ssh)
```
In principle, we could define a variable called `base-operating-system` and extend it in ancestors. However, then we would have to define mandatory fields like `host-name`, `bootloader` with dummy values. Since I'm already using noweb, there is little point.
The following code will be inserted at the top of the `operating-system` definition.
Use the full Linux kernel. I hope I'll be able to use Libre kernel somewhere later.
Inferior in the kernel is used to avoid recompilation. It looks like I can pin these to different commits than in my `channels.scm`
```scheme
(kernel
(let*
((channels
(list (channel
(name 'nonguix)
(url "https://gitlab.com/nonguix/nonguix")
(commit "d3c5eea0cbfe3e5bfbcf1fe15bc916fefacc624f"))
(channel
(name 'guix)
(url "https://git.savannah.gnu.org/git/guix.git")
(commit "cf88c967afbf15c58efb0ba37d6638f1be9a0481"))))
(inferior
(inferior-for-channels channels)))
(first (lookup-inferior-packages inferior "linux" "5.12.9"))))
;; (kernel linux)
(initrd microcode-initrd)
(firmware (list linux-firmware))
(locale "en_US.utf8")
(timezone "Europe/Moscow")
```
US/RU keyboard layout, switch with Alt+Shift.
```scheme
(keyboard-layout (keyboard-layout "us,ru" #:options '("grp:alt_shift_toggle")))
```
User accounts.
```scheme
(users (cons* (user-account
(name "pavel")
(comment "Pavel")
(group "users")
(home-directory "/home/pavel")
(supplementary-groups
'("wheel" ;; sudo
"netdev" ;; network devices
"audio"
"video"
"input"
"tty"
"docker"
"scanner"
"libvirt"
"lp")))
%base-user-accounts))
```
Base packages, necessary right after the installation.
```scheme
(packages
(append
(list nss-certs
git
i3-gaps
i3lock
openbox
xterm
vim)
%base-packages))
```
Default services for each machine:
- override the default `%desktop-services` to add OpenVPN support
- add nix service
- add docker service
- add CUPS service
- add libvirt service
- add a symlink to ELF interpreter to where most Linux binaries expect it
<!--listend-->
```scheme
(define %my-base-services
(cons*
(service openssh-service-type)
(screen-locker-service i3lock "i3lock")
(extra-special-file "/lib64/ld-linux-x86-64.so.2" (file-append glibc "/lib/ld-linux-x86-64.so.2"))
(service nix-service-type)
(service cups-service-type
(cups-configuration
(web-interface? #t)))
(service docker-service-type)
(service libvirt-service-type
(libvirt-configuration
(unix-sock-group "libvirt")
(tls-port "16555")))
(service virtlog-service-type)
(modify-services %desktop-services
(network-manager-service-type
config =>
(network-manager-configuration
(inherit config)
(vpn-plugins (list network-manager-openvpn)))))))
```
### indigo {#indigo}
`indigo` is my desktop PC.
```scheme
<<system-common>>
(operating-system
<<system-base>>
(host-name "indigo")
(services (cons*
(set-xorg-configuration
(xorg-configuration
(keyboard-layout keyboard-layout)))
%my-base-services))
(bootloader
(bootloader-configuration
(bootloader grub-efi-bootloader)
(target "/boot/efi")
(keyboard-layout keyboard-layout)))
(swap-devices
(list (uuid "059a2c26-8f70-4986-adf0-1a2e7b511404")))
(file-systems
(cons* (file-system
(mount-point "/")
(device (file-system-label "my-root"))
(type "ext4"))
(file-system
(mount-point "/boot/efi")
(device "/dev/sda1")
(type "vfat"))
%base-file-systems)))
```
### eminence {#eminence}
`eminence` is a HP 15s laptop.
`%backlight-udev-rule` should enable members of `video` group change the display backlight. See the relevant page at [Arch Wiki](https://wiki.archlinux.org/title/Backlight).
```scheme
<<system-common>>
(define %backlight-udev-rule
(udev-rule
"90-backlight.rules"
(string-append "ACTION==\"add\", SUBSYSTEM==\"backlight\", "
"RUN+=\"/run/current-system/profile/bin/chgrp video /sys/class/backlight/%k/brightness\""
"\n"
"ACTION==\"add\", SUBSYSTEM==\"backlight\", "
"RUN+=\"/run/current-system/profile/bin/chmod g+w /sys/class/backlight/%k/brightness\"")))
(operating-system
<<system-base>>
(host-name "eminence")
(services (cons*
(set-xorg-configuration
(xorg-configuration
(keyboard-layout keyboard-layout)))
(modify-services %my-base-services
(elogind-service-type
config =>
(elogind-configuration
(inherit config)
(handle-lid-switch-external-power 'suspend)))
(udev-service-type
config =>
(udev-configuration
(inherit config)
(rules (cons %backlight-udev-rule
(udev-configuration-rules config))))))))
(bootloader
(bootloader-configuration
(bootloader grub-efi-bootloader)
(target "/boot/efi")
(keyboard-layout keyboard-layout)))
(swap-devices
(list (uuid "f93cf3f6-7ee7-42ec-8ee2-f3d896fdf9b5")))
(file-systems
(cons* (file-system
(mount-point "/")
(device
(uuid "1d937704-bbeb-43b5-bc63-453886c426af"
'ext4))
(type "ext4"))
(file-system
(mount-point "/boot/efi")
(device (uuid "0031-3784" 'fat32))
(type "vfat"))
%base-file-systems)))
```
### azure {#azure}
`azure` is a Lenovo Ideapad 330 laptop.
`%backlight-udev-rule` should enable members of `video` group change the display backlight. See the relevant page at [Arch Wiki](https://wiki.archlinux.org/title/Backlight).
```scheme
<<system-common>>
(define %backlight-udev-rule
(udev-rule
"90-backlight.rules"
(string-append "ACTION==\"add\", SUBSYSTEM==\"backlight\", "
"RUN+=\"/run/current-system/profile/bin/chgrp video /sys/class/backlight/%k/brightness\""
"\n"
"ACTION==\"add\", SUBSYSTEM==\"backlight\", "
"RUN+=\"/run/current-system/profile/bin/chmod g+w /sys/class/backlight/%k/brightness\"")))
(operating-system
<<system-base>>
(host-name "azure")
(services (cons*
(set-xorg-configuration
(xorg-configuration
(keyboard-layout keyboard-layout)))
(modify-services %my-base-services
(elogind-service-type config =>
(elogind-configuration (inherit config)
(handle-lid-switch-external-power 'suspend)))
(udev-service-type config =>
(udev-configuration (inherit config)
(rules (cons %backlight-udev-rule
(udev-configuration-rules config))))))))
(bootloader
(bootloader-configuration
(bootloader grub-efi-bootloader)
(target "/boot/efi")
(keyboard-layout keyboard-layout)))
(swap-devices
(list (uuid "4b2dedb3-b111-4e69-8c05-6daa2b072c76")))
(file-systems
(cons* (file-system
(mount-point "/")
(device (file-system-label "my-root"))
(type "ext4"))
(file-system
(mount-point "/boot/efi")
(device "/dev/sda1")
(type "vfat"))
%base-file-systems)))
```
## System installation {#system-installation}
### Preparation {#preparation}
In my case, the provided ISO doesn't work because of the Libre kernel.
Fortunately, David Wilson has made [a repository](https://github.com/SystemCrafters/guix-installer) with a toolchain to make an ISO with the full kernel. In case it won't be an option, the [nonguix repo](https://gitlab.com/nonguix/nonguix) also has instructions on how to do that.
When an ISO is there, we have to write it on a USB stick. Run `sudo fdisk -l` to get a list of disks.
The approach given in the official instruction is to create a bootable USB with `dd`:
```text
sudo dd of=/dev/sdxX if=<path-to-iso> status=progress && sync
```
However, I couldn't make it work for some strange reason. Fortunately, `gnome-disk-utility` was able to produce a bootable USB.
### Installation {#installation}
Going further, the official instructions for installation & SystemCrafters wiki entry are pretty good, so it's not necessary to repeat them here.
### After installation {#after-installation}
After the installation, the strategy is as follows.
Set a password for the main user (pavel). Login with openbox to get a tolerable interface because i3's default config is horrible.
[Connect to the internet](https://guix.gnu.org/en/manual/en/html%5Fnode/Keyboard-Layout-and-Networking-and-Partitioning.html#Keyboard-Layout-and-Networking-and-Partitioning).
Clone the dotfiles repo:
```text
mkdir Code
cd Code
git clone https://github.com/SqrtMinusOne/dotfiles.git
```
Copy the channels file and run guix pull:
```text
cp ~/Code/dotfiles/.config/guix/channels.scm ~/.config/guix
guix pull
```
The first pull usually takes a while. After that install yadm and pull dotfiles:
```text
guix install yadm
guix clone https://github.com/SqrtMinusOne/dotfiles.git
```
And activate the required profiles. Again, downloading & building Emacs, Starship and stuff will take a while.
Don't forget to install `JetBrainsMono Nerd Font`.
## Misc software & notes {#misc-software-and-notes}
| Category | Guix dependency | Description |
|----------|-----------------|----------------------------------------------------|
| system | patchelf | A program to modify existsing ELF executables |
| system | glibc | A lot of stuff, including ELF interpeter and `ldd` |
### VPN {#vpn}
| Category | Guix dependency |
|----------|-----------------------------|
| system | openvpn |
| system | openvpn-update-resolve-conf |
| system | vpnc |
I'm not sure how to properly spin up VPN on Guix, so here is what ended I'm doing after some trial and error.
I'm using CyberGhost VPN. `~/.vpn` folder stores its OpenVPN config (`openvpn.ovpn`), modified as follows:
- paths to `ca`, `cert` and `key` are made absolute
```vim
ca /home/pavel/.vpn/ca.crt
cert /home/pavel/.vpn/client.crt
key /home/pavel/.vpn/client.key
```
- added `auth-user-pass` with a link to login info
```vim
auth-user-pass /home/pavel/.vpn/auth.conf
```
`auth.conf` looks like this:
```text
login
password
```
- run [openvpn-update-resolv-conf](https://github.com/alfredopalhares/openvpn-update-resolv-conf) script to fix DNS. `openvpn-update-resolve-conf` originates in my [channel-q](https://github.com/SqrtMinusOne/channel-q).
```vim
setenv PATH /home/pavel/.guix-extra-profiles/system/system/bin:/home/pavel/.guix-extra-profiles/system/system/sbin:/home/pavel/.guix-extra-profiles/console/console/bin:/run/current-system/profile/bin:/run/current-system/profile/sbin
up /home/pavel/.guix-extra-profiles/system/system/bin/update-resolv-conf.sh
down /home/pavel/.guix-extra-profiles/system/system/bin/update-resolv-conf.sh
```
`setenv PATH` is necessary because both `resolvconf` (openresolve) and `update-resolv-conf.sh` are shell scripts which need GNU coreutils and stuff, and OpenVPN clears PATH by default.
- run a script to fix Docker routes
```vim
route-up /home/pavel/bin/scripts/vpn-fix-routes
```
References:
- [Github issue](https://github.com/moby/libnetwork/issues/779)
The script itself:
```sh
echo "Adding default route to $route_vpn_gateway with /0 mask..."
IP=/run/current-system/profile/sbin/ip
$IP route add default via $route_vpn_gateway
echo "Removing /1 routes..."
$IP route del 0.0.0.0/1 via $route_vpn_gateway
$IP route del 128.0.0.0/1 via $route_vpn_gateway
```
#### vpn-start {#vpn-start}
As of now, CyberGhost doesn't provide ipv6, so we have to disable it.
```bash
export DISPLAY=:0
CONN=$(nmcli -f NAME con show --active | grep -Ev "(.*docker.*|NAME|br-.*|veth.*|tun.*|vnet.*|virbr.*)" | sed 's/ *$//g')
if [ -z "$CONN" ]; then
echo "No connection!"
notify-send "VPN" "No connection for VPN to run"
exit
fi
echo "Connection: $CONN"
notify-send "VPN" "Initializing for connection: $CONN"
pkexec nmcli con modify "$CONN" ipv6.method ignore
nmcli connection up "$CONN"
pkexec openvpn --config ~/.vpn/openvpn.ovpn
```
#### vpn-stop {#vpn-stop}
Also a script to reverse the changes.
```bash
CONN=$(nmcli -f NAME con show --active | grep -Ev "(.*docker.*|NAME|br-.*|veth.*|tun.*)" | sed 's/ *$//g')
echo "Connection: $CONN"
pkexec nmcli con modify "$CONN" ipv6.method auto
nmcli connection up "$CONN"
```
### flatpak {#flatpak}
As for now, the easiest way to install most of proprietary software is via flatpak. See the relevant section in [Desktop.org]({{< relref "Desktop" >}}).
### conda {#conda}
[conda](https://docs.conda.io/en/latest/) is a package manager, which I use for managing various versions of Python & Node.js.
It is packaged for GNU Guix, although the definition has its fair share of workarounds. It is almost surprising to see it work with all the C libraries and stuff. But there are still some problems.
First, it's impossible to perform `conda init` to patch files like `.bashrc`, because the command is hell-bent on modifying `/gnu/store/`. So I do this manually, look for the `init_conda` procedures in [Console.org]({{< relref "Console" >}}).
Second, the base environment has `/gnu/store/...` as a root, so don't install anything there (and don't run `conda` with superuser rights!).
Third, by default it tries to create envronments in `/gnu/store`. It's enough to create one environment like this to fix it:
```sh
mkdir -p ~/.conda/envs
conda create -p ~/.conda/envs/test
```
Fourth, you may need to unset `$PYTHONPATH` if you have any global packages installed, otherwise Python from anaconda will try to import them instead of the conda versions.
Finally, I also want to have an ability to use global npm. Some settings for that are located in [Console.org](Console). Here we want to unset `NPM_CONFIG_USERCONFIG` if there is npm available in the environment.
So here is a script to set up conda hooks:
```bash
# Get writable conda envs with npm & without it
readarray -t CONDA_ENVS_ALL <<< $(conda env list --json | jq '.envs[]')
CONDA_ENVS_NPM=()
CONDA_ENVS_NO_NPM=()
for env in "${CONDA_ENVS_ALL[@]}"; do
env="${env:1:${#env}-2}"
if [ -w "$env" ]; then
if [ -f "$env/bin/npm" ]; then
CONDA_ENVS_NPM+=($env)
else
CONDA_ENVS_NO_NPM+=($env)
fi
fi
done
for env in "${CONDA_ENVS_NPM[@]}"; do
echo "Found npm in $env"
mkdir -p "$env/etc/conda/activate.d"
mkdir -p "$env/etc/conda/deactivate.d"
echo "unset NPM_CONFIG_USERCONFIG" > "$env/etc/conda/activate.d/conda.sh"
echo "set -e NPM_CONFIG_USERCONFIG" > "$env/etc/conda/activate.d/conda.fish"
echo "export NPM_CONFIG_USERCONFIG=$HOME/._npmrc" > "$env/etc/conda/deactivate.d/conda.sh"
echo "export NPM_CONFIG_USERCONFIG=$HOME/._npmrc" > "$env/etc/conda/deactivate.d/conda.fish"
done
for env in "${CONDA_ENVS_NO_NPM}"; do
echo "Did not found npm in $env"
rm -rf "$env/etc/conda/activate.d/conda.sh" || true
rm -rf "$env/etc/conda/activate.d/conda.fish" || true
rm -rf "$env/etc/conda/deactivate.d/conda.sh" || true
rm -rf "$env/etc/conda/deactivate.d/conda.fish" || true
done
```
### Slack {#slack}
What a nonsense of a program.
I was able to launch the nix version with the following wrapper script:
```bash
export PATH="$HOME/bin/dummies:$PATH"
mkdir -p ~/.cache/slack
slack -r ~/.cache/slack
```
Also, it requires a `lsb_release` in the PATH, so here is one:
```bash
echo "LSB Version: Hey. I spent an hour figuring out why Slack doesn't launch."
echo "Distributor ID: It seems like it requires an lsb_release."
echo "Description: But GNU Guix doesn't have one."
echo "Release: 42.2"
echo "Codename: n/a"
```
### virt-manager {#virt-manager}
Run the following to fix the network:
```sh
sudo virsh net-define /run/current-system/profile/etc/libvirt/qemu/networks/default.xml
sudo virsh net-start default
sudo herd restart libvirtd
```
### wakatime-cli {#wakatime-cli}
| Note | Description |
|------|-----------------------|
| TODO | Package this for Guix |
Before I figure out how to package this for Guix:
- Clone [the repo](https://github.com/wakatime/wakatime-cli)
- Run `go build`
- Copy the binary to the `~/bin` folder
### Manifest {#manifest}
<a id="code-snippet--packages"></a>
```emacs-lisp
(my/format-guix-dependencies category)
```
System
```scheme
(specifications->manifest
'(
<<packages("system")>>))
```

View file

@ -1,793 +0,0 @@
+++
title = "Guix"
author = ["Pavel"]
draft = false
+++
[GNU Guix](https://guix.gnu.org/) is (1) a transactional package manager and (2) a GNU/Linux distribution.
My personal selling points are declarative package configuration and transactional upgrades.
References:
- [Official help](https://guix.gnu.org/en/help/)
- [System Crafters wiki](https://wiki.systemcrafters.cc/guix)
- [Pjotr Prins' Guix notes](https://gitlab.com/pjotrp/guix-notes)
- [Davil Wilson's YouTube series](https://www.youtube.com/watch?v=iBaqOK75cho&list=PLEoMzSkcN8oNxnj7jm5V2ZcGc52002pQU)
<div class="ox-hugo-toc toc">
<div></div>
<div class="heading">Table of Contents</div>
- [Profiles](#profiles)
- [Activate profiles](#activate-profiles)
- [Update profiles](#update-profiles)
- [Channels](#channels)
- [Systems](#systems)
- [Base configuration](#base-configuration)
- [indigo](#indigo)
- [eminence](#eminence)
- [azure](#azure)
- [System installation](#system-installation)
- [Preparation](#preparation)
- [Installation](#installation)
- [After installation](#after-installation)
- [Misc software & notes](#misc-software-and-notes)
- [VPN](#vpn)
- [vpn-start](#vpn-start)
- [vpn-stop](#vpn-stop)
- [flatpak](#flatpak)
- [conda](#conda)
- [Slack](#slack)
- [virt-manager](#virt-manager)
- [wakatime-cli](#wakatime-cli)
- [Manifest](#manifest)
</div>
<!--endtoc-->
## Profiles {#profiles}
A profile is a way to group Guix packages. Amongst its advantages, profiles can be defined by manifests, which in turn can be stored in VCS.
References:
- [Guix Profiles in Practice](https://guix.gnu.org/en/cookbook/en/html%5Fnode/Guix-Profiles-in-Practice.html)
### Activate profiles {#activate-profiles}
A script to activate guix profiles. Usage:
```text
activate-profiles [profile1] [profile2] ...
```
Source: [David Wilson's config](https://github.com/daviwil/dotfiles/blob/master/Systems.org#activating-profiles)
```bash
GREEN='\033[1;32m'
RED='\033[1;30m'
NC='\033[0m'
GUIX_EXTRA_PROFILES=$HOME/.guix-extra-profiles
profiles=$*
if [[ $# -eq 0 ]]; then
profiles="$HOME/.config/guix/manifests/*.scm";
fi
for profile in $profiles; do
# Remove the path and file extension, if any
profileName=$(basename $profile)
profileName="${profileName%.*}"
profilePath="$GUIX_EXTRA_PROFILES/$profileName"
manifestPath=$HOME/.config/guix/manifests/$profileName.scm
if [ -f $manifestPath ]; then
echo
echo -e "${GREEN}Activating profile:" $manifestPath "${NC}"
echo
mkdir -p $profilePath
guix package --manifest=$manifestPath --profile="$profilePath/$profileName"
# Source the new profile
GUIX_PROFILE="$profilePath/$profileName"
if [ -f $GUIX_PROFILE/etc/profile ]; then
. "$GUIX_PROFILE"/etc/profile
else
echo -e "${RED}Couldn't find profile:" $GUIX_PROFILE/etc/profile "${NC}"
fi
else
echo "No profile found at path" $profilePath
fi
done
```
### Update profiles {#update-profiles}
A script to update Guix profiles. Usage:
```text
update-profiles [profile1] [profile2] ...
```
Source: once again, [David Wilson's config](https://github.com/daviwil/dotfiles/blob/master/Systems.org#updating-profiles).
```bash
GREEN='\033[1;32m'
NC='\033[0m'
GUIX_EXTRA_PROFILES=$HOME/.guix-extra-profiles
profiles=$*
if [[ $# -eq 0 ]]; then
profiles="$GUIX_EXTRA_PROFILES/*";
fi
for profile in $profiles; do
profileName=$(basename $profile)
profilePath=$GUIX_EXTRA_PROFILES/$profileName
echo
echo -e "${GREEN}Updating profile:" $profilePath "${NC}"
echo
guix package --profile="$profilePath/$profileName" --manifest="$HOME/.config/guix/manifests/$profileName.scm"
done
```
## Channels {#channels}
Specifying additional channels.
[channel-q](https://github.com/SqrtMinusOne/channel-q) is my Guix channel. Don't use it at home.
References:
- [nonguix channel repo](https://gitlab.com/nonguix/nonguix)
- [Guix channels reference](https://guix.gnu.org/manual/en/html%5Fnode/Channels.html)
<!--listend-->
```scheme
(cons*
(channel
(name 'channel-q)
(url "file:///home/pavel/Code/channel-q"))
(channel
(name 'flat)
(url "https://github.com/flatwhatson/guix-channel.git")
(introduction
(make-channel-introduction
"33f86a4b48205c0dc19d7c036c85393f0766f806"
(openpgp-fingerprint
"736A C00E 1254 378B A982 7AF6 9DBE 8265 81B6 4490"))))
(channel
(name 'nonguix)
(url "https://gitlab.com/nonguix/nonguix")
;; (commit "d54973e47b89fe5772a5b6e2d0c0b86acb089e27")
(introduction
(make-channel-introduction
"897c1a470da759236cc11798f4e0a5f7d4d59fbc"
(openpgp-fingerprint
"2A39 3FFF 68F4 EF7A 3D29 12AF 6F51 20A0 22FB B2D5"))))
%default-channels)
```
## Systems {#systems}
Configuring systems with Guix.
Yes, all my machines are named after colors I like.
### Base configuration {#base-configuration}
The base configuration is shared between all the machines.
While it's possible to make a single `.scm` file with base configuration and load it, I noticed that it produces more cryptic error messages whenever there is an error in the base file, so I opt-in for noweb.
`guix system` invocation is as follows:
```text
sudo -E guix system reconfigure ~/.config/guix/systems/[system].scm
```
Common modules:
```scheme
(use-modules (gnu))
(use-modules (gnu system nss))
(use-modules (gnu packages bash))
(use-modules ((gnu packages base) #:select (coreutils glibc)))
(use-modules (gnu packages certs))
(use-modules (gnu packages version-control))
(use-modules (gnu packages vim))
(use-modules (gnu packages gnome))
(use-modules (gnu packages xorg))
(use-modules (gnu packages wm))
(use-modules (gnu packages openbox))
(use-modules (gnu services docker))
(use-modules (gnu services cups))
(use-modules (gnu services virtualization))
(use-modules (srfi srfi-1))
(use-modules (guix channels))
(use-modules (guix inferior))
(use-modules (nongnu packages linux))
(use-modules (nongnu system linux-initrd))
(use-service-modules desktop networking ssh xorg nix)
(use-package-modules ssh)
```
In principle, we could define a variable called `base-operating-system` and extend it in ancestors. However, then we would have to define mandatory fields like `host-name`, `bootloader` with dummy values. Since I'm already using noweb, there is little point.
The following code will be inserted at the top of the `operating-system` definition.
Use the full Linux kernel. I hope I'll be able to use Libre kernel somewhere later.
Inferior in the kernel is used to avoid recompilation. It looks like I can pin these to different commits than in my `channels.scm`
```scheme
(kernel
(let*
((channels
(list (channel
(name 'nonguix)
(url "https://gitlab.com/nonguix/nonguix")
(commit "d3c5eea0cbfe3e5bfbcf1fe15bc916fefacc624f"))
(channel
(name 'guix)
(url "https://git.savannah.gnu.org/git/guix.git")
(commit "cf88c967afbf15c58efb0ba37d6638f1be9a0481"))))
(inferior
(inferior-for-channels channels)))
(first (lookup-inferior-packages inferior "linux" "5.12.9"))))
;; (kernel linux)
(initrd microcode-initrd)
(firmware (list linux-firmware))
(locale "en_US.utf8")
(timezone "Europe/Moscow")
```
US/RU keyboard layout, switch with Alt+Shift.
```scheme
(keyboard-layout (keyboard-layout "us,ru" #:options '("grp:alt_shift_toggle")))
```
User accounts.
```scheme
(users (cons* (user-account
(name "pavel")
(comment "Pavel")
(group "users")
(home-directory "/home/pavel")
(supplementary-groups
'("wheel" ;; sudo
"netdev" ;; network devices
"audio"
"video"
"input"
"tty"
"docker"
"scanner"
"libvirt"
"lp")))
%base-user-accounts))
```
Base packages, necessary right after the installation.
```scheme
(packages
(append
(list nss-certs
git
i3-gaps
i3lock
openbox
xterm
vim)
%base-packages))
```
Default services for each machine:
- override the default `%desktop-services` to add OpenVPN support
- add nix service
- add docker service
- add CUPS service
- add libvirt service
- add a symlink to ELF interpreter to where most Linux binaries expect it
<!--listend-->
```scheme
(define %my-base-services
(cons*
(service openssh-service-type)
(screen-locker-service i3lock "i3lock")
(extra-special-file "/lib64/ld-linux-x86-64.so.2" (file-append glibc "/lib/ld-linux-x86-64.so.2"))
(service nix-service-type)
(service cups-service-type
(cups-configuration
(web-interface? #t)))
(service docker-service-type)
(service libvirt-service-type
(libvirt-configuration
(unix-sock-group "libvirt")
(tls-port "16555")))
(service virtlog-service-type)
(modify-services %desktop-services
(network-manager-service-type
config =>
(network-manager-configuration
(inherit config)
(vpn-plugins (list network-manager-openvpn)))))))
```
### indigo {#indigo}
`indigo` is my desktop PC.
```scheme
<<system-common>>
(operating-system
<<system-base>>
(host-name "indigo")
(services (cons*
(set-xorg-configuration
(xorg-configuration
(keyboard-layout keyboard-layout)))
%my-base-services))
(bootloader
(bootloader-configuration
(bootloader grub-efi-bootloader)
(target "/boot/efi")
(keyboard-layout keyboard-layout)))
(swap-devices
(list (uuid "059a2c26-8f70-4986-adf0-1a2e7b511404")))
(file-systems
(cons* (file-system
(mount-point "/")
(device (file-system-label "my-root"))
(type "ext4"))
(file-system
(mount-point "/boot/efi")
(device "/dev/sda1")
(type "vfat"))
%base-file-systems)))
```
### eminence {#eminence}
`eminence` is a HP 15s laptop.
`%backlight-udev-rule` should enable members of `video` group change the display backlight. See the relevant page at [Arch Wiki](https://wiki.archlinux.org/title/Backlight).
```scheme
<<system-common>>
(define %backlight-udev-rule
(udev-rule
"90-backlight.rules"
(string-append "ACTION==\"add\", SUBSYSTEM==\"backlight\", "
"RUN+=\"/run/current-system/profile/bin/chgrp video /sys/class/backlight/%k/brightness\""
"\n"
"ACTION==\"add\", SUBSYSTEM==\"backlight\", "
"RUN+=\"/run/current-system/profile/bin/chmod g+w /sys/class/backlight/%k/brightness\"")))
(operating-system
<<system-base>>
(host-name "eminence")
(services (cons*
(set-xorg-configuration
(xorg-configuration
(keyboard-layout keyboard-layout)))
(modify-services %my-base-services
(elogind-service-type
config =>
(elogind-configuration
(inherit config)
(handle-lid-switch-external-power 'suspend)))
(udev-service-type
config =>
(udev-configuration
(inherit config)
(rules (cons %backlight-udev-rule
(udev-configuration-rules config))))))))
(bootloader
(bootloader-configuration
(bootloader grub-efi-bootloader)
(target "/boot/efi")
(keyboard-layout keyboard-layout)))
(swap-devices
(list (uuid "f93cf3f6-7ee7-42ec-8ee2-f3d896fdf9b5")))
(file-systems
(cons* (file-system
(mount-point "/")
(device
(uuid "1d937704-bbeb-43b5-bc63-453886c426af"
'ext4))
(type "ext4"))
(file-system
(mount-point "/boot/efi")
(device (uuid "0031-3784" 'fat32))
(type "vfat"))
%base-file-systems)))
```
### azure {#azure}
`azure` is a Lenovo Ideapad 330 laptop.
`%backlight-udev-rule` should enable members of `video` group change the display backlight. See the relevant page at [Arch Wiki](https://wiki.archlinux.org/title/Backlight).
```scheme
<<system-common>>
(define %backlight-udev-rule
(udev-rule
"90-backlight.rules"
(string-append "ACTION==\"add\", SUBSYSTEM==\"backlight\", "
"RUN+=\"/run/current-system/profile/bin/chgrp video /sys/class/backlight/%k/brightness\""
"\n"
"ACTION==\"add\", SUBSYSTEM==\"backlight\", "
"RUN+=\"/run/current-system/profile/bin/chmod g+w /sys/class/backlight/%k/brightness\"")))
(operating-system
<<system-base>>
(host-name "azure")
(services (cons*
(set-xorg-configuration
(xorg-configuration
(keyboard-layout keyboard-layout)))
(modify-services %my-base-services
(elogind-service-type config =>
(elogind-configuration (inherit config)
(handle-lid-switch-external-power 'suspend)))
(udev-service-type config =>
(udev-configuration (inherit config)
(rules (cons %backlight-udev-rule
(udev-configuration-rules config))))))))
(bootloader
(bootloader-configuration
(bootloader grub-efi-bootloader)
(target "/boot/efi")
(keyboard-layout keyboard-layout)))
(swap-devices
(list (uuid "4b2dedb3-b111-4e69-8c05-6daa2b072c76")))
(file-systems
(cons* (file-system
(mount-point "/")
(device (file-system-label "my-root"))
(type "ext4"))
(file-system
(mount-point "/boot/efi")
(device "/dev/sda1")
(type "vfat"))
%base-file-systems)))
```
## System installation {#system-installation}
### Preparation {#preparation}
In my case, the provided ISO doesn't work because of the Libre kernel.
Fortunately, David Wilson has made [a repository](https://github.com/SystemCrafters/guix-installer) with a toolchain to make an ISO with the full kernel. In case it won't be an option, the [nonguix repo](https://gitlab.com/nonguix/nonguix) also has instructions on how to do that.
When an ISO is there, we have to write it on a USB stick. Run `sudo fdisk -l` to get a list of disks.
The approach given in the official instruction is to create a bootable USB with `dd`:
```text
sudo dd of=/dev/sdxX if=<path-to-iso> status=progress && sync
```
However, I couldn't make it work for some strange reason. Fortunately, `gnome-disk-utility` was able to produce a bootable USB.
### Installation {#installation}
Going further, the official instructions for installation & SystemCrafters wiki entry are pretty good, so it's not necessary to repeat them here.
### After installation {#after-installation}
After the installation, the strategy is as follows.
Set a password for the main user (pavel). Login with openbox to get a tolerable interface because i3's default config is horrible.
[Connect to the internet](https://guix.gnu.org/en/manual/en/html%5Fnode/Keyboard-Layout-and-Networking-and-Partitioning.html#Keyboard-Layout-and-Networking-and-Partitioning).
Clone the dotfiles repo:
```text
mkdir Code
cd Code
git clone https://github.com/SqrtMinusOne/dotfiles.git
```
Copy the channels file and run guix pull:
```text
cp ~/Code/dotfiles/.config/guix/channels.scm ~/.config/guix
guix pull
```
The first pull usually takes a while. After that install yadm and pull dotfiles:
```text
guix install yadm
guix clone https://github.com/SqrtMinusOne/dotfiles.git
```
And activate the required profiles. Again, downloading & building Emacs, Starship and stuff will take a while.
Don't forget to install `JetBrainsMono Nerd Font`.
## Misc software & notes {#misc-software-and-notes}
| Category | Guix dependency | Description |
|----------|-----------------|----------------------------------------------------|
| system | patchelf | A program to modify existsing ELF executables |
| system | glibc | A lot of stuff, including ELF interpeter and `ldd` |
### VPN {#vpn}
| Category | Guix dependency |
|----------|-----------------------------|
| system | openvpn |
| system | openvpn-update-resolve-conf |
| system | vpnc |
I'm not sure how to properly spin up VPN on Guix, so here is what ended I'm doing after some trial and error.
I'm using CyberGhost VPN. `~/.vpn` folder stores its OpenVPN config (`openvpn.ovpn`), modified as follows:
- paths to `ca`, `cert` and `key` are made absolute
```vim
ca /home/pavel/.vpn/ca.crt
cert /home/pavel/.vpn/client.crt
key /home/pavel/.vpn/client.key
```
- added `auth-user-pass` with a link to login info
```vim
auth-user-pass /home/pavel/.vpn/auth.conf
```
`auth.conf` looks like this:
```text
login
password
```
- run [openvpn-update-resolv-conf](https://github.com/alfredopalhares/openvpn-update-resolv-conf) script to fix DNS. `openvpn-update-resolve-conf` originates in my [channel-q](https://github.com/SqrtMinusOne/channel-q).
```vim
setenv PATH /home/pavel/.guix-extra-profiles/system/system/bin:/home/pavel/.guix-extra-profiles/system/system/sbin:/home/pavel/.guix-extra-profiles/console/console/bin:/run/current-system/profile/bin:/run/current-system/profile/sbin
up /home/pavel/.guix-extra-profiles/system/system/bin/update-resolv-conf.sh
down /home/pavel/.guix-extra-profiles/system/system/bin/update-resolv-conf.sh
```
`setenv PATH` is necessary because both `resolvconf` (openresolve) and `update-resolv-conf.sh` are shell scripts which need GNU coreutils and stuff, and OpenVPN clears PATH by default.
- run a script to fix Docker routes
```vim
route-up /home/pavel/bin/scripts/vpn-fix-routes
```
References:
- [Github issue](https://github.com/moby/libnetwork/issues/779)
The script itself:
```sh
echo "Adding default route to $route_vpn_gateway with /0 mask..."
IP=/run/current-system/profile/sbin/ip
$IP route add default via $route_vpn_gateway
echo "Removing /1 routes..."
$IP route del 0.0.0.0/1 via $route_vpn_gateway
$IP route del 128.0.0.0/1 via $route_vpn_gateway
```
#### vpn-start {#vpn-start}
As of now, CyberGhost doesn't provide ipv6, so we have to disable it.
```bash
export DISPLAY=:0
CONN=$(nmcli -f NAME con show --active | grep -Ev "(.*docker.*|NAME|br-.*|veth.*|tun.*|vnet.*|virbr.*)" | sed 's/ *$//g')
if [ -z "$CONN" ]; then
echo "No connection!"
notify-send "VPN" "No connection for VPN to run"
exit
fi
echo "Connection: $CONN"
notify-send "VPN" "Initializing for connection: $CONN"
pkexec nmcli con modify "$CONN" ipv6.method ignore
nmcli connection up "$CONN"
pkexec openvpn --config ~/.vpn/openvpn.ovpn
```
#### vpn-stop {#vpn-stop}
Also a script to reverse the changes.
```bash
CONN=$(nmcli -f NAME con show --active | grep -Ev "(.*docker.*|NAME|br-.*|veth.*|tun.*)" | sed 's/ *$//g')
echo "Connection: $CONN"
pkexec nmcli con modify "$CONN" ipv6.method auto
nmcli connection up "$CONN"
```
### flatpak {#flatpak}
As for now, the easiest way to install most of proprietary software is via flatpak. See the relevant section in [Desktop.org]({{< relref "Desktop" >}}).
### conda {#conda}
[conda](https://docs.conda.io/en/latest/) is a package manager, which I use for managing various versions of Python & Node.js.
It is packaged for GNU Guix, although the definition has its fair share of workarounds. It is almost surprising to see it work with all the C libraries and stuff. But there are still some problems.
First, it's impossible to perform `conda init` to patch files like `.bashrc`, because the command is hell-bent on modifying `/gnu/store/`. So I do this manually, look for the `init_conda` procedures in [Console.org]({{< relref "Console" >}}).
Second, the base environment has `/gnu/store/...` as a root, so don't install anything there (and don't run `conda` with superuser rights!).
Third, by default it tries to create envronments in `/gnu/store`. It's enough to create one environment like this to fix it:
```sh
mkdir -p ~/.conda/envs
conda create -p ~/.conda/envs/test
```
Fourth, you may need to unset `$PYTHONPATH` if you have any global packages installed, otherwise Python from anaconda will try to import them instead of the conda versions.
Finally, I also want to have an ability to use global npm. Some settings for that are located in [Console.org](Console). Here we want to unset `NPM_CONFIG_USERCONFIG` if there is npm available in the environment.
So here is a script to set up conda hooks:
```bash
# Get writable conda envs with npm & without it
readarray -t CONDA_ENVS_ALL <<< $(conda env list --json | jq '.envs[]')
CONDA_ENVS_NPM=()
CONDA_ENVS_NO_NPM=()
for env in "${CONDA_ENVS_ALL[@]}"; do
env="${env:1:${#env}-2}"
if [ -w "$env" ]; then
if [ -f "$env/bin/npm" ]; then
CONDA_ENVS_NPM+=($env)
else
CONDA_ENVS_NO_NPM+=($env)
fi
fi
done
for env in "${CONDA_ENVS_NPM[@]}"; do
echo "Found npm in $env"
mkdir -p "$env/etc/conda/activate.d"
mkdir -p "$env/etc/conda/deactivate.d"
echo "unset NPM_CONFIG_USERCONFIG" > "$env/etc/conda/activate.d/conda.sh"
echo "set -e NPM_CONFIG_USERCONFIG" > "$env/etc/conda/activate.d/conda.fish"
echo "export NPM_CONFIG_USERCONFIG=$HOME/._npmrc" > "$env/etc/conda/deactivate.d/conda.sh"
echo "export NPM_CONFIG_USERCONFIG=$HOME/._npmrc" > "$env/etc/conda/deactivate.d/conda.fish"
done
for env in "${CONDA_ENVS_NO_NPM}"; do
echo "Did not found npm in $env"
rm -rf "$env/etc/conda/activate.d/conda.sh" || true
rm -rf "$env/etc/conda/activate.d/conda.fish" || true
rm -rf "$env/etc/conda/deactivate.d/conda.sh" || true
rm -rf "$env/etc/conda/deactivate.d/conda.fish" || true
done
```
### Slack {#slack}
What a nonsense of a program.
I was able to launch the nix version with the following wrapper script:
```bash
export PATH="$HOME/bin/dummies:$PATH"
mkdir -p ~/.cache/slack
slack -r ~/.cache/slack
```
Also, it requires a `lsb_release` in the PATH, so here is one:
```bash
echo "LSB Version: Hey. I spent an hour figuring out why Slack doesn't launch."
echo "Distributor ID: It seems like it requires an lsb_release."
echo "Description: But GNU Guix doesn't have one."
echo "Release: 42.2"
echo "Codename: n/a"
```
### virt-manager {#virt-manager}
Run the following to fix the network:
```sh
sudo virsh net-define /run/current-system/profile/etc/libvirt/qemu/networks/default.xml
sudo virsh net-start default
sudo herd restart libvirtd
```
### wakatime-cli {#wakatime-cli}
| Note | Description |
|------|-----------------------|
| TODO | Package this for Guix |
Before I figure out how to package this for Guix:
- Clone [the repo](https://github.com/wakatime/wakatime-cli)
- Run `go build`
- Copy the binary to the `~/bin` folder
### Manifest {#manifest}
<a id="code-snippet--packages"></a>
```emacs-lisp
(my/format-guix-dependencies category)
```
System
```scheme
(specifications->manifest
'(
<<packages("system")>>))
```

View file

@ -1,584 +0,0 @@
+++
title = "Mail"
author = ["Pavel"]
draft = false
+++
My email configration. Currently I use [lieer](https://github.com/gauteh/lieer) to fetch emails from Gmail, [davmail](http://davmail.sourceforge.net/) & [offlineimap](http://www.offlineimap.org/) to fetch emails from MS Exchange, [notmuch](https://notmuchmail.org/) to index, [msmtp](https://marlam.de/msmtp/) to send emails. Also using notmuch frontend from Emacs.
My problem with any particular mail setup was that I use Gmail labels quite extensively, and handling these over IMAP is rather awkward. Notmuch seems to be the only software that provides the same first-class support for labels.
But I also have an Exchange account, with which I communicate via IMAP/SMTP adapter, and in this case, I synchronize notmuch tags and IMAP folders.
References:
- [My post](https://sqrtminusone.xyz/posts/2021-02-27-gmail/) about email configuration. I wrote it some time ago, but the general idea remains.
<div class="ox-hugo-toc toc">
<div></div>
<div class="heading">Table of Contents</div>
- [Lieer](#lieer)
- [DavMail](#davmail)
- [OfflineIMAP](#offlineimap)
- [Notmuch](#notmuch)
- [Config](#config)
- [Hooks](#hooks)
- [`pre_new`](#pre-new)
- [`post_new`](#post-new)
- [Sync script](#sync-script)
- [MSMTP](#msmtp)
- [Emacs](#emacs)
- [Saved filters and keybindings](#saved-filters-and-keybindings)
- [Signing messages](#signing-messages)
- [mailcap](#mailcap)
- [Guix settings](#guix-settings)
</div>
<!--endtoc-->
## Lieer {#lieer}
| Guix dependency |
|-----------------|
| python-lieer |
Lieer is a program to link up Gmail and notmuch. Basically, it downloads mail from Gmail via API, stores them in Maildir, and synchronizes labels with notmuch.
I have a separate directory in my `~/Mail` for each address. To init lieer, run the following command in the directory:
```text
gmi init <address>
```
After which the settings will be stored in `gmailieer.json` and the credentials in `.credentials.gmailieer.json`. The latter file is stored encrypted.
My preferred settings:
```text
gmi set --replace-slash-with-dot
gmi set --ignore-tags-local new
```
Running `gmi sync` in the required directory performs the synchronization. The first sync takes a while, the subsequent syncs are pretty fast.
## DavMail {#davmail}
is a gateway between MS Exchange and the rest of the world, which uses IMAP/SMTP/LDAP/etc. As I have one corporate MS Exchange address, this is just the program I need. As of yet, it isn't packaged for Guix, but it's easy enough to download.
It has a GUI mode, but I prefer headless config.
```ini
davmail.server=true
davmail.mode=Auto
davmail.url=https://mail.etu.ru/owa/
davmail.server.certificate.hash=0C:9E:CF:D3:62:26:DB:FA:F1:EE:36:9D:60:E7:31:71:CF:1F:92:85
davmail.caldavPort=1080
davmail.imapPort=1143
davmail.ldapPort=1389
davmail.popPort=1110
davmail.smtpPort=1025
davmail.imapAutoExpunge=false
davmail.enableKeepalive=false
```
Also it's a bit of problem to get it launched as it looks for its jars in the pwd, so here is a script.
```bash
cd $HOME/bin/davmail-6.0.0-3375
./davmail davmail.properties
```
Shepherd service is defined in [Desktop.org]({{< relref "Desktop" >}}).
## OfflineIMAP {#offlineimap}
| Guix dependency |
|-----------------|
| offlineimap |
[OfflineIMAP](https://github.com/OfflineIMAP/offlineimap) is a program that can synchronize IMAP mailbox and Maildir. Lieer does everything by itself, but my pirate Exchange IMAP needs this program. There is also [isync](https://isync.sourceforge.io/), but there I had some weird issues with duplicate UIDs, which don't occur for OfflineIMAP.
I have a few options for setting a username and password. First, I can run `pass` in `remotepasswordeval`, and while this will work, it will keep my keyring unlocked because I want to run `offlineimap` every couple of minutes.
Another option is to use noweb and not push the file below to the version control. Then I have a plaintext password of email on my computer, but I think it's a lesser evil than the entire keyring.
I would use `password-store-get` from password-store.el, but I want this to be able to run without any 3rd party packages, so it's just bash.
<a id="code-snippet--mail-username"></a>
```bash
pass show Job/Infrastructure/pvkorytov@etu.ru | sed -n 's/username: //;2p'
```
<a id="code-snippet--mail-password"></a>
```bash
pass show Job/Infrastructure/pvkorytov@etu.ru | head -n 1
```
```ini
[general]
accounts = pvkorytov
[Account pvkorytov]
localrepository = pvkorytov-local
remoterepository = pvkorytov-remote
[Repository pvkorytov-local]
type = Maildir
localfolders = ~/Mail/pvkorytov_etu/
[Repository pvkorytov-remote]
type = IMAP
remotehost = localhost
remoteuser = <<mail-username()>>
remotepass = <<mail-password()>>
remoteport = 1143
starttls = no
ssl = no
```
## Notmuch {#notmuch}
| Guix dependency |
|-----------------|
| notmuch |
| parallel |
Notmuch is an email indexer program, which handles labels in a way somewhat similar to Gmail. It also provides a frontend for Emacs, but it's not the only one available.
### Config {#config}
Not much is going on here.
First, the database path.
```ini
[database]
path=/home/pavel/Mail
```
My name and list of emails. It's not like it's a secret anyhow.
```ini
[user]
name=Pavel Korytov
primary_email=thexcloud@gmail.com
other_email=progin6304@gmail.com;pvkorytov@etu.ru
```
A list of tags which will be added by `notmuch new` and directory names which will be ignored by `notmuch new`.
```ini
[new]
tags=new;
ignore=.osync_workdir;.mbsyncstate;.uidvalidity;.lock;/.*gmailieer\.json.*/
```
Exclude these tags from search by default.
```ini
[search]
exclude_tags=trash;spam;
```
Maildir compatibility.
```ini
[maildir]
synchronize_flags=true
```
### Hooks {#hooks}
Now we have to link up lieer & davmail's maildir and with notmuch. This is done via the notmuch hook system, which allows running custom scripts before and after any command.
With lieer and Gmail, it is enough to simply run the program, because Gmail has first-class support for tags. Maildir does not, so I decide to synchronize notmuch tags and IMAP folders. In essence, the idea is to:
- move emails to their folders by tags _before_ the synchronization
- tag mails by their folders _after_ the synchronization
The problem is that with that approach one email can have only one tag, but it's better than nothing.
So, here are the rules which match tags & folders:
<a id="table--pvkorytov-tags"></a>
| tag | folder |
|--------------------------|---------------------------|
| inbox | INBOX |
| sent | Sent |
| spam | Junk |
| trash | Trash |
| job.digital | Job\_Digital |
| job.digital.docs | Job\_Digital.Docs |
| job.digital.support | Job\_Digital.Support |
| job.digital.superservice | Job\_Digital.Superservice |
And below is a noweb function, which generates the following commands for notmuch to execute:
- _before_ sync:
- `notmuch search --output files "NOT path:[PATH] AND tag:[TAG] AND tag:[ROOT_TAG]" | xargs -I ! mv ! [PATH]`
Move emails with `TAG` but outside the matching `PATH` to the latter
- `notmuch search --output=files "NOT path:[ARCHIVE_PATH] AND tag:[ROOT_TAG] AND NOT tag:[TAG1] ... AND NOT tag:[TAGN]" | xargs -I ! mv ! [ARCHIVE_PATH]`
Move untagged emails to the `ARCHIVE_PATH`
- _after_ sync:
- `notmuch tag +[TAG] "path:[PATH] AND NOT tag:[TAG]"`
Tag emails in `PATH` which do not yet have the matching `TAG`
- `notmuch tag -[TAG] "NOT path:[PATH] AND tag:[TAG] AND tag:[ROOT_TAG]"`
Remove `TAG` from emails which are outside the matching `PATH`
These rules are getting included in the respective hooks.
<a id="code-snippet--mail-tags"></a>
```emacs-lisp
(setq my/maildir-root "~/Mail")
(let ((rules '()))
(dolist (row tags)
(let ((tag (nth 0 row))
(folder (nth 1 row)))
(unless (string-empty-p make_tag)
(add-to-list
'rules
(format "notmuch tag +%s \"path:%s/%s/cur/** AND NOT tag:%s\""
tag root folder tag)
t))
(unless (string-empty-p remove)
(add-to-list
'rules
(format "notmuch tag -%s \"NOT path:%s/%s/cur/** AND tag:%s AND tag:%s\""
tag root folder tag root_tag)
t))
(unless (string-empty-p move)
(add-to-list
'rules
(concat
(format "notmuch search --output=files \"NOT path:%s/%s/cur/** AND tag:%s AND tag:%s\""
root folder tag root_tag)
(format " | xargs -I ! mv ! %s/%s/%s/cur/" my/maildir-root root folder))
t))))
(unless (string-empty-p archive_root)
(add-to-list
'rules
(concat
(format "notmuch search --output=files \"NOT path:%s/%s/cur/** AND %s AND tag:%s\""
root archive_root
(mapconcat
(lambda (row)
(format "NOT tag:%s" (car row)))
tags
" AND ")
root_tag)
(format " | xargs -I ! mv ! %s/%s/%s/cur/" my/maildir-root root archive_root))
t))
(string-join rules "\n"))
```
#### `pre_new` {#pre-new}
This hook runs fetch from Gmail & offlineimap in parallel before the `notmuch new` command. The `parallel` command is provided by [GNU Parallel](https://www.gnu.org/software/parallel/).
It isn't necessary to run `cd` for offlineimap, but it's easier to write that way.
<a id="code-snippet--pre-new-pvkorytov-tags"></a>
```emacs-lisp
(my/mail-format-tags-rules tags "pvkorytov_etu" "pvkorytov" nil nil t "Archive")
```
```bash
# GMI="/home/pavel/Programs/miniconda3/envs/mail/bin/gmi"
GMI="gmi"
echo "Running pre-new filters"
<<mail-tags(move="t",archive_root="Archive")>>
echo "Pre-new filters done"
parallel --link -j0 "(cd /home/pavel/Mail/{1}/ && {2} {3})" ::: thexcloud progin6304 pvkorytov_etu ::: "$GMI" "$GMI" "offlineimap" ::: sync sync ""
```
#### `post_new` {#post-new}
And this hook tags different mailboxes with different tags.
<a id="code-snippet--post-new-pvkorytov-tags"></a>
```emacs-lisp
(my/mail-format-tags-rules tags "pvkorytov_etu" "pvkorytov" t t)
```
```bash
notmuch tag +main "path:thexcloud/** AND tag:new"
notmuch tag +progin "path:progin6304/** AND tag:new"
notmuch tag +pvkorytov "path:pvkorytov_etu/** AND tag:new"
echo "Running post-new filters"
<<mail-tags(make_tag="t",remove="t")>>
echo "Post-new filters done"
notmuch tag -new "tag:new"
```
## Sync script {#sync-script}
A script to run `notmuch new` and push a notification if there is new mail.
```bash
export DISPLAY=:0
CHECK_FILE="/home/pavel/Mail/.last_check"
QUERY="tag:unread"
ALL_QUERY="tag:unread"
if [ -f "$CHECK_FILE" ]; then
DATE=$(cat "$CHECK_FILE")
QUERY="$QUERY and date:@$DATE.."
fi
notmuch new
NEW_UNREAD=$(notmuch count "$QUERY")
ALL_UNREAD=$(notmuch count "$ALL_QUERY")
if [ $NEW_UNREAD -gt 0 ]; then
MAIN_UNREAD=$(notmuch count "tag:unread AND tag:main")
PROGIN_UNREAD=$(notmuch count "tag:unread AND tag:progin")
ETU_UNREAD=$(notmuch count "tag:unread AND tag:pvkorytov")
read -r -d '' NOTIFICATION <<EOM
$NEW_UNREAD new messages
$MAIN_UNREAD thexcloud@gmail.com
$PROGIN_UNREAD progin6304@gmail.com
$ETU_UNREAD pvkorytov@etu.ru
$ALL_UNREAD total
EOM
notify-send "New Mail" "$NOTIFICATION"
fi
echo "$(date +%s)" > $CHECK_FILE
```
The script is ran via GNU Mcron every 5 minutes.
```scheme
(job "*/5 * * * * " "~/bin/scripts/check-email")
```
## MSMTP {#msmtp}
| Guix dependency |
|-----------------|
| msmtp |
Sending emails can be done with MSMTP. It automatially chooses the email address and server based on the contents of the message, which is handy if there are multiple mailboxes to be managed.
```vim
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile ~/.msmtp.log
account main
host smtp.gmail.com
port 587
from thexcloud@gmail.com
user thexcloud@gmail.com
passwordeval "pass show My_Online/APIs/google-main-app-password | head -n 1"
account progin
host smtp.gmail.com
port 587
from progin6304@gmail.com
user progin6304@gmail.com
passwordeval "pass show My_Online/ETU/progin6304@gmail.com | head -n 1"
account pvkorytov
tls off
auth plain
host localhost
port 1025
from pvkorytov@etu.ru
user pvkorytov
passwordeval "pass show Job/Infrastructure/pvkorytov@etu.ru | head -n 1"
```
## Emacs {#emacs}
Finally, Emacs configuration. Let's start with some variables:
```emacs-lisp
(setq user-mail-address "thexcloud@gmail.com")
(setq user-full-name "Pavel Korytov")
```
Then, the problem with my Guix setup is that Emacs by default doesn't see the elisp files of notmuch, so here is a small workaround:
```emacs-lisp
(let ((default-directory "/home/pavel/.guix-extra-profiles/mail/mail/share/emacs/site-lisp"))
(normal-top-level-add-subdirs-to-load-path))
```
Finally the proper notmuch settings:
```emacs-lisp
(use-package notmuch
;; :ensure nil
:commands (notmuch notmuch-search)
:config
(setq mail-specify-envelope-from t)
(setq message-sendmail-envelope-from 'header)
(setq mail-envelope-from 'header)
(setq notmuch-always-prompt-for-sender t)
(setq sendmail-program (executable-find "msmtp"))
(setq send-mail-function #'sendmail-send-it)
(setq mml-secure-openpgp-sign-with-sender t)
(setq notmuch-mua-user-agent-function 'notmuch-mua-user-agent-full)
(add-hook 'notmuch-hello-mode-hook
(lambda () (display-line-numbers-mode 0))))
```
The file to which this is tangled is read in the init.el.
### Saved filters and keybindings {#saved-filters-and-keybindings}
I want to have the saved filters available in both notmuch interface as as keybindings. So a bit more of abusing org tables.
Root keybindings:
```emacs-lisp
(my-leader-def
:infix "am"
"" '(:which-key "notmuch")
"m" 'notmuch)
```
<a id="table--root-tags"></a>
| Root tag | Prefix | Keybinding description |
|-----------|--------|------------------------|
| main | t | thexcloud@gmail.com |
| progin | p | progin6304@gmail.com |
| pvkorytov | e | pvkorytov@etu.ru |
<a id="table--filter-tags"></a>
| Tag | Prefix | Name |
|--------|--------|----------|
| inbox | i | inbox |
| unread | u | unread |
| sent | s | sent |
| | a | all mail |
The following formats the tables above to a proper syntax for `setq notmuch-saved-searches`:
<a id="code-snippet--format-notmuch-saved-searches"></a>
```emacs-lisp
(let ((searches '()))
(dolist (root_tag root_tags)
(dolist (tag filter_tags)
(add-to-list
'searches
(format "(:name \"%s\" :query \"%s\")"
(format "%s (%s)"
(nth 0 root_tag)
(nth 2 tag))
(concat "tag:" (nth 0 root_tag)
(unless (string-empty-p (nth 0 tag))
(concat " AND tag:" (nth 0 tag)))))
t)))
(string-join searches "\n"))
```
And the following does the same for my general.el definer:
<a id="code-snippet--format-notmuch-keybindings"></a>
```emacs-lisp
(let ((bindings '()))
(dolist (root_tag root_tags)
(add-to-list
'bindings
(format "\"%s\" '(:which-key \"%s\")"
(nth 1 root_tag)
(nth 2 root_tag))
t)
(dolist (tag filter_tags)
(add-to-list
'bindings
(format "\"%s\" '((lambda () (interactive) (notmuch-search \"%s\")) :which-key \"%s\")"
(concat (nth 1 root_tag) (nth 1 tag))
(concat "tag:" (nth 0 root_tag)
(unless (string-empty-p (nth 0 tag))
(concat " AND tag:" (nth 0 tag))))
(nth 2 tag))
t)))
(string-join bindings "\n"))
```
```emacs-lisp
(setq notmuch-saved-searches
'((:name "drafts" :query "tag:draft")
<<format-notmuch-saved-searches()>>))
(my-leader-def
:infix "am"
<<format-notmuch-keybindings()>>)
```
### Signing messages {#signing-messages}
```emacs-lisp
(with-eval-after-load 'notmuch
(add-hook 'message-setup-hook 'mml-secure-sign-pgpmime))
(setq mml-secure-key-preferences
'((OpenPGP
(sign
("thexcloud@gmail.com" "914472A1FD6775C166F96EBEED739ADF81C78160"))
(encrypt))
(CMS
(sign)
(encrypt))))
```
## mailcap {#mailcap}
mailcap file is a file which defines how to read to different MIME types. Notmuch also uses it, so why not keep it here.
```text
audio/*; mpc add %s
image/*; feh %s
application/msword; /usr/bin/xdg-open %s
application/pdf; zathura %s
application/postscript ; zathura %s
text/html; firefox %s
```
## Guix settings {#guix-settings}
<a id="code-snippet--packages"></a>
```emacs-lisp
(my/format-guix-dependencies)
```
```scheme
(specifications->manifest
'(
<<packages()>>))
```

View file

@ -1,584 +0,0 @@
+++
title = "Mail"
author = ["Pavel"]
draft = false
+++
My email configration. Currently I use [lieer](https://github.com/gauteh/lieer) to fetch emails from Gmail, [davmail](http://davmail.sourceforge.net/) & [offlineimap](http://www.offlineimap.org/) to fetch emails from MS Exchange, [notmuch](https://notmuchmail.org/) to index, [msmtp](https://marlam.de/msmtp/) to send emails. Also using notmuch frontend from Emacs.
My problem with any particular mail setup was that I use Gmail labels quite extensively, and handling these over IMAP is rather awkward. Notmuch seems to be the only software that provides the same first-class support for labels.
But I also have an Exchange account, with which I communicate via IMAP/SMTP adapter, and in this case, I synchronize notmuch tags and IMAP folders.
References:
- [My post](https://sqrtminusone.xyz/posts/2021-02-27-gmail/) about email configuration. I wrote it some time ago, but the general idea remains.
<div class="ox-hugo-toc toc">
<div></div>
<div class="heading">Table of Contents</div>
- [Lieer](#lieer)
- [DavMail](#davmail)
- [OfflineIMAP](#offlineimap)
- [Notmuch](#notmuch)
- [Config](#config)
- [Hooks](#hooks)
- [`pre_new`](#pre-new)
- [`post_new`](#post-new)
- [Sync script](#sync-script)
- [MSMTP](#msmtp)
- [Emacs](#emacs)
- [Saved filters and keybindings](#saved-filters-and-keybindings)
- [Signing messages](#signing-messages)
- [mailcap](#mailcap)
- [Guix settings](#guix-settings)
</div>
<!--endtoc-->
## Lieer {#lieer}
| Guix dependency |
|-----------------|
| python-lieer |
Lieer is a program to link up Gmail and notmuch. Basically, it downloads mail from Gmail via API, stores them in Maildir, and synchronizes labels with notmuch.
I have a separate directory in my `~/Mail` for each address. To init lieer, run the following command in the directory:
```text
gmi init <address>
```
After which the settings will be stored in `gmailieer.json` and the credentials in `.credentials.gmailieer.json`. The latter file is stored encrypted.
My preferred settings:
```text
gmi set --replace-slash-with-dot
gmi set --ignore-tags-local new
```
Running `gmi sync` in the required directory performs the synchronization. The first sync takes a while, the subsequent syncs are pretty fast.
## DavMail {#davmail}
is a gateway between MS Exchange and the rest of the world, which uses IMAP/SMTP/LDAP/etc. As I have one corporate MS Exchange address, this is just the program I need. As of yet, it isn't packaged for Guix, but it's easy enough to download.
It has a GUI mode, but I prefer headless config.
```ini
davmail.server=true
davmail.mode=Auto
davmail.url=https://mail.etu.ru/owa/
davmail.server.certificate.hash=0C:9E:CF:D3:62:26:DB:FA:F1:EE:36:9D:60:E7:31:71:CF:1F:92:85
davmail.caldavPort=1080
davmail.imapPort=1143
davmail.ldapPort=1389
davmail.popPort=1110
davmail.smtpPort=1025
davmail.imapAutoExpunge=false
davmail.enableKeepalive=false
```
Also it's a bit of problem to get it launched as it looks for its jars in the pwd, so here is a script.
```bash
cd $HOME/bin/davmail-6.0.0-3375
./davmail davmail.properties
```
Shepherd service is defined in [Desktop.org]({{< relref "Desktop" >}}).
## OfflineIMAP {#offlineimap}
| Guix dependency |
|-----------------|
| offlineimap |
[OfflineIMAP](https://github.com/OfflineIMAP/offlineimap) is a program that can synchronize IMAP mailbox and Maildir. Lieer does everything by itself, but my pirate Exchange IMAP needs this program. There is also [isync](https://isync.sourceforge.io/), but there I had some weird issues with duplicate UIDs, which don't occur for OfflineIMAP.
I have a few options for setting a username and password. First, I can run `pass` in `remotepasswordeval`, and while this will work, it will keep my keyring unlocked because I want to run `offlineimap` every couple of minutes.
Another option is to use noweb and not push the file below to the version control. Then I have a plaintext password of email on my computer, but I think it's a lesser evil than the entire keyring.
I would use `password-store-get` from password-store.el, but I want this to be able to run without any 3rd party packages, so it's just bash.
<a id="code-snippet--mail-username"></a>
```bash
pass show Job/Infrastructure/pvkorytov@etu.ru | sed -n 's/username: //;2p'
```
<a id="code-snippet--mail-password"></a>
```bash
pass show Job/Infrastructure/pvkorytov@etu.ru | head -n 1
```
```ini
[general]
accounts = pvkorytov
[Account pvkorytov]
localrepository = pvkorytov-local
remoterepository = pvkorytov-remote
[Repository pvkorytov-local]
type = Maildir
localfolders = ~/Mail/pvkorytov_etu/
[Repository pvkorytov-remote]
type = IMAP
remotehost = localhost
remoteuser = <<mail-username()>>
remotepass = <<mail-password()>>
remoteport = 1143
starttls = no
ssl = no
```
## Notmuch {#notmuch}
| Guix dependency |
|-----------------|
| notmuch |
| parallel |
Notmuch is an email indexer program, which handles labels in a way somewhat similar to Gmail. It also provides a frontend for Emacs, but it's not the only one available.
### Config {#config}
Not much is going on here.
First, the database path.
```ini
[database]
path=/home/pavel/Mail
```
My name and list of emails. It's not like it's a secret anyhow.
```ini
[user]
name=Pavel Korytov
primary_email=thexcloud@gmail.com
other_email=progin6304@gmail.com;pvkorytov@etu.ru
```
A list of tags which will be added by `notmuch new` and directory names which will be ignored by `notmuch new`.
```ini
[new]
tags=new;
ignore=.osync_workdir;.mbsyncstate;.uidvalidity;.lock;/.*gmailieer\.json.*/
```
Exclude these tags from search by default.
```ini
[search]
exclude_tags=trash;spam;
```
Maildir compatibility.
```ini
[maildir]
synchronize_flags=true
```
### Hooks {#hooks}
Now we have to link up lieer & davmail's maildir and with notmuch. This is done via the notmuch hook system, which allows running custom scripts before and after any command.
With lieer and Gmail, it is enough to simply run the program, because Gmail has first-class support for tags. Maildir does not, so I decide to synchronize notmuch tags and IMAP folders. In essence, the idea is to:
- move emails to their folders by tags _before_ the synchronization
- tag mails by their folders _after_ the synchronization
The problem is that with that approach one email can have only one tag, but it's better than nothing.
So, here are the rules which match tags & folders:
<a id="table--pvkorytov-tags"></a>
| tag | folder |
|--------------------------|---------------------------|
| inbox | INBOX |
| sent | Sent |
| spam | Junk |
| trash | Trash |
| job.digital | Job\_Digital |
| job.digital.docs | Job\_Digital.Docs |
| job.digital.support | Job\_Digital.Support |
| job.digital.superservice | Job\_Digital.Superservice |
And below is a noweb function, which generates the following commands for notmuch to execute:
- _before_ sync:
- `notmuch search --output files "NOT path:[PATH] AND tag:[TAG] AND tag:[ROOT_TAG]" | xargs -I ! mv ! [PATH]`
Move emails with `TAG` but outside the matching `PATH` to the latter
- `notmuch search --output=files "NOT path:[ARCHIVE_PATH] AND tag:[ROOT_TAG] AND NOT tag:[TAG1] ... AND NOT tag:[TAGN]" | xargs -I ! mv ! [ARCHIVE_PATH]`
Move untagged emails to the `ARCHIVE_PATH`
- _after_ sync:
- `notmuch tag +[TAG] "path:[PATH] AND NOT tag:[TAG]"`
Tag emails in `PATH` which do not yet have the matching `TAG`
- `notmuch tag -[TAG] "NOT path:[PATH] AND tag:[TAG] AND tag:[ROOT_TAG]"`
Remove `TAG` from emails which are outside the matching `PATH`
These rules are getting included in the respective hooks.
<a id="code-snippet--mail-tags"></a>
```emacs-lisp
(setq my/maildir-root "~/Mail")
(let ((rules '()))
(dolist (row tags)
(let ((tag (nth 0 row))
(folder (nth 1 row)))
(unless (string-empty-p make_tag)
(add-to-list
'rules
(format "notmuch tag +%s \"path:%s/%s/cur/** AND NOT tag:%s\""
tag root folder tag)
t))
(unless (string-empty-p remove)
(add-to-list
'rules
(format "notmuch tag -%s \"NOT path:%s/%s/cur/** AND tag:%s AND tag:%s\""
tag root folder tag root_tag)
t))
(unless (string-empty-p move)
(add-to-list
'rules
(concat
(format "notmuch search --output=files \"NOT path:%s/%s/cur/** AND tag:%s AND tag:%s\""
root folder tag root_tag)
(format " | xargs -I ! mv ! %s/%s/%s/cur/" my/maildir-root root folder))
t))))
(unless (string-empty-p archive_root)
(add-to-list
'rules
(concat
(format "notmuch search --output=files \"NOT path:%s/%s/cur/** AND %s AND tag:%s\""
root archive_root
(mapconcat
(lambda (row)
(format "NOT tag:%s" (car row)))
tags
" AND ")
root_tag)
(format " | xargs -I ! mv ! %s/%s/%s/cur/" my/maildir-root root archive_root))
t))
(string-join rules "\n"))
```
#### `pre_new` {#pre-new}
This hook runs fetch from Gmail & offlineimap in parallel before the `notmuch new` command. The `parallel` command is provided by [GNU Parallel](https://www.gnu.org/software/parallel/).
It isn't necessary to run `cd` for offlineimap, but it's easier to write that way.
<a id="code-snippet--pre-new-pvkorytov-tags"></a>
```emacs-lisp
(my/mail-format-tags-rules tags "pvkorytov_etu" "pvkorytov" nil nil t "Archive")
```
```bash
# GMI="/home/pavel/Programs/miniconda3/envs/mail/bin/gmi"
GMI="gmi"
echo "Running pre-new filters"
<<mail-tags(move="t",archive_root="Archive")>>
echo "Pre-new filters done"
parallel --link -j0 "(cd /home/pavel/Mail/{1}/ && {2} {3})" ::: thexcloud progin6304 pvkorytov_etu ::: "$GMI" "$GMI" "offlineimap" ::: sync sync ""
```
#### `post_new` {#post-new}
And this hook tags different mailboxes with different tags.
<a id="code-snippet--post-new-pvkorytov-tags"></a>
```emacs-lisp
(my/mail-format-tags-rules tags "pvkorytov_etu" "pvkorytov" t t)
```
```bash
notmuch tag +main "path:thexcloud/** AND tag:new"
notmuch tag +progin "path:progin6304/** AND tag:new"
notmuch tag +pvkorytov "path:pvkorytov_etu/** AND tag:new"
echo "Running post-new filters"
<<mail-tags(make_tag="t",remove="t")>>
echo "Post-new filters done"
notmuch tag -new "tag:new"
```
## Sync script {#sync-script}
A script to run `notmuch new` and push a notification if there is new mail.
```bash
export DISPLAY=:0
CHECK_FILE="/home/pavel/Mail/.last_check"
QUERY="tag:unread"
ALL_QUERY="tag:unread"
if [ -f "$CHECK_FILE" ]; then
DATE=$(cat "$CHECK_FILE")
QUERY="$QUERY and date:@$DATE.."
fi
notmuch new
NEW_UNREAD=$(notmuch count "$QUERY")
ALL_UNREAD=$(notmuch count "$ALL_QUERY")
if [ $NEW_UNREAD -gt 0 ]; then
MAIN_UNREAD=$(notmuch count "tag:unread AND tag:main")
PROGIN_UNREAD=$(notmuch count "tag:unread AND tag:progin")
ETU_UNREAD=$(notmuch count "tag:unread AND tag:pvkorytov")
read -r -d '' NOTIFICATION <<EOM
$NEW_UNREAD new messages
$MAIN_UNREAD thexcloud@gmail.com
$PROGIN_UNREAD progin6304@gmail.com
$ETU_UNREAD pvkorytov@etu.ru
$ALL_UNREAD total
EOM
notify-send "New Mail" "$NOTIFICATION"
fi
echo "$(date +%s)" > $CHECK_FILE
```
The script is ran via GNU Mcron every 5 minutes.
```scheme
(job "*/5 * * * * " "~/bin/scripts/check-email")
```
## MSMTP {#msmtp}
| Guix dependency |
|-----------------|
| msmtp |
Sending emails can be done with MSMTP. It automatially chooses the email address and server based on the contents of the message, which is handy if there are multiple mailboxes to be managed.
```vim
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile ~/.msmtp.log
account main
host smtp.gmail.com
port 587
from thexcloud@gmail.com
user thexcloud@gmail.com
passwordeval "pass show My_Online/APIs/google-main-app-password | head -n 1"
account progin
host smtp.gmail.com
port 587
from progin6304@gmail.com
user progin6304@gmail.com
passwordeval "pass show My_Online/ETU/progin6304@gmail.com | head -n 1"
account pvkorytov
tls off
auth plain
host localhost
port 1025
from pvkorytov@etu.ru
user pvkorytov
passwordeval "pass show Job/Infrastructure/pvkorytov@etu.ru | head -n 1"
```
## Emacs {#emacs}
Finally, Emacs configuration. Let's start with some variables:
```emacs-lisp
(setq user-mail-address "thexcloud@gmail.com")
(setq user-full-name "Pavel Korytov")
```
Then, the problem with my Guix setup is that Emacs by default doesn't see the elisp files of notmuch, so here is a small workaround:
```emacs-lisp
(let ((default-directory "/home/pavel/.guix-extra-profiles/mail/mail/share/emacs/site-lisp"))
(normal-top-level-add-subdirs-to-load-path))
```
Finally the proper notmuch settings:
```emacs-lisp
(use-package notmuch
;; :ensure nil
:commands (notmuch notmuch-search)
:config
(setq mail-specify-envelope-from t)
(setq message-sendmail-envelope-from 'header)
(setq mail-envelope-from 'header)
(setq notmuch-always-prompt-for-sender t)
(setq sendmail-program (executable-find "msmtp"))
(setq send-mail-function #'sendmail-send-it)
(setq mml-secure-openpgp-sign-with-sender t)
(setq notmuch-mua-user-agent-function 'notmuch-mua-user-agent-full)
(add-hook 'notmuch-hello-mode-hook
(lambda () (display-line-numbers-mode 0))))
```
The file to which this is tangled is read in the init.el.
### Saved filters and keybindings {#saved-filters-and-keybindings}
I want to have the saved filters available in both notmuch interface as as keybindings. So a bit more of abusing org tables.
Root keybindings:
```emacs-lisp
(my-leader-def
:infix "am"
"" '(:which-key "notmuch")
"m" 'notmuch)
```
<a id="table--root-tags"></a>
| Root tag | Prefix | Keybinding description |
|-----------|--------|------------------------|
| main | t | thexcloud@gmail.com |
| progin | p | progin6304@gmail.com |
| pvkorytov | e | pvkorytov@etu.ru |
<a id="table--filter-tags"></a>
| Tag | Prefix | Name |
|--------|--------|----------|
| inbox | i | inbox |
| unread | u | unread |
| sent | s | sent |
| | a | all mail |
The following formats the tables above to a proper syntax for `setq notmuch-saved-searches`:
<a id="code-snippet--format-notmuch-saved-searches"></a>
```emacs-lisp
(let ((searches '()))
(dolist (root_tag root_tags)
(dolist (tag filter_tags)
(add-to-list
'searches
(format "(:name \"%s\" :query \"%s\")"
(format "%s (%s)"
(nth 0 root_tag)
(nth 2 tag))
(concat "tag:" (nth 0 root_tag)
(unless (string-empty-p (nth 0 tag))
(concat " AND tag:" (nth 0 tag)))))
t)))
(string-join searches "\n"))
```
And the following does the same for my general.el definer:
<a id="code-snippet--format-notmuch-keybindings"></a>
```emacs-lisp
(let ((bindings '()))
(dolist (root_tag root_tags)
(add-to-list
'bindings
(format "\"%s\" '(:which-key \"%s\")"
(nth 1 root_tag)
(nth 2 root_tag))
t)
(dolist (tag filter_tags)
(add-to-list
'bindings
(format "\"%s\" '((lambda () (interactive) (notmuch-search \"%s\")) :which-key \"%s\")"
(concat (nth 1 root_tag) (nth 1 tag))
(concat "tag:" (nth 0 root_tag)
(unless (string-empty-p (nth 0 tag))
(concat " AND tag:" (nth 0 tag))))
(nth 2 tag))
t)))
(string-join bindings "\n"))
```
```emacs-lisp
(setq notmuch-saved-searches
'((:name "drafts" :query "tag:draft")
<<format-notmuch-saved-searches()>>))
(my-leader-def
:infix "am"
<<format-notmuch-keybindings()>>)
```
### Signing messages {#signing-messages}
```emacs-lisp
(with-eval-after-load 'notmuch
(add-hook 'message-setup-hook 'mml-secure-sign-pgpmime))
(setq mml-secure-key-preferences
'((OpenPGP
(sign
("thexcloud@gmail.com" "914472A1FD6775C166F96EBEED739ADF81C78160"))
(encrypt))
(CMS
(sign)
(encrypt))))
```
## mailcap {#mailcap}
mailcap file is a file which defines how to read to different MIME types. Notmuch also uses it, so why not keep it here.
```text
audio/*; mpc add %s
image/*; feh %s
application/msword; /usr/bin/xdg-open %s
application/pdf; zathura %s
application/postscript ; zathura %s
text/html; firefox %s
```
## Guix settings {#guix-settings}
<a id="code-snippet--packages"></a>
```emacs-lisp
(my/format-guix-dependencies)
```
```scheme
(specifications->manifest
'(
<<packages()>>))
```

View file

@ -1,113 +0,0 @@
+++
title = "My dotfiles"
author = ["Pavel"]
aliases = ["/config"]
draft = false
+++
{{< figure src="https://forthebadge.com/images/badges/works-on-my-machine.svg" >}}
A set of my GNU/Linux configuration files. [View at GitHub](https://github.com/SqrtMinusOne/dotfiles).
The majority of the software is configured with [literate configuration](https://leanpub.com/lit-config/read) strategy via Emacs' Org Mode. This way has its advantages and disadvantages, but overall it's pretty nice to keep the configs interweaved with comments in a handful of files.
The files themselves are managed and deployed via [yadm](https://yadm.io/), but I mostly use Org Mode rich noweb whenever I can instead of what yadm offers.
My current GNU/Linux distribution is [GNU Guix](https://guix.gnu.org/). In the context of this repo, Guix allows me to list all the used programs in manifests, which means I have the same set of programs across multiple machines. Look for tables with "Guix dependency" in the header.
Literate configuration files:
- [Emacs.org]({{< relref "Emacs" >}})
- [Desktop.org]({{< relref "Desktop" >}})
- [Console.org]({{< relref "Console" >}})
- [Guix.org]({{< relref "Guix" >}})
- [Mail.org]({{< relref "Mail" >}})
## Programs used {#programs-used}
Some of the notable programs are listed in the table below.
| Group | Program | Purpose | Status | Documented? | Notes |
|-----------|---------------------------------------------------------|-----------------------------|------------------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------|
| console | bash | shell | launches fish :) | [Console.org]({{< relref "Console" >}}) | |
| console | [fish](https://fishshell.com/) | shell | **active** | [Console.org]({{< relref "Console" >}}) | |
| console | [starship](https://github.com/starship/starship) | prompt | **active** | [Console.org]({{< relref "Console" >}}) | |
| console | [tmux](https://github.com/tmux/tmux) | terminal multiplexer | **active** | [Console.org]({{< relref "Console" >}}) | |
| console | [alacritty](https://github.com/alacritty/alacritty) | terminal emulator | **active** | [Console.org]({{< relref "Console" >}}) | |
| mail | [notmuch](https://notmuchmail.org/) | mail indexer | **active** | [Mail.org,]({{< relref "Mail" >}}) [post](https://sqrtminusone.xyz/posts/2021-02-27-gmail/) | |
| mail | [lieer](https://github.com/gauteh/lieer) | gmail API client | **active** | [Mail.org]({{< relref "Mail" >}}), [post](https://sqrtminusone.xyz/posts/2021-02-27-gmail/) | credentials are encrypted |
| mail | [msmtp](https://marlam.de/msmtp/) | SMTP client | **active** | [Mail.org]({{< relref "Mail" >}}) | |
| editor | [emacs](https://www.gnu.org/software/emacs/) | everything | **active** | [Emacs.org]({{< relref "Emacs" >}}) | GitHub renders .org files without labels and `tangle: no` |
| editor | [vim](https://www.vim.org/) | text edtior | **active** | - | A minimal config to have a lightweight terminal $EDITOR |
| editor | [neovim](https://neovim.io/) | text edtior | archive | - | |
| documents | [latexmk](https://mg.readthedocs.io/latexmk.html) | LaTeX build tool | **active** | - | |
| documents | [zathura](https://pwmt.org/projects/zathura/) | pdf viewer | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [dunst](https://github.com/dunst-project/dunst) | notification manager | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [i3wm](https://i3wm.org/) | tiling WM | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [keynav](https://github.com/jordansissel/keynav) | control mouse with keyboard | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [polybar](https://github.com/polybar/polybar) | status bar | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [rofi](https://github.com/davatorium/rofi) | generic menu | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [flameshot](https://github.com/flameshot-org/flameshot) | screenshot | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [picom](https://github.com/yshui/picom) | X11 compositor | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [i3blocks](https://github.com/vivien/i3blocks) | status bar | archive | - | |
| internet | [tridactyl](https://github.com/tridactyl/tridactyl) | vim bindings for Firefox | **active** | - | templated with yadm |
| internet | [newsboat](https://newsboat.org/) | terminal RSS reader | archive | - | urls are encrypted |
| internet | [qutebrowser](https://qutebrowser.org/) | browser with vim bindings | archive | - | |
| internet | [buku](https://github.com/jarun/buku) | bookmarks manager | archive | - | |
| audio | [mpd](https://www.musicpd.org/) | music player daemon | **active** | - | |
| audio | [ncmpcpp](https://github.com/ncmpcpp/ncmpcpp) | MPD frontend | **active** | - | |
| misc | [yadm](https://yadm.io) | dotfiles manager | **active** | - | |
| misc | [sunwait](https://github.com/risacher/sunwait) | sunrise calculator | **active** | - | |
| misc | [vnstat](https://github.com/vergoh/vnstat) | traffic stats | **active** | - | |
## Posts about my configuration {#posts-about-my-configuration}
- [Replacing Jupyter Notebook with Org Mode](https://sqrtminusone.xyz/posts/2021-05-01-org-python/)
- [Multiple Gmail accounts & labels with Emacs](https://sqrtminusone.xyz/posts/2021-02-27-gmail/)
## Some statistics {#some-statistics}
If you are viewing the file in Emacs, eval the following to show the pictures with reasonable width:
```elisp
(setq-local org-image-actual-width '(1024))
```
### History {#history}
{{< figure src="./dot-stats/img/all.png" >}}
{{< figure src="./dot-stats/img/emacs-vim.png" >}}
{{< figure src="./dot-stats/img/literate-config.png" >}}
## Misc {#misc}
### Notes {#notes}
- `M-u C-c C-v t` to tangle a particular block
- `M-u M-u C-c C-v t` to tangle a particular file
- `C-c C-v d` to demarcate a block
Uses yadm's `post_alt` hook to create symlinks
### Encrypted files {#encrypted-files}
```text
.config/newsboat/urls
.config/filezilla/sitemanager.xml
.config/filezilla/filezilla.xml
Mail/thexcloud/.credentials.gmailieer.json
Mail/progin6304/.credentials.gmailieer.json
.emacs.d/dired-bookmarks.el
.emacs.d/private.org
.emacs.d/private.el
.emacs.d/.trello/sqrtminusone.el
```

View file

@ -1,113 +0,0 @@
+++
title = "My dotfiles"
author = ["Pavel"]
aliases = ["/config"]
draft = false
+++
{{< figure src="https://forthebadge.com/images/badges/works-on-my-machine.svg" >}}
A set of my GNU/Linux configuration files. [View at GitHub](https://github.com/SqrtMinusOne/dotfiles).
The majority of the software is configured with [literate configuration](https://leanpub.com/lit-config/read) strategy via Emacs' Org Mode. This way has its advantages and disadvantages, but overall it's pretty nice to keep the configs interweaved with comments in a handful of files.
The files themselves are managed and deployed via [yadm](https://yadm.io/), but I mostly use Org Mode rich noweb whenever I can instead of what yadm offers.
My current GNU/Linux distribution is [GNU Guix](https://guix.gnu.org/). In the context of this repo, Guix allows me to list all the used programs in manifests, which means I have the same set of programs across multiple machines. Look for tables with "Guix dependency" in the header.
Literate configuration files:
- [Emacs.org]({{< relref "Emacs" >}})
- [Desktop.org]({{< relref "Desktop" >}})
- [Console.org]({{< relref "Console" >}})
- [Guix.org]({{< relref "Guix" >}})
- [Mail.org]({{< relref "Mail" >}})
## Programs used {#programs-used}
Some of the notable programs are listed in the table below.
| Group | Program | Purpose | Status | Documented? | Notes |
|-----------|---------------------------------------------------------|-----------------------------|------------------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------|
| console | bash | shell | launches fish :) | [Console.org]({{< relref "Console" >}}) | |
| console | [fish](https://fishshell.com/) | shell | **active** | [Console.org]({{< relref "Console" >}}) | |
| console | [starship](https://github.com/starship/starship) | prompt | **active** | [Console.org]({{< relref "Console" >}}) | |
| console | [tmux](https://github.com/tmux/tmux) | terminal multiplexer | **active** | [Console.org]({{< relref "Console" >}}) | |
| console | [alacritty](https://github.com/alacritty/alacritty) | terminal emulator | **active** | [Console.org]({{< relref "Console" >}}) | |
| mail | [notmuch](https://notmuchmail.org/) | mail indexer | **active** | [Mail.org,]({{< relref "Mail" >}}) [post](https://sqrtminusone.xyz/posts/2021-02-27-gmail/) | |
| mail | [lieer](https://github.com/gauteh/lieer) | gmail API client | **active** | [Mail.org]({{< relref "Mail" >}}), [post](https://sqrtminusone.xyz/posts/2021-02-27-gmail/) | credentials are encrypted |
| mail | [msmtp](https://marlam.de/msmtp/) | SMTP client | **active** | [Mail.org]({{< relref "Mail" >}}) | |
| editor | [emacs](https://www.gnu.org/software/emacs/) | everything | **active** | [Emacs.org]({{< relref "Emacs" >}}) | GitHub renders .org files without labels and `tangle: no` |
| editor | [vim](https://www.vim.org/) | text edtior | **active** | - | A minimal config to have a lightweight terminal $EDITOR |
| editor | [neovim](https://neovim.io/) | text edtior | archive | - | |
| documents | [latexmk](https://mg.readthedocs.io/latexmk.html) | LaTeX build tool | **active** | - | |
| documents | [zathura](https://pwmt.org/projects/zathura/) | pdf viewer | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [dunst](https://github.com/dunst-project/dunst) | notification manager | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [i3wm](https://i3wm.org/) | tiling WM | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [keynav](https://github.com/jordansissel/keynav) | control mouse with keyboard | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [polybar](https://github.com/polybar/polybar) | status bar | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [rofi](https://github.com/davatorium/rofi) | generic menu | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [flameshot](https://github.com/flameshot-org/flameshot) | screenshot | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [picom](https://github.com/yshui/picom) | X11 compositor | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [i3blocks](https://github.com/vivien/i3blocks) | status bar | archive | - | |
| internet | [tridactyl](https://github.com/tridactyl/tridactyl) | vim bindings for Firefox | **active** | - | templated with yadm |
| internet | [newsboat](https://newsboat.org/) | terminal RSS reader | archive | - | urls are encrypted |
| internet | [qutebrowser](https://qutebrowser.org/) | browser with vim bindings | archive | - | |
| internet | [buku](https://github.com/jarun/buku) | bookmarks manager | archive | - | |
| audio | [mpd](https://www.musicpd.org/) | music player daemon | **active** | - | |
| audio | [ncmpcpp](https://github.com/ncmpcpp/ncmpcpp) | MPD frontend | **active** | - | |
| misc | [yadm](https://yadm.io) | dotfiles manager | **active** | - | |
| misc | [sunwait](https://github.com/risacher/sunwait) | sunrise calculator | **active** | - | |
| misc | [vnstat](https://github.com/vergoh/vnstat) | traffic stats | **active** | - | |
## Posts about my configuration {#posts-about-my-configuration}
- [Replacing Jupyter Notebook with Org Mode](https://sqrtminusone.xyz/posts/2021-05-01-org-python/)
- [Multiple Gmail accounts & labels with Emacs](https://sqrtminusone.xyz/posts/2021-02-27-gmail/)
## Some statistics {#some-statistics}
If you are viewing the file in Emacs, eval the following to show the pictures with reasonable width:
```elisp
(setq-local org-image-actual-width '(1024))
```
### History {#history}
{{< figure src="/ox-hugo/all.png" >}}
{{< figure src="/ox-hugo/emacs-vim.png" >}}
{{< figure src="/ox-hugo/literate-config.png" >}}
## Misc {#misc}
### Notes {#notes}
- `M-u C-c C-v t` to tangle a particular block
- `M-u M-u C-c C-v t` to tangle a particular file
- `C-c C-v d` to demarcate a block
Uses yadm's `post_alt` hook to create symlinks
### Encrypted files {#encrypted-files}
```text
.config/newsboat/urls
.config/filezilla/sitemanager.xml
.config/filezilla/filezilla.xml
Mail/thexcloud/.credentials.gmailieer.json
Mail/progin6304/.credentials.gmailieer.json
.emacs.d/dired-bookmarks.el
.emacs.d/private.org
.emacs.d/private.el
.emacs.d/.trello/sqrtminusone.el
```

View file

@ -1,113 +0,0 @@
+++
title = "My dotfiles"
author = ["Pavel"]
draft = false
+++
{{< figure src="https://forthebadge.com/images/badges/works-on-my-machine.svg" >}}
A set of my GNU/Linux configuration files. [View at GitHub](https://github.com/SqrtMinusOne/dotfiles).
The majority of the software is configured with [literate configuration](https://leanpub.com/lit-config/read) strategy via Emacs' Org Mode. This way has its advantages and disadvantages, but overall it's pretty nice to keep the configs interweaved with comments in a handful of files.
The files themselves are managed and deployed via [yadm](https://yadm.io/), but I mostly use Org Mode rich noweb whenever I can instead of what yadm offers.
My current GNU/Linux distribution is [GNU Guix](https://guix.gnu.org/). In the context of this repo, Guix allows me to list all the used programs in manifests, which means I have the same set of programs across multiple machines. Looks for Org tables with "Guix dependency" in the header.
Literate configuration files:
- [Emacs.org]({{< relref "Emacs" >}})
- [Desktop.org]({{< relref "Desktop" >}})
- [Console.org]({{< relref "Console" >}})
- [Guix.org]({{< relref "Guix" >}})
- [Mail.org]({{< relref "Mail" >}})
## Programs used {#programs-used}
Some of the notable programs are listed in the table below.
| Group | Program | Purpose | Status | Documented? | Notes |
|-----------|---------------------------------------------------------|-----------------------------|------------------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------|
| console | bash | shell | launches fish :) | [Console.org]({{< relref "Console" >}}) | |
| console | [fish](https://fishshell.com/) | shell | **active** | [Console.org]({{< relref "Console" >}}) | |
| console | [starship](https://github.com/starship/starship) | prompt | **active** | [Console.org]({{< relref "Console" >}}) | |
| console | [tmux](https://github.com/tmux/tmux) | terminal multiplexer | **active** | [Console.org]({{< relref "Console" >}}) | |
| console | [alacritty](https://github.com/alacritty/alacritty) | terminal emulator | **active** | [Console.org]({{< relref "Console" >}}) | |
| mail | [notmuch](https://notmuchmail.org/) | mail indexer | **active** | [Mail.org,]({{< relref "Mail" >}}) [post](https://sqrtminusone.xyz/posts/2021-02-27-gmail/) | |
| mail | [lieer](https://github.com/gauteh/lieer) | gmail API client | **active** | [Mail.org]({{< relref "Mail" >}}), [post](https://sqrtminusone.xyz/posts/2021-02-27-gmail/) | credentials are encrypted |
| mail | [msmtp](https://marlam.de/msmtp/) | SMTP client | **active** | [Mail.org]({{< relref "Mail" >}}) | |
| editor | [emacs](https://www.gnu.org/software/emacs/) | everything | **active** | [Emacs.org]({{< relref "Emacs" >}}) | GitHub renders .org files without labels and `tangle: no` |
| editor | [vim](https://www.vim.org/) | text edtior | **active** | - | A minimal config to have a lightweight terminal $EDITOR |
| editor | [neovim](https://neovim.io/) | text edtior | archive | - | |
| documents | [latexmk](https://mg.readthedocs.io/latexmk.html) | LaTeX build tool | **active** | - | |
| documents | [zathura](https://pwmt.org/projects/zathura/) | pdf viewer | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [dunst](https://github.com/dunst-project/dunst) | notification manager | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [i3wm](https://i3wm.org/) | tiling WM | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [keynav](https://github.com/jordansissel/keynav) | control mouse with keyboard | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [polybar](https://github.com/polybar/polybar) | status bar | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [rofi](https://github.com/davatorium/rofi) | generic menu | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [flameshot](https://github.com/flameshot-org/flameshot) | screenshot | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [picom](https://github.com/yshui/picom) | X11 compositor | **active** | [Desktop.org]({{< relref "Desktop" >}}) | |
| desktop | [i3blocks](https://github.com/vivien/i3blocks) | status bar | archive | - | |
| internet | [tridactyl](https://github.com/tridactyl/tridactyl) | vim bindings for Firefox | **active** | - | templated with yadm |
| internet | [newsboat](https://newsboat.org/) | terminal RSS reader | archive | - | urls are encrypted |
| internet | [qutebrowser](https://qutebrowser.org/) | browser with vim bindings | archive | - | |
| internet | [buku](https://github.com/jarun/buku) | bookmarks manager | archive | - | |
| audio | [mpd](https://www.musicpd.org/) | music player daemon | **active** | - | |
| audio | [ncmpcpp](https://github.com/ncmpcpp/ncmpcpp) | MPD frontend | **active** | - | |
| misc | [yadm](https://yadm.io) | dotfiles manager | **active** | - | |
| misc | [sunwait](https://github.com/risacher/sunwait) | sunrise calculator | **active** | - | |
| misc | [vnstat](https://github.com/vergoh/vnstat) | traffic stats | **active** | - | |
## Posts about my configuration {#posts-about-my-configuration}
- [Replacing Jupyter Notebook with Org Mode](https://sqrtminusone.xyz/posts/2021-05-01-org-python/)
- [Multiple Gmail accounts & labels with Emacs](https://sqrtminusone.xyz/posts/2021-02-27-gmail/)
## Some statistics {#some-statistics}
Run the following to show the pictures with reasonable width:
```elisp
(setq-local org-image-actual-width '(1024))
```
### History {#history}
{{< figure src="/ox-hugo/all.png" >}}
{{< figure src="/ox-hugo/emacs-vim.png" >}}
{{< figure src="/ox-hugo/literate-config.png" >}}
## Misc {#misc}
### Notes {#notes}
- `M-u C-c C-v t` to tangle a particular block
- `M-u M-u C-c C-v t` to tangle a particular file
- `C-c C-v d` to demarcate a block
Uses yadm's `post_alt` hook to create symlinks
### Encrypted files {#encrypted-files}
```text
.config/newsboat/urls
.config/filezilla/sitemanager.xml
.config/filezilla/filezilla.xml
Mail/thexcloud/.credentials.gmailieer.json
Mail/progin6304/.credentials.gmailieer.json
.emacs.d/dired-bookmarks.el
.emacs.d/elfeed.org
.emacs.d/private.org
.emacs.d/prodigy-config.el
.emacs.d/private.el
```

View file

@ -1,30 +0,0 @@
import os
import subprocess
from flask import Flask
from dotenv import load_dotenv
from flask_httpauth import HTTPTokenAuth
def create_app():
load_dotenv()
app = Flask(__name__)
auth = HTTPTokenAuth(scheme='Bearer')
@auth.verify_token
def verify_token(token):
return token == os.environ['TOKEN']
@app.route('/', methods=['POST'])
@auth.login_required
def deploy():
subprocess.run(
['git', 'fetch', 'origin'], cwd=os.environ.get('CWD', None)
)
subprocess.run(
['git', 'checkout', 'origin/gh-pages'],
cwd=os.environ.get('CWD', None)
)
return app