summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--accounts/gkleen@sif/autorandr-profiles/bstr.nix21
-rw-r--r--accounts/gkleen@sif/autorandr-profiles/default.nix13
-rw-r--r--accounts/gkleen@sif/backup-patterns24
-rw-r--r--accounts/gkleen@sif/default.nix188
-rw-r--r--accounts/gkleen@sif/dunst-settings.nix64
-rw-r--r--accounts/gkleen@sif/emacs.el111
-rw-r--r--accounts/gkleen@sif/firefox-chrome.css25
-rw-r--r--accounts/gkleen@sif/firefox-content.css12
-rw-r--r--accounts/gkleen@sif/ssh-hosts.nix190
-rw-r--r--accounts/gkleen@sif/store.kdbx.lftp6
-rw-r--r--accounts/gkleen@sif/systemd.nix94
-rw-r--r--accounts/gkleen@sif/xmobar/default.nix7
-rw-r--r--accounts/gkleen@sif/xmobar/nixpkgs.nix9
-rw-r--r--accounts/gkleen@sif/xmobar/package.yaml13
-rw-r--r--accounts/gkleen@sif/xmobar/shell.nix28
-rw-r--r--accounts/gkleen@sif/xmobar/stack.nix17
-rw-r--r--accounts/gkleen@sif/xmobar/stack.yaml10
-rw-r--r--accounts/gkleen@sif/xmobar/stack.yaml.lock12
-rw-r--r--accounts/gkleen@sif/xmobar/stackage.nix31
-rw-r--r--accounts/gkleen@sif/xmobar/xmobar-yggdrasil.nix13
-rw-r--r--accounts/gkleen@sif/xmobar/xmobar.hs52
-rw-r--r--accounts/gkleen@sif/xmonad/.gitignore4
-rw-r--r--accounts/gkleen@sif/xmonad/default.nix7
-rw-r--r--accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs127
-rw-r--r--accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs94
-rw-r--r--accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs105
-rw-r--r--accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs243
-rw-r--r--accounts/gkleen@sif/xmonad/package.yaml30
-rw-r--r--accounts/gkleen@sif/xmonad/stack.nix17
-rw-r--r--accounts/gkleen@sif/xmonad/stack.yaml10
-rw-r--r--accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix21
-rw-r--r--accounts/gkleen@sif/xmonad/xmonad.hs898
-rw-r--r--accounts/gkleen@sif/xresources.nix46
-rw-r--r--accounts/gkleen@sif/zshrc403
-rw-r--r--accounts/root@sif.nix18
-rw-r--r--flake.lock71
-rw-r--r--hosts/sif/default.nix320
-rw-r--r--hosts/sif/hw.nix36
-rw-r--r--hosts/sif/mail/default.nix66
-rw-r--r--hosts/sif/mail/secrets.yaml33
-rw-r--r--hosts/sif/wacom.conf15
-rw-r--r--modules/borgbackup/btrfs-snapshots.nix52
-rw-r--r--modules/borgbackup/default.nix206
-rw-r--r--modules/borgbackup/lvm-snapshots.nix133
-rw-r--r--modules/borgbackup/repokeys/borg_munin__borg.yaml33
-rw-r--r--modules/kill-user.nix13
-rw-r--r--modules/tinc-networkmanager.nix36
-rw-r--r--modules/uucp.nix391
-rw-r--r--modules/yggdrasil/default.nix49
-rw-r--r--modules/yggdrasil/hosts/sif/default.nix13
-rw-r--r--modules/yggdrasil/hosts/sif/private-keys.yaml34
-rw-r--r--modules/yggdrasil/hosts/ymir.nix19
-rw-r--r--overlays/nerdfonts.nix5
-rw-r--r--overlays/nvidia-kernel-5.7.nix19
-rw-r--r--overlays/persistent-nix-shell/default.nix19
-rw-r--r--overlays/persistent-nix-shell/persistent-nix-shell20
-rw-r--r--overlays/pidgin.nix15
-rw-r--r--overlays/urxvt/52-osc.pl41
-rw-r--r--overlays/urxvt/default.nix21
-rw-r--r--overlays/worktime/default.nix19
-rwxr-xr-xoverlays/worktime/worktime.py387
-rw-r--r--system-profiles/default-locale.nix7
-rw-r--r--system-profiles/initrd-all-crypto-modules.nix7
-rw-r--r--system-profiles/openssh/default.nix36
-rw-r--r--system-profiles/openssh/host-keys/sif.yaml34
-rw-r--r--system-profiles/openssh/known-hosts/sif.nix16
-rw-r--r--system-profiles/openssh/known-hosts/ymir.nix16
-rw-r--r--system-profiles/sudo.nix39
-rw-r--r--user-profiles/direnv.nix9
-rw-r--r--user-profiles/mpv/default.nix83
-rw-r--r--user-profiles/tmux/default.nix26
-rw-r--r--user-profiles/tmux/tmux.conf25
-rw-r--r--user-profiles/utils.nix23
-rw-r--r--user-profiles/zsh/default.nix30
-rw-r--r--user-profiles/zsh/p10k.zsh1572
-rw-r--r--users/gkleen/authorized-keys/gkleen-sif.pub1
-rw-r--r--users/gkleen/default.nix47
-rw-r--r--users/root.nix52
78 files changed, 7052 insertions, 0 deletions
diff --git a/accounts/gkleen@sif/autorandr-profiles/bstr.nix b/accounts/gkleen@sif/autorandr-profiles/bstr.nix
new file mode 100644
index 00000000..527f8321
--- /dev/null
+++ b/accounts/gkleen@sif/autorandr-profiles/bstr.nix
@@ -0,0 +1,21 @@
1{
2 fingerprint = {
3 "eDP-1-1" = "00ffffffffffff004c83414100000000131d0104b5221378029491ae513eb7240b505400000001010101010101010101010101010101f0d40040f17018803020440058c21000001bf0d40040f17018803020440058c21000001b0000000f00ff093cff093c2c800000000000000000fe0041544e413536575230382d3020011502030f00e3058000e6060501736d0700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab";
4 "DP-1.3" = "00ffffffffffff000469a3289bdd00000b190104a53e22783a1cb5a3574fa0270d5054bfef00d1c0814081809500b300714f81c0010122cc0050f0703e80181035006d552100001a04740030f2705a80b0588a006d552100001a000000fd001e5018a03c041100f0f838f03c000000fc0041535553205042323837510a2001a8020327714f0102031112130414051f900e0f1d1e23091707830100006a030c0010000078200000565e00a0a0a02950302035006d552100001ee26800a0a0402e60302036006d552100001a011d00bc52d01e20b82855406d552100001e8c0ad090204031200c4055006d55210000180000000000000000000000000000000064";
5 };
6 config = {
7 "DP-1.3" = {
8 enable = true;
9 primary = true;
10 position = "3840x0";
11 rate = "60";
12 mode = "3840x2160";
13 };
14 eDP-1-1 = {
15 enable = true;
16 primary = false;
17 position = "0x0";
18 mode = "3840x2160";
19 };
20 };
21}
diff --git a/accounts/gkleen@sif/autorandr-profiles/default.nix b/accounts/gkleen@sif/autorandr-profiles/default.nix
new file mode 100644
index 00000000..304b4afe
--- /dev/null
+++ b/accounts/gkleen@sif/autorandr-profiles/default.nix
@@ -0,0 +1,13 @@
1{
2 fingerprint = {
3 eDP-1-1 = "00ffffffffffff004c83414100000000131d0104b5221378029491ae513eb7240b505400000001010101010101010101010101010101f0d40040f17018803020440058c21000001bf0d40040f17018803020440058c21000001b0000000f00ff093cff093c2c800000000000000000fe0041544e413536575230382d3020011502030f00e3058000e6060501736d0700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab";
4 };
5 config = {
6 eDP-1-1 = {
7 enable = true;
8 primary = true;
9 position = "0x0";
10 mode = "3840x2160";
11 };
12 };
13}
diff --git a/accounts/gkleen@sif/backup-patterns b/accounts/gkleen@sif/backup-patterns
new file mode 100644
index 00000000..d9d12f5b
--- /dev/null
+++ b/accounts/gkleen@sif/backup-patterns
@@ -0,0 +1,24 @@
1R .
2
3- pp:.cache
4- pp:.antigen
5+ pf:.cabal/config
6- pp:.cabal
7+ pf:.stack/config.yaml
8- pp:.stack
9- pp:.nox
10- pp:.local/share/Steam
11- sh:**/.stack-work
12- sh:**/.~lock.*
13- sh:**/.gup
14+ pf:.config/pulse/default.pa
15- pp:.config/pulse
16- pp:.zplug/log
17- pp:.zplug/cache
18- pp:.compose-cache
19- pp:.undo-tree
20- pp:.saves
21- pp:.mozilla
22- pp:Downloads/tmp
23- pp:secret
24- pp:mail \ No newline at end of file
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix
new file mode 100644
index 00000000..545d40b5
--- /dev/null
+++ b/accounts/gkleen@sif/default.nix
@@ -0,0 +1,188 @@
1{ flake, userName, pkgs, customUtils, lib, config, ... }@inputs:
2let
3 cfg = config.home-manager.users.${userName};
4 xmonad = import ./xmonad pkgs.haskellPackages;
5in {
6 imports = with flake.nixosModules.userProfiles.${userName}; [
7 mpv
8 ];
9
10 home-manager.users.${userName} = {
11 programs = {
12 ssh = {
13 matchBlocks = import ./ssh-hosts.nix; # customUtils.recImport { dir = ./ssh-hosts; };
14 extraConfig = ''
15 Match host uniworx3.ifi.lmu.de,uniworx4.ifi.lmu.de,uni2workgw.ifi.lmu.de,blackbeard.tcs.ifi.lmu.de,gitlab2.rz.ifi.lmu.de,oregon.tcs.ifi.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null"
16 ProxyJump gate
17 '';
18 };
19
20 emacs = {
21 enable = true;
22 extraPackages = epkgs: with epkgs; [
23 evil evil-dvorak evil-magit undo-tree magit haskell-mode
24 nix-mode yaml-mode json-mode shakespeare-mode
25 smart-mode-line highlight-parentheses highlight-symbol
26 notmuch ag sass-mode lua-mode fira-code-mode
27 ];
28 };
29 firefox = {
30 enable = true;
31 profiles.default = {
32 settings = {
33 "layout.css.devPixelsPerPx" = "1.75";
34 "browser.tabs.drawInTitlebar" = false;
35 "toolkit.legacyUserProfileCustomizations.stylesheets" = true;
36 };
37 };
38 };
39
40 urxvt = {
41 enable = true;
42 package = pkgs.rxvt_unicode-with-plugins;
43 fonts = [ "xft:FiraCode Nerd Font Mono:style=Regular:pixelsize=21" ];
44 scroll = {
45 lines = 0;
46 bar.enable = false;
47 };
48 extraConfig = {
49 urgentOnBell = false;
50 print-pipe = "cat >/dev/null";
51 perl-ext-common = "52-osc,url-select";
52 "url-select.launcher" = "firefox";
53 "url-select.underline" = true;
54 };
55 keybindings = {
56 "M-u" = "perl:url-select:select_next";
57 };
58 };
59
60 zathura = {
61 enable = true;
62 package = pkgs.zathura.override { useMupdf = false; };
63 };
64
65 mpv.config = {
66 demuxer-max-bytes = 1073741824;
67 demuxer-max-back-bytes = 268435456;
68 };
69
70 autorandr = {
71 enable = true;
72 hooks.postswitch = {
73 # "restart-compton" = "${pkgs.systemd}/bin/systemctl --user try-restart picom";
74 "restart-trays" = ''
75 ${pkgs.coreutils}/bin/sleep 5
76 ${pkgs.systemd}/bin/systemctl --user try-restart trayer xmobar
77 '';
78 };
79 profiles = customUtils.recImport { dir = ./autorandr-profiles; };
80 };
81
82 zsh.initExtra = "source ${./zshrc}";
83 };
84
85 services = {
86 dunst = {
87 settings = import ./dunst-settings.nix;
88 iconTheme = cfg.gtk.iconTheme;
89 enable = true;
90 };
91 emacs.enable = true;
92 gpg-agent = {
93 enable = true;
94 enableSshSupport = true;
95 extraConfig = ''
96 pinentry-program ${pkgs.pinentry-gtk2}/bin/pinentry
97 grab
98 '';
99 };
100 pasystray.enable = true;
101 udiskie = {
102 enable = true;
103 automount = false;
104 };
105 unclutter = {
106 enable = true;
107 timeout = 5;
108 };
109 network-manager-applet.enable = true;
110 blueman-applet.enable = true;
111
112 sxhkd = {
113 enable = true;
114 keybindings = {
115 "button8" = "pacmd set-source-mute @DEFAULT_SOURCE@ 0";
116 "@button8" = "pacmd set-source-mute @DEFAULT_SOURCE@ 1";
117 "button9" = "pacmd set-sink-mute @DEFAULT_SINK@ 1";
118 "@button9" = "pacmd set-sink-mute @DEFAULT_SINK@ 0";
119 };
120 };
121 };
122
123 gtk = {
124 enable = true;
125 font.name = "DejaVu Sans 6";
126 theme = {
127 package = pkgs.equilux-theme;
128 name = "Equilux-compact";
129 };
130 iconTheme = {
131 package = pkgs.paper-icon-theme;
132 name = "Paper";
133 };
134 };
135
136 xsession = {
137 enable = true;
138
139 windowManager.command = "${xmonad}/bin/xmonad";
140
141 initExtra = let
142 lockScript = pkgs.writeScript "lock" ''
143 #!${pkgs.stdenv.shell}
144 ${pkgs.playerctl}/bin/playerctl -a pause
145 exec ${pkgs.xsecurelock}/bin/xsecurelock
146 '';
147 in ''
148 ${pkgs.coreutils}/bin/env XSECURELOCK_WANT_FIRST_KEYPRESS=1 XSECURELOCK_DIM_ALPHA=1 ${pkgs.xss-lock}/bin/xss-lock -l -n ${pkgs.xsecurelock}/libexec/xsecurelock/dimmer -- ${lockScript} &
149 ${pkgs.xorg.xinput}/bin/xinput disable 'SynPS/2 Synaptics TouchPad' # Synaptics TM3512-010
150 ${pkgs.xorg.xset}/bin/xset s 590 10
151 '';
152 };
153
154 xresources.properties = import ./xresources.nix;
155
156 home = {
157 packages = with pkgs; [
158 fira-code powerline-fonts nerdfonts pavucontrol keepassxc
159 youtube-dl sxiv xclip mumble pulseaudio-ctl libnotify synergy
160 xorg.xbacklight screen-message pidgin-with-plugins
161 google-play-music-desktop-player qt5ct playerctl evince
162 thunderbird zulip zoom-us steam steam-run wireshark skype
163 virt-manager rclone cached-nix-shell xournal discord xmonad
164 worktime fira-code-symbols
165 ];
166
167 file = {
168 ".emacs".source = ./emacs.el;
169 ".backup-munin".source = ./backup-patterns;
170 ".mozilla/firefox/default/chrome/userChrome.css".source = ./firefox-chrome.css;
171 ".mozilla/firefox/default/chrome/userContent.css".source = ./firefox-content.css;
172 };
173
174 sessionVariables = {
175 GDK_SCALE = 96.0 / 282.0;
176 XDG_DATA_DIRS = "${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}:${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}:$XDG_DATA_DIRS";
177 QT_AUTO_SCREEN_SCALE_FACTOR = 1;
178 QT_QPA_PLATFORMTHEME = "qt5ct";
179 };
180
181 stateVersion = "20.03";
182 };
183
184 fonts.fontconfig.enable = true;
185
186 systemd.user = import ./systemd.nix inputs;
187 };
188}
diff --git a/accounts/gkleen@sif/dunst-settings.nix b/accounts/gkleen@sif/dunst-settings.nix
new file mode 100644
index 00000000..8319da03
--- /dev/null
+++ b/accounts/gkleen@sif/dunst-settings.nix
@@ -0,0 +1,64 @@
1{
2 global = {
3 font = "Monospace 6";
4 markup = "full";
5 format = "<i>%s</i> %p\\n%b";
6 alignment = "left";
7 geometry = "1216x10-32+64";
8 shrink = true;
9 monitor = 0;
10 follow = "none";
11 padding = 6;
12 horizontal_padding = 6;
13 separator_height = 1;
14 separator_color = "frame";
15 idle_threshold = 0;
16
17 transparency = 10;
18
19 frame_width = 1;
20 frame_color = "#999999";
21
22 word_wrap = true;
23 show_age_threshold = 15;
24 show_indicators = false;
25 icon_position = "right";
26 sort = false;
27 sticky_history = false;
28 };
29 shortcuts = {
30 close = "ctrl+space";
31 close_all = "ctrl+shift+space";
32 history = "ctrl+comma";
33 context = "ctrl+period";
34 };
35 urgency_low = {
36 background = "#000000";
37 foreground = "#999999";
38 timeout = 5;
39 };
40 urgency_normal = {
41 background = "#000000";
42 foreground = "#ffffff";
43 timeout = 15;
44 };
45 urgency_critical = {
46 background = "#900000";
47 foreground = "#ffffff";
48 timeout = 0;
49 };
50 pulseaudio-ctl = {
51 summary = "Volume *";
52 body = "Current is *";
53 set_stack_tag = "volume";
54 history_ignore = true;
55 };
56 mail = {
57 appname = "notmuch";
58 timeout = 0;
59 };
60 zulip = {
61 appname = "Zulip";
62 timeout = 0;
63 };
64}
diff --git a/accounts/gkleen@sif/emacs.el b/accounts/gkleen@sif/emacs.el
new file mode 100644
index 00000000..92a79910
--- /dev/null
+++ b/accounts/gkleen@sif/emacs.el
@@ -0,0 +1,111 @@
1(menu-bar-mode -1)
2(scroll-bar-mode -1)
3(tool-bar-mode -1)
4
5(set-face-attribute 'default nil :font "FiraCode Nerd Font Mono" :height 49)
6
7(require 'evil)
8(evil-mode 1)
9(setq evil-undo-system 'undo-tree)
10
11(global-subword-mode)
12(global-undo-tree-mode)
13(global-fira-code-mode)
14
15(global-set-key (kbd "RET") 'newline-and-indent)
16(global-set-key (kbd "M-g") 'magit-status)
17(global-set-key (kbd "M-?") 'vc-git-grep)
18
19(setq backup-directory-alist `(("." . "~/.saves")))
20(setq delete-old-versions t
21 kept-new-versions 6
22 kept-old-versions 2
23 version-control t)
24
25(setq undo-tree-visualizer-timestamps t
26 undo-tree-visualizer-diff t
27 ;; 10X bump of the undo limits to avoid issues with premature
28 ;; Emacs GC which truncages the undo history very aggresively
29 undo-limit 800000
30 undo-strong-limit 12000000
31 undo-outer-limit 120000000)
32
33(add-hook 'haskell-mode-hook 'haskell-indentation-mode)
34(add-hook 'haskell-mode-hook 'subword-mode)
35(add-hook 'haskell-mode-hook 'highlight-symbol-mode)
36(add-hook 'haskell-mode-hook 'highlight-paretheses-mode)
37
38(add-hook 'js-mode-hook 'highlight-symbol-mode)
39(add-hook 'js-mode-hook 'highlight-parentheses-mode)
40(defun my-js-mode-hook ()
41 "Custom `js-mode' behaviours."
42 (setq js-indent-level 2)
43 )
44(add-hook 'js-mode-hook 'my-js-mode-hook)
45
46(setq undo-tree-auto-save-history t)
47
48(setq notmuch-address-internal-completion '(received nil))
49(setq notmuch-always-prompt-for-sender t)
50(setq notmuch-command "notmuch-ssh")
51(setq notmuch-crypto-process-mime t)
52(setq notmuch-draft-tags '("+draft" "-inbox"))
53(setq notmuch-fcc-dirs nil)
54(setq notmuch-hello-sections '(notmuch-hello-insert-header notmuch-hello-insert-saved-searches))
55(setq notmuch-hello-thousands-separator " ")
56(setq notmuch-identities '("gkleen@yggdrasil.li" "g@141.li" "kleen@cip.ifi.lmu.de" "Gregor.Kleen@stud.ifi.lmu.de" "G.Kleen@campus.lmu.de" "G.Kleen@lmu.de" "gregor.kleen@ifi.lmu.de" "uni2work@ifi.lmu.de" "gregor@kleen.li"))
57(setq notmuch-message-headers '("Subject" "To" "Cc" "Date"))
58(setq notmuch-message-replied-tags '("+replied" "-unread" "-inbox"))
59(setq notmuch-saved-searches
60 (quote
61 ((:name "inbox" :query "tag:inbox" :key "i")
62 (:name "unread" :query "tag:unread AND tag:inbox" :key "u")
63 (:name "drafts" :query "tag:draft" :key "d")
64 (:name "all mail" :query "date:month.." :key "a" :count-query "*")
65 (:name "sent" :query "is:sent" :key "s" :count-query "is:sent")
66 )))
67(setq notmuch-search-oldest-first nil)
68(setq notmuch-show-all-tags-list t)
69(setq notmuch-show-logo nil)
70
71(setq send-mail-function 'sendmail-send-it)
72(setq mail-envelope-from 'header)
73(setq mail-specify-envelope-from 't)
74(setq mail-default-headers nil)
75(setq message-default-headers "")
76(setq message-default-mail-headers "")
77(setq message-sendmail-envelope-from 'header)
78
79(setq highlight-symbol-idle-delay 0)
80
81(setq indent-tabs-mode nil)
82
83(setq ido-enable-flex-matching t)
84(setq ido-everywhere t)
85(ido-mode 1)
86
87(setq mail-host-address "sif.midgard.yggdrasil")
88(setq user-full-name "Gregor Kleen")
89
90(defun tell-emacsclients-for-buffer-to-die ()
91 "Sends error exit command to every client for the current buffer."
92 (interactive)
93 (dolist (proc server-buffer-clients)
94 (server-send-string proc "-error die")))
95
96(defun kill-buffer-with-special-emacsclient-handling ()
97 "Wrapper around kill-buffer that ensures tell-emacsclients-for-buffer-to-die is on the hooks"
98 (interactive)
99 (add-hook 'kill-buffer-hook 'tell-emacsclients-for-buffer-to-die nil t)
100 (kill-buffer))
101
102;; (global-set-key (kbd "C-x k") 'kill-buffer)
103
104(defun install-emacsclient-wrapped-kill-buffer ()
105 "Installs wrapped kill-buffer with special emacsclient handling.
106Best not to install it unconditionally because the server is not
107necessarily running."
108 (interactive)
109 (global-set-key (kbd "C-x k") 'kill-buffer-with-special-emacsclient-handling))
110
111(add-hook 'server-switch-hook 'install-emacsclient-wrapped-kill-buffer)
diff --git a/accounts/gkleen@sif/firefox-chrome.css b/accounts/gkleen@sif/firefox-chrome.css
new file mode 100644
index 00000000..2d359771
--- /dev/null
+++ b/accounts/gkleen@sif/firefox-chrome.css
@@ -0,0 +1,25 @@
1@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
2
3* {
4 font-size:12px;
5}
6
7#sidebar {
8 min-width:20em !important;
9 max-width:20em !important;
10 border-right:1px solid rgba(30,30,30) !important;
11}
12
13#sidebar-header:nth-last-child(2) > *:last-child {
14 visibility: collapse;
15}
16
17#sidebar-splitter {
18 visibility: collapse;
19}
20
21#toolbar-menubar[inactive="true"] + #TabsToolbar {
22 visibility: collapse !important;
23}
24
25#sidebar-box[sidebarcommand="tabcenter-reborn_ariasuni-sidebar-action"] #sidebar-header { visibility: collapse !important; }
diff --git a/accounts/gkleen@sif/firefox-content.css b/accounts/gkleen@sif/firefox-content.css
new file mode 100644
index 00000000..3ac53a9d
--- /dev/null
+++ b/accounts/gkleen@sif/firefox-content.css
@@ -0,0 +1,12 @@
1@-moz-document url("about:blank") {
2 html {
3 background-color: rgb(40,40,40);
4 }
5}
6
7@-moz-document url(about:newtab) {
8 body, #newtab-customize-overlay {
9 background-color: rgb(40,40,40) !important;
10 color: rgb(166,166,166) !important;
11 }
12}
diff --git a/accounts/gkleen@sif/ssh-hosts.nix b/accounts/gkleen@sif/ssh-hosts.nix
new file mode 100644
index 00000000..ffbd8c00
--- /dev/null
+++ b/accounts/gkleen@sif/ssh-hosts.nix
@@ -0,0 +1,190 @@
1{
2 "git.ymir" =
3 { hostname = "ymir.yggdrasil.li";
4 user = "gitolite";
5 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
6 };
7 "git.yggdrasil.li" =
8 { user = "gitolite";
9 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
10 };
11 "git.rheperire.org" =
12 { user = "gitolite";
13 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
14 };
15 "borg.munin" =
16 { hostname = "u120515.your-storagebox.de";
17 user = "u120515";
18 identityFile = "~/.ssh/borg.munin";
19 port = 23;
20 };
21 "munin" =
22 { hostname = "u120515.your-storagebox.de";
23 user = "u120515";
24 identityFile = "~/.ssh/munin";
25 };
26 "ymir" =
27 { hostname = "ymir.yggdrasil.li";
28 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
29 };
30 "odin" =
31 { hostname = "odin.asgard.yggdrasil";
32 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
33 };
34 "init.odin" =
35 { hostname = "odin.asgard.yggdrasil";
36 user = "root";
37 identityFile = "~/.ssh/rsa.gkleen@hel.midgard.yggdrasil";
38 extraOptions = {
39 StrictHostKeyChecking = "off";
40 };
41 };
42 "heimdallr" =
43 { hostname = "heimdallr.asgard.yggdrasil";
44 user = "root";
45 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
46 };
47 "gitlab2.rz.ifi.lmu.de" =
48 { user = "git";
49 identityFile = "~/.ssh/gkleen@gitlab2.rz.ifi.lmu.de";
50 };
51 "gitlab2.cip.ifi.lmu.de" =
52 { user = "git";
53 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
54 };
55 "hel".hostname = "hel.midgard.yggdrasil";
56 "blackbeard" =
57 { hostname = "blackbeard.tcs.ifi.lmu.de";
58 user = "pi";
59 identityFile = "~/.ssh/blackbeard";
60 };
61 "github.com" =
62 { user = "git";
63 identityFile = "~/.ssh/gkleen@github.com";
64 };
65 "ullr.playat.ch" =
66 { hostname = "ullr.playat.ch";
67 user = "minecraft";
68 identityFile = "~/.ssh/minecraft@ullr.playat.ch";
69 };
70 "ullr" =
71 { hostname = "185.170.112.70";
72 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
73 };
74 "testworx" =
75 { hostname = "testworx.tcs.ifi.lmu.de";
76 user = "root";
77 port = 30363;
78 identityFile = "~/.ssh/testworx";
79 };
80 "remote.cip.ifi.lmu.de".user = "kleen";
81 "uniworx3" =
82 { hostname = "uniworx3.ifi.lmu.de";
83 user = "root";
84 identityFile = "~/.ssh/uni2work";
85 };
86 "uniworx4" =
87 { hostname = "uniworx4.ifi.lmu.de";
88 user = "root";
89 identityFile = "~/.ssh/uni2work";
90 };
91 "uni2workgw" =
92 { hostname = "uni2workgw.ifi.lmu.de";
93 user = "root";
94 identityFile = "~/.ssh/uni2work";
95 };
96 "uniworxdb" =
97 { hostname = "uniworxdb";
98 proxyJump = "uniworx4";
99 user = "root";
100 identityFile = "~/.ssh/uni2work";
101 };
102 "uniworxdb2" =
103 { hostname = "uniworxdb2";
104 proxyJump = "uniworx4";
105 user = "root";
106 identityFile = "~/.ssh/uni2work";
107 };
108 "gate2" =
109 { hostname = "gate2.tcs.ifi.lmu.de";
110 user = "gkleen";
111 identityFile = "~/.ssh/tcs";
112 };
113 "proxy.gate2" =
114 { hostname = "gate2.tcs.ifi.lmu.de";
115 user = "gkleen";
116 identityFile = "~/.ssh/proxy.gkleen@tcs.ifi.lmu.de";
117 dynamicForwards = [ { port = 8118; } ];
118 extraOptions = {
119 ExitOnForwardFailure = "yes";
120 };
121 };
122 "jump.gate2" =
123 { hostname = "gate2.tcs.ifi.lmu.de";
124 user = "gkleen";
125 identityFile = "~/.ssh/proxy.gkleen@tcs.ifi.lmu.de";
126 extraOptions = {
127 ExitOnForwardFailure = "yes";
128 };
129 };
130 "gate" =
131 { hostname = "gate.tcs.ifi.lmu.de";
132 user = "gkleen";
133 identityFile = "~/.ssh/tcs";
134 };
135 "proxy.gate" =
136 { hostname = "gate.tcs.ifi.lmu.de";
137 user = "gkleen";
138 identityFile = "~/.ssh/proxy.gkleen@tcs.ifi.lmu.de";
139 dynamicForwards = [ { port = 8118; } ];
140 extraOptions = {
141 ExitOnForwardFailure = "yes";
142 };
143 };
144 "jump.gate" =
145 { hostname = "gate.tcs.ifi.lmu.de";
146 user = "gkleen";
147 identityFile = "~/.ssh/proxy.gkleen@tcs.ifi.lmu.de";
148 extraOptions = {
149 ExitOnForwardFailure = "yes";
150 };
151 };
152 "oregon" =
153 { hostname = "oregon.tcs.ifi.lmu.de";
154 user = "root";
155 identityFile = "~/.ssh/tcs";
156 };
157 "witbank" =
158 { hostname = "witbank.tcs.ifi.lmu.de";
159 user = "uni2work";
160 identityFile = "~/.ssh/letz";
161 };
162 "git.odin" =
163 { hostname = "odin.asgard.yggdrasil";
164 user = "gitolite";
165 };
166 "notmuch.odin" =
167 { hostname = "odin.asgard.yggdrasil";
168 identityFile = "~/.ssh/notmuch.odin.asgard.yggdrasil";
169 };
170 "status.odin" =
171 { hostname = "odin.asgard.yggdrasil";
172 identityFile = "~/.ssh/status.odin.asgard.yggdrasil";
173 extraOptions.ControlPath = "~/.ssh/status-%r@%n:%p";
174 };
175 "moden" =
176 { hostname = "oristano.tcs.ifi.lmu.de";
177 user = "gkleen";
178 port = 30363;
179 identityFile = "~/.ssh/gkleen@oristano.tcs.ifi.lmu.de";
180 };
181 "ubuntu1804" =
182 { hostname = "192.168.122.30";
183 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
184 forwardAgent = true;
185 };
186 "gitlab.haskell.org" =
187 { hostname = "gitlab.haskell.org";
188 identityFile = "~/.ssh/gkleen@gitlab.haskell.org";
189 };
190}
diff --git a/accounts/gkleen@sif/store.kdbx.lftp b/accounts/gkleen@sif/store.kdbx.lftp
new file mode 100644
index 00000000..4447aded
--- /dev/null
+++ b/accounts/gkleen@sif/store.kdbx.lftp
@@ -0,0 +1,6 @@
1open ftp://gkleen.keepass@yggdrasil.li/
2
3lcd /home/gkleen
4
5mirror -v --only-newer -f store.kdbx
6mirror -v --reverse --only-newer -f store.kdbx \ No newline at end of file
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix
new file mode 100644
index 00000000..a5b71417
--- /dev/null
+++ b/accounts/gkleen@sif/systemd.nix
@@ -0,0 +1,94 @@
1{ pkgs, config, userName, ... }:
2let
3 xmobar = import ./xmobar pkgs.haskellPackages;
4 cfg = config.home-manager.users.${userName};
5in {
6 services = {
7 sync-keepass = {
8 Service = {
9 Type = "oneshot";
10 WorkingDirectory = "~";
11 ExecStart = "${pkgs.lftp}/bin/lftp -f ${./store.kdbx.lftp}";
12 };
13 };
14 urxvtd = {
15 Service = {
16 Type = "simple";
17 WorkingDirectory = "~";
18 ExecStart = "${cfg.programs.urxvt.package}/bin/urxvtd";
19 Restart = "always";
20 };
21 Unit = {
22 After = ["graphical-session.target"];
23 };
24 Install = {
25 WantedBy = ["graphical-session.target"];
26 };
27 };
28 emacs = {
29 Unit = {
30 After = ["graphical-session-pre.target"];
31 };
32 };
33 trayer = {
34 Service = {
35 Type = "simple";
36 WorkingDirectory = "~";
37 ExecStart = "${pkgs.trayer}/bin/trayer --edge top --align right --SetDockType true --SetPartialStrut true --expand true --width 8 --tint 0x000000 --alpha 0 --transparent true --height 32 --monitor primary";
38 Restart = "always";
39 };
40 Install = {
41 WantedBy = ["graphical-session.target"];
42 };
43 };
44 xmobar = {
45 Service = {
46 Type = "simple";
47 WorkingDirectory = "~";
48 ExecStart = "${xmobar}/bin/xmobar";
49 Restart = "always";
50 Environment = "PATH=${pkgs.worktime}/bin:${pkgs.openssh}/bin";
51
52 };
53 Install = {
54 WantedBy = ["graphical-session.target"];
55 };
56 };
57 dunst = {
58 Service = {
59 Restart = "always";
60 };
61 Install = {
62 WantedBy = ["graphical-session.target"];
63 };
64 };
65 xiccd = {
66 Service = {
67 Type = "simple";
68 WorkingDirectory = "~";
69 ExecStart = "${pkgs.xiccd}/bin/xiccd";
70 Restart = "always";
71 };
72 };
73 };
74 timers = {
75 sync-keepass = {
76 Timer = {
77 OnActiveSec = "1m";
78 OnUnitActiveSec = "1m";
79 };
80
81 Install = {
82 WantedBy = ["default.target"];
83 };
84 };
85 };
86 targets = {
87 graphical-session = {
88 Unit = {
89 BindsTo = ["default.target"];
90 After = ["basic.target"];
91 };
92 };
93 };
94}
diff --git a/accounts/gkleen@sif/xmobar/default.nix b/accounts/gkleen@sif/xmobar/default.nix
new file mode 100644
index 00000000..fcac5e60
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/default.nix
@@ -0,0 +1,7 @@
1argumentPackages@{ ... }:
2
3let
4 # defaultPackages = (import ./stackage.nix {});
5 # haskellPackages = defaultPackages // argumentPackages;
6 haskellPackages = argumentPackages;
7in haskellPackages.callPackage ./xmobar-yggdrasil.nix {}
diff --git a/accounts/gkleen@sif/xmobar/nixpkgs.nix b/accounts/gkleen@sif/xmobar/nixpkgs.nix
new file mode 100644
index 00000000..783ede00
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/nixpkgs.nix
@@ -0,0 +1,9 @@
1{ nixpkgs ? import <nixpkgs>
2}:
3
4import ((nixpkgs {}).fetchFromGitHub {
5 owner = "NixOS";
6 repo = "nixpkgs";
7 rev = "10e61bf5be57736035ec7a804cb0bf3d083bf2cf";
8 sha256 = "0fplfm2zx4vk7gs8bdcxnvzkdmpx2w0llqwf8475z9dz9cl132rm";
9})
diff --git a/accounts/gkleen@sif/xmobar/package.yaml b/accounts/gkleen@sif/xmobar/package.yaml
new file mode 100644
index 00000000..b638b6ac
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/package.yaml
@@ -0,0 +1,13 @@
1name: xmobar-yggdrasil
2
3executables:
4 xmobar:
5 dependencies:
6 - base
7 - xmobar
8
9 main: xmobar.hs
10 source-dirs:
11 - .
12
13 ghc-options: -threaded
diff --git a/accounts/gkleen@sif/xmobar/shell.nix b/accounts/gkleen@sif/xmobar/shell.nix
new file mode 100644
index 00000000..18188e78
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/shell.nix
@@ -0,0 +1,28 @@
1{ nixpkgs ? import ./nixpkgs.nix {} }:
2
3let
4 inherit (nixpkgs {}) pkgs;
5 haskellPackages = import ./stackage.nix { inherit nixpkgs; };
6
7 drv = haskellPackages.callPackage ./xmobar-yggdrasil.nix {};
8 override = oldAttrs: {
9 nativeBuildInputs = oldAttrs.nativeBuildInputs ++ (with pkgs; []) ++ (with haskellPackages; [ stack cabal-install cabal2nix ]);
10 shellHook = ''
11 export PROMPT_INFO="${oldAttrs.name}"
12
13 if [ -n "$ZSH_VERSION" ]; then
14 autoload -U +X compinit && compinit
15 autoload -U +X bashcompinit && bashcompinit
16 fi
17 eval "$(stack --bash-completion-script stack)"
18
19 ${oldAttrs.shellHook}
20 '';
21 };
22
23 dummy = pkgs.stdenv.mkDerivation {
24 name = "interactive-xmobar-environment";
25 shellHook = "";
26 };
27in pkgs.stdenv.lib.overrideDerivation dummy override
28 #pkgs.stdenv.lib.overrideDerivation drv.env override
diff --git a/accounts/gkleen@sif/xmobar/stack.nix b/accounts/gkleen@sif/xmobar/stack.nix
new file mode 100644
index 00000000..17a49e04
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/stack.nix
@@ -0,0 +1,17 @@
1{ ghc, nixpkgs ? import ./nixpkgs.nix {} }:
2
3let
4 haskellPackages = import ./stackage.nix { inherit nixpkgs; };
5 inherit (nixpkgs {}) pkgs;
6in pkgs.haskell.lib.buildStackProject {
7 inherit ghc;
8 inherit (haskellPackages) stack;
9 name = "stackenv";
10 buildInputs = (with pkgs;
11 [ xorg.libX11 xorg.libXrandr xorg.libXinerama xorg.libXScrnSaver xorg.libXext xorg.libXft
12 cairo
13 glib
14 ]) ++ (with haskellPackages;
15 [
16 ]);
17}
diff --git a/accounts/gkleen@sif/xmobar/stack.yaml b/accounts/gkleen@sif/xmobar/stack.yaml
new file mode 100644
index 00000000..b8ed1147
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/stack.yaml
@@ -0,0 +1,10 @@
1nix:
2 enable: true
3 shell-file: stack.nix
4
5resolver: lts-13.21
6
7packages:
8 - .
9
10extra-deps: []
diff --git a/accounts/gkleen@sif/xmobar/stack.yaml.lock b/accounts/gkleen@sif/xmobar/stack.yaml.lock
new file mode 100644
index 00000000..fcc2f5f3
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/stack.yaml.lock
@@ -0,0 +1,12 @@
1# This file was autogenerated by Stack.
2# You should not edit this file by hand.
3# For more information, please see the documentation at:
4# https://docs.haskellstack.org/en/stable/lock_files
5
6packages: []
7snapshots:
8- completed:
9 size: 498180
10 url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/13/21.yaml
11 sha256: eff2de19a6d4691ccbf6edc1fba858f1918683047dce0f09adede874bbd2a8f3
12 original: lts-13.21
diff --git a/accounts/gkleen@sif/xmobar/stackage.nix b/accounts/gkleen@sif/xmobar/stackage.nix
new file mode 100644
index 00000000..c162ca2c
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/stackage.nix
@@ -0,0 +1,31 @@
1{ nixpkgs ? import ./nixpkgs.nix {}
2, snapshot ? "lts-13.21"
3}:
4
5let
6 stackage = import (fetchTarball {
7 url = "https://stackage.serokell.io/zb36jsy3r5h4ydz0pnp00g9vk94dvv03-stackage/default.nix.tar.gz";
8 sha256 = "0h6f80gds0ds77y51hhiadh2h2k8njqq8n0gayp729ana9m9agma";
9 });
10
11 overlays =
12 [ stackage."${snapshot}"
13 (self: super: {
14 haskell = super.haskell // {
15 packages = super.haskell.packages // {
16 "${snapshot}" = super.haskell.packages."${snapshot}".override {
17 overrides = hself: hsuper: {
18 zip-archive = self.haskell.lib.overrideCabal hsuper.zip-archive (old: {
19 testToolDepends = old.testToolDepends ++ (with self; [ unzip which ]);
20 });
21 alex = self.haskell.lib.dontCheck hsuper.alex;
22 };
23 };
24 };
25 };
26 }
27 )
28 ];
29
30 inherit (nixpkgs { inherit overlays; }) pkgs;
31in pkgs.haskell.packages."${snapshot}"
diff --git a/accounts/gkleen@sif/xmobar/xmobar-yggdrasil.nix b/accounts/gkleen@sif/xmobar/xmobar-yggdrasil.nix
new file mode 100644
index 00000000..1dfc619b
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/xmobar-yggdrasil.nix
@@ -0,0 +1,13 @@
1{ mkDerivation, base, hpack, stdenv, xmobar }:
2mkDerivation {
3 pname = "xmobar-yggdrasil";
4 version = "0.0.0";
5 src = ./.;
6 isLibrary = false;
7 isExecutable = true;
8 libraryToolDepends = [ hpack ];
9 executableHaskellDepends = [ base xmobar ];
10 preConfigure = "hpack";
11 license = "unknown";
12 hydraPlatforms = stdenv.lib.platforms.none;
13}
diff --git a/accounts/gkleen@sif/xmobar/xmobar.hs b/accounts/gkleen@sif/xmobar/xmobar.hs
new file mode 100644
index 00000000..ed1f6e55
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/xmobar.hs
@@ -0,0 +1,52 @@
1import Xmobar
2
3import Data.List (intercalate)
4
5
6main :: IO ()
7main = xmobar config
8 where
9 config = defaultConfig
10 { font = "xft:FiraCode Nerd Font Mono:style=Regular:pixelsize=21"
11 , position = OnScreen 0 $ TopP 0 307
12 , bgColor = "black"
13 , fgColor = "grey"
14 , overrideRedirect = False
15 , template =
16 let left = intercalate " | "
17 [ "%XMonadWorkspaces%"
18 , "%XMonadLayout%"
19 , "%XMonadTitle%"
20 ]
21 right = intercalate " | "
22 [ {- "%status%"
23 , -} "%battery%"
24 , "%kbd%"
25 , "%worktime%"
26 , "%worktime-today%"
27 , "%time%"
28 , "%date%"
29 ]
30 in left <> "}{" <> right
31 , commands =
32 [ Run $ NamedXPropertyLog "_XMONAD_WORKSPACES" "XMonadWorkspaces"
33 , Run $ NamedXPropertyLog "_XMONAD_LAYOUT" "XMonadLayout"
34 , Run $ NamedXPropertyLog "_XMONAD_TITLE" "XMonadTitle"
35 , Run $ Date "%H:%M" "time" 50
36 , Run $ Date "%a %b %_d" "date" 50
37 , Run $ Com "worktime" [] "worktime" 1500
38 , Run $ Com "worktime" ["today"] "worktime-today" 1500
39 , Run $ Com "ssh" ["status.odin"] "status" 600
40 , Run $ Kbd [("us(dvp)", "dvp")]
41 , Run $ Battery
42 [ "--template", "<watts> <left> (<timeleft>) AC <acstatus>"
43 , "--suffix", "On"
44 , "--Low", "10"
45 , "--High", "80"
46 , "--low", "darkred"
47 , "--normal", "darkorange"
48 , "--high", "darkgreen"
49 , "-p", "3"
50 ] 50
51 ]
52 }
diff --git a/accounts/gkleen@sif/xmonad/.gitignore b/accounts/gkleen@sif/xmonad/.gitignore
new file mode 100644
index 00000000..c11891cd
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/.gitignore
@@ -0,0 +1,4 @@
1**/#*#
2**/.stack-work/
3/stack.yaml.lock
4/*.cabal
diff --git a/accounts/gkleen@sif/xmonad/default.nix b/accounts/gkleen@sif/xmonad/default.nix
new file mode 100644
index 00000000..8790c12f
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/default.nix
@@ -0,0 +1,7 @@
1argumentPackages@{ ... }:
2
3let
4 # defaultPackages = (import ./stackage.nix {});
5 # haskellPackages = defaultPackages // argumentPackages;
6 haskellPackages = argumentPackages;
7in haskellPackages.callPackage ./xmonad-yggdrasil.nix {}
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs
new file mode 100644
index 00000000..e6accdcc
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs
@@ -0,0 +1,127 @@
1{-# LANGUAGE DeriveGeneric, OverloadedLists, OverloadedStrings, ViewPatterns, ExistentialQuantification, MultiWayIf #-}
2
3module XMonad.Mpv
4 ( MpvCommand(..), MpvResponse(..), MpvException(..)
5 , mpv
6 , mpvDir
7 , mpvAll, mpvOne
8 , mpvResponse
9 ) where
10
11import Data.Aeson
12
13import Data.Monoid
14
15import Network.Socket hiding (recv)
16import Network.Socket.ByteString
17
18import qualified Data.ByteString as BS
19import qualified Data.ByteString.Char8 as CBS
20import qualified Data.ByteString.Lazy as LBS
21
22import GHC.Generics (Generic)
23import Data.Typeable (Typeable)
24import Data.String (IsString(..))
25
26import Control.Exception
27
28import System.IO.Temp (getCanonicalTemporaryDirectory)
29
30import Control.Monad
31import Control.Exception (bracket)
32import Control.Monad.IO.Class (MonadIO(..))
33
34import System.FilePath
35import System.Directory (getDirectoryContents)
36
37import Data.List
38import Data.Either
39import Data.Maybe
40
41import Debug.Trace
42
43
44data MpvCommand
45 = forall a. ToJSON a => MpvSetProperty String a
46 | MpvGetProperty String
47data MpvResponse
48 = MpvError String
49 | MpvSuccess (Maybe Value)
50 deriving (Read, Show, Generic, Eq)
51data MpvException = MpvException String
52 | MpvNoValue
53 | MpvNoParse String
54 deriving (Generic, Typeable, Read, Show)
55instance Exception MpvException
56
57
58instance ToJSON MpvCommand where
59 toJSON (MpvSetProperty name val) = Array ["set_property", fromString name, toJSON val]
60 toJSON (MpvGetProperty name) = Array ["get_property", fromString name]
61
62instance FromJSON MpvResponse where
63 parseJSON = withObject "response object" $ \obj -> do
64 mval <- obj .:? "data"
65 err <- obj .: "error"
66
67 let ret
68 | err == "success" = MpvSuccess mval
69 | otherwise = MpvError err
70
71 return ret
72
73mpvSocket :: FilePath -> (Socket -> IO a) -> IO a
74mpvSocket sockPath = withSocketsDo . bracket mkSock close
75 where
76 mkSock = do
77 sock <- socket AF_UNIX Stream defaultProtocol
78 connect sock $ SockAddrUnix (traceId sockPath)
79 return sock
80
81mpvResponse :: FromJSON v => MpvResponse -> IO v
82mpvResponse (MpvError str) = throwIO $ MpvException str
83mpvResponse (MpvSuccess Nothing) = throwIO MpvNoValue
84mpvResponse (MpvSuccess (Just v)) = case fromJSON v of
85 Success v' -> return v'
86 Error str -> throwIO $ MpvNoParse str
87
88mpv :: FilePath -> MpvCommand -> IO MpvResponse
89mpv sockPath cmd = mpvSocket sockPath $ \sock -> do
90 let message = (`BS.append` "\n") . LBS.toStrict . encode $ Object [("command", toJSON cmd)]
91 traceIO $ show message
92 sendAll sock message
93 let recvAll = do
94 prefix <- recv sock 4096
95 if
96 | (prefix', rest) <- CBS.break (== '\n') prefix
97 , not (BS.null rest) -> return prefix'
98 | BS.null prefix -> return prefix
99 | otherwise -> BS.append prefix <$> recvAll
100 response <- recvAll
101 traceIO $ show response
102 either (ioError . userError) return . traceShowId $ eitherDecodeStrict' response
103
104mpvDir :: Exception e => FilePath -> (FilePath -> [(FilePath, Either e MpvResponse)] -> Maybe MpvCommand) -> IO [(FilePath, Either e MpvResponse)]
105mpvDir dir step = do
106 socks <- filter (".sock" `isSuffixOf`) <$> getDirectoryContents dir
107 go [] socks
108 where
109 go acc [] = return acc
110 go acc (sock:socks)
111 | Just cmd <- step sock acc = do
112 res <- try $ mpv (dir </> sock) cmd
113 go ((sock, res) : acc) socks
114 | otherwise =
115 go acc socks
116
117mpvAll :: FilePath -> MpvCommand -> IO [MpvResponse]
118mpvAll dir cmd = do
119 results <- map snd <$> (mpvDir dir (\_ _ -> Just cmd) :: IO [(FilePath, Either SomeException MpvResponse)])
120 mapM (either throwIO return) results
121
122mpvOne :: FilePath -> MpvCommand -> IO (Maybe MpvResponse)
123mpvOne dir cmd = listToMaybe . snd . partitionEithers . map snd <$> (mpvDir dir step :: IO [(FilePath, Either SomeException MpvResponse)])
124 where
125 step _ results
126 | any (isRight . snd) results = Nothing
127 | otherwise = Just cmd
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs
new file mode 100644
index 00000000..1caefae5
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs
@@ -0,0 +1,94 @@
1module XMonad.Prompt.MyPass
2 (
3 -- * Usages
4 -- $usages
5 mkPassPrompt
6 ) where
7
8import Control.Monad (liftM)
9import XMonad.Core
10import XMonad.Prompt ( XPrompt
11 , showXPrompt
12 , commandToComplete
13 , nextCompletion
14 , getNextCompletion
15 , XPConfig
16 , mkXPrompt
17 , searchPredicate)
18import System.Directory (getHomeDirectory)
19import System.FilePath (takeExtension, dropExtension, combine)
20import System.Posix.Env (getEnv)
21import XMonad.Util.Run (runProcessWithInput)
22
23-- $usages
24-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
25--
26-- > import XMonad.Prompt.Pass
27--
28-- Then add a keybinding for 'passPrompt', 'passGeneratePrompt' or 'passRemovePrompt':
29--
30-- > , ((modMask x , xK_p) , passPrompt xpconfig)
31-- > , ((modMask x .|. controlMask, xK_p) , passGeneratePrompt xpconfig)
32-- > , ((modMask x .|. controlMask .|. shiftMask, xK_p), passRemovePrompt xpconfig)
33--
34-- For detailed instructions on:
35--
36-- - editing your key bindings, see "XMonad.Doc.Extending#Editing_key_bindings".
37--
38-- - how to setup the password storage, see <http://git.zx2c4.com/password-store/about/>
39--
40
41type Predicate = String -> String -> Bool
42
43getPassCompl :: [String] -> Predicate -> String -> IO [String]
44getPassCompl compls p s
45 | length s <= minL
46 , all ((> minL) . length) compls = return []
47 | otherwise = do return $ filter (p s) compls
48 where
49 minL = 3
50
51type PromptLabel = String
52
53data Pass = Pass PromptLabel
54
55instance XPrompt Pass where
56 showXPrompt (Pass prompt) = prompt ++ ": "
57 commandToComplete _ c = c
58 nextCompletion _ = getNextCompletion
59
60-- | Default password store folder in $HOME/.password-store
61--
62passwordStoreFolderDefault :: String -> String
63passwordStoreFolderDefault home = combine home ".password-store"
64
65-- | Compute the password store's location.
66-- Use the PASSWORD_STORE_DIR environment variable to set the password store.
67-- If empty, return the password store located in user's home.
68--
69passwordStoreFolder :: IO String
70passwordStoreFolder =
71 getEnv "PASSWORD_STORE_DIR" >>= computePasswordStoreDir
72 where computePasswordStoreDir Nothing = liftM passwordStoreFolderDefault getHomeDirectory
73 computePasswordStoreDir (Just storeDir) = return storeDir
74
75-- | A pass prompt factory
76--
77mkPassPrompt :: PromptLabel -> (String -> X ()) -> XPConfig -> X ()
78mkPassPrompt promptLabel passwordFunction xpconfig = do
79 passwords <- io (passwordStoreFolder >>= getPasswords)
80 mkXPrompt (Pass promptLabel) xpconfig (getPassCompl passwords $ searchPredicate xpconfig) passwordFunction
81
82-- | Retrieve the list of passwords from the password storage 'passwordStoreDir
83getPasswords :: FilePath -> IO [String]
84getPasswords passwordStoreDir = do
85 files <- runProcessWithInput "find" [
86 passwordStoreDir,
87 "-type", "f",
88 "-name", "*.gpg",
89 "-printf", "%P\n"] []
90 return $ map removeGpgExtension $ lines files
91
92removeGpgExtension :: String -> String
93removeGpgExtension file | takeExtension file == ".gpg" = dropExtension file
94 | otherwise = file
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs
new file mode 100644
index 00000000..c268f87d
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs
@@ -0,0 +1,105 @@
1module XMonad.Prompt.MyShell
2 ( Shell (..)
3 , shellPrompt
4 , prompt
5 , safePrompt
6 , unsafePrompt
7 , getCommands
8 , getShellCompl
9 , split
10 ) where
11
12import Codec.Binary.UTF8.String (encodeString)
13import Control.Exception as E
14import Control.Monad (forM)
15import Data.List (isPrefixOf)
16import System.Directory (doesDirectoryExist, getDirectoryContents)
17import System.Environment (getEnv)
18import System.Posix.Files (getFileStatus, isDirectory)
19
20import XMonad hiding (config)
21import XMonad.Prompt
22import XMonad.Util.Run
23
24econst :: Monad m => a -> IOException -> m a
25econst = const . return
26
27data Shell = Shell String
28
29instance XPrompt Shell where
30 showXPrompt (Shell q) = q
31 completionToCommand _ = escape
32
33shellPrompt :: String -> XPConfig -> X ()
34shellPrompt q c = do
35 cmds <- io getCommands
36 mkXPrompt (Shell q) c (getShellCompl cmds) spawn
37
38{- $spawns
39 See safe and unsafeSpawn in "XMonad.Util.Run".
40 prompt is an alias for safePrompt;
41 safePrompt and unsafePrompt work on the same principles, but will use
42 XPrompt to interactively query the user for input; the appearance is
43 set by passing an XPConfig as the second argument. The first argument
44 is the program to be run with the interactive input.
45 You would use these like this:
46
47 > , ((modm, xK_b), safePrompt "firefox" greenXPConfig)
48 > , ((modm .|. shiftMask, xK_c), prompt ("xterm" ++ " -e") greenXPConfig)
49
50 Note that you want to use safePrompt for Firefox input, as Firefox
51 wants URLs, and unsafePrompt for the XTerm example because this allows
52 you to easily start a terminal executing an arbitrary command, like
53 'top'. -}
54
55prompt, unsafePrompt, safePrompt :: String -> FilePath -> XPConfig -> X ()
56prompt = unsafePrompt
57safePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run
58 where run = safeSpawn c . return
59unsafePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run
60 where run a = unsafeSpawn $ c ++ " " ++ a
61
62getShellCompl :: [String] -> String -> IO [String]
63getShellCompl cmds s | s == "" || last s == ' ' = return []
64 | otherwise = do
65 f <- fmap lines $ runProcessWithInput "bash" [] ("compgen -A file -- "
66 ++ s ++ "\n")
67 files <- case f of
68 [x] -> do fs <- getFileStatus (encodeString x)
69 if isDirectory fs then return [x ++ "/"]
70 else return [x]
71 _ -> return f
72 return . uniqSort $ files ++ commandCompletionFunction cmds s
73
74commandCompletionFunction :: [String] -> String -> [String]
75commandCompletionFunction cmds str | '/' `elem` str = []
76 | otherwise = filter (isPrefixOf str) cmds
77
78getCommands :: IO [String]
79getCommands = do
80 p <- getEnv "PATH" `E.catch` econst []
81 let ds = filter (/= "") $ split ':' p
82 es <- forM ds $ \d -> do
83 exists <- doesDirectoryExist d
84 if exists
85 then getDirectoryContents d
86 else return []
87 return . uniqSort . filter ((/= '.') . head) . concat $ es
88
89split :: Eq a => a -> [a] -> [[a]]
90split _ [] = []
91split e l =
92 f : split e (rest ls)
93 where
94 (f,ls) = span (/=e) l
95 rest s | s == [] = []
96 | otherwise = tail s
97
98escape :: String -> String
99escape [] = ""
100escape (x:xs)
101 | isSpecialChar x = '\\' : x : escape xs
102 | otherwise = x : escape xs
103
104isSpecialChar :: Char -> Bool
105isSpecialChar = flip elem " &\\@\"'#?$*()[]{};"
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs
new file mode 100644
index 00000000..c85d0f92
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs
@@ -0,0 +1,243 @@
1module XMonad.Prompt.MySsh
2 ( -- * Usage
3 -- $usage
4 sshPrompt,
5 Ssh,
6 Override (..),
7 mkOverride,
8 Conn (..),
9 moshCmd,
10 moshCmd',
11 sshCmd,
12 inTmux,
13 withEnv
14 ) where
15
16import XMonad
17import XMonad.Util.Run
18import XMonad.Prompt
19
20import System.Directory
21import System.Environment
22import qualified Control.Exception as E
23
24import Control.Monad
25import Data.Maybe
26
27import Text.Parsec.String
28import Text.Parsec
29import Data.Char (isSpace)
30
31econst :: Monad m => a -> E.IOException -> m a
32econst = const . return
33
34-- $usage
35-- 1. In your @~\/.xmonad\/xmonad.hs@:
36--
37-- > import XMonad.Prompt
38-- > import XMonad.Prompt.Ssh
39--
40-- 2. In your keybindings add something like:
41--
42-- > , ((modm .|. controlMask, xK_s), sshPrompt defaultXPConfig)
43--
44-- Keep in mind, that if you want to use the completion you have to
45-- disable the "HashKnownHosts" option in your ssh_config
46--
47-- For detailed instruction on editing the key binding see
48-- "XMonad.Doc.Extending#Editing_key_bindings".
49
50data Override = Override
51 { oUser :: Maybe String
52 , oHost :: String
53 , oPort :: Maybe Int
54 , oCommand :: Conn -> String
55 }
56
57mkOverride = Override { oUser = Nothing, oHost = "", oPort = Nothing, oCommand = sshCmd }
58sshCmd c = concat
59 [ "ssh -t "
60 , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else ""
61 , cHost c
62 , if isJust $ cPort c then " -p " ++ (show $ fromJust $ cPort c) else ""
63 , " -- "
64 , cCommand c
65 ]
66moshCmd c = concat
67 [ "mosh "
68 , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else ""
69 , cHost c
70 , if isJust $ cPort c then " --ssh=\"ssh -p " ++ (show $ fromJust $ cPort c) ++ "\"" else ""
71 , " -- "
72 , cCommand c
73 ]
74moshCmd' p c = concat
75 [ "mosh "
76 , "--server=" ++ p ++ " "
77 , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else ""
78 , cHost c
79 , if isJust $ cPort c then " --ssh=\"ssh -p " ++ (show $ fromJust $ cPort c) ++ "\"" else ""
80 , " -- "
81 , cCommand c
82 ]
83inTmux c
84 | null $ cCommand c = c { cCommand = "tmux new-session" }
85 | otherwise = c { cCommand = "tmux new-session \"" ++ (cCommand c) ++ "\"" }
86withEnv :: [(String, String)] -> Conn -> Conn
87withEnv envs c = c { cCommand = "env" ++ (concat $ map (\(n, v) -> ' ' : (n ++ "=" ++ v)) envs) ++ " " ++ (cCommand c) }
88
89data Conn = Conn
90 { cUser :: Maybe String
91 , cHost :: String
92 , cPort :: Maybe Int
93 , cCommand :: String
94 } deriving (Eq, Show, Read)
95
96data Ssh = Ssh
97
98instance XPrompt Ssh where
99 showXPrompt Ssh = "SSH to: "
100 commandToComplete _ c = c
101 nextCompletion _ = getNextCompletion
102
103toConn :: String -> Maybe Conn
104toConn = toConn' . parse connParser "(unknown)"
105toConn' :: Either ParseError Conn -> Maybe Conn
106toConn' (Left _) = Nothing
107toConn' (Right a) = Just a
108
109connParser :: Parser Conn
110connParser = do
111 spaces
112 user' <- optionMaybe $ try $ do
113 str <- many1 $ satisfy (\c -> (not $ isSpace c) && (c /= '@'))
114 char '@'
115 return str
116 host' <- many1 $ satisfy (not . isSpace)
117 port' <- optionMaybe $ try $ do
118 space
119 string "-p"
120 spaces
121 int <- many1 digit
122 (space >> return ()) <|> eof
123 return $ (read int :: Int)
124 spaces
125 command' <- many anyChar
126 eof
127 return $ Conn
128 { cHost = host'
129 , cUser = user'
130 , cPort = port'
131 , cCommand = command'
132 }
133
134sshPrompt :: [Override] -> XPConfig -> X ()
135sshPrompt o c = do
136 sc <- io sshComplList
137 mkXPrompt Ssh c (mkComplFunFromList sc) $ ssh o
138
139ssh :: [Override] -> String -> X ()
140ssh overrides str = do
141 let cmd = applyOverrides overrides str
142 liftIO $ putStr "SSH Command: "
143 liftIO $ putStrLn cmd
144 runInTerm "" cmd
145
146applyOverrides :: [Override] -> String -> String
147applyOverrides [] str = "ssh " ++ str
148applyOverrides (o:os) str = case (applyOverride o str) of
149 Just str -> str
150 Nothing -> applyOverrides os str
151
152applyOverride :: Override -> String -> Maybe String
153applyOverride o str = let
154 conn = toConn str
155 in
156 if isNothing conn then Nothing else
157 case (fromJust conn) `matches` o of
158 True -> Just $ (oCommand o) (fromJust conn)
159 False -> Nothing
160
161matches :: Conn -> Override -> Bool
162a `matches` b = and
163 [ justBool (cUser a) (oUser b) (==)
164 , (cHost a) == (oHost b)
165 , justBool (cPort a) (oPort b) (==)
166 ]
167
168justBool :: Eq a => Maybe a -> Maybe a -> (a -> a -> Bool) -> Bool
169justBool Nothing _ _ = True
170justBool _ Nothing _ = True
171justBool (Just a) (Just b) match = a `match` b
172
173sshComplList :: IO [String]
174sshComplList = uniqSort `fmap` liftM2 (++) sshComplListLocal sshComplListGlobal
175
176sshComplListLocal :: IO [String]
177sshComplListLocal = do
178 h <- getEnv "HOME"
179 s1 <- sshComplListFile $ h ++ "/.ssh/known_hosts"
180 s2 <- sshComplListConf $ h ++ "/.ssh/config"
181 return $ s1 ++ s2
182
183sshComplListGlobal :: IO [String]
184sshComplListGlobal = do
185 env <- getEnv "SSH_KNOWN_HOSTS" `E.catch` econst "/nonexistent"
186 fs <- mapM fileExists [ env
187 , "/usr/local/etc/ssh/ssh_known_hosts"
188 , "/usr/local/etc/ssh_known_hosts"
189 , "/etc/ssh/ssh_known_hosts"
190 , "/etc/ssh_known_hosts"
191 ]
192 case catMaybes fs of
193 [] -> return []
194 (f:_) -> sshComplListFile' f
195
196sshComplListFile :: String -> IO [String]
197sshComplListFile kh = do
198 f <- doesFileExist kh
199 if f then sshComplListFile' kh
200 else return []
201
202sshComplListFile' :: String -> IO [String]
203sshComplListFile' kh = do
204 l <- readFile kh
205 return $ map (getWithPort . takeWhile (/= ',') . concat . take 1 . words)
206 $ filter nonComment
207 $ lines l
208
209sshComplListConf :: String -> IO [String]
210sshComplListConf kh = do
211 f <- doesFileExist kh
212 if f then sshComplListConf' kh
213 else return []
214
215sshComplListConf' :: String -> IO [String]
216sshComplListConf' kh = do
217 l <- readFile kh
218 return $ map (!!1)
219 $ filter isHost
220 $ map words
221 $ lines l
222 where
223 isHost ws = take 1 ws == ["Host"] && length ws > 1
224
225fileExists :: String -> IO (Maybe String)
226fileExists kh = do
227 f <- doesFileExist kh
228 if f then return $ Just kh
229 else return Nothing
230
231nonComment :: String -> Bool
232nonComment [] = False
233nonComment ('#':_) = False
234nonComment ('|':_) = False -- hashed, undecodeable
235nonComment _ = True
236
237getWithPort :: String -> String
238getWithPort ('[':str) = host ++ " -p " ++ port
239 where (host,p) = break (==']') str
240 port = case p of
241 ']':':':x -> x
242 _ -> "22"
243getWithPort str = str
diff --git a/accounts/gkleen@sif/xmonad/package.yaml b/accounts/gkleen@sif/xmonad/package.yaml
new file mode 100644
index 00000000..48de1a53
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/package.yaml
@@ -0,0 +1,30 @@
1name: xmonad-yggdrasil
2
3executables:
4 xmonad:
5 dependencies:
6 - base
7 - xmonad
8 - xmonad-contrib
9 - aeson
10 - bytestring
11 - text
12 - temporary
13 - filepath
14 - directory
15 - network
16 - unix
17 - utf8-string
18 - parsec
19 - process
20 - mtl
21 - X11
22 - transformers
23 - containers
24 - hostname
25 - libnotify
26
27 main: xmonad.hs
28 source-dirs:
29 - .
30 - lib
diff --git a/accounts/gkleen@sif/xmonad/stack.nix b/accounts/gkleen@sif/xmonad/stack.nix
new file mode 100644
index 00000000..17a49e04
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/stack.nix
@@ -0,0 +1,17 @@
1{ ghc, nixpkgs ? import ./nixpkgs.nix {} }:
2
3let
4 haskellPackages = import ./stackage.nix { inherit nixpkgs; };
5 inherit (nixpkgs {}) pkgs;
6in pkgs.haskell.lib.buildStackProject {
7 inherit ghc;
8 inherit (haskellPackages) stack;
9 name = "stackenv";
10 buildInputs = (with pkgs;
11 [ xorg.libX11 xorg.libXrandr xorg.libXinerama xorg.libXScrnSaver xorg.libXext xorg.libXft
12 cairo
13 glib
14 ]) ++ (with haskellPackages;
15 [
16 ]);
17}
diff --git a/accounts/gkleen@sif/xmonad/stack.yaml b/accounts/gkleen@sif/xmonad/stack.yaml
new file mode 100644
index 00000000..b8ed1147
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/stack.yaml
@@ -0,0 +1,10 @@
1nix:
2 enable: true
3 shell-file: stack.nix
4
5resolver: lts-13.21
6
7packages:
8 - .
9
10extra-deps: []
diff --git a/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix b/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix
new file mode 100644
index 00000000..e8786d35
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix
@@ -0,0 +1,21 @@
1{ mkDerivation, aeson, base, bytestring, containers, directory
2, filepath, hostname, hpack, mtl, network, parsec, process, stdenv
3, temporary, transformers, unix, utf8-string, X11, xmonad
4, xmonad-contrib, libnotify
5}:
6mkDerivation {
7 pname = "xmonad-yggdrasil";
8 version = "0.0.0";
9 src = ./.;
10 isLibrary = false;
11 isExecutable = true;
12 libraryToolDepends = [ hpack ];
13 executableHaskellDepends = [
14 aeson base bytestring containers directory filepath hostname mtl
15 network parsec process temporary transformers unix utf8-string X11
16 xmonad xmonad-contrib libnotify
17 ];
18 preConfigure = "hpack";
19 license = "unknown";
20 hydraPlatforms = stdenv.lib.platforms.none;
21}
diff --git a/accounts/gkleen@sif/xmonad/xmonad.hs b/accounts/gkleen@sif/xmonad/xmonad.hs
new file mode 100644
index 00000000..f3a59f34
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/xmonad.hs
@@ -0,0 +1,898 @@
1{-# LANGUAGE TupleSections, ViewPatterns, OverloadedStrings, FlexibleInstances, UndecidableInstances, MultiWayIf #-}
2
3import XMonad
4import XMonad.Hooks.DynamicLog
5import XMonad.Hooks.ManageDocks
6import XMonad.Util.Run
7import XMonad.Util.Loggers
8import XMonad.Util.EZConfig(additionalKeys)
9import System.IO
10import System.IO.Error
11import System.Environment
12import Data.Map (Map)
13import qualified Data.Map as Map
14import qualified XMonad.StackSet as W
15import System.Exit
16import Control.Monad.State (get)
17-- import XMonad.Layout.Spiral
18import Data.Ratio
19import Data.List
20import Data.Char
21import Data.Maybe (fromMaybe, listToMaybe, maybeToList, catMaybes, isJust)
22import XMonad.Layout.Tabbed
23import XMonad.Prompt
24import XMonad.Prompt.Input
25import XMonad.Util.Scratchpad
26import XMonad.Util.NamedScratchpad
27import Control.Monad (sequence, liftM, liftM2, join, void)
28import XMonad.Util.WorkspaceCompare
29import XMonad.Layout.NoBorders
30import XMonad.Layout.PerWorkspace
31import XMonad.Layout.SimplestFloat
32import XMonad.Layout.Renamed
33import XMonad.Layout.Reflect
34import XMonad.Layout.OnHost
35import XMonad.Layout.Combo
36import XMonad.Layout.ComboP
37import XMonad.Layout.Column
38import XMonad.Layout.TwoPane
39import XMonad.Layout.IfMax
40import XMonad.Layout.LayoutBuilder
41import XMonad.Layout.WindowNavigation
42import XMonad.Layout.Dwindle
43import XMonad.Layout.TrackFloating
44import System.Process
45import System.Directory (removeFile)
46import System.Posix.Files
47import System.FilePath ((</>))
48import Control.Concurrent
49import System.Posix.Process (getProcessID)
50import System.IO.Error
51import System.IO
52import XMonad.Hooks.ManageHelpers hiding (CW)
53import XMonad.Hooks.UrgencyHook as U
54import XMonad.Hooks.EwmhDesktops
55import XMonad.StackSet (RationalRect (..))
56import Control.Monad (when, filterM, (<=<))
57import Graphics.X11.ExtraTypes.XF86
58import XMonad.Util.Cursor
59import XMonad.Actions.Warp
60import XMonad.Actions.FloatKeys
61import XMonad.Util.SpawnOnce
62import System.Directory
63import System.FilePath
64import XMonad.Actions.CopyWindow
65import XMonad.Hooks.ServerMode
66import XMonad.Actions.Commands
67import XMonad.Actions.CycleWS
68import XMonad.Actions.RotSlaves
69import XMonad.Actions.UpdatePointer
70import XMonad.Prompt.Window
71import Data.IORef
72import Data.Monoid
73import Data.String
74import qualified XMonad.Actions.PhysicalScreens as P
75
76import XMonad.Layout.IM
77
78import XMonad.Prompt.MyShell
79import XMonad.Prompt.MyPass
80import XMonad.Prompt.MySsh
81
82import XMonad.Mpv
83
84import Network.HostName
85
86import Control.Applicative ((<$>))
87
88import Libnotify as Notify hiding (appName)
89import qualified Libnotify as Notify (appName)
90import Libnotify (Notification)
91-- import System.Information.Battery
92
93import Data.Int (Int32)
94
95import System.Posix.Process
96import System.Posix.Signals
97import System.Posix.IO as Posix
98import Control.Exception
99
100import System.IO.Unsafe
101
102import Control.Monad.Trans.Class
103import Control.Monad.Trans.Maybe
104
105import Data.Fixed (Micro)
106
107import qualified Data.Text as Text
108import Data.Ord (comparing)
109import Debug.Trace
110
111instance MonadIO m => IsString (m ()) where
112 fromString = spawn
113
114type KeyMap = Map (ButtonMask, KeySym) (X ())
115
116data Host = Host
117 { hName :: HostName
118 , hManageHook :: ManageHook
119 , hWsp :: Integer -> WorkspaceId
120 , hCoWsp :: String -> Maybe WorkspaceId
121 , hKeysMod :: XConfig Layout -> (KeyMap -> KeyMap)
122 , hScreens :: [P.PhysicalScreen]
123 , hKbLayouts :: [(String, Maybe String)]
124 , hCmds :: X [(String, X ())]
125 , hKeyUpKeys :: XConfig Layout -> KeyMap
126 }
127
128defaultHost = Host { hName = "unkown"
129 , hManageHook = composeOne [manageScratchTerm]
130 , hWsp = show
131 , hCoWsp = const Nothing
132 , hKeysMod = const id
133 , hScreens = [0,1..]
134 , hKbLayouts = [ ("us", Just "dvp")
135 , ("us", Nothing)
136 , ("de", Nothing)
137 ]
138 , hCmds = return []
139 , hKeyUpKeys = const Map.empty
140 }
141
142browser :: String
143browser = "env MOZ_USE_XINPUT2=1 firefox"
144
145hostFromName :: HostName -> Host
146hostFromName h@("vali") = defaultHost { hName = h
147 , hManageHook = composeOne $ catMaybes [ Just manageScratchTerm
148 , assign "web" $ className =? ".dwb-wrapped"
149 , assign "web" $ className =? "Chromium"
150 , assign "work" $ className =? "Emacs"
151 , assign "media" $ className =? "mpv"
152 ]
153 , hWsp = hWsp
154 , hCoWsp = hCoWsp
155 , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_d, ["chromium", "chromium $(xclip -o)"])
156 , (xK_e, ["emacsclient -c"])
157 ])
158 `Map.union`
159 ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), scratchpadSpawnActionCustom $ (XMonad.terminal conf) ++ " -name scratchpad -title scratchpad -e tmux new-session -D -s scratch")
160 ] )
161 , hScreens = hScreens defaultHost
162 }
163 where
164 workspaceNames = Map.fromList [ (2, "web")
165 , (3, "work")
166 , (10, "media")
167 ]
168 hWsp = wspFromMap workspaceNames
169 hCoWsp = coWspFromMap workspaceNames
170 assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp
171hostFromName h
172 | h `elem` ["hel", "sif"] = defaultHost { hName = h
173 , hManageHook = namedScratchpadManageHook scratchpads <+> composeOne (catMaybes
174 [ assign "mpv" $ className =? "mpv"
175 , assign "mpv" $ (className =? "URxvt" <&&> title =? "irssi")
176 , assign "mpv" $ (className =? "URxvt" <&&> resource =? "presentation")
177 , assign "mpv" $ stringProperty "WM_WINDOW_ROLE" =? "presentation"
178 , assign "read" $ stringProperty "WM_WINDOW_ROLE" =? "presenter"
179 , assign "mpv" $ className =? "factorio"
180 , assign "web" $ className =? "chromium-browser"
181 , assign "web" $ className =? "Google-chrome"
182 , assign "work" $ (appName =? "Devtools" <&&> className =? "Firefox")
183 , assign "work" $ className =? "Postman"
184 , assign "web" $ className =? "Firefox"
185 , assign "comm" $ (className =? "URxvt" <&&> resource =? "comm")
186 , assign "comm" $ (className =? "Emacs" <&&> title =? "Mail")
187 , assign "comm" $ className =? "Zulip"
188 , assign "comm" $ className =? "Discord"
189 , assign "media" $ (className =? "URxvt" <&&> resource =? "media")
190 , assign "media" $ (className =? "URxvt" <&&> title =? "streamlink")
191 , assign "media" $ (className =? "URxvt" <&&> title =? "mpv")
192 , assign "monitor" $ (className =? "URxvt" <&&> fmap ("monitor" `isInfixOf`) title)
193 , assign "monitor" $ className =? "Grafana"
194 , Just $ (className =? "URxvt" <&&> resource =? "htop") -?> centerFloat
195 , Just $ (className =? "Scp-dbus-service.py") -?> centerFloat
196 , Just $ (className =? "URxvt" <&&> resource =? "log") -?> centerFloat
197 , assign "work" $ className =? "URxvt"
198 , assign' ["work", "uni"] $ (className =? "Emacs" <&&> appName /=? "Edit_with_Emacs_FRAME")
199 , assign' ["work", "uni"] $ className =? "jetbrains-idea-ce"
200 , assign "read" $ className =? "llpp"
201 , assign "read" $ className =? "Evince"
202 , assign "read" $ fmap ("zathura" `isInfixOf`) title
203 , assign "read" $ className =? "MuPDF"
204 , assign "read" $ className =? "Xournal"
205 , assign "read" $ appName =? "com-trollworks-gcs-app-GCS"
206 , assign "read" $ appName =? "Tux.py"
207 , assign "read" $ className =? "Gnucash"
208 , assign "comm" $ className =? "Skype"
209 , assign "comm" $ className =? "Daily"
210 , assign "comm" $ className =? "Pidgin"
211 , assign "comm" $ className =? "Slack"
212 , Just $ (resource =? "xvkbd") -?> doRectFloat $ RationalRect (1 % 8) (3 % 8) (6 % 8) (4 % 8)
213 , Just $ (stringProperty "_NET_WM_WINDOW_TYPE" =? "_NET_WM_WINDOW_TYPE_DIALOG") -?> doFloat
214 , Just $ (className =? "Dunst") -?> doFloat
215 , Just $ (className =? "Xmessage") -?> doCenterFloat
216 , Just $ (className =? "Nm-openconnect-auth-dialog") -?> centerFloat
217 , Just $ (className =? "Pinentry") -?> doCenterFloat
218 , Just $ (className =? "pinentry") -?> doCenterFloat
219 , Just $ (appName =? "Edit_with_Emacs_FRAME") -?> centerFloat
220 , Just $ (stringProperty "WM_WINDOW_ROLE" =? "GtkFileChooseDialog") -?> centerFloatSmall
221 , Just $ (className =? "Nvidia-settings") -?> doCenterFloat
222 , Just $ fmap ("Minetest" `isInfixOf`) title -?> doIgnore
223 , Just $ fmap ("Automachef" `isInfixOf`) title -?> doIgnore
224 ])
225 , hWsp = hWsp
226 , hCoWsp = hCoWsp
227 , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_e, ["emacsclient -c"])
228 , (xK_d, [fromString browser, fromString $ browser ++ " $(xclip -o)", fromString $ "notmuch-links"])
229 , (xK_f, ["urxvtc -name comm -title Feeds -e mosh odin -- tmux new-session -ADs comm"])
230 , (xK_c, [ inputPrompt xPConfig "dc" ?+ dc ])
231 , (xK_g, ["pidgin"])
232 , (xK_s, ["skype"])
233 -- , (xK_p, [mkPassPrompt "Type password" pwType xPConfig, mkPassPrompt "Show password" pwShow xPConfig, mkPassPrompt "Copy password" pwClip xPConfig])
234 , (xK_w, ["sudo rewacom"])
235 , (xK_y, [ "tmux new-window -dt media /var/media/link.hs $(xclip -o)"
236 , "urxvtc -name media -e tmuxp load /var/media"
237 ])
238 , (xK_l, [ "tmux new-window -dt media mpv $(xclip -o)"
239 , "tmux new-window -dt media streamlink --retry-open 10 $(xclip -o)"
240 ])
241 , (xK_m, [ "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch)'"
242 , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch-mua-new-mail)'"
243 , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e \"(browse-url-mail \"$(xclip -o)\")\""
244 ])
245 , (xK_Return, ["keynav start,windowzoom", "keynav start"])
246 , (xK_t, [inputPrompt xPConfig "fuzzytime timer" ?+ fuzzytime, fuzzytime "unset", work_fuzzytime])
247 , (xK_a, [inputPrompt xPConfig "adjmix" ?+ adjmix])
248 , (xK_s, [ inputPromptWithCompl xPConfig "start synergy" synergyCompl ?+ synergyStart
249 , inputPromptWithCompl xPConfig "stop synergy" synergyCompl ?+ synergyStop
250 ])
251 , (xK_h, [ "urxvtc -name htop -e htop"
252 , "urxvtc -name log -e journalctl -xef"
253 ])
254 , (xK_x, [ "autorandr -c"
255 , "autorandr -fl default"
256 ])
257 , (xK_z, [ "zulip -- --force-device-scale-factor=2"
258 ])
259 ])
260 `Map.union`
261 ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), namedScratchpadAction scratchpads "term")
262 , ((XMonad.modMask conf .|. controlMask, xK_a), namedScratchpadAction scratchpads "pavucontrol")
263 , ((XMonad.modMask conf .|. controlMask, xK_w), namedScratchpadAction scratchpads "alarms")
264 , ((XMonad.modMask conf .|. controlMask, xK_b), namedScratchpadAction scratchpads "blueman")
265 , ((XMonad.modMask conf .|. controlMask, xK_p), namedScratchpadAction scratchpads "keepassxc")
266 , ((XMonad.modMask conf .|. controlMask, xK_t), namedScratchpadAction scratchpads "toggl")
267 , ((XMonad.modMask conf .|. controlMask, xK_e), namedScratchpadAction scratchpads "emacs")
268 , ((XMonad.modMask conf .|. controlMask, xK_m), namedScratchpadAction scratchpads "calendar")
269 , ((XMonad.modMask conf .|. controlMask, xK_f), namedScratchpadAction scratchpads "music")
270 , ((XMonad.modMask conf .|. mod1Mask, xK_Up), rotate U)
271 , ((XMonad.modMask conf .|. mod1Mask, xK_Down), rotate D)
272 , ((XMonad.modMask conf .|. mod1Mask, xK_Left), rotate L)
273 , ((XMonad.modMask conf .|. mod1Mask, xK_Right), rotate R)
274 -- , ((XMonad.modMask conf .|. shiftMask, xK_a), startMute "hel")
275 ] )
276 , hKeyUpKeys = \conf -> Map.fromList [ -- ((XMonad.modMask conf .|. shiftMask, xK_a), stopMute "hel")
277 ]
278 , hScreens = hScreens defaultHost
279 , hCmds = return [ ("prev-workspace", prevWS)
280 , ("next-workspace", nextWS)
281 , ("prev-window", rotAllDown)
282 , ("next-window", rotAllUp)
283 , ("banish", banishScreen LowerRight)
284 , ("update-gpg-tty", safeSpawn "gpg-connect-agent" ["UPDATESTARTUPTTY", "/bye"])
285 , ("rescreen", rescreen)
286 , ("repanel", do
287 spawn "nm-applet"
288 spawn "blueman-applet"
289 spawn "pasystray"
290 spawn "kdeconnect-indicator"
291 spawn "dunst -print"
292 spawn "udiskie"
293 spawn "autocutsel -s PRIMARY"
294 spawn "autocutsel -s CLIPBOARD"
295 )
296 , ("pause", mediaMpv $ MpvSetProperty "pause" True)
297 , ("unpause", mediaMpv $ MpvSetProperty "pause" False)
298 , ("exit", io $ exitWith ExitSuccess)
299 ]
300 }
301 where
302 withGdkScale act = void . xfork $ setEnv "GDK_SCALE" "2" >> act
303 workspaceNames = Map.fromList [ (1, "comm")
304 , (2, "web")
305 , (3, "work")
306 , (4, "read")
307 , (5, "monitor")
308 , (6, "uni")
309 , (9, "media")
310 , (10, "mpv")
311 ]
312 scratchpads = [ NS "term" "urxvtc -name scratchpad -title scratchpad -e tmux new-session -AD -s scratch" (resource =? "scratchpad") centerFloat
313 , NS "pavucontrol" "pavucontrol" (resource =? "pavucontrol") centerFloat
314 , NS "alarms" "alarm-clock-applet" (className =? "Alarm-clock-applet" <&&> title =? "Alarms") centerFloat
315 , NS "blueman" "blueman-manager" (className =? ".blueman-manager-wrapped") centerFloat
316 , NS "keepassxc" "keepassxc" (className =? "KeePassXC") centerFloat
317 , NS "toggl" "toggldesktop" (className =? "Toggl Desktop") centerFloat
318 , NS "calendar" "minetime -- --force-device-scale-factor=1.6" (className =? "MineTime") centerFloat
319 , NS "emacs" "emacsclient -c -F \"'(title . \\\"Scratchpad\\\")\"" (className =? "Emacs" <&&> title =? "Scratchpad") centerFloat
320 , NS "music" "google-play-music-desktop-player --force-device-scale-factor=1.6" (className =? "Google Play Music Desktop Player") centerFloat
321 ]
322 centerFloat = customFloating $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8)
323 centerFloatSmall = customFloating $ RationalRect (1 % 4) (1 % 4) (1 % 2) (1 % 2)
324 hWsp = wspFromMap workspaceNames
325 hCoWsp = coWspFromMap workspaceNames
326 assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp
327 assign' :: [String] -> Query Bool -> Maybe MaybeManageHook
328 assign' wsps test = do
329 wsIds <- mapM hCoWsp wsps
330 return $ test -?> go wsIds
331 where
332 go :: [WorkspaceId] -> ManageHook
333 go wsps = do
334 visWsps <- liftX $ (\wset -> W.tag . W.workspace <$> W.current wset : W.visible wset) <$> gets windowset
335 case (filter (`elem` visWsps) wsps, wsps) of
336 (wsp : _, _) -> doShift wsp
337 (_, wsp : _) -> doShift wsp
338 ([], []) -> return mempty
339 rotate rot = do
340 safeSpawn "xrandr" ["--output", "eDP-1", "--rotate", xrandrDir]
341 mapM_ rotTouch touchscreens
342 where
343 xrandrDir = case rot of
344 U -> "normal"
345 L -> "left"
346 R -> "right"
347 D -> "inverted"
348 matrix = case rot of
349 U -> [ [ 1, 0, 0]
350 , [ 0, 1, 0]
351 , [ 0, 0, 1]
352 ]
353 L -> [ [ 0, -1, 1]
354 , [ 1, 0, 0]
355 , [ 0, 0, 1]
356 ]
357 R -> [ [ 0, 1, 0]
358 , [-1, 0, 1]
359 , [ 0, 0, 1]
360 ]
361 D -> [ [-1, 0, 1]
362 , [ 0, -1, 1]
363 , [ 0, 0, 1]
364 ]
365 touchscreens = [ "Wacom Co.,Ltd. Pen and multitouch sensor Finger touch"
366 , "Wacom Co.,Ltd. Pen and multitouch sensor Pen stylus"
367 , "Wacom Co.,Ltd. Pen and multitouch sensor Pen eraser"
368 ]
369 rotTouch screen = do
370 safeSpawn "xinput" $ ["set-prop", screen, "Coordinate Transformation Matrix"] ++ map (\n -> show n ++ ",") (concat matrix)
371 safeSpawn "xinput" ["map-to-output", screen, "eDP-1"]
372 withPw f label = io . void . forkProcess $ do
373 uninstallSignalHandlers
374 void $ createSession
375 (dropWhileEnd isSpace -> pw) <- readCreateProcess (proc "pass" ["show", label]) ""
376 void $ f pw
377 pwType :: String -> X ()
378 pwType = withPw $ readCreateProcess (proc "xdotool" ["type", "--clearmodifiers", "--file", "-"])
379 pwClip label = safeSpawn "pass" ["show", "--clip", label]
380 pwShow :: String -> X ()
381 pwShow = withPw $ \pw -> do
382 xmessage <- fromMaybe "xmessage" <$> liftIO (lookupEnv "XMONAD_XMESSAGE")
383 readCreateProcess (proc xmessage ["-file", "-"]) pw
384 fuzzytime str = safeSpawn "fuzzytime" $ "timer" : words str
385 work_fuzzytime = io . void . forkProcess $ do
386 readCreateProcess (proc "worktime" []) "" >>= safeSpawn "fuzzytime" . ("timer" : ) . pure
387 adjmix str = safeSpawn "adjmix" $ words str
388 dc expr = void . xfork $ do
389 result <- readProcess "dc" [] $ expr ++ "f"
390 let
391 (first : rest) = filter (not . null) $ lines result
392 notification = Notify.summary first <> Notify.body (unlines rest) <> Notify.timeout Infinite <> Notify.urgency Normal <> Notify.appName "dc"
393 void $ Notify.display notification
394 synergyCompl = mkComplFunFromList' ["mathw86"]
395 synergyStart host = safeSpawn "systemctl" ["--user", "start", "synergy-rtunnel@" ++ host ++ ".service"]
396 synergyStop host = safeSpawn "systemctl" ["--user", "stop", "synergy-rtunnel@" ++ host ++ ".service"]
397
398hostFromName _ = defaultHost
399
400-- muteRef :: IORef (Maybe (String, Notification))
401-- {-# NOINLINE muteRef #-}
402-- muteRef = unsafePerformIO $ newIORef Nothing
403
404-- startMute, stopMute :: String -> X ()
405-- startMute sink = liftIO $ do
406-- muted <- isJust <$> readIORef muteRef
407-- when (not muted) $ do
408-- let
409-- notification = Notify.summary "Muted" <> Notify.timeout Infinite <> Notify.urgency Normal
410-- level = "0.0dB"
411-- -- level <- runProcessWithInput "ssh" ["bragi", "cat", "/dev/shm/mix/" ++ sink ++ "/level"] ""
412-- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", "0"]
413-- hPutStrLn stderr "Mute"
414-- writeIORef muteRef . Just . (level, ) =<< Notify.display notification
415-- stopMute sink = liftIO $ do
416-- let
417-- unmute (Just (level, notification)) = do
418-- hPutStrLn stderr "Unmute"
419-- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", level]
420-- Notify.close notification
421-- unmute Nothing = return ()
422-- muted <- isJust <$> readIORef muteRef
423-- when muted . join . atomicModifyIORef muteRef $ (Nothing, ) . unmute
424
425wspFromMap workspaceNames = \i -> case Map.lookup i workspaceNames of
426 Just str -> show i ++ " " ++ str
427 Nothing -> show i
428
429coWspFromMap workspaceNames = \str -> case filter ((== str) . snd) $ Map.toList workspaceNames of
430 [] -> Nothing
431 [(i, _)] -> Just $ wspFromMap workspaceNames i
432 _ -> Nothing
433
434spawnModifiers = [0, controlMask, shiftMask .|. controlMask]
435spawnBindings :: XConfig layout -> (KeySym, [X ()]) -> [((KeyMask, KeySym), X ())]
436spawnBindings conf (k, cmds) = zipWith (\m cmd -> ((modm .|. mod1Mask .|. m, k), cmd)) spawnModifiers cmds
437 where
438 modm = XMonad.modMask conf
439
440manageScratchTerm = (resource =? "scratchpad" <||> resource =? "keysetup") -?> doRectFloat $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8)
441
442tabbedLayout t = renamed [Replace "Tabbed"] $ reflectHoriz $ t CustomShrink $ tabbedTheme
443tabbedLayoutHoriz t = renamed [Replace "Tabbed Horiz"] $ reflectVert $ t CustomShrink $ tabbedTheme
444tabbedTheme = def
445 { activeColor = "black"
446 , inactiveColor = "black"
447 , urgentColor = "black"
448 , activeBorderColor = "grey"
449 , inactiveBorderColor = "#202020"
450 , urgentBorderColor = "#bb0000"
451 , activeTextColor = "grey"
452 , inactiveTextColor = "grey"
453 , urgentTextColor = "grey"
454 , decoHeight = 32
455 , fontName = "xft:Fira Mono for Powerline:style=Medium:pixelsize=22.5"
456 }
457
458main :: IO ()
459main = do
460 arguments <- either (const []) id <$> tryIOError getArgs
461 case arguments of
462 ["--command", s] -> do
463 d <- openDisplay ""
464 rw <- rootWindow d $ defaultScreen d
465 a <- internAtom d "XMONAD_COMMAND" False
466 m <- internAtom d s False
467 allocaXEvent $ \e -> do
468 setEventType e clientMessage
469 setClientMessageEvent e rw a 32 m currentTime
470 sendEvent d rw False structureNotifyMask e
471 sync d False
472 _ -> do
473 -- batteryMon <- xfork $ monitorBattery Nothing Nothing
474 hostname <- getHostName
475 let
476 host = hostFromName hostname
477 setEnv "HOST" hostname
478 let myConfig = withHostUrgency . ewmh $ docks def
479 { manageHook = hManageHook host
480 , terminal = "urxvtc"
481 , layoutHook = smartBorders . avoidStruts $ windowNavigation layout'
482 , logHook = do
483 dynamicLogString xmobarPP' >>= writeProps
484 updatePointer (99 % 100, 98 % 100) (0, 0)
485 , modMask = mod4Mask
486 , keys = \conf -> hKeysMod host conf $ myKeys' conf host
487 , workspaces = take (length numKeys) $ map wsp [1..]
488 , startupHook = setDefaultCursor xC_left_ptr
489 , normalBorderColor = "#202020"
490 , focusedBorderColor = "grey"
491 , handleEventHook = fullscreenEventHook <+> (serverModeEventHookCmd' $ hCmds host) <+> keyUpEventHook
492 }
493 writeProps str = do
494 let encodeCChar = map $ fromIntegral . fromEnum
495 atoms = [ "_XMONAD_WORKSPACES"
496 , "_XMONAD_LAYOUT"
497 , "_XMONAD_TITLE"
498 ]
499 (flip mapM_) (zip atoms (lines str)) $ \(atom', content) -> do
500 ustring <- getAtom "UTF8_STRING"
501 atom <- getAtom atom'
502 withDisplay $ \dpy -> io $ do
503 root <- rootWindow dpy $ defaultScreen dpy
504 changeProperty8 dpy root atom ustring propModeReplace $ encodeCChar content
505 sync dpy True
506 wsp = hWsp host
507 -- We can´t define per-host layout modifiers because we lack dependent types
508 layout' = onHost "skadhi" ( onWorkspace (wsp 1) (Full ||| withIM (1%5) (Title "Buddy List") tabbedLayout') $
509 onWorkspace (wsp 10) Full $
510 onWorkspace (wsp 2) (Full ||| tabbedLayout') $
511 onWorkspace (wsp 5) tabbedLayout' $
512 onWorkspace (wsp 8) (withIM (1%5) (Title "Friends") tabbedLayout') $
513 defaultLayouts
514 ) $
515 onHost "vali" ( onWorkspace (wsp 2) (Full ||| tabbedLayout' ||| combineTwo (TwoPane 0.01 0.57) Full tabbedLayout') $
516 onWorkspace (wsp 3) workLayouts $
517 defaultLayouts
518 ) $
519 onHost "hel" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $
520 onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
521 onWorkspace (wsp 3) workLayouts $
522 onWorkspace (wsp 6) workLayouts $
523 onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $
524 onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
525 onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $
526 defaultLayouts
527 ) $
528 onHost "sif" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $
529 onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
530 onWorkspace (wsp 3) workLayouts $
531 onWorkspace (wsp 6) workLayouts $
532 onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $
533 onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
534 onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $
535 defaultLayouts
536 ) $
537 defaultLayouts
538 -- tabbedLayout''' = renamed [Replace "Tabbed'"] $ IfMax 1 (noBorders Full) (tabbedLayout tabbedBottomAlways)
539 tabbedLayout''' = tabbedLayout tabbedBottom
540 tabbedLayout' = tabbedLayout tabbedBottomAlways
541 tabbedLayoutHoriz' = tabbedLayoutHoriz tabbedLeftAlways
542 defaultLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW 1 (5 % 100) ||| tabbedLayout' ||| Full
543 -- workLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW (2 % 1) (5 % 100) ||| tabbedLayout' ||| Full
544 workLayouts = tabbedLayout' ||| (renamed [Replace "Combined"] $ combineTwoP (TwoPane (1 % 100) (1891 % 2560)) tabbedLayout''' (Column 1.6) (ClassName "Postman" `Or` ClassName "Emacs" `Or` ClassName "jetbrains-idea-ce" `Or` (Resource "Devtools" `And` ClassName "Firefox"))) ||| Full ||| Dwindle R CW 1 (5 % 100)
545 sqrtTwo = approxRational (sqrt 2) (1 / 2560)
546 xmobarPP' = xmobarPP { ppTitle = shorten 80
547 , ppSort = (liftM2 (.)) getSortByIndex $ return scratchpadFilterOutWorkspace
548 , ppUrgent = wrap "(" ")" . xmobarColor "red" ""
549 , ppHiddenNoWindows = xmobarColor "#202020" "" . wrap "(" ")"
550 , ppVisible = wrap "(" ")" . xmobarColor "yellow" ""
551 , ppCurrent = wrap "(" ")" . xmobarColor "green" ""
552 , ppHidden = wrap "(" ")"
553 , ppWsSep = " "
554 , ppSep = "\n"
555 }
556 withHostUrgency = case hostname of
557 "hel" -> withUrgencyHookC urgencyHook' $ urgencyConfig { suppressWhen = U.Never, remindWhen = Dont }
558 "sif" -> withUrgencyHookC urgencyHook' $ urgencyConfig { suppressWhen = U.Never, remindWhen = Dont }
559 _ -> id
560 urgencyHook' window = do
561 runQuery ((resource =? "comm" <||> resource =? "Pidgin" <||> className =? "Gajim" <||> className =? "Skype") --> safeSpawn "thinklight" ["Blink", "100"]) window
562 urgencyHook (BorderUrgencyHook { urgencyBorderColor = "#bb0000" }) window
563 shutdown :: SomeException -> IO a
564 shutdown e = do
565 let pids = [ -- batteryMon
566 ]
567 mapM_ (signalProcess sigTERM) pids
568 mapM_ (getProcessStatus False False) pids
569 throw e
570 keyUpEventHook :: Event -> X All
571 keyUpEventHook event = handle event >> return (All True)
572 where
573 handle (KeyEvent { ev_event_type = t, ev_state = m, ev_keycode = code })
574 | t == keyRelease = withDisplay $ \dpy -> do
575 s <- io $ keycodeToKeysym dpy code 0
576 mClean <- cleanMask m
577 ks <- asks $ hKeyUpKeys host . config
578 userCodeDef () $ whenJust (Map.lookup (mClean, s) ks) id
579 | otherwise = return ()
580 handle _ = return ()
581 handle shutdown $ launch myConfig
582
583secs :: Int -> Int
584secs = (* 1000000)
585
586-- monitorBattery :: Maybe BatteryContext -> Maybe Notification -> IO ()
587-- monitorBattery Nothing n = do
588-- ctx <- batteryContextNew
589-- case ctx of
590-- Nothing -> threadDelay (secs 10) >> monitorBattery Nothing n
591-- Just _ -> monitorBattery ctx n
592-- monitorBattery ctx@(Just ctx') n = do
593-- batInfo <- getBatteryInfo ctx'
594-- case batInfo of
595-- Nothing -> threadDelay (secs 1) >> monitorBattery ctx n
596-- Just batInfo -> do
597-- let n'
598-- | batteryState batInfo == BatteryStateDischarging
599-- , timeLeft <= 1200
600-- , timeLeft > 0 = Just $ summary "Discharging" <> hint "value" percentage <> urgency u <> body (duz timeLeft ++ "left")
601-- | otherwise = Nothing
602-- u
603-- | timeLeft <= 600 = Critical
604-- | timeLeft <= 1800 = Normal
605-- | otherwise = Low
606-- timeLeft = batteryTimeToEmpty batInfo
607-- percentage :: Int32
608-- percentage = round $ batteryPercentage batInfo
609-- ts = [("s", 60), ("m", 60), ("h", 24), ("d", 365), ("y", 1)]
610-- duz ms = ss
611-- where (ss, _) = foldl (\(ss, x) (s, y) -> ((if rem x y > 0 then show (rem x y) ++ s ++ " " else "") ++ ss , quot x y)) ("", ms) ts
612-- case n' of
613-- Just n' -> Notify.display (maybe mempty reuse n <> Notify.appName "monitorBattery" <> n') >>= (\n -> threadDelay (secs 2) >> monitorBattery ctx (Just n))
614-- Nothing -> threadDelay (secs 30) >> monitorBattery ctx n
615
616disableTouchpad, disableTrackpoint, enableTrackpoint, enableTouchpad :: X ()
617enableTouchpad = safeSpawn "xinput" ["enable", "SynPS/2 Synaptics TouchPad"]
618disableTouchpad = safeSpawn "xinput" ["disable", "SynPS/2 Synaptics TouchPad"]
619enableTrackpoint = safeSpawn "xinput" ["enable", "TPPS/2 IBM TrackPoint"]
620disableTrackpoint = safeSpawn "xinput" ["disable", "TPPS/2 IBM TrackPoint"]
621
622isDisabled :: String -> X Bool
623isDisabled str = do
624 out <- runProcessWithInput "xinput" ["list", str] ""
625 return $ "disabled" `isInfixOf` out
626
627
628spawnKeychain :: X ()
629spawnKeychain = do
630 home <- liftIO getHomeDirectory
631 let keys = (map ((home </>) . (".ssh/" ++)) ["id", "id-rsa"]) ++ ["6B13AA67"]
632 liftIO (maybe (return ()) (setEnv "SSH_ASKPASS") =<< findAskpass)
633 safeSpawn "keychain" . (["--agents", "gpg,ssh"] ++)=<< liftIO (filterM doesFileExist keys)
634 where
635 findAskpass = filter `liftM` readFile "/etc/zshrc"
636 filter = listToMaybe . catMaybes . map (stripPrefix "export SSH_ASKPASS=") . lines
637
638assimilateKeychain :: X ()
639assimilateKeychain = liftIO $ assimilateKeychain' >> return ()
640assimilateKeychain' = tryIOError $ do
641 -- pid <- getProcessID
642 -- tmpDir <- lookupEnv "TMPDIR"
643 -- let tmpDir' = fromMaybe "/tmp" tmpDir
644 -- tmpFile = tmpDir' </> "xmonad-keychain" ++ (show pid) ++ ".env"
645 env <- runProcessWithInput "sh" ["-c", "eval $(keychain --eval --noask --agents gpg,ssh); env"] "" -- > " ++ tmpFile] ""
646 -- env <- readFile tmpFile
647 let envVars = Map.fromList $ map (\(k, v) -> (k, tail' v)) $ map (span (/= '=')) $ envLines
648 envVars' = Map.filterWithKey (\k _ -> k `elem` transfer) envVars
649 transfer = ["SSH_AUTH_SOCK", "SSH_AGENT_PID", "GPG_AGENT_INFO"]
650 envLines = filter (elem '=') $ lines env :: [String]
651 sequence $ map (\(k, c) -> setEnv k c) $ Map.toList envVars'
652 -- removeFile tmpFile
653 where
654 tail' [] = []
655 tail' (x:xs) = xs
656
657
658numKeys = [xK_parenleft, xK_parenright, xK_braceright, xK_plus, xK_braceleft, xK_bracketright, xK_bracketleft, xK_exclam, xK_equal, xK_asterisk]
659
660instance Shrinker CustomShrink where
661 shrinkIt _ "" = [""]
662 shrinkIt s cs
663 | length cs >= 4 = cs : shrinkIt s ((reverse . drop 4 . reverse $ cs) ++ "...")
664 | otherwise = cs : shrinkIt s (init cs)
665
666xPConfig :: XPConfig
667xPConfig = def
668 { font = "xft:Fira Mono for Powerline:style=Medium:pixelsize=22.5"
669 , height = 32
670 , bgColor = "black"
671 , fgColor = "grey"
672 , fgHLight = "green"
673 , bgHLight = "black"
674 , borderColor = "grey"
675 , searchPredicate = (\needle haystack -> all (`isInfixOf` map toLower haystack) . map (map toLower) $ words needle)
676 , position = Top
677 }
678
679sshOverrides = map (\h -> mkOverride { oHost = h, oCommand = moshCmd . inTmux } )
680 [
681 "odin", "odin.asgard.yggdrasil"
682 , "ymir", "ymir.yggdrasil.li", "ymir.niflheim.yggdrasil"
683 , "surtr", "yggdrasil.li", "surtr.yggdrasil.li", "praseodym.org", "surtr.praseodym.org", "surtr.141.li", "141.li"
684 , "vindler", "vindler.alfheim.yggdrasil"
685 , "ullr"
686 , "heimdallr", "heimdallr.asgard.yggdrasil"
687 , "testworx"
688 ]
689 ++
690 map (\h -> mkOverride { oHost = h, oCommand = moshCmd' "/run/current-system/sw/bin/mosh-server" . withEnv [("TERM", "xterm")] . inTmux} )
691 [ "bragi", "bragi.asgard.yggdrasil"
692 ]
693 ++
694 map (\h -> mkOverride { oHost = h, oCommand = sshCmd . withEnv [("TERM", "xterm")] . inTmux } )
695 [ "remote.cip.ifi.lmu.de"
696 , "uniworx3", "uniworx4", "uniworxdb"
697 ]
698
699backlight :: (Rational -> Rational) -> X ()
700backlight f = void . xfork . liftIO $ do
701 [ _device
702 , _class
703 , read . Text.unpack -> currentBright
704 , _currentPercentage
705 , read . Text.unpack -> maximumBright
706 ] <- Text.splitOn "," . Text.pack <$> readProcess "brightnessctl" ["-m"] ""
707 let current = currentBright % maximumBright
708 new' = f current * fromIntegral maximumBright
709 new :: Integer
710 new | floor new' < 0 = 0
711 | ceiling new' > maximumBright = maximumBright
712 | new' >= maximumBright % 2 = ceiling new'
713 | otherwise = floor new'
714 callProcess "brightnessctl" ["-m", "s", show new]
715
716cycleThrough :: [Rational] -> (Rational -> Rational)
717cycleThrough opts current = fromMaybe currentOpt $ listToMaybe next'
718 where currentOpt = minimumBy (comparing $ abs . subtract current) opts
719 (_, _ : next') = break (== currentOpt) opts
720
721cycleKbLayout :: [(String, Maybe String)] -> X ()
722cycleKbLayout [] = return ()
723cycleKbLayout layouts = liftIO $ do
724 next <- (getNext . extract) `liftM` runProcessWithInput "setxkbmap" ["-query"] ""
725 let
726 args = case next of
727 (l, Just v) -> [l, v]
728 (l, Nothing) -> [l]
729 safeSpawn "setxkbmap" args
730 where
731 extract :: String -> Maybe (String, Maybe String)
732 extract str = listToMaybe $ do
733 ["layout:", l] <- str'
734 [(l, Just v) | ["variant:", v] <- str'] ++ pure (l, Nothing)
735 where
736 str' = map words $ lines str
737 getNext :: Maybe (String, Maybe String) -> (String, Maybe String)
738 getNext = maybe (head layouts) getNext'
739 getNext' x = case elemIndex x layouts of
740 Nothing -> getNext Nothing
741 Just i -> layouts !! ((i + 1) `mod` length layouts)
742
743mpvAll' :: MpvCommand -> IO [MpvResponse]
744mpvAll' = mpvAll "/var/media/.mpv-ipc"
745
746mpvOne' :: MpvCommand -> IO (Maybe MpvResponse)
747mpvOne' = mpvOne "/var/media/.mpv-ipc"
748
749mediaMpv :: MpvCommand -> X ()
750mediaMpv cmd = void . xfork $ print =<< mpvAll' cmd
751
752mediaMpvTogglePause :: X ()
753mediaMpvTogglePause = void . xfork $ do
754 paused <- mapM mpvResponse <=< mpvAll' $ MpvGetProperty "pause"
755 if
756 | and paused -> print <=< mpvAll' $ MpvSetProperty "pause" False
757 | otherwise -> print <=< mpvOne' $ MpvSetProperty "pause" True
758
759myKeys' conf host = Map.fromList $
760 -- launch a terminal
761 [ ((modm, xK_Return), spawn $ (XMonad.terminal conf) ++ " -e tmux")
762 , ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
763
764 -- launch dmenu
765 --, ((modm, xK_d ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
766 , ((modm, xK_d ), shellPrompt "Run: " xPConfig)
767 , ((modm .|. shiftMask, xK_d ), prompt "Run in Terminal: " ("urxvtc" ++ " -e") xPConfig)
768 , ((modm, xK_at ), sshPrompt sshOverrides xPConfig)
769
770 -- close focused window
771 , ((modm .|. shiftMask, xK_q ), kill)
772 , ((modm .|. controlMask .|. shiftMask, xK_q ), spawn "xkill")
773
774 -- Rotate through the available layout algorithms
775 , ((modm, xK_space ), sendMessage NextLayout)
776
777 -- Reset the layouts on the current workspace to default
778 , ((modm .|. controlMask, xK_r ), (setLayout $ XMonad.layoutHook conf) >> refresh)
779
780 -- Resize viewed windows to the correct size
781 , ((modm, xK_r ), refresh)
782
783 -- Move focus to the next window
784 , ((modm, xK_t ), windows W.focusDown)
785
786 -- Move focus to the previous window
787 , ((modm, xK_n ), windows W.focusUp )
788
789 -- Move focus to the master window
790 , ((modm, xK_m ), windows W.focusMaster )
791
792 -- Swap the focused window and the master window
793 , ((modm .|. shiftMask, xK_m ), windows W.swapMaster)
794
795 -- Swap the focused window with the next window
796 , ((modm .|. shiftMask, xK_t ), windows W.swapDown )
797
798 -- Swap the focused window with the previous window
799 , ((modm .|. shiftMask, xK_n ), windows W.swapUp )
800
801 -- Swap the focused window with the previous window
802 , ((modm .|. shiftMask .|. controlMask, xK_m), sendMessage SwapWindow)
803
804 , ((modm, xK_Right), sendMessage $ Go R)
805 , ((modm, xK_Left ), sendMessage $ Go L)
806 , ((modm, xK_Up ), sendMessage $ Go U)
807 , ((modm, xK_Down ), sendMessage $ Go D)
808 , ((modm .|. shiftMask , xK_Right), sendMessage $ Move R)
809 , ((modm .|. shiftMask , xK_Left ), sendMessage $ Move L)
810 , ((modm .|. shiftMask , xK_Up ), sendMessage $ Move U)
811 , ((modm .|. shiftMask , xK_Down ), sendMessage $ Move D)
812 -- , ((modm .|. controlMask, xK_Right), withFocused $ keysMoveWindow (10, 0))
813 -- , ((modm .|. controlMask, xK_Left ), withFocused $ keysMoveWindow (-10, 0))
814 -- , ((modm .|. controlMask, xK_Up ), withFocused $ keysMoveWindow (0, -10))
815 -- , ((modm .|. controlMask, xK_Down ), withFocused $ keysMoveWindow (0, 10))
816 -- Shrink the master area
817 , ((modm, xK_h ), sendMessage Shrink)
818
819 -- Expand the master area
820 , ((modm, xK_s ), sendMessage Expand)
821
822 -- Push window back into tiling
823 , ((modm .|. shiftMask, xK_space ), withFocused $ windows . W.sink)
824 , ((modm, xK_BackSpace), focusUrgent)
825 , ((modm .|. shiftMask, xK_BackSpace), clearUrgents)
826
827 -- Increment the number of windows in the master area
828 , ((modm , xK_comma ), sendMessage (IncMasterN 1))
829
830 -- Deincrement the number of windows in the master area
831 , ((modm , xK_period), sendMessage (IncMasterN (-1)))
832
833 , ((0, xF86XK_AudioRaiseVolume), safeSpawn "pulseaudio-ctl" ["up", "2"])
834 , ((0, xF86XK_AudioLowerVolume), safeSpawn "pulseaudio-ctl" ["down", "2"])
835 , ((0, xF86XK_AudioMute), safeSpawn "pulseaudio-ctl" ["mute"])
836 , ((0, xF86XK_AudioPause), mediaMpv $ MpvSetProperty "pause" False)
837 , ((0, {-xF86XK_AudioMicMute-} 269025202), safeSpawn "pulseaudio-ctl" ["mute-input"])
838 , ((0, xF86XK_AudioPlay), mediaMpvTogglePause)
839 , ((modm .|. mod1Mask, xK_space), mediaMpvTogglePause)
840
841 , ((0, xF86XK_MonBrightnessDown), backlight (subtract 5))
842 , ((0, xF86XK_MonBrightnessUp), backlight (+ 5))
843
844 , ((modm , xK_Escape), cycleKbLayout (hKbLayouts host))
845 , ((modm .|. controlMask, xK_Escape), safeSpawn "setxkbmap" $ fst (head $ hKbLayouts host) : maybeToList (snd . head $ hKbLayouts host))
846
847 -- Toggle the status bar gap
848 -- Use this binding with avoidStruts from Hooks.ManageDocks.
849 -- See also the statusBar function from Hooks.DynamicLog.
850 --
851 , ((modm , xK_b ), sendMessage ToggleStruts)
852
853 , ((modm .|. shiftMask, xK_p ), safeSpawn "playerctl" ["-a", "pause"])
854
855 -- Quit xmonad
856 , ((modm .|. shiftMask, xK_e ), io (exitWith ExitSuccess))
857
858 -- Restart xmonad
859 -- , ((modm .|. shiftMask .|. controlMask, xK_r ), void . xfork $ recompile False >>= flip when (safeSpawn "xmonad" ["--restart"]))
860 , ((modm .|. shiftMask, xK_r ), void . liftIO $ executeFile "xmonad" True [] Nothing)
861 , ((modm .|. shiftMask, xK_l ), void . xfork $ do
862 sessId <- getEnv "XDG_SESSION_ID"
863 safeSpawn "loginctl" ["lock-session", sessId]
864 )
865 , ((modm .|. shiftMask, xK_s ), safeSpawn "systemctl" ["suspend"])
866 , ((modm .|. shiftMask, xK_h ), safeSpawn "systemctl" ["hibernate"])
867 , ((modm .|. shiftMask, xK_b ), backlight $ cycleThrough [1, 3 % 4, 1 % 2, 1 % 4, 1 % 10, 1 % 100, 0]
868 )
869 , ((modm .|. shiftMask .|. controlMask, xK_b), backlight $ cycleThrough [0, 1 % 100, 1 % 10, 1 % 4, 1 % 2, 3 % 4, 1]
870 )
871 , ((modm, xK_v ), windows copyToAll) -- @@ Make focused window always visible
872 , ((modm .|. shiftMask, xK_v ), killAllOtherCopies) -- @@ Toggle window state back
873 , ((modm .|. shiftMask, xK_g ), windowPrompt xPConfig Goto wsWindows)
874 , ((modm .|. shiftMask .|. controlMask, xK_g ), windowPrompt xPConfig Bring allWindows)
875 ]
876 ++
877
878 --
879 -- mod-[1..9], Switch to workspace N
880 --
881 -- mod-[1..9], Switch to workspace N
882 -- mod-shift-[1..9], Move client to workspace N
883 --
884 [((m .|. modm, k), windows $ f i)
885 | (i, k) <- zip (XMonad.workspaces conf) $ numKeys
886 , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]
887 ]
888 ++
889 [((m .|. modm .|. controlMask, k), void . runMaybeT $
890 MaybeT (P.getScreen def i) >>= MaybeT . screenWorkspace >>= lift . windows . f
891 )
892 | (i, k) <- zip (hScreens host) [xK_g, xK_c, xK_r, xK_l]
893 , (f, m) <- [(W.view, 0), (W.shift, shiftMask)]
894 ]
895 where
896 modm = XMonad.modMask conf
897
898
diff --git a/accounts/gkleen@sif/xresources.nix b/accounts/gkleen@sif/xresources.nix
new file mode 100644
index 00000000..3bd9af2c
--- /dev/null
+++ b/accounts/gkleen@sif/xresources.nix
@@ -0,0 +1,46 @@
1{
2 "Xft.dpi" = 282;
3 "Xft.autohint" = false;
4 "Xft.lcdfilter" = "lcddefault";
5 "Xft.hintstyle" = "hintfull";
6 "Xft.hinting" = true;
7 "Xft.antialias" = true;
8 "Xft.rgba" = "rgb";
9
10 # special
11 "*.foreground" = "#d9d9d9";
12 "*.background" = "#000000";
13 "*.cursorColor" = "#d9d9d9";
14
15 # black
16 "*.color0" = "#000000";
17 "*.color8" = "#757a80";
18
19 # red
20 "*.color1" = "#bf4949";
21 "*.color9" = "#e66e6e";
22
23 # green
24 "*.color2" = "#9fb346";
25 "*.color10" = "#cbd676";
26
27 # yellow
28 "*.color3" = "#e69650";
29 "*.color11" = "#ffa74f";
30
31 # blue
32 "*.color4" = "#759fbf";
33 "*.color12" = "#98b8d9";
34
35 # magenta
36 "*.color5" = "#9b79a6";
37 "*.color13" = "#ceadd9";
38
39 # cyan
40 "*.color6" = "#79a69b";
41 "*.color14" = "#a3d9ce";
42
43 # white
44 "*.color7" = "#d9d9d9";
45 "*.color15" = "#ffffff";
46} \ No newline at end of file
diff --git a/accounts/gkleen@sif/zshrc b/accounts/gkleen@sif/zshrc
new file mode 100644
index 00000000..f095ee21
--- /dev/null
+++ b/accounts/gkleen@sif/zshrc
@@ -0,0 +1,403 @@
1filebin() {
2 basePath=/srv/www/files
3 ssh ymir find "${basePath}" -type f -print0 \
4 | while IFS= read -r -d $'\0' p; do
5 printf "https://f.141.li/%s\n" "${p#${basePath}/}"
6 done
7}
8
9genmail() {
10 local baseName=""
11 local target=""
12 if [[ ${#@} -ge 1 ]]; then
13 target=${1}
14 shift
15 fi
16
17 if [[ ${#@} -ge 1 ]]; then
18 baseName=$(pwgen ${@})
19 else
20 baseName=$(pwgen -v -A -0 16 1)
21 fi
22 baseName=$(tr -cd $'[:alnum:]!#$%&*+-/=?^_{|}~.' <<<${baseName})
23 address=${baseName}@141.li
24 insertAddr() {
25 echo "${baseName} gkleen" | ssh ymir tee -a /srv/mail/spm 1>/dev/null \
26 }
27
28 printf "%s\n" ${address}
29 read -q 'cont?Continue [y/N]? ' || return
30
31 insertAddr
32}
33
34s() {
35 dir=$(pwd)
36 [[ ${#@} -ge 1 ]] && dir=$1
37
38 shellFile=$(findNix ${@})
39 [[ ${#@} -ge 1 ]] && shift
40
41 typeset -a cmd
42 if [[ -d ${dir}/.nix-gc-roots ]]; then
43 cmd=(persistent-nix-shell ${shellFile} ${S_EXTRA_ARGS} ${@})
44 else
45 cmd=(nix-shell ${shellFile} ${S_EXTRA_ARGS} ${@})
46 fi
47
48 if [[ -n "${S_SYSTEMD}" ]]; then
49 systemd-run --user --slice=development.slice --collect -E PATH=${PATH} -p WorkingDirectory=${dir} -- ${cmd}
50 else
51 (
52 cd ${dir}
53
54 exec ${cmd}
55 )
56 fi
57}
58
59sz() {
60 typeset -a S_EXTRA_ARGS
61 S_EXTRA_ARGS=(--run "env __ETC_ZSHENV_SOURCED=1 zsh") s ${@}
62}
63st() {
64 typeset -a S_EXTRA_ARGS
65 S_EXTRA_ARGS=(--run "tmux new-session env __ETC_ZSHENV_SOURCED=1 zsh") s ${@}
66}
67stt() {
68 typeset -a S_EXTRA_ARGS
69 S_SYSTEMD=true S_EXTRA_ARGS=(--run "urxvt -e tmux -S .tmux.sock new-session env __ETC_ZSHENV_SOURCED=1 zsh") s ${@}
70}
71se() {
72 typeset -a S_EXTRA_ARGS
73 S_SYSTEMD=true S_EXTRA_ARGS=(--run "emacs") s ${@}
74}
75
76findNix() {
77 if [[ $#@ -eq 0 ]]; then
78 findNix $(pwd)
79 elif [[ -f "$1" ]]; then
80 print ${1:a}
81 elif [[ -d "$1" && -f "$1"/shell.nix ]]; then
82 print ${1:a}/shell.nix
83 elif [[ -d "$1" && -f "$1"/default.nix ]]; then
84 print ${1:a}/default.nix
85 elif [[ -d "$1" && "$1" != "/" ]]; then
86 findNix ${1:h}
87 else
88 printf "Traversed directories to ‘/’ and found no shell specification\n" >&2
89 return 1
90 fi
91}
92
93dir() {
94 curlArchive=false
95 templateArchive=""
96 repoUrl=""
97 nixShell=""
98 findNix=false
99 dir=""
100 forceShell=false
101 wormhole=false
102 gitWorktree=""
103 notmuchMsg=""
104 quickserve=false
105
106 while getopts ':t:a:s:Sd:ir:wqg:n:' arg; do
107 case $arg in
108 "t") ;;
109 "a")
110 if [[ ${OPTARG} =~ "://" ]]; then
111 templateArchive=${OPTARG}
112 curlArchive=true
113 else
114 templateArchive=${OPTARG:a}
115 fi
116 ;;
117 "s") nixShell=${OPTARG:a} ;;
118 "S") findNix=true ;;
119 "d") dir=${OPTARG} ;;
120 "i") forceShell=true ;;
121 "r") repoUrl=${OPTARG} ;;
122 "w") wormhole=true ;;
123 "g") gitWorktree=${OPTARG} ;;
124 "n") notmuchMsg=${OPTARG} ;;
125 "q") quickserve=true ;;
126 *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;;
127 esac
128 done
129
130 shift $((OPTIND - 1))
131
132 if [[ -z ${dir} && ${#@} -ge 1 ]]; then
133 dir=${1}
134 shift
135 fi
136
137 [[ -n ${dir} ]] || return 2;
138
139 if [[ ! -e ${dir} ]]; then
140 if [[ -z "${gitWorktree}" ]]; then
141 mkdir -vp ${dir}
142 else
143 git -C ${gitWorktree} worktree add ${dir}
144 fi
145 else
146 gitWorktree=""
147 fi
148
149 (
150 cd ${dir}
151 export dir;
152
153 ${findNix} && { nixShell=$(findNix) || return $? }
154
155 [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} .
156
157 if [[ -n ${templateArchive} ]]; then
158 (
159 archiveFile=""
160 cleanup() {
161 [[ -n "${archiveFile}" ]] && rm -fv ${archiveFile}
162 }
163 trap cleanup EXIT
164
165 if ${curlArchive}; then
166 archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}")
167
168 curl -L -o ${archiveFile} ${templateArchive}
169
170 templateArchive=${archiveFile}
171 fi
172
173 case $(file --brief --mime-type ${templateArchive}) in
174 application/zip) unzip ${templateArchive} ;;
175 *) tar -xvaf ${templateArchive} ;;
176 esac
177 )
178 fi
179
180
181 if [[ -n ${notmuchMsg} ]]; then
182 getMimeTypes() {
183 nix-shell -p mailcap --run "find \${buildInputs} -path '*/etc/mime.types' | head -n 1 | xargs -- cat"
184 }
185
186 typeset -a messages
187 messages=(${(z)$(notmuch search --output=messages ${notmuchMsg})})
188
189 for message (${messages}); do
190 typeset -A notmuchAtts
191 notmuchAtts=()
192
193 while IFS= read -r -d $'\n' line; do
194 [[ ${line} =~ '(attachment|part)\{ ID: ([0-9]+)' ]] || continue
195 attId=${match[2]}
196
197 [[ ${line} =~ 'Content-type: multipart/' ]] && continue
198
199 fName="part_${attId}"
200 [[ ${line} =~ 'Filename: (([^,]|,[^ ])+)' ]] && fName=${match[1]}
201
202 if [[ ${#messages} -gt 1 ]]; then
203 fName="${message}/${fName}"
204 fi
205
206 fExt="${fName:e}"
207 [[ -n "${fExt}" ]] && fName="${fName:r}"
208
209 if [[ -z "${fExt}" && ${line} =~ 'Content-type: (([^,]|,[^ ])+)$' ]]; then
210 fExt=$(getMimeTypes | grep ${match[1]}$'\t' | head -n 1 | awk '{ print $2; }')
211 fi
212
213 mkdir -p ${fName:h}
214 if [[ -n "${fExt}" ]]; then
215 fName=$(mktemp -p . "${fName}.XXXXXX.${fExt}")
216 else
217 fName=$(mktemp -p . "${fName}.XXXXXX")
218 fi
219
220 notmuchAtts[${attId}]=${fName}
221 done <<(notmuch show --decrypt=false -- ${message} | tr -d $'\f')
222
223 for attId fName in ${(kv)notmuchAtts}; do
224 [[ -d ${fName:h} ]] || mkdir -p ${fName:h}
225 printf "#%d → ‘%s’\n" "${attId}" "${fName}" >&2
226
227 notmuch show --decrypt=false --part=${attId} -- ${message} | pv -W -D 2 -i 0.1 >${fName}
228 done
229 done
230 fi
231
232
233 ${wormhole} && wormhole receive
234
235 if ${quickserve}; then
236 quickserve --root . --upload . --show-hidden --tar gz
237 fi
238
239
240 if [[ ${#@} -eq 0 ]] || ${forceShell}; then
241 if [[ ${#@} -gt 0 ]]; then
242 if [[ -z ${nixShell} ]]; then
243 ${@}
244 else
245 nix-shell ${nixShell} --run "${@}"
246 fi
247 fi
248
249 cd $(pwd) # Needed for mounting to work
250
251 isSingleDir() {
252 typeset -a contents
253 contents=(*(N) .*(N))
254
255 if [[ ${#contents} -eq 1 && -d ${contents[1]} ]]; then
256 print ${contents[1]}
257 return 0
258 else
259 return 1
260 fi
261 }
262 while d=$(isSingleDir); do cd ${d}; done
263
264
265 if [[ -z ${nixShell} ]]; then
266 exec -- zsh
267 else
268 exec -- nix-shell ${nixShell} --run zsh
269 fi
270 else
271 if [[ -z ${nixShell} ]]; then
272 exec -- ${@}
273 else
274 exec -- nix-shell ${nixShell} --run "${@}"
275 fi
276 fi
277 )
278}
279
280tmpdir() {
281 cleanup()
282 {
283 cd /
284 unmount() {
285 printf "Unmounting %s\n" ${1} >&2
286 fusermount -u ${1} || umount ${1} || sudo umount ${1}
287 }
288
289 if mountpoint -q -- ${dir}; then
290 unmount ${dir} || return $?
291 else
292 while read -d $'\0' subDir; do
293 mountpoint -q -- ${subDir} || continue
294 unmount ${subDir} || return $?
295 done <<<$(find ${dir} -xdev -type d -print0 | sort -zr)
296 fi
297
298 rm -rfv --one-file-system -- ${dir}
299 }
300
301 local tmpdir=""
302
303 while getopts ':t:a:s:Sd:ir:wqg:n:' arg; do
304 case $arg in
305 "t") tmpdir="=${OPTARG}" ;;
306 "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;;
307 esac
308 done
309
310 (
311 trap cleanup EXIT
312
313
314 local dir=$(mktemp -ud --tmpdir${tmpdir} ${0}.XXXXXXXXXX || return $?)
315
316 dir -d ${dir} ${@}
317 )
318}
319
320inhibit-sleep() {
321 if systemctl --user is-active prevent-suspend.service 1>/dev/null; then
322 echo "Allowing suspend"
323 systemctl --user stop prevent-suspend.service
324 else
325 echo "Inhibiting suspend"
326 systemctl --user start prevent-suspend.service
327 fi
328}
329
330qr() {
331 qrencode -l M -o - -t ANSIUTF8 $@
332}
333
334clock() {
335 tty-clock -sSbc -C 7 -d 0 -a 100000000
336}
337
338public-ip() {
339 curl -s -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip'
340}
341
342nix-ghci() {
343 pkgExpr=""
344 if [[ ${#@} -gt 0 ]]; then
345 pkgExpr="${1}"
346 shift
347 fi
348
349 nix-shell -p "with (import <nixpkgs> {}); pkgs.haskellPackages.ghcWithPackages (p: with p; [${pkgExpr}])" --run "ghci ${@}"
350}
351
352swap() {
353 f1=${1}
354 f2=${2}
355
356 if [[ -z "${f1}" || ! -e "${f1}" ]]; then
357 printf "‘%s’ does not exist\n" "${f1}" >&2
358 return 2
359 fi
360 if [[ -z "${f2}" || ! -e "${f2}" ]]; then
361 printf "‘%s’ does not exist\n" "${f2}" >&2
362 return 2
363 fi
364
365 tmpfile=$(mktemp --dry-run --tmpdir=${f1:h} .swap.XXXXXXXXXX)
366 mv -v ${f1} ${tmpfile}
367 mv -v ${f2} ${f1}
368 mv -v ${tmpfile} ${f2}
369}
370
371l() {
372 exa --binary --git --time-style=iso --long --all --header --group-directories-first --colour=always $@
373}
374
375ll() {
376 l $@ | less --mouse -FR
377}
378
379alias '..'='cd ..'
380alias -g L='| less'
381alias -g S='&> /dev/null'
382alias -g G='| grep'
383alias -g B='&> /dev/null &'
384alias -g BB='&> /dev/null &!'
385
386fancy-ctrl-z () {
387 emulate -LR zsh
388 if [[ $#BUFFER -eq 0 ]]; then
389 [[ -n $(jobs -s) ]] && bg
390 zle redisplay
391 else
392 zle push-input
393 fi
394}
395zle -N fancy-ctrl-z
396bindkey '^Z' fancy-ctrl-z
397
398export DEFAULT_USER=gkleen
399export EDITOR=emacsclient
400
401bindkey -e
402bindkey ';5C' emacs-forward-word
403bindkey ';5D' emacs-backward-word \ No newline at end of file
diff --git a/accounts/root@sif.nix b/accounts/root@sif.nix
new file mode 100644
index 00000000..979463ba
--- /dev/null
+++ b/accounts/root@sif.nix
@@ -0,0 +1,18 @@
1{ userName, ... }:
2{
3 home-manager.users.${userName} = {
4 programs.ssh.matchBlocks = {
5 "git.yggdrasil.li" = {
6 user = "gitolite";
7 identityFile = "~/.ssh/sysconf";
8 };
9 "borg.munin" = {
10 hostname = "u120515.your-storagebox.de";
11 user = "u120515";
12 identityFile = "~/.ssh/borg.munin";
13 identitiesOnly = true;
14 port = 23;
15 };
16 };
17 };
18}
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 00000000..d9afa42f
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,71 @@
1{
2 "nodes": {
3 "home-manager": {
4 "inputs": {
5 "nixpkgs": [
6 "nixpkgs"
7 ]
8 },
9 "locked": {
10 "lastModified": 1609269962,
11 "narHash": "sha256-YvkJhcBBls39JFZzh/S3oRKyDFAgy2KoW5AzJ+MvNgQ=",
12 "owner": "nix-community",
13 "repo": "home-manager",
14 "rev": "8e0c1c55fbb7f16f9fd313275ddf63c97b34394c",
15 "type": "github"
16 },
17 "original": {
18 "owner": "nix-community",
19 "ref": "master",
20 "repo": "home-manager",
21 "type": "github"
22 }
23 },
24 "nixpkgs": {
25 "locked": {
26 "lastModified": 1609337906,
27 "narHash": "sha256-xj027twGqdK/xRzxlnM8icyUUF4GANlBevHqLYhqb7w=",
28 "owner": "NixOS",
29 "repo": "nixpkgs",
30 "rev": "58f3c19b78594e1839abf702fa73ddf9d7a96437",
31 "type": "github"
32 },
33 "original": {
34 "owner": "NixOS",
35 "ref": "master",
36 "repo": "nixpkgs",
37 "type": "github"
38 }
39 },
40 "root": {
41 "inputs": {
42 "home-manager": "home-manager",
43 "nixpkgs": "nixpkgs",
44 "sops-nix": "sops-nix"
45 }
46 },
47 "sops-nix": {
48 "inputs": {
49 "nixpkgs": [
50 "nixpkgs"
51 ]
52 },
53 "locked": {
54 "lastModified": 1609306567,
55 "narHash": "sha256-CPVjO4tdmhHW7sOTbo8i9JN7HlNhakwpUi3u3+V6gnY=",
56 "owner": "Mic92",
57 "repo": "sops-nix",
58 "rev": "da343afab9aace88875f24bfb2d90e3d9afaafc4",
59 "type": "github"
60 },
61 "original": {
62 "owner": "Mic92",
63 "ref": "master",
64 "repo": "sops-nix",
65 "type": "github"
66 }
67 }
68 },
69 "root": "root",
70 "version": 7
71}
diff --git a/hosts/sif/default.nix b/hosts/sif/default.nix
new file mode 100644
index 00000000..b54b6caf
--- /dev/null
+++ b/hosts/sif/default.nix
@@ -0,0 +1,320 @@
1{ flake, pkgs, customUtils, lib, config, path, ... }:
2{
3 imports = with flake.nixosModules.systemProfiles; [
4 ./hw.nix
5 ./mail
6 initrd-all-crypto-modules default-locale openssh
7 ];
8
9 config = {
10 nixpkgs = {
11 system = "x86_64-linux";
12 config = {
13 allowUnfree = true;
14 };
15 };
16
17 boot = {
18 initrd = {
19 luks.devices = {
20 nvm0.device = "/dev/disk/by-uuid/fe641e81-0812-4181-a5f6-382ebba509bb";
21 nvm1.device = "/dev/disk/by-uuid/43df1ba8-1728-4193-8855-920a82d4494a";
22 };
23 availableKernelModules = [ "drbg" "nvme" "fbcon" "xhci_pci" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
24 kernelModules = [ "dm-raid" "dm-integrity" "dm-snapshot" "dm-thin-pool" ];
25 };
26
27 blacklistedKernelModules = [ "nouveau" ];
28
29 # Use the systemd-boot EFI boot loader.
30 loader = {
31 systemd-boot.enable = true;
32 efi.canTouchEfiVariables = true;
33 timeout = null;
34 };
35
36 plymouth.enable = true;
37
38 kernelPackages = pkgs.linuxPackages_latest;
39 kernelParams = [ "i915.fastboot=1" "intel_pstate=no_hwp" "acpi_backlight=vendor" "thinkpad-acpi.brightness_enable=1" "quiet" ];
40
41 tmpOnTmpfs = true;
42 };
43
44 networking = {
45 domain = "midgard.yggdrasil";
46 hosts = {
47 "127.0.0.1" = [ "sif.midgard.yggdrasil" "sif" ];
48 "::1" = [ "sif.midgard.yggdrasil" "sif" ];
49 };
50
51 firewall = {
52 enable = true;
53 allowedTCPPorts = [ 22 # ssh
54 8000 # quickserve
55 ];
56 };
57
58 networkmanager = {
59 enable = true;
60 dhcp = "internal";
61 dns = "dnsmasq";
62 extraConfig = ''
63 [connectivity]
64 uri=https://online.yggdrasil.li
65 '';
66 };
67
68 dhcpcd.enable = false;
69
70 interfaces.yggdrasil = {
71 virtual = true;
72 virtualType = config.services.tinc.networks.yggdrasil.interfaceType;
73 macAddress = "5c:93:21:c3:61:39";
74 };
75 };
76
77 environment.etc."NetworkManager/dnsmasq.d/libvirtd_dnsmasq.conf" = {
78 text = ''
79 server=/sif.libvirt/192.168.122.1
80 '';
81 };
82
83 powerManagement.enable = true;
84
85 environment.systemPackages = with pkgs; [
86 nvtop brightnessctl
87 ];
88
89 services = {
90 tinc.yggdrasil.enable = true;
91
92 uucp = {
93 enable = true;
94 nodeName = "sif";
95 remoteNodes = {
96 "ymir" = {
97 publicKeys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG6KNtsCOl5fsZ4rV7udTulGMphJweLBoKapzerWNoLY root@ymir"];
98 hostnames = ["ymir.yggdrasil.li" "ymir.niflheim.yggdrasil"];
99 };
100 };
101
102 defaultCommands = lib.mkForce [];
103 };
104
105 avahi.enable = true;
106
107 fwupd.enable = true;
108
109 fprintd.enable = true;
110
111 blueman.enable = true;
112
113 colord.enable = true;
114
115 vnstat.enable = true;
116
117 logind = {
118 lidSwitch = "suspend";
119 lidSwitchDocked = "lock";
120 lidSwitchExternalPower = "lock";
121 };
122
123 atd = {
124 enable = true;
125 allowEveryone = true;
126 };
127
128 xserver = {
129 enable = true;
130
131 layout = "us";
132 xkbVariant = "dvp";
133 xkbOptions = "compose:caps";
134
135 displayManager.lightdm = {
136 enable = true;
137 greeters.gtk = {
138 clock-format = "%H:%M %a %b %_d";
139 indicators = ["~host" "~spacer" "~clock" "~session" "~power"];
140 theme = {
141 package = pkgs.equilux-theme;
142 name = "Equilux-compact";
143 };
144 iconTheme = {
145 package = pkgs.paper-icon-theme;
146 name = "Paper";
147 };
148 extraConfig = ''
149 background = #000000
150 user-background = false
151 active-monitor = #cursor
152 hide-user-image = true
153
154 [monitor: DP-2]
155 laptop = true
156 '';
157 };
158 };
159
160 displayManager.setupCommands = ''
161 ${pkgs.xorg.xinput}/bin/xinput disable 'SynPS/2 Synaptics TouchPad'
162 '';
163
164 desktopManager.xterm.enable = true;
165 windowManager.twm.enable = true;
166 displayManager.defaultSession = "xterm+twm";
167
168 wacom.enable = true;
169 libinput.enable = true;
170
171 dpi = 282;
172
173 videoDrivers = [ "nvidia" ];
174
175 screenSection = ''
176 Option "metamodes" "nvidia-auto-select +0+0 { ForceCompositionPipeline = On }"
177 '';
178
179 deviceSection = ''
180 Option "AccelMethod" "SNA"
181 Option "TearFree" "True"
182 '';
183
184 exportConfiguration = true;
185 };
186 };
187
188 users = {
189 users.gkleen.extraGroups = [ "media" ];
190 groups.media = {};
191 };
192
193 hardware = {
194 pulseaudio = {
195 enable = true;
196 package = with pkgs; pulseaudioFull;
197 support32Bit = true;
198 };
199
200 bluetooth = {
201 enable = true;
202 config = {
203 General = {
204 Enable = "Source,Sink,Media,Socket";
205 };
206 };
207 };
208
209 trackpoint = {
210 enable = true;
211 emulateWheel = true;
212 sensitivity = 255;
213 speed = 255;
214 };
215
216 nvidia = {
217 modesetting.enable = true;
218 prime = {
219 nvidiaBusId = "PCI:1:0:0";
220 intelBusId = "PCI:0:2:0";
221 sync.enable = true;
222 };
223 };
224
225 opengl = {
226 enable = true;
227 driSupport32Bit = true;
228 setLdLibraryPath = true;
229 };
230
231 firmware = [ pkgs.firmwareLinuxNonfree ];
232 };
233
234 sound.enable = true;
235
236 nix = {
237 autoOptimiseStore = true;
238 daemonNiceLevel = 10;
239 daemonIONiceLevel = 3;
240 };
241
242 environment.etc."X11/xorg.conf.d/50-wacom.conf".source = lib.mkForce ./wacom.conf;
243
244 systemd.services."ac-plugged" = {
245 description = "Inhibit handling of lid-switch and sleep";
246
247 path = with pkgs; [ systemd coreutils ];
248
249 script = ''
250 exec systemd-inhibit --what=handle-lid-switch --why="AC is connected" --mode=block sleep infinity
251 '';
252
253 serviceConfig = {
254 Type = "simple";
255 };
256 };
257
258 services.udev.extraRules = with pkgs; ''
259 SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="0", RUN+="${systemd}/bin/systemctl --no-block stop ac-plugged.service"
260 SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="1", RUN+="${systemd}/bin/systemctl --no-block start ac-plugged.service"
261 '';
262
263 services.borgbackup = {
264 snapshots = "btrfs";
265 prefix = "yggdrasil.midgard.sif.";
266 targets = {
267 "munin" = {
268 repo = "borg.munin:borg";
269 paths = [ "/home/gkleen" ];
270 prune = {
271 "home" =
272 [ "--keep-within" "24H"
273 "--keep-daily" "31"
274 "--keep-monthly" "12"
275 "--keep-yearly" "-1"
276 ];
277 };
278 keyFile = "/run/secrets/borg-repokey--borg_munin__borg";
279 };
280 };
281 };
282 sops.secrets.borg-repokey--borg_munin__borg = {
283 sopsFile = /. + path + "/modules/borgbackup/repokeys/borg_munin__borg.yaml";
284 key = "key";
285 };
286
287 services.btrfs.autoScrub = {
288 enable = true;
289 fileSystems = [ "/" "/home" ];
290 interval = "weekly";
291 };
292
293 systemd.services."nix-daemon".serviceConfig = {
294 MemoryAccounting = true;
295 MemoryHigh = "50%";
296 MemoryMax = "75%";
297 };
298
299 services.journald.extraConfig = ''
300 SystemMaxUse=100M
301 '';
302
303 services.dbus.packages = with pkgs;
304 [ dbus gnome3.dconf
305 ];
306
307 programs = {
308 light.enable = true;
309 wireshark.enable = true;
310 };
311
312 virtualisation.libvirtd = {
313 enable = true;
314 };
315
316 zramSwap.enable = true;
317
318 system.stateVersion = "20.03";
319 };
320}
diff --git a/hosts/sif/hw.nix b/hosts/sif/hw.nix
new file mode 100644
index 00000000..4a3e6c86
--- /dev/null
+++ b/hosts/sif/hw.nix
@@ -0,0 +1,36 @@
1{ config, lib, pkgs, ... }:
2
3{
4 fileSystems."/" =
5 { device = "/dev/disk/by-uuid/f094bf06-66f9-40a8-9ab2-2b54d05223d2";
6 fsType = "btrfs";
7 };
8
9 fileSystems."/boot" =
10 { device = "/dev/disk/by-uuid/B3A2-D029";
11 fsType = "vfat";
12 };
13
14 fileSystems."/home" =
15 { device = "/dev/disk/by-uuid/9e932072-3c56-4a9c-8da7-3163d2a8bf28";
16 fsType = "btrfs";
17 };
18
19 fileSystems."/var/media" =
20 { device = "/dev/disk/by-uuid/437eca70-d017-4d52-a1fa-2f4c7a87f096";
21 fsType = "btrfs";
22 };
23
24 swapDevices =
25 [ { device = "/dev/disk/by-uuid/50f3f856-cc17-4614-846a-34a14d5006ec"; }
26 ];
27
28 nix.maxJobs = 12;
29 powerManagement.cpuFreqGovernor = "powersave";
30 # High-DPI console
31 console.font = "${pkgs.terminus_font}/share/consolefonts/ter-u28n.psf.gz";
32
33 hardware.cpu.intel.updateMicrocode = true;
34
35 hardware.enableRedistributableFirmware = true;
36}
diff --git a/hosts/sif/mail/default.nix b/hosts/sif/mail/default.nix
new file mode 100644
index 00000000..2addba9d
--- /dev/null
+++ b/hosts/sif/mail/default.nix
@@ -0,0 +1,66 @@
1{ config, pkgs, ... }:
2{
3 services.postfix = {
4 enable = true;
5 enableSmtp = true;
6 enableSubmission = false;
7 setSendmail = true;
8 networksStyle = "host";
9 hostname = "sif.midgard.yggdrasil";
10 destination = [];
11 relayHost = "uucp:ymir";
12 recipientDelimiter = "+";
13 masterConfig = {
14 uucp = {
15 type = "unix";
16 private = true;
17 privileged = true;
18 chroot = false;
19 command = "pipe";
20 args = [ "flags=Fqhu" "user=uucp" ''argv=${config.security.wrapperDir}/uux -z -a $sender - $nexthop!rmail ($recipient)'' ];
21 };
22 };
23 transport = ''
24 odin.asgard.yggdrasil uucp:odin
25 '';
26 config = {
27 always_bcc = "gkleen+sent@odin.asgard.yggdrasil";
28
29 default_transport = "uucp:ymir";
30
31 inet_interfaces = "loopback-only";
32
33 authorized_submit_users = ["!uucp" "static:anyone"];
34 message_size_limit = "0";
35
36 sender_dependent_default_transport_maps = ''regexp:${pkgs.writeText "sender_relay" ''
37 /@(cip|stud)\.ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtp.ifi.lmu.de
38 /@ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtpin1.ifi.lmu.de:587
39 /@(campus\.)?lmu\.de$/ smtp:postout.lrz.de
40 ''}'';
41 sender_bcc_maps = ''texthash:${pkgs.writeText "sender_bcc" ''
42 uni2work@ifi.lmu.de uni2work@ifi.lmu.de
43 @ifi.lmu.de gregor.kleen@ifi.lmu.de
44 ''}'';
45
46 smtp_sasl_auth_enable = true;
47 smtp_sender_dependent_authentication = true;
48 smtp_sasl_tls_security_options = "noanonymous";
49 smtp_sasl_mechanism_filter = ["plain"];
50 smtp_sasl_password_maps = "texthash:/var/db/postfix/sasl_passwd";
51 smtp_cname_overrides_servername = false;
52 smtp_always_send_ehlo = true;
53
54 smtp_tls_loglevel = "1";
55 smtp_dns_support_level = "dnssec";
56 };
57 useDane = true;
58 };
59
60 sops.secrets.postfix-sasl-passwd = {
61 key = "sasl-passwd";
62 path = "/var/db/postfix/sasl_passwd";
63 owner = "postfix";
64 sopsFile = ./secrets.yaml;
65 };
66}
diff --git a/hosts/sif/mail/secrets.yaml b/hosts/sif/mail/secrets.yaml
new file mode 100644
index 00000000..00422f82
--- /dev/null
+++ b/hosts/sif/mail/secrets.yaml
@@ -0,0 +1,33 @@
1sasl-passwd: ENC[AES256_GCM,data:RDZHUgQJHH7IzJD5j+LOuQb4OuPopUEa6CwDRoD/FqoHFW/YKarF3Hxxu4HKA5GDf3SRrFOcPBXmf+0f1CucUQwJQh4nY4fmDVqrH0UXRowuAkIhYpt0sLXlzrOzSeZz788A9xK4AGPzEOx1va7GOqJIaPJ+pyyzazQsSgCJaFkUMriCfKbZ0zhRCr0pk2RPLOLKGuo2mDFf5c3EZYAn7vEzhZj+B3XbNWotV/JXTX7JPK6GPcsX2RMKEYBdmxZzrMCTTFU23W1DbiDJ01mxJh3ckIX+KTmaWNoVg4Tong1vBe2wxKchXajmykwFLJFR1Kj5wv4uAxy2qNvKtQIF/LJosG6LXcdk5QDQBXUINqswupBdV8lt08mk53JHLJPXcV8RpEHT3NUL,iv:2u203xTmUEfWIJDB2ZkOKzhYQrV4TGT7rfOd0md+VOw=,tag:RJ/iLbbq8B8dMmXGWjok/g==,type:str]
2sops:
3 kms: []
4 gcp_kms: []
5 azure_kv: []
6 hc_vault: []
7 lastmodified: '2021-01-02T19:29:40Z'
8 mac: ENC[AES256_GCM,data:g8wNpsFXiGoENSteWa1w1UkF8LQwnwtoeEHskKhGqAlCFtA1cVdyFSItm8/h1/eqJl/NWXRGU25XpZysCAkJi+uCq4bNGjV+gjqeIT8Dv5teQbVwthoFqkE/s3jew35+f29/xxb5Cro6EihlTrs5Lt3wExv2+NUdim1aeNgR+4Q=,iv:bj/igDT7GPiCjj4BwE7ihM8wR8CbJeXu/s550rc+QEw=,tag:KKt6tWlqxu5C/L/ZYbQL3g==,type:str]
9 pgp:
10 - created_at: '2021-01-02T19:29:14Z'
11 enc: |
12 -----BEGIN PGP MESSAGE-----
13
14 hF4Dgwm4NZSaLAcSAQdAE/883Tbc7WXuzOxjm5jVrOSbnYe+BEg75ijtZP2L3UMw
15 4mhqzy576jEQLPGrnMpX2zA2MwFAwGnMwC98sQ4vVTp/xgNQ0VHHNM4GnTi6VoUb
16 0l4BLgQrT6p2ul69ADecadWJsGm6roqMHrpNGZeeczDLOBIzrrwN4sL92jQiEPw9
17 Ih+EXJpJ1K4NouU1VRsfQPqJ6y+i295TnEgunlJeYc/MNQgBT4ABiPZgUZXnkhxl
18 =7rOv
19 -----END PGP MESSAGE-----
20 fp: F1AF20B9511B63F681A14E8D51AEFBCD1DEF68F8
21 - created_at: '2021-01-02T19:29:14Z'
22 enc: |
23 -----BEGIN PGP MESSAGE-----
24
25 hF4DXxoViZlp6dISAQdAGifJ6qk40VdF/WKaYa9v97PdSVkPvHZt+j0G8+ZDJSEw
26 8XC1622ElTWRCZ2bjUwMF77DMgMy3rEr8B7Bj6MnEzDd/Af63Np1cO+7juybxqhz
27 0l4BO6uZ+gCvKg45jWX0GE6ZBkoUTvh24djTngHFyIHDnpCxSB6s+jcYR9otco2F
28 ++E2pcoQR4GuOeyYa/8UsW+RzKWpCfskYbSIt4gAXyCt8ua1y5Rw0DEVdw91uJNC
29 =E/qh
30 -----END PGP MESSAGE-----
31 fp: 30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51
32 unencrypted_suffix: _unencrypted
33 version: 3.6.1
diff --git a/hosts/sif/wacom.conf b/hosts/sif/wacom.conf
new file mode 100644
index 00000000..864409f1
--- /dev/null
+++ b/hosts/sif/wacom.conf
@@ -0,0 +1,15 @@
1Section "InputClass"
2 Identifier "Wacom USB device class"
3 MatchUSBID "056a:*"
4 MatchDevicePath "/dev/input/event*"
5 Driver "wacom"
6EndSection
7
8Section "InputClass"
9 Identifier "calibration"
10 MatchProduct "Wacom USB device class"
11 Option "MinX" "58"
12 Option "MaxX" "30982"
13 Option "MinY" "87"
14 Option "MaxY" "17328"
15EndSection \ No newline at end of file
diff --git a/modules/borgbackup/btrfs-snapshots.nix b/modules/borgbackup/btrfs-snapshots.nix
new file mode 100644
index 00000000..96d2b2ba
--- /dev/null
+++ b/modules/borgbackup/btrfs-snapshots.nix
@@ -0,0 +1,52 @@
1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.btrfs-snapshots;
7
8 snapshotMount = str: "${str}${cfg.mountSuffix}";
9in {
10 options = {
11
12 services.btrfs-snapshots = {
13 enable = mkEnableOption "a systemd unit for btrfs snapshots";
14
15 mountSuffix = mkOption {
16 type = types.str;
17 default = ".snapshot";
18 };
19
20 readOnly = mkOption {
21 type = types.bool;
22 default = true;
23 };
24
25 persist = mkOption {
26 type = types.bool;
27 default = false;
28 };
29 };
30
31 };
32
33
34 config = mkIf cfg.enable {
35 systemd.services."btrfs-snapshot@" = {
36 enable = true;
37
38 unitConfig = {
39 StopWhenUnneeded = !cfg.persist;
40 };
41
42 serviceConfig = with pkgs; {
43 Type = "oneshot";
44 ExecStartPre = "-${btrfs-progs}/bin/btrfs subvolume delete -c ${snapshotMount "%f"}";
45 ExecStart = "${btrfs-progs}/bin/btrfs subvolume snapshot ${optionalString cfg.readOnly "-r"} %f ${snapshotMount "%f"}";
46 RemainAfterExit = true;
47 ExecStop = "${btrfs-progs}/bin/btrfs subvolume delete -c ${snapshotMount "%f"}";
48 };
49 };
50
51 };
52}
diff --git a/modules/borgbackup/default.nix b/modules/borgbackup/default.nix
new file mode 100644
index 00000000..a0419d0e
--- /dev/null
+++ b/modules/borgbackup/default.nix
@@ -0,0 +1,206 @@
1{ config, lib, utils, pkgs, ... }:
2
3with utils;
4with lib;
5
6let
7 cfg = config.services.borgbackup;
8
9 lvmPath = {
10 options = {
11 LV = mkOption {
12 type = types.str;
13 };
14 VG = mkOption {
15 type = types.str;
16 };
17 };
18 };
19
20 pathType = if cfg.snapshots == "lvm" then types.submodule lvmPath else types.path;
21
22 systemdPath = path: escapeSystemdPath (if cfg.snapshots == "lvm" then "${path.VG}-${path.LV}" else path);
23
24 withSuffix = path: path + (if cfg.snapshots == "btrfs" then config.services.btrfs-snapshots.mountSuffix else config.services.lvm-snapshots.mountSuffix);
25
26 mountPoint = if cfg.snapshots == "lvm" then config.services.lvm-snapshots.mountPoint else "";
27
28 targetOptions = {
29 options = {
30 repo = mkOption {
31 type = types.str;
32 };
33
34 paths = mkOption {
35 type = types.listOf pathType;
36 default = [];
37 };
38
39 prune = mkOption {
40 type = types.attrsOf (types.listOf types.str);
41 default = {};
42 };
43
44 interval = mkOption {
45 type = types.str;
46 default = "6h";
47 };
48
49 jitter = mkOption {
50 type = with types; nullOr str;
51 default = "6h";
52 };
53
54 lock = mkOption {
55 type = types.nullOr types.str;
56 default = "backup";
57 };
58
59 network = mkOption {
60 type = types.bool;
61 default = true;
62 };
63
64 lockWait = mkOption {
65 type = types.int;
66 default = 600;
67 };
68
69 keyFile = mkOption {
70 type = types.nullOr types.path;
71 default = null;
72 };
73 };
74 };
75in {
76 disabledModules = [ "services/backup/borgbackup.nix" ];
77
78 options = {
79 services.borgbackup = {
80 snapshots = mkOption {
81 type = types.nullOr (types.enum ["btrfs" "lvm"]);
82 default = null;
83 };
84
85 targets = mkOption {
86 type = types.attrsOf (types.submodule targetOptions);
87 default = {};
88 };
89
90 prefix = mkOption {
91 type = types.str;
92 };
93 };
94 };
95
96 imports =
97 [ ./lvm-snapshots.nix
98 ./btrfs-snapshots.nix
99 ];
100
101 config = mkIf (any (t: t.paths != []) (attrValues cfg.targets)) {
102 services.btrfs-snapshots.enable = mkIf (cfg.snapshots == "btrfs") true;
103
104 services.lvm-snapshots.snapshots = mkIf (cfg.snapshots == "lvm") (listToAttrs (map (path: nameValuePair (path.VG + "-" + path.LV) {
105 inherit (path) LV VG;
106 mountName = withSuffix (path.VG + "-" + path.LV);
107 }) (unique (flatten (mapAttrsToList (target: tCfg: tCfg.paths) cfg.targets)))));
108
109 systemd.targets."timers-borg" = {
110 wantedBy = [ "timers.target" ];
111 };
112
113 systemd.slices."system-borgbackup" = {};
114
115 systemd.timers = (listToAttrs (map ({ target, path, tCfg }: nameValuePair "borgbackup-${target}@${systemdPath path}" {
116 requiredBy = [ "timers-borg.target" ];
117
118 timerConfig = {
119 Persistent = false;
120 OnBootSec = tCfg.interval;
121 OnUnitActiveSec = tCfg.interval;
122 RandomizedDelaySec = mkIf (tCfg.jitter != null) tCfg.jitter;
123 };
124 }) (flatten (mapAttrsToList (target: tCfg: map (path: { inherit target path tCfg; }) tCfg.paths) cfg.targets)))) // (mapAttrs' (target: tCfg: nameValuePair "borgbackup-prune-${target}" {
125 enable = tCfg.prune != {};
126
127 requiredBy = [ "timers-borg.target" ];
128
129 timerConfig = {
130 Persistent = false;
131 OnBootSec = tCfg.interval;
132 OnUnitActiveSec = tCfg.interval;
133 RandomizedDelaySec = mkIf (tCfg.jitter != null) tCfg.jitter;
134 };
135 }) cfg.targets);
136
137 systemd.services = (mapAttrs' (target: tCfg: nameValuePair "borgbackup-${target}@" (let
138 deps = flatten [
139 (optional (cfg.snapshots == "btrfs") "btrfs-snapshot@%i.service")
140 (optional tCfg.network "network-online.target")
141 ];
142 in {
143 bindsTo = deps;
144 after = deps;
145
146 path = with pkgs; [borgbackup] ++ optional (tCfg.lock != null) utillinux;
147
148 script = let
149 borgCmd = ''
150 borg create \
151 --lock-wait ${toString tCfg.lockWait} \
152 --stats \
153 --list \
154 --filter 'AME' \
155 --exclude-caches \
156 --keep-exclude-tags \
157 --patterns-from .backup-${target} \
158 --one-file-system \
159 --compression auto,lzma \
160 ${tCfg.repo}::${cfg.prefix}$1-{utcnow}
161 '';
162 in if tCfg.lock == null then borgCmd else "flock -xo /var/lock/${tCfg.lock} ${borgCmd}";
163 scriptArgs = if cfg.snapshots == "lvm" then "%I" else "%i";
164
165 unitConfig = {
166 AssertPathIsDirectory = mkIf (tCfg.lock != null) "/var/lock";
167 DefaultDependencies = false;
168 RequiresMountsFor = mkIf (cfg.snapshots == "lvm") [ "${mountPoint}/${withSuffix "%I"}" ];
169 };
170
171 serviceConfig = {
172 Type = "oneshot";
173 WorkingDirectory = if (cfg.snapshots == null) then "%I" else (if (cfg.snapshots == "lvm") then "${mountPoint}/${withSuffix "%I"}" else "${withSuffix "%f"}");
174 Nice = 15;
175 IOSchedulingClass = 2;
176 IOSchedulingPriority = 7;
177 SuccessExitStatus = [1 2];
178 Slice = "system-borgbackup.slice";
179 Environment = lib.mkIf (tCfg.keyFile != null) "BORG_KEY_FILE=${tCfg.keyFile}";
180 };
181 })) cfg.targets) // (mapAttrs' (target: tCfg: nameValuePair "borgbackup-prune-${target}" {
182 enable = tCfg.prune != {};
183
184 bindsTo = ["network-online.target"];
185 after = ["network-online.target"];
186
187 path = with pkgs; [borgbackup];
188
189 script = concatStringsSep "\n" (mapAttrsToList (path: args: ''
190 borg prune \
191 --lock-wait ${toString tCfg.lockWait} \
192 --list \
193 --stats \
194 --prefix ${escapeShellArg "${cfg.prefix}${path}"} \
195 ${escapeShellArgs args} \
196 ${tCfg.repo}
197 '') tCfg.prune);
198
199 serviceConfig = {
200 Type = "oneshot";
201 Slice = "system-borgbackup.slice";
202 Environment = lib.mkIf (tCfg.keyFile != null) "BORG_KEY_FILE=${tCfg.keyFile}";
203 };
204 }) cfg.targets);
205 };
206}
diff --git a/modules/borgbackup/lvm-snapshots.nix b/modules/borgbackup/lvm-snapshots.nix
new file mode 100644
index 00000000..9b2a6562
--- /dev/null
+++ b/modules/borgbackup/lvm-snapshots.nix
@@ -0,0 +1,133 @@
1{ config, lib, utils, pkgs, ... }:
2
3with utils;
4with lib;
5
6let
7 cfg = config.services.lvm-snapshots;
8
9 snapshotMount = name: "${cfg.mountPoint}/${if isNull cfg.snapshots."${name}".mountName then name else cfg.snapshots."${name}".mountName}";
10 snapshotName = name: "${name}-${cfg.mountSuffix}";
11
12 snapshotConfig = {
13 options = {
14 LV = mkOption {
15 type = types.str;
16 };
17
18 VG = mkOption {
19 type = types.str;
20 };
21
22 mountName = mkOption {
23 type = types.nullOr types.str;
24 default = null;
25 };
26
27 cowSize = mkOption {
28 type = types.str;
29 default = "-l20%ORIGIN";
30 };
31
32 readOnly = mkOption {
33 type = types.bool;
34 default = true;
35 };
36
37 persist = mkOption {
38 type = types.bool;
39 default = false;
40 };
41 };
42 };
43in {
44 options = {
45
46 services.lvm-snapshots = {
47 snapshots = mkOption {
48 type = types.attrsOf (types.submodule snapshotConfig);
49 default = {};
50 };
51
52 mountPoint = mkOption {
53 type = types.path;
54 default = "/mnt";
55 };
56
57 mountSuffix = mkOption {
58 type = types.str;
59 default = "-snapshot";
60 };
61 };
62 };
63
64
65 config = mkIf (cfg != {}) {
66
67 boot.kernelModules = [ "dm_snapshot" ];
68
69 # system.activationScripts = mapAttrs' (name: scfg: nameValuePair ("lvm-mountpoint" + name) ''
70 # mkdir -p ${snapshotMount name}
71 # '') cfg.snapshots;
72
73 systemd.services = mapAttrs' (name: scfg: nameValuePair ("lvm-snapshot@" + escapeSystemdPath name) {
74 enable = true;
75
76 description = "LVM-snapshot of ${scfg.VG}/${scfg.LV}";
77
78 bindsTo = ["${escapeSystemdPath "/dev/${scfg.VG}/${scfg.LV}"}.device"];
79 after = ["${escapeSystemdPath "/dev/${scfg.VG}/${scfg.LV}"}.device"];
80
81 unitConfig = {
82 StopWhenUnneeded = !scfg.persist;
83 AssertPathIsDirectory = "/var/lock";
84 };
85
86 path = with pkgs; [ devicemapper utillinux ];
87
88 script = ''
89 (
90 flock -xn -E 4 9
91 if [[ "$?" -ne 0 ]]; then
92 exit $?
93 fi
94
95 lvcreate -s ${scfg.cowSize} --name ${snapshotName name} ${scfg.VG}/${scfg.LV}
96
97 sleep infinity &
98 ) 9>/var/lock/lvm-snapshot.${scfg.VG}
99 '';
100
101 preStart = ''
102 lvremove -f ${scfg.VG}/${snapshotName name}
103 '';
104
105 preStop = ''
106 lvremove -f ${scfg.VG}/${snapshotName name}
107 '';
108
109 serviceConfig = with pkgs; {
110 Type = "forking";
111 RestartForceExitStatus = [ "4" ];
112 RestartSec = "5min";
113 };
114 }) cfg.snapshots;
115
116 systemd.mounts = mapAttrsToList (name: scfg: {
117 enable = true;
118
119 unitConfig = {
120 # AssertPathIsDirectory = snapshotMount name;
121 StopWhenUnneeded = !scfg.persist;
122 };
123
124 bindsTo = [ ("lvm-snapshot@" + escapeSystemdPath name + ".service") ];
125 after = [ ("lvm-snapshot@" + escapeSystemdPath name + ".service") ];
126
127 options = concatStringsSep "," ([ "noauto" ] ++ optional scfg.readOnly "ro");
128
129 where = snapshotMount name;
130 what = "/dev/" + scfg.VG + "/" + snapshotName name;
131 }) cfg.snapshots;
132 };
133}
diff --git a/modules/borgbackup/repokeys/borg_munin__borg.yaml b/modules/borgbackup/repokeys/borg_munin__borg.yaml
new file mode 100644
index 00000000..f302fe06
--- /dev/null
+++ b/modules/borgbackup/repokeys/borg_munin__borg.yaml
@@ -0,0 +1,33 @@
1key: ENC[AES256_GCM,data:mxh+Jtxx+HyD246yPwo0vy7vSTz3IG8VmfbxPMwqJRreh9ZwkGnH5aCTDOvWOHIrkmzaRMF3oCi1P8D29+abMUZdt0MuJ3UE6iL8+SXlflR+WACgALM2Df+x9B3BwQM3yeoCiWG+ebr0iQPHM3jqqpkjoRv1CcythxG2deZueur9lzgC2CwG1g3O8Prnl9z0JQGOa+gjic8Zwfn38B1BECeNPrbjzICGBOrSbN/6EnfBDygI2QzseamzK2I6R6jT+QxHvkl+Zi1m2TRB+4o82VgTjPhIReJyT7PrlDnUyrKObhCOlb3v+LiSdp16IPIDVs968kyDzgyi7QPOpGr+5tutWCZrau5xhPDrONKByl/0nVVwEZfRIYATvEXtn5okJru/mglcpeD0I7AtLt+Vfv9CB9pQczvkHo0cDtgudQDf9ADt/nkmqHugm5VfMg9m9aGbKqzXt6pPOMsXSbS43K7wgDaduLZ/PW4Ookx9gTNLtJHnZ64GBorOv4QSrZIZF8pE1FsQdUhmp/YzVhaNBnjCr+Jh77sYjoOwzF77Xy+VP2C/yVIf492P+FcgkSj6XhYYqHffpFW9l/xmUvyQF5gjj2k5T21UvgChhI1HeLPzQ7W9+xuGSMtg58aD/VPe1loCy8zLITNl71bneararRS5vItoZyzMdmIRMLAZD1klPmDNe1yufTpubOXzNYbWUqFUZtwH/mDL5GRZBD9dqs2b3F26c1CUyw==,iv:NJBHesKSZ1zuKk8qHnYKqIwMnFkH+rkQD1bam5XpLXU=,tag:EiYbIFY/r/eTSTJIhYV+GA==,type:str]
2sops:
3 kms: []
4 gcp_kms: []
5 azure_kv: []
6 hc_vault: []
7 lastmodified: '2021-01-02T20:38:48Z'
8 mac: ENC[AES256_GCM,data:3rkFTOk3r2dx3hOqu1u7XIIibTDfqNlRcWY9X2N/LFa/BKojgDt5tcpbphV4HqWvl8nS+fPcVrIElJfQ/QGFEOx68G95BhByntT9+JhSbHJt73dGnCSroZCw5QefdydREGvA5n00Vo9yT9IMvQsQbmpRzo6hcrSSUvagZqmZckA=,iv:F/HllDzyxgulIWZbfz9bFKR+SFg4PoaUYZ5N5hfIzw0=,tag:h2NXmvj/thhBg1rIkwdXXA==,type:str]
9 pgp:
10 - created_at: '2021-01-02T20:38:09Z'
11 enc: |
12 -----BEGIN PGP MESSAGE-----
13
14 hF4Dgwm4NZSaLAcSAQdAwmvyXlr9MyfPfLgkfQkoktKBV2WA2xhZrGL7NeeGfhAw
15 REk+clJ9WgiJ0iceRAONPnEjeiK0J6Fsj+5Ulq8flFGkoj5Pta0pm/9fudKmcPdC
16 0l4BF0G5LSpG1EmY+LmVdSdas16rWgthnojoXPvbbHG6jZs3aDETshdiN8Bdlqsf
17 aVhq2LYzscnYezNcdernR4uojtiFny8qcmdF3tFacr+mkgfgIQr0W9yWFhDH15gm
18 =4TwU
19 -----END PGP MESSAGE-----
20 fp: F1AF20B9511B63F681A14E8D51AEFBCD1DEF68F8
21 - created_at: '2021-01-02T20:38:09Z'
22 enc: |
23 -----BEGIN PGP MESSAGE-----
24
25 hF4DXxoViZlp6dISAQdAruPXj9IsllEN7R5jk4gF7bW0ZirhvX7qsu22/6HbSw8w
26 66RwN3WGjYO1CcVbHKuLqVVaUBCnrR/4XHN0JYUaqjubrSZBTWFKTBFsKSTT0LZq
27 0l4BKcsXrbGpYC5+yQvg0RHJ7LplxpKOmqMY8KGckvGnVf2xg7k6wuWQREFzqwt+
28 lOa3x+xFy9c0JwE8AafyKjb/cgqJiMb96lhsH57BpXJa2E39ImQbXqzDzdx2jEUt
29 =3rxi
30 -----END PGP MESSAGE-----
31 fp: 30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51
32 unencrypted_suffix: _unencrypted
33 version: 3.6.1
diff --git a/modules/kill-user.nix b/modules/kill-user.nix
new file mode 100644
index 00000000..dd897b36
--- /dev/null
+++ b/modules/kill-user.nix
@@ -0,0 +1,13 @@
1{ lib, pkgs, config, ... }:
2{
3 options = {
4 systemd.kill-user.enable = lib.mkEnableOption "Systemd kill-user@ services";
5 };
6
7 config.systemd.services."kill-user@" = lib.mkIf config.systemd.kill-user.enable {
8 serviceConfig = {
9 Type = "oneshot";
10 ExecStart = "${pkgs.systemd}/bin/loginctl kill-user %I";
11 };
12 };
13}
diff --git a/modules/tinc-networkmanager.nix b/modules/tinc-networkmanager.nix
new file mode 100644
index 00000000..ff03abd2
--- /dev/null
+++ b/modules/tinc-networkmanager.nix
@@ -0,0 +1,36 @@
1{ lib, config, pkgs, ... }:
2let
3 cfg = config.services.tinc;
4in {
5 options = {
6 services.tinc.networks = lib.mkOption {
7 type = lib.types.attrsOf (lib.types.submodule {
8 options.nmDispatch = lib.mkOption {
9 type = lib.types.bool;
10 default = config.networking.networkmanager.enable;
11 description = ''
12 Install a network-manager dispatcher script to automatically
13 connect to all remotes when networking is available
14 '';
15 };
16 });
17 };
18 };
19
20 config = {
21 networking.networkmanager.dispatcherScripts = lib.concatLists (lib.flip lib.mapAttrsToList cfg.networks (network: data: lib.optional data.nmDispatch {
22 type = "basic";
23 source = pkgs.writeScript "connect-${network}.sh" ''
24 #!${pkgs.stdenv.shell}
25
26 shopt -s extglob
27
28 case "''${2}" in
29 (?(vpn-)up)
30 ${data.package}/bin/tinc -n ${network} --pidfile /run/tinc.${network}.pid --batch retry
31 ;;
32 esac
33 '';
34 }));
35 };
36}
diff --git a/modules/uucp.nix b/modules/uucp.nix
new file mode 100644
index 00000000..8d8ac1a2
--- /dev/null
+++ b/modules/uucp.nix
@@ -0,0 +1,391 @@
1{ flake, config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 portSpec = name: node: concatStringsSep "\n" (map (port: ''
7 port ${name}.${port}
8 type pipe
9 protocol ${node.protocols}
10 reliable true
11 command ${pkgs.openssh}/bin/ssh -x -o batchmode=yes ${name}.${port}
12 '') node.hostnames);
13 sysSpec = name: node: ''
14 system ${name}
15 time any
16 chat-seven-bit false
17 chat . ""
18 protocol ${node.protocols}
19 command-path ${concatStringsSep " " cfg.commandPath}
20 commands ${concatStringsSep " " node.commands}
21 ${concatStringsSep "\nalternate\n" (map (port: ''
22 port ${name}.${port}
23 '') node.hostnames)}
24 '';
25 sshConfig = name: node: concatStringsSep "\n" (map (port: ''
26 Host ${name}.${port}
27 Hostname ${port}
28 IdentitiesOnly Yes
29 IdentityFile ${cfg.sshKeyDir}/${name}
30 '') node.hostnames);
31 sshKeyGen = name: node: ''
32 if [[ ! -e ${cfg.sshKeyDir}/${name} ]]; then
33 ${pkgs.openssh}/bin/ssh-keygen ${escapeShellArgs node.generateKey} -f ${cfg.sshKeyDir}/${name}
34 fi
35 '';
36 restrictKey = key: ''
37 restrict,command="${chat}" ${key}
38 '';
39 chat = pkgs.writeScript "chat" ''
40 #!${pkgs.stdenv.shell}
41
42 echo .
43 exec ${config.security.wrapperDir}/uucico
44 '';
45
46 nodeCfg = {
47 options = {
48 commands = mkOption {
49 type = types.listOf types.str;
50 default = cfg.defaultCommands;
51 description = "Commands to allow for this remote";
52 };
53
54 protocols = mkOption {
55 type = types.separatedString "";
56 default = cfg.defaultProtocols;
57 description = "UUCP protocols to use for this remote";
58 };
59
60 publicKeys = mkOption {
61 type = types.listOf types.str;
62 default = [];
63 description = "SSH client public keys for this node";
64 };
65
66 generateKey = mkOption {
67 type = types.listOf types.str;
68 default = [ "-t" "ed25519" "-N" "" ];
69 description = "Arguments to pass to `ssh-keygen` to generate a keypair for communication with this host";
70 };
71
72 hostnames = mkOption {
73 type = types.listOf types.str;
74 default = [];
75 description = "Hostnames to try in order when connecting";
76 };
77 };
78 };
79
80 cfg = config.services.uucp;
81in {
82 options = {
83 services.uucp = {
84 enable = mkOption {
85 type = types.bool;
86 default = false;
87 description = ''
88 If enabled we set up an account accesible via uucp over ssh
89 '';
90 };
91
92 nodeName = mkOption {
93 type = types.str;
94 default = "nixos";
95 description = "uucp node name";
96 };
97
98 sshUser = mkOption {
99 type = types.attrs;
100 default = {};
101 description = "Overrides for the local uucp linux-user";
102 };
103
104 extraSSHConfig = mkOption {
105 type = types.str;
106 default = "";
107 description = "Extra SSH config";
108 };
109
110 remoteNodes = mkOption {
111 type = types.attrsOf (types.submodule nodeCfg);
112 default = {};
113 description = ''
114 Ports to set up
115 Names will probably need to be configured in sshConfig
116 '';
117 };
118
119 commandPath = mkOption {
120 type = types.listOf types.path;
121 default = [ "${pkgs.rmail}/bin" ];
122 description = ''
123 Command search path for all systems
124 '';
125 };
126
127 defaultCommands = mkOption {
128 type = types.listOf types.str;
129 default = ["rmail"];
130 description = "Commands allowed for remotes without explicit override";
131 };
132
133 defaultProtocols = mkOption {
134 type = types.separatedString "";
135 default = "te";
136 description = "UUCP protocol to use within ssh unless overriden";
137 };
138
139 incomingProtocols = mkOption {
140 type = types.separatedString "";
141 default = "te";
142 description = "UUCP protocols to use when called";
143 };
144
145 homeDir = mkOption {
146 type = types.path;
147 default = "/var/uucp";
148 description = "Home of the uucp user";
149 };
150
151 sshKeyDir = mkOption {
152 type = types.path;
153 default = "${cfg.homeDir}/.ssh/";
154 description = "Directory to store ssh keypairs";
155 };
156
157 spoolDir = mkOption {
158 type = types.path;
159 default = "/var/spool/uucp";
160 description = "Spool directory";
161 };
162
163 lockDir = mkOption {
164 type = types.path;
165 default = "/var/spool/uucp";
166 description = "Lock directory";
167 };
168
169 pubDir = mkOption {
170 type = types.path;
171 default = "/var/spool/uucppublic";
172 description = "Public directory";
173 };
174
175 logFile = mkOption {
176 type = types.path;
177 default = "/var/log/uucp";
178 description = "Log file";
179 };
180
181 statFile = mkOption {
182 type = types.path;
183 default = "/var/log/uucp.stat";
184 description = "Statistics file";
185 };
186
187 debugFile = mkOption {
188 type = types.path;
189 default = "/var/log/uucp.debug";
190 description = "Debug file";
191 };
192
193 interval = mkOption {
194 type = types.nullOr types.str;
195 default = "1h";
196 description = ''
197 Specification of when to run `uucico' in format used by systemd timers
198 The default is to do so every hour
199 '';
200 };
201
202 nmDispatch = mkOption {
203 type = types.bool;
204 default = config.networking.networkmanager.enable;
205 description = ''
206 Install a network-manager dispatcher script to automatically
207 call all remotes when networking is available
208 '';
209 };
210
211 extraConfig = mkOption {
212 type = types.lines;
213 default = ''
214 run-uuxqt 1
215 '';
216 description = "Extra configuration to append verbatim to `/etc/uucp/config'";
217 };
218
219 extraSys = mkOption {
220 type = types.lines;
221 default = ''
222 protocol-parameter g packet-size 4096
223 '';
224 description = "Extra configuration to prepend verbatim to `/etc/uucp/sys`";
225 };
226 };
227 };
228
229 config = mkIf cfg.enable {
230 environment.etc."uucp/config" = {
231 text = ''
232 hostname ${cfg.nodeName}
233
234 spool ${cfg.spoolDir}
235 lockdir ${cfg.lockDir}
236 pubdir ${cfg.pubDir}
237 logfile ${cfg.logFile}
238 statfile ${cfg.statFile}
239 debugfile ${cfg.debugFile}
240
241 ${cfg.extraConfig}
242 '';
243 };
244
245 users.users."uucp" = {
246 name = "uucp";
247 isSystemUser = true;
248 isNormalUser = false;
249 createHome = true;
250 home = cfg.homeDir;
251 description = "User for uucp over ssh";
252 useDefaultShell = true;
253 openssh.authorizedKeys.keys = map restrictKey (concatLists (mapAttrsToList (name: node: node.publicKeys) cfg.remoteNodes));
254 } // cfg.sshUser;
255
256 system.activationScripts."uucp-sshconfig" = ''
257 mkdir -p ${config.users.users."uucp".home}/.ssh
258 chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${config.users.users."uucp".home}/.ssh
259 chmod 700 ${config.users.users."uucp".home}/.ssh
260 ln -fs ${builtins.toFile "ssh-config" ''
261 ${concatStringsSep "\n" (mapAttrsToList sshConfig cfg.remoteNodes)}
262
263 ${cfg.extraSSHConfig}
264 ''} ${config.users.users."uucp".home}/.ssh/config
265
266 mkdir -p ${cfg.sshKeyDir}
267 chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.sshKeyDir}
268 chmod 700 ${cfg.sshKeyDir}
269
270 ${concatStringsSep "\n" (mapAttrsToList sshKeyGen cfg.remoteNodes)}
271 '';
272
273 system.activationScripts."uucp-logs" = ''
274 touch ${cfg.logFile}
275 chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.logFile}
276 chmod 644 ${cfg.logFile}
277 touch ${cfg.statFile}
278 chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.statFile}
279 chmod 644 ${cfg.statFile}
280 touch ${cfg.debugFile}
281 chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.debugFile}
282 chmod 644 ${cfg.debugFile}
283 '';
284
285 environment.etc."uucp/port" = {
286 text = ''
287 port ssh
288 type stdin
289 protocol ${cfg.incomingProtocols}
290 '' + concatStringsSep "\n" (mapAttrsToList portSpec cfg.remoteNodes);
291 };
292 environment.etc."uucp/sys" = {
293 text = cfg.extraSys + "\n" + concatStringsSep "\n" (mapAttrsToList sysSpec cfg.remoteNodes);
294 };
295
296 security.wrappers = let
297 wrapper = p: {
298 name = p;
299 value = {
300 source = "${pkgs.uucp}/bin/${p}";
301 owner = "root";
302 group = "root";
303 setuid = true;
304 setgid = false;
305 };
306 };
307 in listToAttrs (map wrapper ["uucico" "cu" "uucp" "uuname" "uustat" "uux" "uuxqt"]);
308
309 nixpkgs.overlays = [(self: super: {
310 uucp = super.stdenv.lib.overrideDerivation super.uucp (oldAttrs: {
311 configureFlags = "--with-newconfigdir=/etc/uucp";
312 patches = [
313 (super.writeText "mailprogram" ''
314 policy.h | 2 +-
315 1 file changed, 1 insertion(+), 1 deletion(-)
316
317 diff --git a/policy.h b/policy.h
318 index 5afe34b..8e92c8b 100644
319 --- a/policy.h
320 +++ b/policy.h
321 @@ -240,7 +240,7 @@
322 the sendmail choice below. Otherwise, select one of the other
323 choices as appropriate. */
324 #if 1
325 -#define MAIL_PROGRAM "/usr/lib/sendmail -t"
326 +#define MAIL_PROGRAM "${config.security.wrapperDir}/sendmail -t"
327 /* #define MAIL_PROGRAM "/usr/sbin/sendmail -t" */
328 #define MAIL_PROGRAM_TO_BODY 1
329 #define MAIL_PROGRAM_SUBJECT_BODY 1
330 '')
331 ];
332 });
333 rmail = super.writeScriptBin "rmail" ''
334 #!${super.stdenv.shell}
335
336 # Dummy UUCP rmail command for postfix/qmail systems
337
338 IFS=" " read junk from junk junk junk junk junk junk junk relay
339
340 case "$from" in
341 *[@!]*) ;;
342 *) from="$from@$relay";;
343 esac
344
345 exec ${config.security.wrapperDir}/sendmail -G -i -f "$from" -- "$@"
346 '';
347 })];
348
349 environment.systemPackages = with pkgs; [
350 uucp
351 ];
352
353 systemd.services."uucico@" = {
354 serviceConfig = {
355 User = "uucp";
356 Type = "oneshot";
357 ExecStart = "${config.security.wrapperDir}/uucico -D -S %i";
358 };
359 };
360
361 systemd.timers."uucico@" = {
362 timerConfig.OnActiveSec = cfg.interval;
363 timerConfig.OnUnitActiveSec = cfg.interval;
364 };
365
366 systemd.targets."multi-user" = {
367 wants = mapAttrsToList (name: node: "uucico@${name}.timer") cfg.remoteNodes;
368 };
369
370 systemd.kill-user.enable = true;
371 systemd.targets."sleep" = {
372 after = [ "kill-user@uucp.service" ];
373 wants = [ "kill-user@uucp.service" ];
374 };
375
376 networking.networkmanager.dispatcherScripts = optional cfg.nmDispatch {
377 type = "basic";
378 source = pkgs.writeScript "callRemotes.sh" ''
379 #!${pkgs.stdenv.shell}
380
381 shopt -s extglob
382
383 case "''${2}" in
384 (?(vpn-)up)
385 ${concatStringsSep "\n " (mapAttrsToList (name: node: "${pkgs.systemd}/bin/systemctl start uucico@${name}.service") cfg.remoteNodes)}
386 ;;
387 esac
388 '';
389 };
390 };
391}
diff --git a/modules/yggdrasil/default.nix b/modules/yggdrasil/default.nix
new file mode 100644
index 00000000..91a550d6
--- /dev/null
+++ b/modules/yggdrasil/default.nix
@@ -0,0 +1,49 @@
1{ config, lib, customUtils, ... }:
2let
3 cfg = config.services.tinc.yggdrasil;
4in {
5 options = {
6 services.tinc.yggdrasil = lib.mkOption {
7 type = lib.types.submodule {
8 options = {
9 enable = lib.mkEnableOption "Yggdrasil tinc network";
10
11 connect = lib.mkOption {
12 default = true;
13 type = lib.types.bool;
14 description = ''
15 Connect to central server
16 '';
17 };
18 };
19 };
20 };
21 };
22
23 config = lib.mkIf cfg.enable {
24 services.tinc.networks.yggdrasil = {
25 name = config.networking.hostName;
26 hostSettings = customUtils.recImport { dir = ./hosts; };
27 debugLevel = 2;
28 interfaceType = "tap";
29 settings = {
30 Mode = "switch";
31 PingTimeout = 30;
32 ConnectTo = lib.mkIf cfg.connect "ymir";
33 };
34 };
35
36 sops.secrets = {
37 tinc-yggdrasil-rsa = {
38 key = "rsa";
39 path = "/etc/tinc/yggdrasil/rsa_key.priv";
40 sopsFile = ./hosts + "/${config.services.tinc.networks.yggdrasil.name}/private-keys.yaml";
41 };
42 tinc-yggdrasil-ed25519 = {
43 key = "ed25519";
44 path = "/etc/tinc/yggdrasil/rsa_key.priv";
45 sopsFile = ./hosts + "/${config.services.tinc.networks.yggdrasil.name}/private-keys.yaml";
46 };
47 };
48 };
49}
diff --git a/modules/yggdrasil/hosts/sif/default.nix b/modules/yggdrasil/hosts/sif/default.nix
new file mode 100644
index 00000000..32b844de
--- /dev/null
+++ b/modules/yggdrasil/hosts/sif/default.nix
@@ -0,0 +1,13 @@
1{
2 settings.Ed25519PublicKey = "qJqty+wiTNcYaHQCvQNiMqXYz30C9M3+LI/qjmU/9hK";
3 rsaPublicKey = ''
4 -----BEGIN RSA PUBLIC KEY-----
5 MIIBCgKCAQEA0ACaacg9EN0hBQct8ZwQ/i6EsXKP4DIwKwabM2rp8azValTHU2uI
6 WW6JRY+Eii6zRx9B5kJ96C4rJJeAGV6lZPAogaC2LbM7lcsZ7oRDWZGaQKcZFNGi
7 laEcDg2dRuDx1W4at0rb03SDLNPt8sXSV6BcK9n/7m7+s9cwM/+PB8FHDMnWvwbC
8 usbP23020s+CVr/PU1z/7J0y3Eat+Acut6x5X8DNewpqV96wQpqdAggbhtYERMFH
9 +i0sa1WUDQtJ6HGChbENRTMlsPJ6lnzXY+J0pzatzzvetLsOljES9uJ8dtk6qBC7
10 KRZo5lvdUwR6j9XiHMQeRerUt23b9ATFXQIDAQAB
11 -----END RSA PUBLIC KEY-----
12 '';
13}
diff --git a/modules/yggdrasil/hosts/sif/private-keys.yaml b/modules/yggdrasil/hosts/sif/private-keys.yaml
new file mode 100644
index 00000000..9be82bc1
--- /dev/null
+++ b/modules/yggdrasil/hosts/sif/private-keys.yaml
@@ -0,0 +1,34 @@
1ed25519: ENC[AES256_GCM,data:1CqB4y6CIm5JUsznpXPqqLJqCKmmoAJOZQTWb7+Jbn0oZMX27qSMK4CchHF7Bmo24EK8rk5EyW5aQLnoxp/2NA62p8SXdaoI8Qgz3EgsQ5QrlJrt1jvERpNs4vttT9V6+aK3Yojr9IuQSvJ4jyKSLrzrTnLzF9pXlaOf1Ru5SxySRWtVzynzurRpdUVS6goE+lb+Irg6x2geV719iQ9bu1C2smeQDREdS+dlfoxp02/pU6kTFA7KAm5vA91HKEfMqfSEzuBgUB0=,iv:n6Yh0zZ9AbT+83P42QNO2rCCISJV5nbO9wYcwaRYD2E=,tag:dJpXV9ZzLSO1B+LsyV3vAg==,type:str]
2rsa: ENC[AES256_GCM,data:7faQJAhoYt3MJidg4TVwysmLGZ4V1fA9NYYKgEMgky4q0Q9tBGhEsA60uj7iKcMMRhGku7feIFkj2+1qjKy+e1Bajfs2rqxgyqYmM6yOTrmorbXBVyrPOTOwJp3yp7O1vIXwoUS9vWIYxFszpfaLL0/8aARYVrYmpxf3gsBfQ4LciM1VKEgjG3uRBf1tDLaNuMNyzdan0DFghwuDojPOXUFv/6yuPxU2U0TagVjwAk4FThGwEasvV454RSm/GmqYtX+P4Vc3pEWNYAK1rXJAuXm1392Uash+HGQ+3ln5N9yWneewgPPr0pePAugxxN0qnwhy5MRKGQE3ZHCZ0beslfOm6pkmYTfww3lKNIJGabMfMD3COoAI7zWebUvksZPsgH6f1olbzABkZdS1s//WNMnWQHGxsWePXkLFe8bfnNXouEXHtLvQ7On0KPyt8y5QBI9bDPpTn92/O9jCevXSttrez4buBdCHFmCE8xgW5JKKEXgMubPPjEF3MABiGu0TMeWM4a1ibY7HfvNrRkO1pE9RhdRT/dFV/MrPxk7P0k16x9H4+QnE7VglfNZO3Wd3bnYxcH7hmAbIzpFnUJvolyNfmynwL2WwaYuBskXASD1FuqpM0tbhantqGyHVPe62+KimU0zDAJ1HMyqhIN0MD1MSXsdoItAsw033GYLB83L8xPatARJR9qEdKwrhmgSDY36AbJ8VI/RUzicZoYdhK8+M7bNGIkD5MgrQO35q+3oa6Xcib+5MtW0RVJKLP4y5/XNkjd4EPl6nahcVi63/FG7LJmO+/I7bkLIAWmIq8BHcXEwbz0womYp404pSfEPr3cy1N5S3yqRdzVxavTJb0PLMpHq2rWuHK2DIY77hEOAt0XcReWYsRkmTl+v9iQLF+D4GBLr+O2oZNJrocNVZYkfdjsrUd2cUOCV7ZQphO5Yc+yKrqzmCqUUvdoJ3vlaPxMXx4LACeMImo1sAFxoOgIpyfklo/bdhi9osiL55I8pAIh5hGes/uCbwaRnW+wbaYcMliCuUO8XelfXwBot8W+0l0wk2zKRSKtYKcX1n/Ax5mIt6mIoQkvyL82lccS9ppJLjt7DYlvK8L6imeV11ATf1ZhSGB3c67/XYik5BXz827Rj29K6fg/CvU65f/bEAuE39gSJ4mHsRl3bvkNLiUMEBrDuZnText33fCbqVA5DUIfqSbLUzXtqNl8vHnlOBICYwjv8PtUMJ6VTCDu33SmtQzJAfnmuewOKAC51FPsyaDhouTKllUaqx34NfEP8k2C8/4oNPgDcLjInm3f43tIuJbScdp8ltNVCLoChS8jbBOvrVYTI0eP+BuAuEfWYldUYq96oH/x9d0yvPqZ1rnwmqg4y6GfkACw6+/QvrDdtcM+1uI86RxZ7KGurb8KG7NPdSWhzz+72+TO5Tq29K8QETLzzalnVzaVWj/xGsjgkslxmDMKxLJQw0o24lgg/R30aU9BL6YwDVi10nu+Tv5kayb/NVLdMNWxfKNg1KZcf8M2ApgonjingbpUlinZ25/IIcQB9lMT4HSyvtGtIqnsPL4SQNsgBLcMzdwbL0EvS3qMAEVWKfUm2v9AA2+RMsKEKtD4UNF2xF7oACJiyTcw/xUOmkaTIZZ2ev0JVb4IYs1qx5Skz+IMAvWQ2FjBMXna5e/LYgBl6kdLSTcDvlymHpbjjuRdRq+uq+ZMXIACyZ+qUnZ0qcfWGPxOCI0hXPc5ac/zSGkPKYiWT/rCSuo+MoijjK4YZ2fub9TCYjZRS+QvLlXOM8F06Or0jQQOveezqJFZdoBGj248BtcPAVbYqfaytIlYjARlhQL/lKaaOrbONk6kIlDpwkhlzO50OkhALItlbW4Aa8zZ/WeXkfkb/6A7NLce42XDoOnvZt9UdYVTRphf8yxjRE2YMwZsmeTIieg8KwwJdnoJIhiQFdVDFgXb2xPZA2CbdvZwGwuFkLWgJUg6H+aHdw39UnNM+S9PYaOQ9oaS7IyeWhXMgP7TKM98uILsBg/Xn9tafHaslQfjVRDEaYtrmDZMYhb+h/MZKngx7uwmUyqHszAYN/M+RMJVy3s4uBu/EufWYVMorunpPEXGYA4Rg1HUuAOvWSvpM3PJG9Wnrazw6xmkwIUSKju5irpWATYmqSX3pPkG5C0sTatszVDAvTs9+/9Xdbney7/6QskSHMph8Kn/Udpq7PPrZWADkIi1k4oibgABOXOWBk5ZbNbiDrZA==,iv:ZUAqvOpcVCXQD2PFzUh0e2m20t6gVT3mYb7S50iV/m8=,tag:AssxMqjVUEwQ4R6Y7eG9Tg==,type:str]
3sops:
4 kms: []
5 gcp_kms: []
6 azure_kv: []
7 hc_vault: []
8 lastmodified: '2021-01-02T14:46:16Z'
9 mac: ENC[AES256_GCM,data:Phng7z7UlE6nO3FFIQPOHgKCqDm2uOGL57ryJbokjipSSdoWPinpz0zIJv9Z67b9uOf3CQoGtV4YwcudNkzDBKOyD8uA6RYwCKpbYcZIdiy8DLL46+VT/wq9toTkeDXM6jKupzzOARZhHT8DCOLqW7u8Q3S645cbTJmw0+LMIGk=,iv:y4KEh0+bKhtnSobKVdfaPuRsueNC1lcrEbUGfEAn+Bg=,tag:3Oi4e/hSgPVsoFQpnVQj+g==,type:str]
10 pgp:
11 - created_at: '2021-01-02T14:45:04Z'
12 enc: |
13 -----BEGIN PGP MESSAGE-----
14
15 hF4Dgwm4NZSaLAcSAQdAwWM12Zara3T2xDIX3rhakGxXFyme4LE5QZgE2GjnnWEw
16 T/vhPfsKFCjA2kAmj41NupjvTPL/nzfd7+MrdHRfC462Jrq+UF1W8A4bUa3OMH5J
17 0l4BuFhl93w/VBftvnG8oSBAFCPNDapNADjTVJQStgsZa0/uD93NnCxyQmtuJYsQ
18 URlH0KMT6Kouaec4qk3SqkAHzaIIAukahBHAPf2C5cvXYw7AAOOBOdRaWycsmZDc
19 =S4Ig
20 -----END PGP MESSAGE-----
21 fp: F1AF20B9511B63F681A14E8D51AEFBCD1DEF68F8
22 - created_at: '2021-01-02T14:45:04Z'
23 enc: |
24 -----BEGIN PGP MESSAGE-----
25
26 hF4DXxoViZlp6dISAQdA7apd+ipJ0lUiuPI5Sq6uj6iOQYFfuNDuzse1JFJMfn4w
27 McsGPcbMorZV0OVFmg9vuZ0GP9sb7mkm+oRuY9OeMDEifjWGHJ2UN4TvdEcCO1zx
28 0l4BvYyzFbShlQjge7+nrzVi2lzEvqsozEW76K3arWb/iYLCRyl0/Vhw5WT4K/UE
29 fw4cbqz7JrogVLFNeWSRPk3Y+Dg4Pf9rQnw1EJhUEIczYjnfajPhYe5K4M01mOby
30 =B0n7
31 -----END PGP MESSAGE-----
32 fp: 30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51
33 unencrypted_suffix: _unencrypted
34 version: 3.6.1
diff --git a/modules/yggdrasil/hosts/ymir.nix b/modules/yggdrasil/hosts/ymir.nix
new file mode 100644
index 00000000..b77a9216
--- /dev/null
+++ b/modules/yggdrasil/hosts/ymir.nix
@@ -0,0 +1,19 @@
1{
2 addresses = [{ address = "ymir.yggdrasil.li"; }];
3 settings.Ed25519PublicKey = "b/SobnMqByzHOQeO+iU7OZ1liD8a++knbi5ebNawnaC";
4 rsaPublicKey = ''
5 -----BEGIN RSA PUBLIC KEY-----
6 MIICCgKCAgEAuInSfQf5euFXEVkLLzf9TumQJ+3WRsxX4uKdOXBqrIC7yjSBP8j9
7 ql5rNWPzgXxFF5ERmwW+E3cyzJLU9Htu7r3muqM6nhSZizhCskifPRFc3e5ssSke
8 XhHICHfe90+qvab/hWx/NjkW59bBYIzDuJfq+ijDFMVNgOxaiM2f3/2prUUhP7bN
9 r3wVI8KCkOaknc0SOOmOhLzfJaD5wosqLOjgaNhlro2eMgMjQlxbyW8dVVgjwseR
10 Cl/mpu7r1pSMhS66RFH68wDoC3X81f7Zs9ZGDLTD8KXWhx0qgUMUAH4n6YGY0RM6
11 BZ3qR/3KFRU64QPVAERpb0JdsU9ggCVydHkjrWW23ptHOPAOO5+yQj7tSDCKTRy9
12 dHMQnbtPrgAb6iMhO1XTxA8Hdta1sCHsewsQekarwsA1bmk3hTgi/k8vwoGDUWtk
13 jgiDEPuutfmH4C6qxq9s+6lRboNKH8wgkVGpHiaq7mmePFdhzFdrj4+fYAMZTbil
14 2iygsJ+yFOjA7U+iT6QDK33/MLsrQg0Ue6RPiG1qnDyax7gBAjz52iWkiuSkUXk0
15 E5ImdP4XMILgGcWk8iPq5iRS03edE0pCpxGX3ZZwFE5+CoXgO6wR1ToL1vZEEHMQ
16 SHJPufKjkavPKbejPps/mLaJQVw3W10PAJssB9nxW2aHX3n0ugGaIvMCAwEAAQ==
17 -----END RSA PUBLIC KEY-----
18 '';
19}
diff --git a/overlays/nerdfonts.nix b/overlays/nerdfonts.nix
new file mode 100644
index 00000000..28581d72
--- /dev/null
+++ b/overlays/nerdfonts.nix
@@ -0,0 +1,5 @@
1final: prev: {
2 nerdfonts = prev.nerdfonts.override {
3 fonts = ["FiraMono" "FiraCode"];
4 };
5}
diff --git a/overlays/nvidia-kernel-5.7.nix b/overlays/nvidia-kernel-5.7.nix
new file mode 100644
index 00000000..92d3abb3
--- /dev/null
+++ b/overlays/nvidia-kernel-5.7.nix
@@ -0,0 +1,19 @@
1final: prev: {
2 linuxPackages_latest = prev.linuxPackages_latest.extend (self: super: {
3 nvidiaPackages = super.nvidiaPackages // {
4 stable = super.nvidiaPackages.stable.overrideAttrs (attrs: {
5 patches = [
6 (prev.fetchpatch {
7 name = "nvidia-kernel-5.7.patch";
8 url = "https://gitlab.com/snippets/1965550/raw";
9 sha256 = "03iwxhkajk65phc0h5j7v4gr4fjj6mhxdn04pa57am5qax8i2g9w";
10 })
11 ];
12
13 passthru = {
14 inherit (super.nvidiaPackages.stable) settings persistenced persistencedVersion settingsVersion;
15 };
16 });
17 };
18 });
19}
diff --git a/overlays/persistent-nix-shell/default.nix b/overlays/persistent-nix-shell/default.nix
new file mode 100644
index 00000000..60396335
--- /dev/null
+++ b/overlays/persistent-nix-shell/default.nix
@@ -0,0 +1,19 @@
1final: prev: {
2 persistent-nix-shell = prev.stdenv.mkDerivation {
3 name = "persistent-nix-shell";
4 src = ./persistent-nix-shell;
5
6 phases = [ "buildPhase" "installPhase" ];
7
8 inherit (final) zsh;
9
10 buildPhase = ''
11 substituteAll $src persistent-nix-shell
12 '';
13
14 installPhase = ''
15 install -m 0755 -D -t $out/bin \
16 persistent-nix-shell
17 '';
18 };
19}
diff --git a/overlays/persistent-nix-shell/persistent-nix-shell b/overlays/persistent-nix-shell/persistent-nix-shell
new file mode 100644
index 00000000..a17f6de6
--- /dev/null
+++ b/overlays/persistent-nix-shell/persistent-nix-shell
@@ -0,0 +1,20 @@
1#!@zsh@/bin/zsh
2
3set -e
4
5gcrootsDir=${PWD}/.nix-gc-roots
6
7if [[ ${#@} -ge 1 ]]; then
8 shellFile=${1}
9 shift
10else
11 shellFile=${PWD}/shell.nix
12fi
13
14set -x
15mkdir -p ${gcrootsDir}
16nix-instantiate ${shellFile} --indirect --add-root ${gcrootsDir}/shell.drv
17nix-store --indirect --add-root ${gcrootsDir}/shell.dep --realise $(nix-store --query --references ${gcrootsDir}/shell.drv)
18set +x
19
20exec nix-shell $(readlink ${gcrootsDir}/shell.drv) ${@}
diff --git a/overlays/pidgin.nix b/overlays/pidgin.nix
new file mode 100644
index 00000000..d346c7a1
--- /dev/null
+++ b/overlays/pidgin.nix
@@ -0,0 +1,15 @@
1final: prev:
2let
3 mucHistory = prev.fetchpatch {
4 url = "https://developer.pidgin.im/raw-attachment/ticket/16524/0001-only-request-unseed-chat-history-from-jabber-group-c.patch";
5 sha256 = "083wvmq7417xz55fxxhllqwql1hgjvin2sak08844121yw1jvc44";
6 };
7in {
8 pidgin-with-plugins = import (/. + prev.path + "/pkgs/applications/networking/instant-messengers/pidgin/wrapper.nix") {
9 inherit (prev) makeWrapper symlinkJoin;
10 plugins = with final; [ purple-lurch pidgin-carbons pidgin-opensteamworks pidgin-xmpp-receipts ];
11 pidgin = prev.pidgin.overrideAttrs (oldAttrs: {
12 patches = (oldAttrs.patches or []) ++ [mucHistory];
13 });
14 };
15}
diff --git a/overlays/urxvt/52-osc.pl b/overlays/urxvt/52-osc.pl
new file mode 100644
index 00000000..3292e8c4
--- /dev/null
+++ b/overlays/urxvt/52-osc.pl
@@ -0,0 +1,41 @@
1#! perl
2
3=head1 NAME
4
552-osc - Implement OSC 32 ; Interact with X11 clipboard
6
7=head1 SYNOPSIS
8
9 urxvt -pe 52-osc
10
11=head1 DESCRIPTION
12
13This extension implements OSC 52 for interacting with system clipboard
14
15Most code stolen from:
16http://ailin.tucana.uberspace.de/static/nei/*/Code/urxvt/
17
18=cut
19
20use MIME::Base64;
21use Encode;
22
23sub on_osc_seq {
24 my ($term, $op, $args) = @_;
25 return () unless $op eq 52;
26
27 my ($clip, $data) = split ';', $args, 2;
28 if ($data eq '?') {
29 # my $data_free = $term->selection();
30 # Encode::_utf8_off($data_free); # XXX
31 # $term->tt_write("\e]52;$clip;".encode_base64($data_free, '')."\a");
32 }
33 else {
34 my $data_decoded = decode_base64($data);
35 Encode::_utf8_on($data_decoded); # XXX
36 $term->selection($data_decoded, $clip =~ /c|^$/);
37 $term->selection_grab(urxvt::CurrentTime, $clip =~ /c|^$/);
38 }
39
40 ()
41}
diff --git a/overlays/urxvt/default.nix b/overlays/urxvt/default.nix
new file mode 100644
index 00000000..3c57d000
--- /dev/null
+++ b/overlays/urxvt/default.nix
@@ -0,0 +1,21 @@
1final: prev: {
2 rxvt_unicode-with-plugins = prev.rxvt-unicode.override {
3 configure = { availablePlugins, ... }: {
4 plugins = [ final.urxvt_osc_52 ] ++ builtins.attrValues availablePlugins;
5 };
6 };
7 urxvt_osc_52 = prev.stdenv.mkDerivation {
8 name = "rxvt_unicode-osc_52-0";
9 src = ./52-osc.pl;
10 unpackPhase = ''
11 cp $src 52-osc
12 '';
13 buildPhase = ''
14 sed -i 's|#! perl|#! ${final.perl}/bin/perl|g' 52-osc
15 '';
16 installPhase = ''
17 mkdir -p $out/lib/urxvt/perl
18 cp 52-osc $out/lib/urxvt/perl
19 '';
20 };
21}
diff --git a/overlays/worktime/default.nix b/overlays/worktime/default.nix
new file mode 100644
index 00000000..26e1dfed
--- /dev/null
+++ b/overlays/worktime/default.nix
@@ -0,0 +1,19 @@
1final: prev: {
2 worktime = prev.stdenv.mkDerivation rec {
3 name = "worktime";
4 src = ./worktime.py;
5
6 phases = [ "buildPhase" "installPhase" ];
7
8 python = prev.python37.withPackages (ps: with ps; [pyxdg dateutil uritools requests configparser]);
9
10 buildPhase = ''
11 substituteAll $src worktime
12 '';
13
14 installPhase = ''
15 install -m 0755 -D -t $out/bin \
16 worktime
17 '';
18 };
19}
diff --git a/overlays/worktime/worktime.py b/overlays/worktime/worktime.py
new file mode 100755
index 00000000..9e514e65
--- /dev/null
+++ b/overlays/worktime/worktime.py
@@ -0,0 +1,387 @@
1#!@python@/bin/python
2
3import requests
4from requests.exceptions import HTTPError
5from requests.auth import HTTPBasicAuth
6from datetime import *
7from xdg import (BaseDirectory)
8import configparser
9from uritools import uricompose
10
11from dateutil.easter import *
12from dateutil.tz import *
13from dateutil.parser import isoparse
14
15from enum import Enum
16
17from math import (copysign, ceil)
18
19import calendar
20
21import argparse
22
23from copy import deepcopy
24
25class TogglAPISection(Enum):
26 TOGGL = '/api/v8'
27 REPORTS = '/reports/api/v2'
28
29class TogglAPIError(Exception):
30 def __init__(self, http_error, response):
31 self.http_error = http_error
32 self.response = response
33
34 def __str__(self):
35 if not self.http_error is None:
36 return str(self.http_error)
37 else:
38 return self.response.text
39
40class TogglAPI(object):
41 def __init__(self, api_token, workspace_id):
42 self._api_token = api_token
43 self._workspace_id = workspace_id
44
45 def _make_url(self, api=TogglAPISection.TOGGL, section=['time_entries', 'current'], params={}):
46 if api is TogglAPISection.REPORTS:
47 params.update({'user_agent': 'worktime', 'workspace_id': self._workspace_id})
48
49 api_path = api.value
50 section_path = '/'.join(section)
51 uri = uricompose(scheme='https', host='www.toggl.com', path=f"{api_path}/{section_path}", query=params)
52
53 return uri
54
55 def _query(self, url, method):
56
57 headers = {'content-type': 'application/json'}
58 response = None
59
60 if method == 'GET':
61 response = requests.get(url, headers=headers, auth=HTTPBasicAuth(self._api_token, 'api_token'))
62 elif method == 'POST':
63 response = requests.post(url, headers=headers, auth=HTTPBasicAuth(self._api_token, 'api_token'))
64 else:
65 raise ValueError(f"Undefined HTTP method “{method}”")
66
67 response.raise_for_status()
68
69 return response
70
71 def get_billable_hours(self, start_date, end_date=datetime.now(timezone.utc), rounding=False):
72 url = self._make_url(api = TogglAPISection.REPORTS, section = ['summary'], params={'since': start_date.astimezone(timezone.utc).isoformat(), 'until': end_date.astimezone(timezone.utc).isoformat(), 'rounding': rounding})
73 r = self._query(url = url, method='GET')
74 if not r or not r.json():
75 raise TogglAPIError(r)
76
77 return timedelta(milliseconds=r.json()['total_billable']) if r.json()['total_billable'] else timedelta(milliseconds=0)
78
79 def get_running_clock(self, now=datetime.now(timezone.utc)):
80 url = self._make_url(api = TogglAPISection.TOGGL, section = ['time_entries', 'current'])
81 r = self._query(url = url, method='GET')
82
83 if not r or not r.json():
84 raise TogglAPIError(r)
85
86 if not r.json()['data'] or not r.json()['data']['billable']:
87 return None
88
89 start = isoparse(r.json()['data']['start'])
90
91 return now - start if start <= now else None
92
93class Worktime(object):
94 time_worked = timedelta()
95 running_entry = None
96 now = datetime.now(tzlocal())
97 time_pulled_forward = timedelta()
98 is_workday = False
99 include_running = True
100 time_to_work = None
101 force_day_to_work = True
102
103 def __init__(self, start_datetime=None, end_datetime=None, now=None, include_running=True, force_day_to_work=True, **kwargs):
104 self.include_running = include_running
105 self.force_day_to_work = force_day_to_work
106
107 if now:
108 self.now = now
109
110 config = configparser.ConfigParser()
111 config_dir = BaseDirectory.load_first_config('worktime')
112 config.read(f"{config_dir}/worktime.ini")
113 api = TogglAPI(api_token=config['TOGGL']['ApiToken'], workspace_id=config['TOGGL']['Workspace'])
114 date_format = config.get('WORKTIME', 'DateFormat', fallback='%Y-%m-%d')
115
116 start_date = start_datetime or datetime.strptime(config['WORKTIME']['StartDate'], date_format).replace(tzinfo=tzlocal())
117 end_date = end_datetime or self.now
118
119 try:
120 with open(f"{config_dir}/reset", 'r') as reset:
121 for line in reset:
122 stripped_line = line.strip()
123 reset_date = datetime.strptime(stripped_line, date_format).replace(tzinfo=tzlocal())
124
125 if reset_date > start_date and reset_date < end_date:
126 start_date = reset_date
127 except IOError as e:
128 if e.errno != 2:
129 raise e
130
131
132 hours_per_week = float(config.get('WORKTIME', 'HoursPerWeek', fallback=40))
133 workdays = set([int(d.strip()) for d in config.get('WORKTIME', 'Workdays', fallback='1,2,3,4,5').split(',')])
134 time_per_day = timedelta(hours = hours_per_week) / len(workdays)
135
136 holidays = dict()
137
138 for year in range(start_date.year, end_date.year + 1):
139 y_easter = datetime.combine(easter(year), time(), tzinfo=tzlocal())
140
141 # Legal holidays in munich, bavaria
142 holidays[datetime(year, 1, 1, tzinfo=tzlocal()).date()] = time_per_day
143 holidays[datetime(year, 1, 6, tzinfo=tzlocal()).date()] = time_per_day
144 holidays[(y_easter+timedelta(days=-2)).date()] = time_per_day
145 holidays[(y_easter+timedelta(days=+1)).date()] = time_per_day
146 holidays[datetime(year, 5, 1, tzinfo=tzlocal()).date()] = time_per_day
147 holidays[(y_easter+timedelta(days=+39)).date()] = time_per_day
148 holidays[(y_easter+timedelta(days=+50)).date()] = time_per_day
149 holidays[(y_easter+timedelta(days=+60)).date()] = time_per_day
150 holidays[datetime(year, 8, 15, tzinfo=tzlocal()).date()] = time_per_day
151 holidays[datetime(year, 10, 3, tzinfo=tzlocal()).date()] = time_per_day
152 holidays[datetime(year, 11, 1, tzinfo=tzlocal()).date()] = time_per_day
153 holidays[datetime(year, 12, 25, tzinfo=tzlocal()).date()] = time_per_day
154 holidays[datetime(year, 12, 26, tzinfo=tzlocal()).date()] = time_per_day
155
156 try:
157 with open(f"{config_dir}/excused", 'r') as excused:
158 for line in excused:
159 stripped_line = line.strip()
160 if stripped_line:
161 splitLine = stripped_line.split(' ')
162 if len(splitLine) == 2:
163 [hours, date] = splitLine
164 day = datetime.strptime(date, date_format).replace(tzinfo=tzlocal()).date()
165 holidays[day] = timedelta(hours = float(hours))
166 else:
167 holidays[datetime.strptime(stripped_line, date_format).replace(tzinfo=tzlocal()).date()] = time_per_day
168 except IOError as e:
169 if e.errno != 2:
170 raise e
171
172 pull_forward = dict()
173
174 start_day = start_date.date()
175 end_day = end_date.date()
176
177 try:
178 with open(f"{config_dir}/pull-forward", 'r') as excused:
179 for line in excused:
180 stripped_line = line.strip()
181 if stripped_line:
182 [hours, date] = stripped_line.split(' ')
183 constr = date.split(',')
184 for d in [start_day + timedelta(days = x) for x in range(0, (end_day - start_day).days + 1 + int(timedelta(hours = float(hours)).total_seconds() / 60 * (7 / len(workdays)) * 2))]:
185 for c in constr:
186 if c in calendar.day_abbr:
187 if not d.strftime('%a') == c: break
188 elif "--" in c:
189 [fromDay,toDay] = c.split('--')
190 if fromDay != "":
191 fromDay = datetime.strptime(fromDay, date_format).replace(tzinfo=tzlocal()).date()
192 if not fromDay <= d: break
193 if toDay != "":
194 toDay = datetime.strptime(toDay, date_format).replace(tzinfo=tzlocal()).date()
195 if not d <= toDay: break
196 else:
197 if not d == datetime.strptime(c, date_format).replace(tzinfo=tzlocal()).date(): break
198 else:
199 if d >= end_date.date():
200 pull_forward[d] = min(timedelta(hours = float(hours)), time_per_day - (holidays[d] if d in holidays else timedelta()))
201 except IOError as e:
202 if e.errno != 2:
203 raise e
204
205 days_to_work = dict()
206
207 if pull_forward:
208 end_day = max(end_day, max(list(pull_forward)))
209
210 for day in [start_day + timedelta(days = x) for x in range(0, (end_day - start_day).days + 1)]:
211 if day.isoweekday() in workdays:
212 time_to_work = time_per_day
213 if day in holidays.keys():
214 time_to_work -= holidays[day]
215 if time_to_work > timedelta():
216 days_to_work[day] = time_to_work
217
218 extra_days_to_work = dict()
219
220 try:
221 with open(f"{config_dir}/days-to-work", 'r') as extra_days_to_work_file:
222 for line in extra_days_to_work_file:
223 stripped_line = line.strip()
224 if stripped_line:
225 splitLine = stripped_line.split(' ')
226 if len(splitLine) == 2:
227 [hours, date] = splitLine
228 day = datetime.strptime(date, date_format).replace(tzinfo=tzlocal()).date()
229 extra_days_to_work[day] = timedelta(hours = float(hours))
230 else:
231 extra_days_to_work[datetime.strptime(stripped_line, date_format).replace(tzinfo=tzlocal()).date()] = time_per_day
232 except IOError as e:
233 if e.errno != 2:
234 raise e
235
236
237 self.is_workday = self.now.date() in days_to_work or self.now.date() in extra_days_to_work
238
239 self.time_worked = timedelta()
240
241 if self.include_running:
242 self.running_entry = api.get_running_clock(self.now)
243
244 if self.running_entry:
245 self.time_worked += self.running_entry
246
247 if self.running_entry and self.include_running and self.force_day_to_work and not (self.now.date() in days_to_work or self.now.date() in extra_days_to_work):
248 extra_days_to_work[self.now.date()] = timedelta()
249
250 self.time_to_work = sum([days_to_work[day] for day in days_to_work.keys() if day <= end_date.date()], timedelta())
251 for day in [d for d in list(pull_forward) if d > end_date.date()]:
252 days_forward = set([d for d in days_to_work.keys() if d >= end_date.date() and d < day and (not d in pull_forward or d == end_date.date())])
253 extra_days_forward = set([d for d in extra_days_to_work.keys() if d >= end_date.date() and d < day and (not d in pull_forward or d == end_date.date())])
254 days_forward = days_forward.union(extra_days_forward)
255
256 extra_day_time_left = timedelta()
257 for extra_day in extra_days_forward:
258 day_time = max(timedelta(), time_per_day - extra_days_to_work[extra_day])
259 extra_day_time_left += day_time
260 extra_day_time = min(extra_day_time_left, pull_forward[day])
261 time_forward = pull_forward[day] - extra_day_time
262 if extra_day_time_left > timedelta():
263 for extra_day in extra_days_forward:
264 day_time = max(timedelta(), time_per_day - extra_days_to_work[extra_day])
265 extra_days_to_work[extra_day] += extra_day_time * (day_time / extra_day_time_left)
266
267 hours_per_day_forward = time_forward / len(days_forward) if len(days_forward) > 0 else timedelta()
268 days_forward.discard(end_date.date())
269
270 self.time_pulled_forward += time_forward - hours_per_day_forward * len(days_forward)
271
272 if end_date.date() in extra_days_to_work:
273 self.time_pulled_forward += extra_days_to_work[end_date.date()]
274
275 self.time_to_work += self.time_pulled_forward
276
277 self.time_worked += api.get_billable_hours(start_date, self.now, rounding = config.getboolean('WORKTIME', 'rounding', fallback=True))
278
279def worktime(**args):
280 worktime = Worktime(**args)
281
282 def format_worktime(worktime):
283 def difference_string(difference):
284 total_minutes_difference = round(difference / timedelta(minutes = 1))
285 (hours_difference, minutes_difference) = divmod(abs(total_minutes_difference), 60)
286 sign = '' if total_minutes_difference >= 0 else '-'
287
288 difference_string = f"{sign}"
289 if hours_difference != 0:
290 difference_string += f"{hours_difference}h"
291 if hours_difference == 0 or minutes_difference != 0:
292 difference_string += f"{minutes_difference}m"
293
294 return difference_string
295
296 difference = worktime.time_to_work - worktime.time_worked
297 total_minutes_difference = 5 * ceil(difference / timedelta(minutes = 5))
298
299 if worktime.running_entry and abs(difference) < timedelta(days = 1) and (total_minutes_difference > 0 or abs(worktime.running_entry) >= abs(difference)) :
300 clockout_time = worktime.now + difference
301 clockout_time += (5 - clockout_time.minute % 5) * timedelta(minutes = 1)
302 clockout_time = clockout_time.replace(second = 0, microsecond = 0)
303
304 if total_minutes_difference >= 0:
305 difference_string = difference_string(total_minutes_difference * timedelta(minutes = 1))
306 return "{difference_string}/{clockout_time}".format(difference_string = difference_string, clockout_time = clockout_time.strftime("%H:%M"))
307 else:
308 difference_string = difference_string(abs(total_minutes_difference) * timedelta(minutes = 1))
309 return "{clockout_time}/{difference_string}".format(difference_string = difference_string, clockout_time = clockout_time.strftime("%H:%M"))
310 else:
311 if worktime.running_entry:
312 difference_string = difference_string(abs(total_minutes_difference) * timedelta(minutes = 1))
313 indicator = '↓' if total_minutes_difference >= 0 else '↑' # '\u25b6'
314
315 return f"{indicator}{difference_string}"
316 else:
317 difference_string = difference_string(total_minutes_difference * timedelta(minutes = 1))
318 if worktime.is_workday:
319 return difference_string
320 else:
321 return f"({difference_string})"
322
323 if worktime.time_pulled_forward >= timedelta(minutes = 15):
324 worktime_no_pulled_forward = deepcopy(worktime)
325 worktime_no_pulled_forward.time_to_work -= worktime_no_pulled_forward.time_pulled_forward
326 worktime_no_pulled_forward.time_pulled_forward = timedelta()
327
328 difference_string = format_worktime(worktime)
329 difference_string_no_pulled_forward = format_worktime(worktime_no_pulled_forward)
330
331 print(f"{difference_string_no_pulled_forward}…{difference_string}")
332 else:
333 print(format_worktime(worktime))
334
335def time_worked(now, **args):
336 then = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0)
337 if now.time() == time():
338 now = now + timedelta(days = 1)
339
340 then = Worktime(**dict(args, now = then))
341 now = Worktime(**dict(args, now = now))
342
343 worked = now.time_worked - then.time_worked
344
345 if args['do_round']:
346 total_minutes_difference = 5 * ceil(worked / timedelta(minutes = 5))
347 (hours_difference, minutes_difference) = divmod(abs(total_minutes_difference), 60)
348 sign = '' if total_minutes_difference >= 0 else '-'
349
350 difference_string = f"{sign}"
351 if hours_difference != 0:
352 difference_string += f"{hours_difference}h"
353 if hours_difference == 0 or minutes_difference != 0:
354 difference_string += f"{minutes_difference}m"
355
356 print(difference_string)
357 else:
358 print(worked)
359
360def diff(now, **args):
361 now = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0)
362 then = now - timedelta.resolution
363
364 then = Worktime(**dict(args, now = then, include_running = False))
365 now = Worktime(**dict(args, now = now, include_running = False))
366
367 print(now.time_to_work - then.time_to_work)
368
369
370def main():
371 parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using toggl API')
372 parser.add_argument('--time', dest = 'now', metavar = 'TIME', type = lambda s: datetime.fromisoformat(s).replace(tzinfo=tzlocal()), help = 'Time to calculate status for (default: current time)', default = datetime.now(tzlocal()))
373 parser.add_argument('--no-running', dest = 'include_running', action = 'store_false')
374 parser.add_argument('--no-force-day-to-work', dest = 'force_day_to_work', action = 'store_false')
375 subparsers = parser.add_subparsers(help = 'Subcommands')
376 parser.set_defaults(cmd = worktime)
377 time_worked_parser = subparsers.add_parser('time_worked', aliases = ['time', 'worked', 'today'])
378 time_worked_parser.add_argument('--no-round', dest = 'do_round', action = 'store_false')
379 time_worked_parser.set_defaults(cmd = time_worked)
380 diff_parser = subparsers.add_parser('diff')
381 diff_parser.set_defaults(cmd = diff)
382 args = parser.parse_args()
383
384 args.cmd(**vars(args))
385
386if __name__ == "__main__":
387 sys.exit(main())
diff --git a/system-profiles/default-locale.nix b/system-profiles/default-locale.nix
new file mode 100644
index 00000000..9775c095
--- /dev/null
+++ b/system-profiles/default-locale.nix
@@ -0,0 +1,7 @@
1{...}:
2{
3 i18n.defaultLocale = "en_US.UTF-8";
4 console.keyMap = "dvp";
5
6 time.timeZone = "Europe/Berlin";
7}
diff --git a/system-profiles/initrd-all-crypto-modules.nix b/system-profiles/initrd-all-crypto-modules.nix
new file mode 100644
index 00000000..6b1da298
--- /dev/null
+++ b/system-profiles/initrd-all-crypto-modules.nix
@@ -0,0 +1,7 @@
1{...}:
2{
3 boot.initrd.luks.cryptoModules = [
4 "serpent_generic" "algif_rng" "authencesn" "crct10dif_generic" "blowfish_generic" "aegis128" "crc32c_generic" "md4" "lz4hc" "cbc" "adiantum" "authenc" "seqiv" "ecdh_generic" "842" "pcbc" "curve25519-generic" "sha256_generic" "cmac" "async_tx" "async_raid6_recov" "async_memcpy" "async_xor" "gcm" "ccm" "async_pq" "sha512_generic" "echainiv" "anubis" "blowfish_common" "algif_hash" "tgr192" "ghash-generic" "crypto_simd" "michael_mic" "ansi_cprng" "cast_common" "rmd128" "sm4_generic" "twofish_common" "wp512" "zstd" "cast5_generic" "algif_skcipher" "crc32_generic" "sm3_generic" "nhpoly1305" "cryptd" "twofish_generic" "crypto_user" "af_alg" "des_generic" "rmd320" "salsa20_generic" "xts" "xxhash_generic" "ecrdsa_generic" "deflate" "rmd256" "camellia_generic" "lrw" "xor" "gf128mul" "ecc" "arc4" "crypto_engine" "ecb" "lz4" "xcbc" "aes_ti" "khazad" "streebog_generic" "cast6_generic" "blake2b_generic" "keywrap" "chacha_generic" "tea" "aes_generic" "fcrypt" "cts" "chacha20poly1305" "essiv" "hmac" "vmac" "poly1305_generic" "sha3_generic" "rmd160" "algif_aead" "ctr" "crct10dif_common" "jitterentropy_rng" "pcrypt" "serpent-avx-x86_64" "cast5-avx-x86_64" "twofish-x86_64-3way" "sha1-ssse3" "seed" "cfb" "blake2s_generic" "ofb" "cast6-avx-x86_64" "twofish-x86_64" "drbg" "serpent-sse2-x86_64" "camellia-aesni-avx2" "crct10dif-pclmul" "sha256-ssse3" "sha512-ssse3" "crc32-pclmul" "camellia-x86_64" "curve25519-x86_64" "nhpoly1305-avx2" "ghash-clmulni-intel" "poly1305-x86_64" "aegis128-aesni" "camellia-aesni-avx-x86_64" "blowfish-x86_64" "nhpoly1305-sse2" "crc32c-intel" "aesni-intel" "blake2s-x86_64" "twofish-avx-x86_64" "glue_helper" "chacha-x86_64" "serpent-avx2" "des3_ede-x86_64" "asym_tpm" "pkcs7_test_key" "tpm_key_parser"
5 "encrypted_keys"
6 ];
7}
diff --git a/system-profiles/openssh/default.nix b/system-profiles/openssh/default.nix
new file mode 100644
index 00000000..4db3d7db
--- /dev/null
+++ b/system-profiles/openssh/default.nix
@@ -0,0 +1,36 @@
1{ customUtils, lib, config, hostName, ... }:
2{
3 services.openssh = {
4 enable = true;
5 knownHosts = lib.zipAttrsWith (_name: values: builtins.head values) (lib.mapAttrsToList (name: lib.mapAttrs' (type: value: lib.nameValuePair "${name}-${type}" value)) (customUtils.recImport { dir = ./known-hosts; }));
6
7 hostKeys = [
8 { path = "/etc/ssh/ssh_host_rsa_key";
9 type = "rsa";
10 }
11 { path = "/etc/ssh/ssh_host_ed25519_key";
12 type = "ed25519";
13 }
14 ];
15 };
16
17 sops.secrets = {
18 ssh_host_rsa_key = {
19 key = "rsa";
20 path = "/etc/ssh/ssh_host_rsa_key";
21 sopsFile = ./host-keys + "/${hostName}.yaml";
22 };
23 ssh_host_ed25519_key = {
24 key = "ed25519";
25 path = "/etc/ssh/ssh_host_ed25519_key";
26 sopsFile = ./host-keys + "/${hostName}.yaml";
27 };
28 };
29
30 environment.etc = {
31 "ssh/ssh_host_rsa_key.pub".text = config.services.openssh.knownHosts."${hostName}-rsa".publicKey;
32 "ssh/ssh_host_ed25519_key.pub".text = config.services.openssh.knownHosts."${hostName}-ed25519".publicKey;
33 };
34
35 systemd.user.services."ssh-agent".enable = lib.mkForce false; # ssh-agent should be done via home-manager
36}
diff --git a/system-profiles/openssh/host-keys/sif.yaml b/system-profiles/openssh/host-keys/sif.yaml
new file mode 100644
index 00000000..ddef6dd5
--- /dev/null
+++ b/system-profiles/openssh/host-keys/sif.yaml
@@ -0,0 +1,34 @@
1ed25519: ENC[AES256_GCM,data:R7Ejs0DrCJOtEquvxuPCpwrOvV1xwCRtSMgzt7H6Dbv5z3zp94Ei8WKRPfju9dSz/4etHa1FV+1Zy7pAExWOOLU6qvaj4ZQa1FEMnJH76SN974D0hp2TON1l7QS/uRfopJJ0vnzITeCmeQcvvv0Rdz7ZUmyfPv8e76/k3h7FsGndu4wEkVg1/0a+E2dNST+/cp+l8RjXljYTiVLAByaMNz1XoK6wupef40Ce11zAGSmJS57gCwmc2yyq01sgnwex1TeDi+Pd43dTR/21n7AssqnpZGsSqpC4+RzHnxP3YGHN1dLTjHZ5fWW+zEHJZ/lG2eW4Gful+TnQ3fw2SHCjW/9BxpCjzo8GAByuJEr569fRXXAiDnmLG8GXGCpQDgSpjdkL4bFEDs0Uss3ydJEmwL3DaNkr/SHUyovwE2k5KnfIt6v0uEH+HsDSPRQpVoOgn7q3GgDmqwADfGt8MStdFVo3el/s6Rs+Q6r/ukYYu+Mon7Akv7HPGAnHDBSGOPBwy/a5Di3AA0TH/CHCmNdv,iv:HD2JAEUDz5BvZDOMAxb83UjoGZBewdePfSktD5Vh7qw=,tag:CIcXaGYLFeJrp+AU3dpStQ==,type:str]
2rsa: ENC[AES256_GCM,data:48rCmH0Id6ACaz4oGNfb72sIhfP5P8flVU4DniyTnRbaS98CIg9B3Utj7kZQYwFOOT/esQY7o/82udh8vW2j3D5eF6HfJPZa6ata5SV5b0HIZd+HMNEz2eo4FQ+ev5JWRA0I0FlYMYM/3+w855d2qolHc+voQf/+g4edlU460hx9rbhyn4injrGFdK/AxCNUwB1YXSB+UzRAV3p3U6IXd4ULQydWjJnm5lePZdBEa2q5ZKggFUVoF59fCrKPuxoRuiQTFB9UX2cRxS/MDp70m76NuxP93wGNFRfqWsVxZW7d5WH42ifLYTpIdnB0nRV4VcYdZbVxSTH2/CdRTrQ+BXut4pEsrxq91k6H3EM1FhYYDYTO+fOoKhpe/uidBTuqf3m2A7pM4zdZoTfT/0dcOxIDbtDl+2xfZEiXwXPEsoPAzLcnA23CasY0rc3I+xFgI2yIa+DFUXTiwOuqMbTRooacYVGWa5UsYO0JztizNNgp+GApxBu5fco3J2TVVBIaOpoeRZsX3BlT/8NdR3DBw00ERdj3UVBOUCNV9s++3BfnQgXDdSk/FJ0otrgXQgYxiUN6jbldP+vxt/b4S+tALu7iVApjS0UHP76zvnYotcW57UCX0Gpf9zJc2jPjxGmtIkenVSNB9ZufRg0zvGQ1qN6Ct3j/U2Ka/mfs5AE8PXYC6wFxqlOB5P5OdmVXF9srtnTirgeWbsA4Jeg+OvwAq9DbIvKDDHk0miVMu7JRsPsuCpGjgA1hHHunqFJ6vwKwKvF9k1kCmd61Kgj5MuF4BDnfRC4V22g84eO0ojJH2vU8vzjF9k5MA5zznGYgojZAtQnCXcC3k9CFbPjJE8fqk4fJ3kKoZZy+M3d4rn7JY4HZfjVs2iHZtQv7CskAi9JSULPCEirRsMnzm7jvKVUm0viS4E8dWeUoEDtMnZ1PU/IeWxlJua9hkzWmH3HqigmUq22SBTVJeb24KXyLJcvaeOsoQMOwmdJm7VUYqM8h9L8iomFf1bbExgfmhN20ACCs39sgW6wrW3ODmW85BiC/QkdApxEk65fjL+T23fN1KbXf9la/xz2FCXGUdgKMjUc+HdCtOB7VgvIehfRKodgq4l9E0zIkukT+4RdHiEA0GVga9sesLsLVtgM0UzBca7Sx9NSxEjRNrOxABohu30WPaFHoIOGg+szpD3GOGI4EpmwPK3NEjfG/RTG0ncTf910Shr9OTO69DUcoN9RVR/j6Kq7+WG9G9KxiSn2Qa0F5KFj+Pq5oIPIvGR4YkMPAvlWvvXtfTwweLr8OsNpd716WKap75iykjleeE7qGWPiFtyaiBn98VzZ9e85I8gPVCZcDTtMNXxATYDd1q0yzKV87dOL2SuzC1a2t8htb3lQuugemSfXcoLlYTdZloCClb63oLbLkDtmfd3WCpUFFlYSIxbnamixqdeLeiSL7FwkF/QpUhw0ILm+EQrZGiQ+JcOyPiiJQTDmtcAS6LbprHmapMeQCMtP3/Jk6WdfN5Jhj0x7feVJfATIoLPx0aqox7ekbU4aSzZ/8qjxlMqBcsHUEKfKCoZdVOwuklEKPhPJzGAyom/FNIVrU4EjSxcPtotMP/munJ1BWHFL5UJWPoTAZHz3Ywt50e9Fpwz9coo5NbPhRtFqVs4bOeFqJb1kq6/gPMY7J57+QSetcbBJvwVzCBsQSHoUHgvnIn7kAwM8QbfpilKZd5McL6AiGUtQbAgalXtptnnI/U59IJ8ILkdffQioM0PmzXRdTcSpe6pS6M60stQPwFE4Z9+LHr5LBuuM34YkCq1jd4EMWti+KbEFzipvdAfCYLVdacCqPXzEg3mZ00A/xCXkKk4GDpOIbC3u2T6d5UyIY6b5aX1ZJEbTa1ndVU41+Rt5z0x8tuJkqNVRK8VJ2aVqc8t+o7Ga9IRV9YcIPtDWYcVRt6zTz+DqFEmTRrnBO0AwXdT+eGrMn6X3Nj+DIjPf2B5Nve/98PyRZDRlXkcAKe5FLrVqh1d888MX7pfH+BK8I4kmFokqitxKMw63aeakjpotgN1jUdMtZR9weo6pp4TCStQwlRA41fMLgjxiiZquiajxz1zTlYKr2CVpaw6fDLkBvUw44+ZNHHn75O1/lCuT6MmK9Bu1TwsNsNd4JQSpeY0KHF6VZM0EaS3rIsnZz61X02fdpmo6Nw0mFtB4AidghSI1l+tKVVMnYJqB48eU0w+PHhdkWsi6F3Agp4L4043tWr1o4+WGBnOnx0/rNUEEu30Dtwsty0V87k1c5GRo8PKnzSHtis+QaDOL+xSIVMhsFOcuLV+VhVm84NWjgpKcCb++NInR2BbAwNRBArdIaOfQVw/HlkfnAJNWmOr6y8db1PEPNQVPVflb8bfMsZjZpBy6w1JN2Y15ZL7L0tltyUyDomT3IfMURpIW6OoyaIQS+T6NAkqq0bOllnkRnBziTNae57zl/iNg4AlWtq0NRSed0ls7Dr3yYe5Y+JvGmuW+HEA1r1/Yp9ZYQrhAc6qmAZU4+XuOgANr/4tRFA2091M05+Ow0QMquZr5odLlSyVxDU+5P/+lxb4U2ltLg/lcDeTT7qJZt3vqNVRaWg+864uUO5GFtq56x+egmYpXrgGtIRjABA6oxEHRH8RENeVMIpriv0KQ0WrGvlCnZYOMkydN1YBIX9B5gXZQBDzN3POKyo7KiBtcyMi+iOkH15AaB6G5yZymckAsfVzA/VPi/M115vjOtcqK/yyYbKpzF5ZybRlDlwsWww8aymN5i9b6q0ZI1iQoV/8P8lnal1ziYABr1s30ZqYByZ3PS1OIg6Qc/UtFT8C0OhtBPtwPEy/Xalvtqhl8/9y4Yuv6D5MNCwbL5oQ2JafEaFCOslGklg0ORCwgF+Ho5ZcZPB1tK2c424wde+KBDrBDfDrrCrYoxpeuvpFoRyy0DF3xh+25OkBfxb7+VXQxn3wXDBR7Yr1tuBLQUlbiZ0d4uPj9Pw7RA4UQTMXCRfmUiHP2nTdCbBg41M/5Cx6xaOzXJPOrkvsCZnXvNpc7+nkynsHYMqOuuWuuedh4WMU7YEPZt4CasJ5iofrhjzJNI5G5+rTMwaLTNA5AM2hi9DPqvFOaRUv/noYlTxtBT3LzUfgprPYnCKQd4j+6wO/bfbrlQq8WFH+RwFw4W0eMTJRFYFF2ctX0HOi9TOyTTHh/4l/mzTi4OaeFi5GsboDeczeh4acQCt5IjJih4qRnB7fdQy7PF8ccNuGvKWpN2mnDZJnoCrjbX8ihwzePzYPkUQ/ZKUI4KWVuixiis8b+CH0p92/dSOzGvP2MeQVMwRMpqk2EZdj3oE/gYG7RvfgM12N5ZLAUACczbwS17rquSwgznojewY+g/8oTolY81w4P/hPqX6FhWVeeZZaBJVKVDJSm4Aqp67FSYtoOHkEsOROtqklA7JVourGOgU6DfHlaSGGU6khAPv0smaYgi7+8tztNHoaaGSrtWU5+fxEzfD+NrOLm6Wz+ZZNvxWMMwrwkdeYqSc1tJACNo6CDtN/Tx+ZZCVVE+O+Qi5HFHx13c/ChQhRVMCha0VS8sYMokcZ5H2FLFLApOpwYLLFAiNwWr18PxRf3830xRwSM5bbASl9XLz2xxrbzpApDz5EDvvWJ/lPqnxYxVKCY9cEmDLuRtpEezTUpC8t6LqxhkqCTGiHjto668Mx8OTCDI8ys/D2qOFojbDyYIzl3l+AiVucXHXUrOkPJhhiOufiR6rks5UYqC6Mffb14e8JAY4IGsszmPqVY0hFS9AWp/aHuQ61stnMmhNxUw11fCDuUd4R+NvQlWSmN4Nyv0JmC6rHaa+Y/FEIQTCmFRiutTX+0nLPwJYKx+oQYklRDZ1DLtqMcF7PPUZGSFnZGGLN6UxoDhmrwz6NYwn+bBViL6iWFIgLbpRme/3TFBplDVIDUsSmbqsiysOmtzjw3y4YgiYmlZoEc//WK109KVrQUDSKKpjCMki6wsJ+27v5rlvhyBrG8QMUqWtTTB7Xu63iRrlASXcLk8Y99ehrxw/R3bjyXu5jHe0sowoKATerpiukBIOBTzPyFWQp/8MG6wWTzDM+HyGrAoCnkC3RanF56ldyqBzbqMbBIOx+fRWWuUl27YTwWLaPoZbO/+kgwzmguUj+bfqxExa4OTiMamLoZQh3LfIIJhQMd6MR1SDpRNg6D4YI7ftzrgQIe/CjjlWAfzkgeaPLx7Qeer0Te7wdJymWnQs5NBNAdWGcBgAS7/h4JZPBaqvd8KYPl2cg0Mv9k6+ogExv8YAz25rRPaJB1pKbhB5bkNIBRbUzCUhD3nk5uumvgKS0p6qn8hcD1CqEPt+ZH2CemlP5wjt9aHxplVZgG5vFZX0SREGDTSKwQoUbig1cHmRMOkO7vgS3B8eB7/J1UK3QeQe6roQSM80kbcLYH2flUmqQluGO1ZZDUttlp0ACP2lwlaBysp4X3KYPIwiHf1mXH/EQqgt305FNiKynDV6or7VWYQe5CXvTGg,iv:X57Ayvq6r0m1SGeVrBH8WCZ7TihobLLhy7spX4NIly8=,tag:caDTP5SwuWJAWGpwr9x0eQ==,type:str]
3sops:
4 kms: []
5 gcp_kms: []
6 azure_kv: []
7 hc_vault: []
8 lastmodified: '2021-01-02T19:05:26Z'
9 mac: ENC[AES256_GCM,data:yJGzs0W0R+b6WPkUaQc9cxeTBBEXot0ffUAG77Of88kREFsD5ams9qEDCs8LhPhMtLSH5L8bqMLF28n2w6d9gf41NDBl/oj+XTJE26c4D+MWF2A0fqTvwv1l3524TfavVU8iur0bCbytNfcHSZ3zCQAYElswOGupO+K0Y3hwKKI=,iv:jHSgQV6Jg2Yckp8G0Z23Ny74ZQxZ/+C/neXKrEWUVak=,tag:DhOr2cVhIq8i4JAO+fdXxA==,type:str]
10 pgp:
11 - created_at: '2021-01-02T19:04:29Z'
12 enc: |
13 -----BEGIN PGP MESSAGE-----
14
15 hF4Dgwm4NZSaLAcSAQdArkswGx9w0Rbfp1N89qALAbPMhboirsnlNvms/FomXiUw
16 taW9n4oEJ5oW2UYzNNn72SwF1jYbrqczAbxt3dM9PSz1gHFoh+ZJhGokVFJvJ7sO
17 0l4BEOkWmL/9uyOiCq574nH6OxxTPu9C4GNU8lv/Z/qJ+oAocJkGknsIJzd8M5ax
18 Fo/HqAGGfvnH3RI5FO3tTxfAKlfxlO2MJ2lsCypJuez5WewPnaTPjTbogjhzG2aQ
19 =HXLp
20 -----END PGP MESSAGE-----
21 fp: F1AF20B9511B63F681A14E8D51AEFBCD1DEF68F8
22 - created_at: '2021-01-02T19:04:29Z'
23 enc: |
24 -----BEGIN PGP MESSAGE-----
25
26 hF4DXxoViZlp6dISAQdAUSTwFAciB+Yh2IieFoN/xmQd+GU/g+cuKej6cZk78TUw
27 ETM8c1TSovML5q9usUX0pl/AbRBwp2In47RMkTn4Mul1XxJuXhgCnrc5swwYrS+h
28 0l4BOxJ3bF/yYyKfGrmc/mNe51sdHH+fgQ9IXaQhcopw4kyZqvBXhJF/oP/mhnOL
29 VMhsfg50ol1XmXVefyo5JPedbzABm3vRZv9U+/zvKNJxIro2hWchd5CxvzN4l/MR
30 =30r5
31 -----END PGP MESSAGE-----
32 fp: 30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51
33 unencrypted_suffix: _unencrypted
34 version: 3.6.1
diff --git a/system-profiles/openssh/known-hosts/sif.nix b/system-profiles/openssh/known-hosts/sif.nix
new file mode 100644
index 00000000..8326d389
--- /dev/null
+++ b/system-profiles/openssh/known-hosts/sif.nix
@@ -0,0 +1,16 @@
1let
2 hostNames = ["sif.asgard.yggdrasil" "sif.faraday.asgard.yggdrasil" "sif.midgard.yggdrasil"];
3in {
4 rsa = {
5 inherit hostNames;
6 publicKey = ''
7 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCeFqJep1CuWakcoiAkz4bSaHbAIwM89Er46o3KUpjCWGTmDmhJyBiG38pupcctH0awwElkX09GsNx230mTtjT6qcxN+vGsGMJIqFD+/7ZobSLJDHYCo6Jx23jZUjg1SqxYjwB5ooWGI61Vh6SaOy8WRrUn0q8rJyd9SEC+3tJlKO5QqRi/Vnwzj47m+YjGz2UlqJ9a4GeRh1O5SiGx5jd4a/VoeK1XJcW94XeWpPQdUGnVYUXZn9cwYVrogmXdr18ImnPxghsQg4xwS2A6KMjUw9m56XkqIq7vTslmL9JaYcjlSCHbsSVq9+Wu1oKxoyndN7Sim7SkAZwHFUEMBNlontBitgYl6z10VdKX739os6h07uXjGEs+mPk4/CkGZhvhnErV2T9FO+65jnU3mZkeX5tfJHqJ8PnDch2JD6O7+Mjpce4zs/x3mwH36peER6iiIBYGlSF0AlUDShdqj+fPWFu6gZ9piOAZ2L3YXDA0ulM6pL69SbulrUNOwtTy6LkBfKDwpaQK1KO1VOYBaKa7s+krOJXW18k+tpfo4aKSeTuwvykMPndKMKvxcsxNymkGo2AzLw017Qgshzv9rRbLNMBFd85S3krakGyBVL0HAVrAdkjvsWqj5FnHAjgBc1AZnZPbJu3g9/wm7k8rPMV0jxKMpW+zxjVFYDhFUWYp9w==
8 '';
9 };
10 ed25519 = {
11 inherit hostNames;
12 publicKey = ''
13 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfiwlzGcNQjamtIwv7fmXnddjajraeovaM6gRNui1+v
14 '';
15 };
16}
diff --git a/system-profiles/openssh/known-hosts/ymir.nix b/system-profiles/openssh/known-hosts/ymir.nix
new file mode 100644
index 00000000..f29baf1d
--- /dev/null
+++ b/system-profiles/openssh/known-hosts/ymir.nix
@@ -0,0 +1,16 @@
1let
2 hostNames = ["ymir.yggdrasil.li" "ymir.niflheim.yggdrasil"];
3in {
4 rsa = {
5 inherit hostNames;
6 publicKey = ''
7 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDNr7oFNneR3sVuAhdbnU83PuG6gTU6rDmiz+qykkRUr5Qdtm0NIr9lI7nhoO/MaALWmkMXsBGjvJ2UxvY959g0wQRHJZnuJDwOMo3YJjfuDGMTtp8ikzd646uMHQB+y/xb4dou6f0INr94eRsZcji7AQgZQnyWVV3DZuSADBfNK0Tx6sT6IdbJXaCwYoexnfSfzDdu3i5zMuReF4zdkFUEfAdcbOM8Cr0Abnn4+iLVrof/QaOEuZDC+Pf5QUhkAArETdavSCUIbV6+1md0jz/T8yalgrTCsYOoEUbSPwM/8vmiYDWSo/tvAf3KnVIPjjK2UFz7Qu0HyK0y1dBEXoYLGZ1ep4x67aE4zy7GlR2GZdAYilHknugZB+/kvYGDEixHFfcUh/uvF5PY8sm63C6HUBT1s/aQHXGHgE4uUru6YvbU3UW3fRdslABY/atZ9gc3MuKu9Zk27b1SYfAAoK1R8rKsOKWqUWvvMVCfKBNKqqb7+30q75iGeneB8Tb1C9lToyDG2Yl5p+Gpfnj8YmaU/xFm0HFEC42crRbaQyz01LmupHWf8VwH/O2LsjztAF9b4Oe2q/NwqQAF+h5hIm2tfM2fzxHGCmw1sFYf6dEdkyV5pge/IJrnuQn27iO06tRC6tvrt/ocbpwEEOk/3WWpAWW4oT8L5ceh7iAXrCRWpw==
8 '';
9 };
10 ed25519 = {
11 inherit hostNames;
12 publicKey = ''
13 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDeBBux2bIXnS/RUv+Y/NCpzI/SCW0KOJSzf48KDiEZD
14 '';
15 };
16}
diff --git a/system-profiles/sudo.nix b/system-profiles/sudo.nix
new file mode 100644
index 00000000..f2401b9f
--- /dev/null
+++ b/system-profiles/sudo.nix
@@ -0,0 +1,39 @@
1{ ... }:
2{
3 security.sudo.extraRules = [
4 { groups = "wheel";
5 commands = map (command: { inherit command; options = "NOPASSWD"; }) [
6 "/run/current-system/sw/sbin/shutdown"
7 "/run/current-system/sw/sbin/reboot"
8 "/run/current-system/sw/sbin/halt"
9 "/run/current-system/sw/bin/systemctl"
10 ];
11 }
12 ];
13
14 users.extraGroups.network = {};
15
16 security.polkit = {
17 enable = true;
18 extraConfig = ''
19 polkit.addRule(function(action, subject) {
20 if ( action.id == "org.freedesktop.systemd1.manage-units"
21 && subject.isInGroup("wheel")
22 ) {
23 return polkit.Result.YES;
24 }
25 });
26
27 polkit.addRule(function(action, subject) {
28 if ((action.id == "org.blueman.rfkill.setstate" ||
29 action.id == "org.blueman.network.setup" ||
30 action.id == "org.freedesktop.NetworkManager.settings.modify.system"
31 ) && subject.local
32 && subject.active && subject.isInGroup("network")
33 ) {
34 return polkit.Result.YES;
35 }
36 });
37 '';
38 };
39}
diff --git a/user-profiles/direnv.nix b/user-profiles/direnv.nix
new file mode 100644
index 00000000..2c1e58d6
--- /dev/null
+++ b/user-profiles/direnv.nix
@@ -0,0 +1,9 @@
1{ userName, ... }:
2{
3 home-manager.users.${userName} = {
4 programs.direnv = {
5 enable = true;
6 enableNixDirenvIntegration = true;
7 };
8 };
9}
diff --git a/user-profiles/mpv/default.nix b/user-profiles/mpv/default.nix
new file mode 100644
index 00000000..ae791668
--- /dev/null
+++ b/user-profiles/mpv/default.nix
@@ -0,0 +1,83 @@
1{ userName, pkgs, ... }:
2{
3 home-manager.users.${userName}.programs.mpv = {
4 enable = true;
5 bindings = {
6 "CTRL+n" = "af toggle \"lavfi=[dynaudnorm=f=100:g=31:s=20.0]\"";
7 };
8 config = {
9 ytdl = true;
10 ytdl-format = "bestvideo[width<=2560][height<=1440][fps<=60][protocol!=http_dash_segments]+bestaudio[protocol!=http_dash_segments]/best[width<=2560][height<=1440][fps<=60][protocol!=http_dash_segments]/best[protocol!=http_dash_segments]";
11 ytdl-raw-options = "netrc=,mark-watched=";
12 sub = false;
13 osd-font = "DejaVu Sans";
14 vo = "gpu";
15 hwdec = "auto";
16 force-window = "yes";
17 script-opts = "osc-layout=topbar,vidscale=no,deadzonesize=0.9";
18 af = "lavfi=[dynaudnorm=f=100:g=31:s=20.0]";
19 };
20 scripts = let
21 reload = pkgs.stdenv.mkDerivation rec {
22 version = "2b8a719f";
23 pname = "reload";
24 name = "${pname}-${version}";
25
26 src = pkgs.fetchFromGitHub {
27 owner = "4e6";
28 repo = "mpv-reload";
29 rev = "2b8a719fe166d6d42b5f1dd64761f97997b54a86";
30 sha256 = "19ycvnwzf8vgv0g63d4k1ll6hlfrd92is9gl8hzfic7w32ycphbg";
31 };
32
33 installPhase = ''
34 install -d $out/share/mpv/scripts
35 install -m 0644 reload.lua $out/share/mpv/scripts/${passthru.scriptName}
36 '';
37
38 passthru.scriptName = "reload.lua";
39 };
40 autosave = pkgs.stdenv.mkDerivation rec {
41 version = "0bv9wjrq";
42 pname = "autosave";
43 name = "${pname}-${version}.lua";
44
45 src = pkgs.fetchzip {
46 url = "https://gist.github.com/Hakkin/5489e511bd6c8068a0fc09304c9c5a82/archive/7a19f7cdb6dd0b1c6878b41e13b244e2503c15fc.zip";
47 sha256 = "0bv9wjrqm2ragd7rp8vw768bja2ghascwlljd6rzzf2ybi10fxs2";
48 };
49
50 installPhase = ''
51 install -d $out/share/mpv/scripts
52 install -m 0644 autosave.lua $out/share/mpv/scripts/${passthru.scriptName}
53 '';
54
55 passthru.scriptName = "autosave.lua";
56 };
57 mpris = pkgs.stdenv.mkDerivation rec {
58 version = "0.4";
59 pname = "mpv-mpris";
60 name = "${pname}-${version}.so";
61
62 src = pkgs.fetchFromGitHub {
63 owner = "hoyon";
64 repo = "mpv-mpris";
65 rev = version;
66 sha256 = "1fr3jvja8s2gdpx8qyk9r17977flms3qpm8zci62nd9r5wjdvr5i";
67 };
68
69 installPhase = ''
70 install -d $out/share/mpv/scripts
71 install -m 0644 mpris.so $out/share/mpv/scripts/${passthru.scriptName}
72 '';
73
74 nativeBuildInputs = with pkgs; [ pkgconfig glib mpv ];
75
76 passthru.scriptName = "mpris.so";
77 };
78 in [ reload
79 autosave
80 mpris
81 ];
82 };
83}
diff --git a/user-profiles/tmux/default.nix b/user-profiles/tmux/default.nix
new file mode 100644
index 00000000..9e66cadd
--- /dev/null
+++ b/user-profiles/tmux/default.nix
@@ -0,0 +1,26 @@
1{ userName, pkgs, lib, ... }:
2{
3 home-manager.users.${userName} = {
4 programs.tmux = {
5 enable = true;
6 clock24 = true;
7 historyLimit = 50000;
8 extraConfig = lib.readFile (pkgs.stdenv.mkDerivation {
9 name = "tmux.conf";
10 src = ./tmux.conf;
11
12 buildInputs = with pkgs; [ makeWrapper ];
13
14 phases = [ "installPhase" ];
15
16 inherit (pkgs) zsh;
17 mandb = pkgs.man-db;
18
19 installPhase = ''
20 substituteAll $src $out
21 '';
22 });
23 tmuxp.enable = true;
24 };
25 };
26}
diff --git a/user-profiles/tmux/tmux.conf b/user-profiles/tmux/tmux.conf
new file mode 100644
index 00000000..1403698d
--- /dev/null
+++ b/user-profiles/tmux/tmux.conf
@@ -0,0 +1,25 @@
1set-option -g history-limit 50000
2set-option -g status-bg black
3set-option -g status-fg white
4set-option -g clock-mode-colour white
5set-option -g clock-mode-style 24
6set-option -g bell-action any
7set-option -g default-shell @zsh@/bin/zsh
8set-option -g update-environment 'DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY PROMPT_INFO PATH PGHOST PGLOG'
9set-option -g mouse on
10set-option -g set-clipboard on
11set-option -g terminal-overrides 'rxvt-uni*:XT:Ms=\E]52;%p1%s;%p2%s\007'
12
13## determine if we should enable 256-colour support
14if "[[ ''${TERM} =~ 256color || ''${TERM} == fbterm ]]" 'set -g default-terminal tmux-256color'
15
16set-option -g status-right ""
17
18bind / command-prompt "split-window -h 'exec @mandb@/bin/man %%'"
19bind C clock-mode
20bind r respawn-pane -k
21
22bind -n M-Left select-pane -L
23bind -n M-Right select-pane -R
24bind -n M-Up select-pane -U
25bind -n M-Down select-pane -D \ No newline at end of file
diff --git a/user-profiles/utils.nix b/user-profiles/utils.nix
new file mode 100644
index 00000000..484af0a3
--- /dev/null
+++ b/user-profiles/utils.nix
@@ -0,0 +1,23 @@
1{ userName, pkgs, ... }:
2{
3 home-manager.users.${userName} = {
4 programs = {
5 htop = {
6 enable = true;
7 delay = 5;
8 highlightBaseName = true;
9 treeView = true;
10 };
11
12 jq.enable = true;
13 };
14
15 home.packages = with pkgs; [
16 autossh usbutils pciutils exa ag pwgen unzip magic-wormhole
17 qrencode tty-clock dnsutils openssl sshfs psmisc mosh tree
18 vnstat file pv bc fast-cli zip nmap aspell aspellDicts.de
19 aspellDicts.en borgbackup man-pages rsync socat telnet yq
20 cached-nix-shell persistent-nix-shell
21 ];
22 };
23}
diff --git a/user-profiles/zsh/default.nix b/user-profiles/zsh/default.nix
new file mode 100644
index 00000000..58fa0376
--- /dev/null
+++ b/user-profiles/zsh/default.nix
@@ -0,0 +1,30 @@
1{ userName, pkgs, customUtils, lib, config, ... }:
2let
3 dotDir = ".config/zsh";
4 p10kZsh = "${dotDir}/.p10k.zsh";
5 cfg = config.home-manager.users.${userName};
6in {
7 home-manager.users.${userName} = {
8 programs.zsh = {
9 inherit dotDir;
10 enable = true;
11 autocd = true;
12 enableCompletion = true;
13
14 plugins = [
15 { name = "powerlevel10k";
16 file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme";
17 src = pkgs.zsh-powerlevel10k;
18 }
19 ];
20 initExtraBeforeCompInit = ''
21 source "${cfg.home.homeDirectory}/${p10kZsh}"
22 '';
23 initExtra = lib.mkAfter ''
24 source "${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
25 '';
26 };
27
28 home.file.${p10kZsh}.source = ./p10k.zsh;
29 };
30}
diff --git a/user-profiles/zsh/p10k.zsh b/user-profiles/zsh/p10k.zsh
new file mode 100644
index 00000000..fb9af12c
--- /dev/null
+++ b/user-profiles/zsh/p10k.zsh
@@ -0,0 +1,1572 @@
1# Generated by Powerlevel10k configuration wizard on 2021-01-03 at 15:43 CET.
2# Based on romkatv/powerlevel10k/config/p10k-lean.zsh.
3# Wizard options: nerdfont-complete + powerline, small icons, unicode, lean, 24h time,
4# 2 lines, solid, no frame, darkest-ornaments, sparse, many icons, concise,
5# transient_prompt, instant_prompt=quiet.
6# Type `p10k configure` to generate another config.
7#
8# Config for Powerlevel10k with lean prompt style. Type `p10k configure` to generate
9# your own config based on it.
10#
11# Tip: Looking for a nice color? Here's a one-liner to print colormap.
12#
13# for i in {0..255}; do print -Pn "%K{$i} %k%F{$i}${(l:3::0:)i}%f " ${${(M)$((i%6)):#3}:+$'\n'}; done
14
15# Temporarily change options.
16'builtin' 'local' '-a' 'p10k_config_opts'
17[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases')
18[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob')
19[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand')
20'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'
21
22() {
23 emulate -L zsh -o extended_glob
24
25 # Unset all configuration options. This allows you to apply configuration changes without
26 # restarting zsh. Edit ~/.p10k.zsh and type `source ~/.p10k.zsh`.
27 unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR'
28
29 # Zsh >= 5.1 is required.
30 autoload -Uz is-at-least && is-at-least 5.1 || return
31
32 # The list of segments shown on the left. Fill it with the most important segments.
33 typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(
34 # =========================[ Line #1 ]=========================
35 # os_icon # os identifier
36 dir # current directory
37 vcs # git status
38 # =========================[ Line #2 ]=========================
39 newline # \n
40 prompt_char # prompt symbol
41 )
42
43 # The list of segments shown on the right. Fill it with less important segments.
44 # Right prompt on the last prompt line (where you are typing your commands) gets
45 # automatically hidden when the input line reaches it. Right prompt above the
46 # last prompt line gets hidden if it would overlap with left prompt.
47 typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(
48 # =========================[ Line #1 ]=========================
49 status # exit code of the last command
50 command_execution_time # duration of the last command
51 background_jobs # presence of background jobs
52 direnv # direnv status (https://direnv.net/)
53 asdf # asdf version manager (https://github.com/asdf-vm/asdf)
54 virtualenv # python virtual environment (https://docs.python.org/3/library/venv.html)
55 anaconda # conda environment (https://conda.io/)
56 pyenv # python environment (https://github.com/pyenv/pyenv)
57 goenv # go environment (https://github.com/syndbg/goenv)
58 nodenv # node.js version from nodenv (https://github.com/nodenv/nodenv)
59 nvm # node.js version from nvm (https://github.com/nvm-sh/nvm)
60 nodeenv # node.js environment (https://github.com/ekalinin/nodeenv)
61 # node_version # node.js version
62 # go_version # go version (https://golang.org)
63 # rust_version # rustc version (https://www.rust-lang.org)
64 # dotnet_version # .NET version (https://dotnet.microsoft.com)
65 # php_version # php version (https://www.php.net/)
66 # laravel_version # laravel php framework version (https://laravel.com/)
67 # java_version # java version (https://www.java.com/)
68 # package # name@version from package.json (https://docs.npmjs.com/files/package.json)
69 rbenv # ruby version from rbenv (https://github.com/rbenv/rbenv)
70 rvm # ruby version from rvm (https://rvm.io)
71 fvm # flutter version management (https://github.com/leoafarias/fvm)
72 luaenv # lua version from luaenv (https://github.com/cehoffman/luaenv)
73 jenv # java version from jenv (https://github.com/jenv/jenv)
74 plenv # perl version from plenv (https://github.com/tokuhirom/plenv)
75 phpenv # php version from phpenv (https://github.com/phpenv/phpenv)
76 scalaenv # scala version from scalaenv (https://github.com/scalaenv/scalaenv)
77 haskell_stack # haskell version from stack (https://haskellstack.org/)
78 kubecontext # current kubernetes context (https://kubernetes.io/)
79 terraform # terraform workspace (https://www.terraform.io)
80 aws # aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html)
81 aws_eb_env # aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/)
82 azure # azure account name (https://docs.microsoft.com/en-us/cli/azure)
83 gcloud # google cloud cli account and project (https://cloud.google.com/)
84 google_app_cred # google application credentials (https://cloud.google.com/docs/authentication/production)
85 context # user@hostname
86 nordvpn # nordvpn connection status, linux only (https://nordvpn.com/)
87 ranger # ranger shell (https://github.com/ranger/ranger)
88 nnn # nnn shell (https://github.com/jarun/nnn)
89 vim_shell # vim shell indicator (:sh)
90 midnight_commander # midnight commander shell (https://midnight-commander.org/)
91 nix_shell # nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html)
92 # vpn_ip # virtual private network indicator
93 # load # CPU load
94 # disk_usage # disk usage
95 # ram # free RAM
96 # swap # used swap
97 todo # todo items (https://github.com/todotxt/todo.txt-cli)
98 timewarrior # timewarrior tracking status (https://timewarrior.net/)
99 taskwarrior # taskwarrior task count (https://taskwarrior.org/)
100 time # current time
101 # =========================[ Line #2 ]=========================
102 newline
103 # ip # ip address and bandwidth usage for a specified network interface
104 # public_ip # public IP address
105 # proxy # system-wide http/https/ftp proxy
106 # battery # internal battery
107 # wifi # wifi speed
108 # example # example user-defined segment (see prompt_example function below)
109 )
110
111 # Defines character set used by powerlevel10k. It's best to let `p10k configure` set it for you.
112 typeset -g POWERLEVEL9K_MODE=nerdfont-complete
113 # When set to `moderate`, some icons will have an extra space after them. This is meant to avoid
114 # icon overlap when using non-monospace fonts. When set to `none`, spaces are not added.
115 typeset -g POWERLEVEL9K_ICON_PADDING=none
116
117 # Basic style options that define the overall look of your prompt. You probably don't want to
118 # change them.
119 typeset -g POWERLEVEL9K_BACKGROUND= # transparent background
120 typeset -g POWERLEVEL9K_{LEFT,RIGHT}_{LEFT,RIGHT}_WHITESPACE= # no surrounding whitespace
121 typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SUBSEGMENT_SEPARATOR=' ' # separate segments with a space
122 typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_SEPARATOR= # no end-of-line symbol
123
124 # When set to true, icons appear before content on both sides of the prompt. When set
125 # to false, icons go after content. If empty or not set, icons go before content in the left
126 # prompt and after content in the right prompt.
127 #
128 # You can also override it for a specific segment:
129 #
130 # POWERLEVEL9K_STATUS_ICON_BEFORE_CONTENT=false
131 #
132 # Or for a specific segment in specific state:
133 #
134 # POWERLEVEL9K_DIR_NOT_WRITABLE_ICON_BEFORE_CONTENT=false
135 typeset -g POWERLEVEL9K_ICON_BEFORE_CONTENT=true
136
137 # Add an empty line before each prompt.
138 typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=true
139
140 # Connect left prompt lines with these symbols.
141 typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_PREFIX=
142 typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_PREFIX=
143 typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_PREFIX=
144 # Connect right prompt lines with these symbols.
145 typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_SUFFIX=
146 typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_SUFFIX=
147 typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_SUFFIX=
148
149 # The left end of left prompt.
150 typeset -g POWERLEVEL9K_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL=
151 # The right end of right prompt.
152 typeset -g POWERLEVEL9K_RIGHT_PROMPT_LAST_SEGMENT_END_SYMBOL=
153
154 # Ruler, a.k.a. the horizontal line before each prompt. If you set it to true, you'll
155 # probably want to set POWERLEVEL9K_PROMPT_ADD_NEWLINE=false above and
156 # POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR=' ' below.
157 typeset -g POWERLEVEL9K_SHOW_RULER=false
158 typeset -g POWERLEVEL9K_RULER_CHAR='─' # reasonable alternative: '·'
159 typeset -g POWERLEVEL9K_RULER_FOREGROUND=238
160
161 # Filler between left and right prompt on the first prompt line. You can set it to '·' or '─'
162 # to make it easier to see the alignment between left and right prompt and to separate prompt
163 # from command output. It serves the same purpose as ruler (see above) without increasing
164 # the number of prompt lines. You'll probably want to set POWERLEVEL9K_SHOW_RULER=false
165 # if using this. You might also like POWERLEVEL9K_PROMPT_ADD_NEWLINE=false for more compact
166 # prompt.
167 typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR='─'
168 if [[ $POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR != ' ' ]]; then
169 # The color of the filler.
170 typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND=238
171 # Add a space between the end of left prompt and the filler.
172 typeset -g POWERLEVEL9K_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL=' '
173 # Add a space between the filler and the start of right prompt.
174 typeset -g POWERLEVEL9K_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL=' '
175 # Start filler from the edge of the screen if there are no left segments on the first line.
176 typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_FIRST_SEGMENT_END_SYMBOL='%{%}'
177 # End filler on the edge of the screen if there are no right segments on the first line.
178 typeset -g POWERLEVEL9K_EMPTY_LINE_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='%{%}'
179 fi
180
181 #################################[ os_icon: os identifier ]##################################
182 # OS identifier color.
183 typeset -g POWERLEVEL9K_OS_ICON_FOREGROUND=
184 # Custom icon.
185 # typeset -g POWERLEVEL9K_OS_ICON_CONTENT_EXPANSION='⭐'
186
187 ################################[ prompt_char: prompt symbol ]################################
188 # Green prompt symbol if the last command succeeded.
189 typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=76
190 # Red prompt symbol if the last command failed.
191 typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=196
192 # Default prompt symbol.
193 typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯'
194 # Prompt symbol in command vi mode.
195 typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮'
196 # Prompt symbol in visual vi mode.
197 typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='V'
198 # Prompt symbol in overwrite vi mode.
199 typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIOWR_CONTENT_EXPANSION='▶'
200 typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=true
201 # No line terminator if prompt_char is the last segment.
202 typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL=''
203 # No line introducer if prompt_char is the first segment.
204 typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL=
205
206 ##################################[ dir: current directory ]##################################
207 # Default current directory color.
208 typeset -g POWERLEVEL9K_DIR_FOREGROUND=31
209 # If directory is too long, shorten some of its segments to the shortest possible unique
210 # prefix. The shortened directory can be tab-completed to the original.
211 typeset -g POWERLEVEL9K_SHORTEN_STRATEGY=truncate_to_unique
212 # Replace removed segment suffixes with this symbol.
213 typeset -g POWERLEVEL9K_SHORTEN_DELIMITER=
214 # Color of the shortened directory segments.
215 typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND=103
216 # Color of the anchor directory segments. Anchor segments are never shortened. The first
217 # segment is always an anchor.
218 typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND=39
219 # Display anchor directory segments in bold.
220 typeset -g POWERLEVEL9K_DIR_ANCHOR_BOLD=true
221 # Don't shorten directories that contain any of these files. They are anchors.
222 local anchor_files=(
223 .bzr
224 .citc
225 .git
226 .hg
227 .node-version
228 .python-version
229 .go-version
230 .ruby-version
231 .lua-version
232 .java-version
233 .perl-version
234 .php-version
235 .tool-version
236 .shorten_folder_marker
237 .svn
238 .terraform
239 CVS
240 Cargo.toml
241 composer.json
242 go.mod
243 package.json
244 stack.yaml
245 )
246 typeset -g POWERLEVEL9K_SHORTEN_FOLDER_MARKER="(${(j:|:)anchor_files})"
247 # If set to "first" ("last"), remove everything before the first (last) subdirectory that contains
248 # files matching $POWERLEVEL9K_SHORTEN_FOLDER_MARKER. For example, when the current directory is
249 # /foo/bar/git_repo/nested_git_repo/baz, prompt will display git_repo/nested_git_repo/baz (first)
250 # or nested_git_repo/baz (last). This assumes that git_repo and nested_git_repo contain markers
251 # and other directories don't.
252 #
253 # Optionally, "first" and "last" can be followed by ":<offset>" where <offset> is an integer.
254 # This moves the truncation point to the right (positive offset) or to the left (negative offset)
255 # relative to the marker. Plain "first" and "last" are equivalent to "first:0" and "last:0"
256 # respectively.
257 typeset -g POWERLEVEL9K_DIR_TRUNCATE_BEFORE_MARKER=false
258 # Don't shorten this many last directory segments. They are anchors.
259 typeset -g POWERLEVEL9K_SHORTEN_DIR_LENGTH=1
260 # Shorten directory if it's longer than this even if there is space for it. The value can
261 # be either absolute (e.g., '80') or a percentage of terminal width (e.g, '50%'). If empty,
262 # directory will be shortened only when prompt doesn't fit or when other parameters demand it
263 # (see POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS and POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT below).
264 # If set to `0`, directory will always be shortened to its minimum length.
265 typeset -g POWERLEVEL9K_DIR_MAX_LENGTH=80
266 # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least this
267 # many columns for typing commands.
268 typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS=40
269 # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least
270 # COLUMNS * POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT * 0.01 columns for typing commands.
271 typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT=50
272 # If set to true, embed a hyperlink into the directory. Useful for quickly
273 # opening a directory in the file manager simply by clicking the link.
274 # Can also be handy when the directory is shortened, as it allows you to see
275 # the full directory that was used in previous commands.
276 typeset -g POWERLEVEL9K_DIR_HYPERLINK=false
277
278 # Enable special styling for non-writable and non-existent directories. See POWERLEVEL9K_LOCK_ICON
279 # and POWERLEVEL9K_DIR_CLASSES below.
280 typeset -g POWERLEVEL9K_DIR_SHOW_WRITABLE=v3
281
282 # The default icon shown next to non-writable and non-existent directories when
283 # POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3.
284 # typeset -g POWERLEVEL9K_LOCK_ICON='⭐'
285
286 # POWERLEVEL9K_DIR_CLASSES allows you to specify custom icons and colors for different
287 # directories. It must be an array with 3 * N elements. Each triplet consists of:
288 #
289 # 1. A pattern against which the current directory ($PWD) is matched. Matching is done with
290 # extended_glob option enabled.
291 # 2. Directory class for the purpose of styling.
292 # 3. An empty string.
293 #
294 # Triplets are tried in order. The first triplet whose pattern matches $PWD wins.
295 #
296 # If POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3, non-writable and non-existent directories
297 # acquire class suffix _NOT_WRITABLE and NON_EXISTENT respectively.
298 #
299 # For example, given these settings:
300 #
301 # typeset -g POWERLEVEL9K_DIR_CLASSES=(
302 # '~/work(|/*)' WORK ''
303 # '~(|/*)' HOME ''
304 # '*' DEFAULT '')
305 #
306 # Whenever the current directory is ~/work or a subdirectory of ~/work, it gets styled with one
307 # of the following classes depending on its writability and existence: WORK, WORK_NOT_WRITABLE or
308 # WORK_NON_EXISTENT.
309 #
310 # Simply assigning classes to directories doesn't have any visible effects. It merely gives you an
311 # option to define custom colors and icons for different directory classes.
312 #
313 # # Styling for WORK.
314 # typeset -g POWERLEVEL9K_DIR_WORK_VISUAL_IDENTIFIER_EXPANSION='⭐'
315 # typeset -g POWERLEVEL9K_DIR_WORK_FOREGROUND=31
316 # typeset -g POWERLEVEL9K_DIR_WORK_SHORTENED_FOREGROUND=103
317 # typeset -g POWERLEVEL9K_DIR_WORK_ANCHOR_FOREGROUND=39
318 #
319 # # Styling for WORK_NOT_WRITABLE.
320 # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_VISUAL_IDENTIFIER_EXPANSION='⭐'
321 # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND=31
322 # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_SHORTENED_FOREGROUND=103
323 # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_ANCHOR_FOREGROUND=39
324 #
325 # # Styling for WORK_NON_EXISTENT.
326 # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_VISUAL_IDENTIFIER_EXPANSION='⭐'
327 # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_FOREGROUND=31
328 # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_SHORTENED_FOREGROUND=103
329 # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_ANCHOR_FOREGROUND=39
330 #
331 # If a styling parameter isn't explicitly defined for some class, it falls back to the classless
332 # parameter. For example, if POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND is not set, it falls
333 # back to POWERLEVEL9K_DIR_FOREGROUND.
334 #
335 # typeset -g POWERLEVEL9K_DIR_CLASSES=()
336
337 # Custom prefix.
338 # typeset -g POWERLEVEL9K_DIR_PREFIX='%fin '
339
340 #####################################[ vcs: git status ]######################################
341 # Branch icon. Set this parameter to '\uF126 ' for the popular Powerline branch icon.
342 typeset -g POWERLEVEL9K_VCS_BRANCH_ICON='\uF126 '
343
344 # Untracked files icon. It's really a question mark, your font isn't broken.
345 # Change the value of this parameter to show a different icon.
346 typeset -g POWERLEVEL9K_VCS_UNTRACKED_ICON='?'
347
348 # Formatter for Git status.
349 #
350 # Example output: master ⇣42⇡42 *42 merge ~42 +42 !42 ?42.
351 #
352 # You can edit the function to customize how Git status looks.
353 #
354 # VCS_STATUS_* parameters are set by gitstatus plugin. See reference:
355 # https://github.com/romkatv/gitstatus/blob/master/gitstatus.plugin.zsh.
356 function my_git_formatter() {
357 emulate -L zsh
358
359 if [[ -n $P9K_CONTENT ]]; then
360 # If P9K_CONTENT is not empty, use it. It's either "loading" or from vcs_info (not from
361 # gitstatus plugin). VCS_STATUS_* parameters are not available in this case.
362 typeset -g my_git_format=$P9K_CONTENT
363 return
364 fi
365
366 if (( $1 )); then
367 # Styling for up-to-date Git status.
368 local meta='%f' # default foreground
369 local clean='%76F' # green foreground
370 local modified='%178F' # yellow foreground
371 local untracked='%39F' # blue foreground
372 local conflicted='%196F' # red foreground
373 else
374 # Styling for incomplete and stale Git status.
375 local meta='%244F' # grey foreground
376 local clean='%244F' # grey foreground
377 local modified='%244F' # grey foreground
378 local untracked='%244F' # grey foreground
379 local conflicted='%244F' # grey foreground
380 fi
381
382 local res
383 local where # branch or tag
384 if [[ -n $VCS_STATUS_LOCAL_BRANCH ]]; then
385 res+="${clean}${(g::)POWERLEVEL9K_VCS_BRANCH_ICON}"
386 where=${(V)VCS_STATUS_LOCAL_BRANCH}
387 elif [[ -n $VCS_STATUS_TAG ]]; then
388 res+="${meta}#"
389 where=${(V)VCS_STATUS_TAG}
390 fi
391
392 # If local branch name or tag is at most 32 characters long, show it in full.
393 # Otherwise show the first 12 … the last 12.
394 # Tip: To always show local branch name in full without truncation, delete the next line.
395 (( $#where > 32 )) && where[13,-13]="…"
396
397 res+="${clean}${where//\%/%%}" # escape %
398
399 # Display the current Git commit if there is no branch or tag.
400 # Tip: To always display the current Git commit, remove `[[ -z $where ]] &&` from the next line.
401 [[ -z $where ]] && res+="${meta}@${clean}${VCS_STATUS_COMMIT[1,8]}"
402
403 # Show tracking branch name if it differs from local branch.
404 if [[ -n ${VCS_STATUS_REMOTE_BRANCH:#$VCS_STATUS_LOCAL_BRANCH} ]]; then
405 res+="${meta}:${clean}${(V)VCS_STATUS_REMOTE_BRANCH//\%/%%}" # escape %
406 fi
407
408 # ⇣42 if behind the remote.
409 (( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}"
410 # ⇡42 if ahead of the remote; no leading space if also behind the remote: ⇣42⇡42.
411 (( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" "
412 (( VCS_STATUS_COMMITS_AHEAD )) && res+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}"
413 # ⇠42 if behind the push remote.
414 (( VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" ${clean}⇠${VCS_STATUS_PUSH_COMMITS_BEHIND}"
415 (( VCS_STATUS_PUSH_COMMITS_AHEAD && !VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" "
416 # ⇢42 if ahead of the push remote; no leading space if also behind: ⇠42⇢42.
417 (( VCS_STATUS_PUSH_COMMITS_AHEAD )) && res+="${clean}⇢${VCS_STATUS_PUSH_COMMITS_AHEAD}"
418 # *42 if have stashes.
419 (( VCS_STATUS_STASHES )) && res+=" ${clean}*${VCS_STATUS_STASHES}"
420 # 'merge' if the repo is in an unusual state.
421 [[ -n $VCS_STATUS_ACTION ]] && res+=" ${conflicted}${VCS_STATUS_ACTION}"
422 # ~42 if have merge conflicts.
423 (( VCS_STATUS_NUM_CONFLICTED )) && res+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}"
424 # +42 if have staged changes.
425 (( VCS_STATUS_NUM_STAGED )) && res+=" ${modified}+${VCS_STATUS_NUM_STAGED}"
426 # !42 if have unstaged changes.
427 (( VCS_STATUS_NUM_UNSTAGED )) && res+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}"
428 # ?42 if have untracked files. It's really a question mark, your font isn't broken.
429 # See POWERLEVEL9K_VCS_UNTRACKED_ICON above if you want to use a different icon.
430 # Remove the next line if you don't want to see untracked files at all.
431 (( VCS_STATUS_NUM_UNTRACKED )) && res+=" ${untracked}${(g::)POWERLEVEL9K_VCS_UNTRACKED_ICON}${VCS_STATUS_NUM_UNTRACKED}"
432 # "─" if the number of unstaged files is unknown. This can happen due to
433 # POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY (see below) being set to a non-negative number lower
434 # than the number of files in the Git index, or due to bash.showDirtyState being set to false
435 # in the repository config. The number of staged and untracked files may also be unknown
436 # in this case.
437 (( VCS_STATUS_HAS_UNSTAGED == -1 )) && res+=" ${modified}─"
438
439 typeset -g my_git_format=$res
440 }
441 functions -M my_git_formatter 2>/dev/null
442
443 # Don't count the number of unstaged, untracked and conflicted files in Git repositories with
444 # more than this many files in the index. Negative value means infinity.
445 #
446 # If you are working in Git repositories with tens of millions of files and seeing performance
447 # sagging, try setting POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY to a number lower than the output
448 # of `git ls-files | wc -l`. Alternatively, add `bash.showDirtyState = false` to the repository's
449 # config: `git config bash.showDirtyState false`.
450 typeset -g POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY=-1
451
452 # Don't show Git status in prompt for repositories whose workdir matches this pattern.
453 # For example, if set to '~', the Git repository at $HOME/.git will be ignored.
454 # Multiple patterns can be combined with '|': '~(|/foo)|/bar/baz/*'.
455 typeset -g POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN='~'
456
457 # Disable the default Git status formatting.
458 typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true
459 # Install our own Git status formatter.
460 typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter(1)))+${my_git_format}}'
461 typeset -g POWERLEVEL9K_VCS_LOADING_CONTENT_EXPANSION='${$((my_git_formatter(0)))+${my_git_format}}'
462 # Enable counters for staged, unstaged, etc.
463 typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED,COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=-1
464
465 # Icon color.
466 typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_COLOR=76
467 typeset -g POWERLEVEL9K_VCS_LOADING_VISUAL_IDENTIFIER_COLOR=244
468 # Custom icon.
469 # typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_EXPANSION='⭐'
470 # Custom prefix.
471 # typeset -g POWERLEVEL9K_VCS_PREFIX='%fon '
472
473 # Show status of repositories of these types. You can add svn and/or hg if you are
474 # using them. If you do, your prompt may become slow even when your current directory
475 # isn't in an svn or hg reposotiry.
476 typeset -g POWERLEVEL9K_VCS_BACKENDS=(git)
477
478 # These settings are used for repositories other than Git or when gitstatusd fails and
479 # Powerlevel10k has to fall back to using vcs_info.
480 typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND=76
481 typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND=76
482 typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND=178
483
484 ##########################[ status: exit code of the last command ]###########################
485 # Enable OK_PIPE, ERROR_PIPE and ERROR_SIGNAL status states to allow us to enable, disable and
486 # style them independently from the regular OK and ERROR state.
487 typeset -g POWERLEVEL9K_STATUS_EXTENDED_STATES=true
488
489 # Status on success. No content, just an icon. No need to show it if prompt_char is enabled as
490 # it will signify success by turning green.
491 typeset -g POWERLEVEL9K_STATUS_OK=false
492 typeset -g POWERLEVEL9K_STATUS_OK_FOREGROUND=70
493 typeset -g POWERLEVEL9K_STATUS_OK_VISUAL_IDENTIFIER_EXPANSION='✔'
494
495 # Status when some part of a pipe command fails but the overall exit status is zero. It may look
496 # like this: 1|0.
497 typeset -g POWERLEVEL9K_STATUS_OK_PIPE=true
498 typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND=70
499 typeset -g POWERLEVEL9K_STATUS_OK_PIPE_VISUAL_IDENTIFIER_EXPANSION='✔'
500
501 # Status when it's just an error code (e.g., '1'). No need to show it if prompt_char is enabled as
502 # it will signify error by turning red.
503 typeset -g POWERLEVEL9K_STATUS_ERROR=false
504 typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND=160
505 typeset -g POWERLEVEL9K_STATUS_ERROR_VISUAL_IDENTIFIER_EXPANSION='✘'
506
507 # Status when the last command was terminated by a signal.
508 typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL=true
509 typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND=160
510 # Use terse signal names: "INT" instead of "SIGINT(2)".
511 typeset -g POWERLEVEL9K_STATUS_VERBOSE_SIGNAME=false
512 typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_VISUAL_IDENTIFIER_EXPANSION='✘'
513
514 # Status when some part of a pipe command fails and the overall exit status is also non-zero.
515 # It may look like this: 1|0.
516 typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE=true
517 typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND=160
518 typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_VISUAL_IDENTIFIER_EXPANSION='✘'
519
520 ###################[ command_execution_time: duration of the last command ]###################
521 # Show duration of the last command if takes at least this many seconds.
522 typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=3
523 # Show this many fractional digits. Zero means round to seconds.
524 typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0
525 # Execution time color.
526 typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=101
527 # Duration format: 1d 2h 3m 4s.
528 typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s'
529 # Custom icon.
530 # typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_VISUAL_IDENTIFIER_EXPANSION='⭐'
531 # Custom prefix.
532 # typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PREFIX='%ftook '
533
534 #######################[ background_jobs: presence of background jobs ]#######################
535 # Don't show the number of background jobs.
536 typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE=false
537 # Background jobs color.
538 typeset -g POWERLEVEL9K_BACKGROUND_JOBS_FOREGROUND=70
539 # Custom icon.
540 # typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VISUAL_IDENTIFIER_EXPANSION='⭐'
541
542 #######################[ direnv: direnv status (https://direnv.net/) ]########################
543 # Direnv color.
544 typeset -g POWERLEVEL9K_DIRENV_FOREGROUND=178
545 # Custom icon.
546 # typeset -g POWERLEVEL9K_DIRENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
547
548 ###############[ asdf: asdf version manager (https://github.com/asdf-vm/asdf) ]###############
549 # Default asdf color. Only used to display tools for which there is no color override (see below).
550 # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_FOREGROUND.
551 typeset -g POWERLEVEL9K_ASDF_FOREGROUND=66
552
553 # There are four parameters that can be used to hide asdf tools. Each parameter describes
554 # conditions under which a tool gets hidden. Parameters can hide tools but not unhide them. If at
555 # least one parameter decides to hide a tool, that tool gets hidden. If no parameter decides to
556 # hide a tool, it gets shown.
557 #
558 # Special note on the difference between POWERLEVEL9K_ASDF_SOURCES and
559 # POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW. Consider the effect of the following commands:
560 #
561 # asdf local python 3.8.1
562 # asdf global python 3.8.1
563 #
564 # After running both commands the current python version is 3.8.1 and its source is "local" as
565 # it takes precedence over "global". If POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW is set to false,
566 # it'll hide python version in this case because 3.8.1 is the same as the global version.
567 # POWERLEVEL9K_ASDF_SOURCES will hide python version only if the value of this parameter doesn't
568 # contain "local".
569
570 # Hide tool versions that don't come from one of these sources.
571 #
572 # Available sources:
573 #
574 # - shell `asdf current` says "set by ASDF_${TOOL}_VERSION environment variable"
575 # - local `asdf current` says "set by /some/not/home/directory/file"
576 # - global `asdf current` says "set by /home/username/file"
577 #
578 # Note: If this parameter is set to (shell local global), it won't hide tools.
579 # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SOURCES.
580 typeset -g POWERLEVEL9K_ASDF_SOURCES=(shell local global)
581
582 # If set to false, hide tool versions that are the same as global.
583 #
584 # Note: The name of this parameter doesn't reflect its meaning at all.
585 # Note: If this parameter is set to true, it won't hide tools.
586 # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_PROMPT_ALWAYS_SHOW.
587 typeset -g POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW=false
588
589 # If set to false, hide tool versions that are equal to "system".
590 #
591 # Note: If this parameter is set to true, it won't hide tools.
592 # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_SYSTEM.
593 typeset -g POWERLEVEL9K_ASDF_SHOW_SYSTEM=true
594
595 # If set to non-empty value, hide tools unless there is a file matching the specified file pattern
596 # in the current directory, or its parent directory, or its grandparent directory, and so on.
597 #
598 # Note: If this parameter is set to empty value, it won't hide tools.
599 # Note: SHOW_ON_UPGLOB isn't specific to asdf. It works with all prompt segments.
600 # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_ON_UPGLOB.
601 #
602 # Example: Hide nodejs version when there is no package.json and no *.js files in the current
603 # directory, in `..`, in `../..` and so on.
604 #
605 # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.js|package.json'
606 typeset -g POWERLEVEL9K_ASDF_SHOW_ON_UPGLOB=
607
608 # Ruby version from asdf.
609 typeset -g POWERLEVEL9K_ASDF_RUBY_FOREGROUND=168
610 # typeset -g POWERLEVEL9K_ASDF_RUBY_VISUAL_IDENTIFIER_EXPANSION='⭐'
611 # typeset -g POWERLEVEL9K_ASDF_RUBY_SHOW_ON_UPGLOB='*.foo|*.bar'
612
613 # Python version from asdf.
614 typeset -g POWERLEVEL9K_ASDF_PYTHON_FOREGROUND=37
615 # typeset -g POWERLEVEL9K_ASDF_PYTHON_VISUAL_IDENTIFIER_EXPANSION='⭐'
616 # typeset -g POWERLEVEL9K_ASDF_PYTHON_SHOW_ON_UPGLOB='*.foo|*.bar'
617
618 # Go version from asdf.
619 typeset -g POWERLEVEL9K_ASDF_GOLANG_FOREGROUND=37
620 # typeset -g POWERLEVEL9K_ASDF_GOLANG_VISUAL_IDENTIFIER_EXPANSION='⭐'
621 # typeset -g POWERLEVEL9K_ASDF_GOLANG_SHOW_ON_UPGLOB='*.foo|*.bar'
622
623 # Node.js version from asdf.
624 typeset -g POWERLEVEL9K_ASDF_NODEJS_FOREGROUND=70
625 # typeset -g POWERLEVEL9K_ASDF_NODEJS_VISUAL_IDENTIFIER_EXPANSION='⭐'
626 # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.foo|*.bar'
627
628 # Rust version from asdf.
629 typeset -g POWERLEVEL9K_ASDF_RUST_FOREGROUND=37
630 # typeset -g POWERLEVEL9K_ASDF_RUST_VISUAL_IDENTIFIER_EXPANSION='⭐'
631 # typeset -g POWERLEVEL9K_ASDF_RUST_SHOW_ON_UPGLOB='*.foo|*.bar'
632
633 # .NET Core version from asdf.
634 typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_FOREGROUND=134
635 # typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_VISUAL_IDENTIFIER_EXPANSION='⭐'
636 # typeset -g POWERLEVEL9K_ASDF_DOTNET_SHOW_ON_UPGLOB='*.foo|*.bar'
637
638 # Flutter version from asdf.
639 typeset -g POWERLEVEL9K_ASDF_FLUTTER_FOREGROUND=38
640 # typeset -g POWERLEVEL9K_ASDF_FLUTTER_VISUAL_IDENTIFIER_EXPANSION='⭐'
641 # typeset -g POWERLEVEL9K_ASDF_FLUTTER_SHOW_ON_UPGLOB='*.foo|*.bar'
642
643 # Lua version from asdf.
644 typeset -g POWERLEVEL9K_ASDF_LUA_FOREGROUND=32
645 # typeset -g POWERLEVEL9K_ASDF_LUA_VISUAL_IDENTIFIER_EXPANSION='⭐'
646 # typeset -g POWERLEVEL9K_ASDF_LUA_SHOW_ON_UPGLOB='*.foo|*.bar'
647
648 # Java version from asdf.
649 typeset -g POWERLEVEL9K_ASDF_JAVA_FOREGROUND=32
650 # typeset -g POWERLEVEL9K_ASDF_JAVA_VISUAL_IDENTIFIER_EXPANSION='⭐'
651 # typeset -g POWERLEVEL9K_ASDF_JAVA_SHOW_ON_UPGLOB='*.foo|*.bar'
652
653 # Perl version from asdf.
654 typeset -g POWERLEVEL9K_ASDF_PERL_FOREGROUND=67
655 # typeset -g POWERLEVEL9K_ASDF_PERL_VISUAL_IDENTIFIER_EXPANSION='⭐'
656 # typeset -g POWERLEVEL9K_ASDF_PERL_SHOW_ON_UPGLOB='*.foo|*.bar'
657
658 # Erlang version from asdf.
659 typeset -g POWERLEVEL9K_ASDF_ERLANG_FOREGROUND=125
660 # typeset -g POWERLEVEL9K_ASDF_ERLANG_VISUAL_IDENTIFIER_EXPANSION='⭐'
661 # typeset -g POWERLEVEL9K_ASDF_ERLANG_SHOW_ON_UPGLOB='*.foo|*.bar'
662
663 # Elixir version from asdf.
664 typeset -g POWERLEVEL9K_ASDF_ELIXIR_FOREGROUND=129
665 # typeset -g POWERLEVEL9K_ASDF_ELIXIR_VISUAL_IDENTIFIER_EXPANSION='⭐'
666 # typeset -g POWERLEVEL9K_ASDF_ELIXIR_SHOW_ON_UPGLOB='*.foo|*.bar'
667
668 # Postgres version from asdf.
669 typeset -g POWERLEVEL9K_ASDF_POSTGRES_FOREGROUND=31
670 # typeset -g POWERLEVEL9K_ASDF_POSTGRES_VISUAL_IDENTIFIER_EXPANSION='⭐'
671 # typeset -g POWERLEVEL9K_ASDF_POSTGRES_SHOW_ON_UPGLOB='*.foo|*.bar'
672
673 # PHP version from asdf.
674 typeset -g POWERLEVEL9K_ASDF_PHP_FOREGROUND=99
675 # typeset -g POWERLEVEL9K_ASDF_PHP_VISUAL_IDENTIFIER_EXPANSION='⭐'
676 # typeset -g POWERLEVEL9K_ASDF_PHP_SHOW_ON_UPGLOB='*.foo|*.bar'
677
678 # Haskell version from asdf.
679 typeset -g POWERLEVEL9K_ASDF_HASKELL_FOREGROUND=172
680 # typeset -g POWERLEVEL9K_ASDF_HASKELL_VISUAL_IDENTIFIER_EXPANSION='⭐'
681 # typeset -g POWERLEVEL9K_ASDF_HASKELL_SHOW_ON_UPGLOB='*.foo|*.bar'
682
683 # Julia version from asdf.
684 typeset -g POWERLEVEL9K_ASDF_JULIA_FOREGROUND=70
685 # typeset -g POWERLEVEL9K_ASDF_JULIA_VISUAL_IDENTIFIER_EXPANSION='⭐'
686 # typeset -g POWERLEVEL9K_ASDF_JULIA_SHOW_ON_UPGLOB='*.foo|*.bar'
687
688 ##########[ nordvpn: nordvpn connection status, linux only (https://nordvpn.com/) ]###########
689 # NordVPN connection indicator color.
690 typeset -g POWERLEVEL9K_NORDVPN_FOREGROUND=39
691 # Hide NordVPN connection indicator when not connected.
692 typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_CONTENT_EXPANSION=
693 typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_VISUAL_IDENTIFIER_EXPANSION=
694 # Custom icon.
695 # typeset -g POWERLEVEL9K_NORDVPN_VISUAL_IDENTIFIER_EXPANSION='⭐'
696
697 #################[ ranger: ranger shell (https://github.com/ranger/ranger) ]##################
698 # Ranger shell color.
699 typeset -g POWERLEVEL9K_RANGER_FOREGROUND=178
700 # Custom icon.
701 # typeset -g POWERLEVEL9K_RANGER_VISUAL_IDENTIFIER_EXPANSION='⭐'
702
703 ######################[ nnn: nnn shell (https://github.com/jarun/nnn) ]#######################
704 # Nnn shell color.
705 typeset -g POWERLEVEL9K_NNN_FOREGROUND=72
706 # Custom icon.
707 # typeset -g POWERLEVEL9K_NNN_VISUAL_IDENTIFIER_EXPANSION='⭐'
708
709 ###########################[ vim_shell: vim shell indicator (:sh) ]###########################
710 # Vim shell indicator color.
711 typeset -g POWERLEVEL9K_VIM_SHELL_FOREGROUND=34
712 # Custom icon.
713 # typeset -g POWERLEVEL9K_VIM_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐'
714
715 ######[ midnight_commander: midnight commander shell (https://midnight-commander.org/) ]######
716 # Midnight Commander shell color.
717 typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_FOREGROUND=178
718 # Custom icon.
719 # typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_VISUAL_IDENTIFIER_EXPANSION='⭐'
720
721 #[ nix_shell: nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) ]##
722 # Nix shell color.
723 typeset -g POWERLEVEL9K_NIX_SHELL_FOREGROUND=74
724
725 # Tip: If you want to see just the icon without "pure" and "impure", uncomment the next line.
726 # typeset -g POWERLEVEL9K_NIX_SHELL_CONTENT_EXPANSION=
727
728 # Custom icon.
729 # typeset -g POWERLEVEL9K_NIX_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐'
730
731 ##################################[ disk_usage: disk usage ]##################################
732 # Colors for different levels of disk usage.
733 typeset -g POWERLEVEL9K_DISK_USAGE_NORMAL_FOREGROUND=35
734 typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_FOREGROUND=220
735 typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_FOREGROUND=160
736 # Thresholds for different levels of disk usage (percentage points).
737 typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL=90
738 typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL=95
739 # If set to true, hide disk usage when below $POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL percent.
740 typeset -g POWERLEVEL9K_DISK_USAGE_ONLY_WARNING=false
741 # Custom icon.
742 # typeset -g POWERLEVEL9K_DISK_USAGE_VISUAL_IDENTIFIER_EXPANSION='⭐'
743
744 ######################################[ ram: free RAM ]#######################################
745 # RAM color.
746 typeset -g POWERLEVEL9K_RAM_FOREGROUND=66
747 # Custom icon.
748 # typeset -g POWERLEVEL9K_RAM_VISUAL_IDENTIFIER_EXPANSION='⭐'
749
750 #####################################[ swap: used swap ]######################################
751 # Swap color.
752 typeset -g POWERLEVEL9K_SWAP_FOREGROUND=96
753 # Custom icon.
754 # typeset -g POWERLEVEL9K_SWAP_VISUAL_IDENTIFIER_EXPANSION='⭐'
755
756 ######################################[ load: CPU load ]######################################
757 # Show average CPU load over this many last minutes. Valid values are 1, 5 and 15.
758 typeset -g POWERLEVEL9K_LOAD_WHICH=5
759 # Load color when load is under 50%.
760 typeset -g POWERLEVEL9K_LOAD_NORMAL_FOREGROUND=66
761 # Load color when load is between 50% and 70%.
762 typeset -g POWERLEVEL9K_LOAD_WARNING_FOREGROUND=178
763 # Load color when load is over 70%.
764 typeset -g POWERLEVEL9K_LOAD_CRITICAL_FOREGROUND=166
765 # Custom icon.
766 # typeset -g POWERLEVEL9K_LOAD_VISUAL_IDENTIFIER_EXPANSION='⭐'
767
768 ################[ todo: todo items (https://github.com/todotxt/todo.txt-cli) ]################
769 # Todo color.
770 typeset -g POWERLEVEL9K_TODO_FOREGROUND=110
771 # Hide todo when the total number of tasks is zero.
772 typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_TOTAL=true
773 # Hide todo when the number of tasks after filtering is zero.
774 typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_FILTERED=false
775
776 # Todo format. The following parameters are available within the expansion.
777 #
778 # - P9K_TODO_TOTAL_TASK_COUNT The total number of tasks.
779 # - P9K_TODO_FILTERED_TASK_COUNT The number of tasks after filtering.
780 #
781 # These variables correspond to the last line of the output of `todo.sh -p ls`:
782 #
783 # TODO: 24 of 42 tasks shown
784 #
785 # Here 24 is P9K_TODO_FILTERED_TASK_COUNT and 42 is P9K_TODO_TOTAL_TASK_COUNT.
786 #
787 # typeset -g POWERLEVEL9K_TODO_CONTENT_EXPANSION='$P9K_TODO_FILTERED_TASK_COUNT'
788
789 # Custom icon.
790 # typeset -g POWERLEVEL9K_TODO_VISUAL_IDENTIFIER_EXPANSION='⭐'
791
792 ###########[ timewarrior: timewarrior tracking status (https://timewarrior.net/) ]############
793 # Timewarrior color.
794 typeset -g POWERLEVEL9K_TIMEWARRIOR_FOREGROUND=110
795 # If the tracked task is longer than 24 characters, truncate and append "…".
796 # Tip: To always display tasks without truncation, delete the following parameter.
797 # Tip: To hide task names and display just the icon when time tracking is enabled, set the
798 # value of the following parameter to "".
799 typeset -g POWERLEVEL9K_TIMEWARRIOR_CONTENT_EXPANSION='${P9K_CONTENT:0:24}${${P9K_CONTENT:24}:+…}'
800
801 # Custom icon.
802 # typeset -g POWERLEVEL9K_TIMEWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐'
803
804 ##############[ taskwarrior: taskwarrior task count (https://taskwarrior.org/) ]##############
805 # Taskwarrior color.
806 typeset -g POWERLEVEL9K_TASKWARRIOR_FOREGROUND=74
807
808 # Taskwarrior segment format. The following parameters are available within the expansion.
809 #
810 # - P9K_TASKWARRIOR_PENDING_COUNT The number of pending tasks: `task +PENDING count`.
811 # - P9K_TASKWARRIOR_OVERDUE_COUNT The number of overdue tasks: `task +OVERDUE count`.
812 #
813 # Zero values are represented as empty parameters.
814 #
815 # The default format:
816 #
817 # '${P9K_TASKWARRIOR_OVERDUE_COUNT:+"!$P9K_TASKWARRIOR_OVERDUE_COUNT/"}$P9K_TASKWARRIOR_PENDING_COUNT'
818 #
819 # typeset -g POWERLEVEL9K_TASKWARRIOR_CONTENT_EXPANSION='$P9K_TASKWARRIOR_PENDING_COUNT'
820
821 # Custom icon.
822 # typeset -g POWERLEVEL9K_TASKWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐'
823
824 ##################################[ context: user@hostname ]##################################
825 # Context color when running with privileges.
826 typeset -g POWERLEVEL9K_CONTEXT_ROOT_FOREGROUND=178
827 # Context color in SSH without privileges.
828 typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_FOREGROUND=180
829 # Default context color (no privileges, no SSH).
830 typeset -g POWERLEVEL9K_CONTEXT_FOREGROUND=180
831
832 # Context format when running with privileges: bold user@hostname.
833 typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE='%B%n@%m'
834 # Context format when in SSH without privileges: user@hostname.
835 typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_TEMPLATE='%n@%m'
836 # Default context format (no privileges, no SSH): user@hostname.
837 typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE='%n@%m'
838
839 # Don't show context unless running with privileges or in SSH.
840 # Tip: Remove the next line to always show context.
841 typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_{CONTENT,VISUAL_IDENTIFIER}_EXPANSION=
842
843 # Custom icon.
844 # typeset -g POWERLEVEL9K_CONTEXT_VISUAL_IDENTIFIER_EXPANSION='⭐'
845 # Custom prefix.
846 # typeset -g POWERLEVEL9K_CONTEXT_PREFIX='%fwith '
847
848 ###[ virtualenv: python virtual environment (https://docs.python.org/3/library/venv.html) ]###
849 # Python virtual environment color.
850 typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=37
851 # Don't show Python version next to the virtual environment name.
852 typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false
853 # If set to "false", won't show virtualenv if pyenv is already shown.
854 # If set to "if-different", won't show virtualenv if it's the same as pyenv.
855 typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_WITH_PYENV=false
856 # Separate environment name from Python version only with a space.
857 typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER=
858 # Custom icon.
859 # typeset -g POWERLEVEL9K_VIRTUALENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
860
861 #####################[ anaconda: conda environment (https://conda.io/) ]######################
862 # Anaconda environment color.
863 typeset -g POWERLEVEL9K_ANACONDA_FOREGROUND=37
864
865 # Anaconda segment format. The following parameters are available within the expansion.
866 #
867 # - CONDA_PREFIX Absolute path to the active Anaconda/Miniconda environment.
868 # - CONDA_DEFAULT_ENV Name of the active Anaconda/Miniconda environment.
869 # - CONDA_PROMPT_MODIFIER Configurable prompt modifier (see below).
870 # - P9K_ANACONDA_PYTHON_VERSION Current python version (python --version).
871 #
872 # CONDA_PROMPT_MODIFIER can be configured with the following command:
873 #
874 # conda config --set env_prompt '({default_env}) '
875 #
876 # The last argument is a Python format string that can use the following variables:
877 #
878 # - prefix The same as CONDA_PREFIX.
879 # - default_env The same as CONDA_DEFAULT_ENV.
880 # - name The last segment of CONDA_PREFIX.
881 # - stacked_env Comma-separated list of names in the environment stack. The first element is
882 # always the same as default_env.
883 #
884 # Note: '({default_env}) ' is the default value of env_prompt.
885 #
886 # The default value of POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION expands to $CONDA_PROMPT_MODIFIER
887 # without the surrounding parentheses, or to the last path component of CONDA_PREFIX if the former
888 # is empty.
889 typeset -g POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION='${${${${CONDA_PROMPT_MODIFIER#\(}% }%\)}:-${CONDA_PREFIX:t}}'
890
891 # Custom icon.
892 # typeset -g POWERLEVEL9K_ANACONDA_VISUAL_IDENTIFIER_EXPANSION='⭐'
893
894 ################[ pyenv: python environment (https://github.com/pyenv/pyenv) ]################
895 # Pyenv color.
896 typeset -g POWERLEVEL9K_PYENV_FOREGROUND=37
897 # Hide python version if it doesn't come from one of these sources.
898 typeset -g POWERLEVEL9K_PYENV_SOURCES=(shell local global)
899 # If set to false, hide python version if it's the same as global:
900 # $(pyenv version-name) == $(pyenv global).
901 typeset -g POWERLEVEL9K_PYENV_PROMPT_ALWAYS_SHOW=false
902 # If set to false, hide python version if it's equal to "system".
903 typeset -g POWERLEVEL9K_PYENV_SHOW_SYSTEM=true
904
905 # Pyenv segment format. The following parameters are available within the expansion.
906 #
907 # - P9K_CONTENT Current pyenv environment (pyenv version-name).
908 # - P9K_PYENV_PYTHON_VERSION Current python version (python --version).
909 #
910 # The default format has the following logic:
911 #
912 # 1. Display "$P9K_CONTENT $P9K_PYENV_PYTHON_VERSION" if $P9K_PYENV_PYTHON_VERSION is not
913 # empty and unequal to $P9K_CONTENT.
914 # 2. Otherwise display just "$P9K_CONTENT".
915 typeset -g POWERLEVEL9K_PYENV_CONTENT_EXPANSION='${P9K_CONTENT}${${P9K_PYENV_PYTHON_VERSION:#$P9K_CONTENT}:+ $P9K_PYENV_PYTHON_VERSION}'
916
917 # Custom icon.
918 # typeset -g POWERLEVEL9K_PYENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
919
920 ################[ goenv: go environment (https://github.com/syndbg/goenv) ]################
921 # Goenv color.
922 typeset -g POWERLEVEL9K_GOENV_FOREGROUND=37
923 # Hide go version if it doesn't come from one of these sources.
924 typeset -g POWERLEVEL9K_GOENV_SOURCES=(shell local global)
925 # If set to false, hide go version if it's the same as global:
926 # $(goenv version-name) == $(goenv global).
927 typeset -g POWERLEVEL9K_GOENV_PROMPT_ALWAYS_SHOW=false
928 # If set to false, hide go version if it's equal to "system".
929 typeset -g POWERLEVEL9K_GOENV_SHOW_SYSTEM=true
930 # Custom icon.
931 # typeset -g POWERLEVEL9K_GOENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
932
933 ##########[ nodenv: node.js version from nodenv (https://github.com/nodenv/nodenv) ]##########
934 # Nodenv color.
935 typeset -g POWERLEVEL9K_NODENV_FOREGROUND=70
936 # Hide node version if it doesn't come from one of these sources.
937 typeset -g POWERLEVEL9K_NODENV_SOURCES=(shell local global)
938 # If set to false, hide node version if it's the same as global:
939 # $(nodenv version-name) == $(nodenv global).
940 typeset -g POWERLEVEL9K_NODENV_PROMPT_ALWAYS_SHOW=false
941 # If set to false, hide node version if it's equal to "system".
942 typeset -g POWERLEVEL9K_NODENV_SHOW_SYSTEM=true
943 # Custom icon.
944 # typeset -g POWERLEVEL9K_NODENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
945
946 ##############[ nvm: node.js version from nvm (https://github.com/nvm-sh/nvm) ]###############
947 # Nvm color.
948 typeset -g POWERLEVEL9K_NVM_FOREGROUND=70
949 # Custom icon.
950 # typeset -g POWERLEVEL9K_NVM_VISUAL_IDENTIFIER_EXPANSION='⭐'
951
952 ############[ nodeenv: node.js environment (https://github.com/ekalinin/nodeenv) ]############
953 # Nodeenv color.
954 typeset -g POWERLEVEL9K_NODEENV_FOREGROUND=70
955 # Don't show Node version next to the environment name.
956 typeset -g POWERLEVEL9K_NODEENV_SHOW_NODE_VERSION=false
957 # Separate environment name from Node version only with a space.
958 typeset -g POWERLEVEL9K_NODEENV_{LEFT,RIGHT}_DELIMITER=
959 # Custom icon.
960 # typeset -g POWERLEVEL9K_NODEENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
961
962 ##############################[ node_version: node.js version ]###############################
963 # Node version color.
964 typeset -g POWERLEVEL9K_NODE_VERSION_FOREGROUND=70
965 # Show node version only when in a directory tree containing package.json.
966 typeset -g POWERLEVEL9K_NODE_VERSION_PROJECT_ONLY=true
967 # Custom icon.
968 # typeset -g POWERLEVEL9K_NODE_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
969
970 #######################[ go_version: go version (https://golang.org) ]########################
971 # Go version color.
972 typeset -g POWERLEVEL9K_GO_VERSION_FOREGROUND=37
973 # Show go version only when in a go project subdirectory.
974 typeset -g POWERLEVEL9K_GO_VERSION_PROJECT_ONLY=true
975 # Custom icon.
976 # typeset -g POWERLEVEL9K_GO_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
977
978 #################[ rust_version: rustc version (https://www.rust-lang.org) ]##################
979 # Rust version color.
980 typeset -g POWERLEVEL9K_RUST_VERSION_FOREGROUND=37
981 # Show rust version only when in a rust project subdirectory.
982 typeset -g POWERLEVEL9K_RUST_VERSION_PROJECT_ONLY=true
983 # Custom icon.
984 # typeset -g POWERLEVEL9K_RUST_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
985
986 ###############[ dotnet_version: .NET version (https://dotnet.microsoft.com) ]################
987 # .NET version color.
988 typeset -g POWERLEVEL9K_DOTNET_VERSION_FOREGROUND=134
989 # Show .NET version only when in a .NET project subdirectory.
990 typeset -g POWERLEVEL9K_DOTNET_VERSION_PROJECT_ONLY=true
991 # Custom icon.
992 # typeset -g POWERLEVEL9K_DOTNET_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
993
994 #####################[ php_version: php version (https://www.php.net/) ]######################
995 # PHP version color.
996 typeset -g POWERLEVEL9K_PHP_VERSION_FOREGROUND=99
997 # Show PHP version only when in a PHP project subdirectory.
998 typeset -g POWERLEVEL9K_PHP_VERSION_PROJECT_ONLY=true
999 # Custom icon.
1000 # typeset -g POWERLEVEL9K_PHP_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
1001
1002 ##########[ laravel_version: laravel php framework version (https://laravel.com/) ]###########
1003 # Laravel version color.
1004 typeset -g POWERLEVEL9K_LARAVEL_VERSION_FOREGROUND=161
1005 # Custom icon.
1006 # typeset -g POWERLEVEL9K_LARAVEL_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
1007
1008 ####################[ java_version: java version (https://www.java.com/) ]####################
1009 # Java version color.
1010 typeset -g POWERLEVEL9K_JAVA_VERSION_FOREGROUND=32
1011 # Show java version only when in a java project subdirectory.
1012 typeset -g POWERLEVEL9K_JAVA_VERSION_PROJECT_ONLY=true
1013 # Show brief version.
1014 typeset -g POWERLEVEL9K_JAVA_VERSION_FULL=false
1015 # Custom icon.
1016 # typeset -g POWERLEVEL9K_JAVA_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
1017
1018 ###[ package: name@version from package.json (https://docs.npmjs.com/files/package.json) ]####
1019 # Package color.
1020 typeset -g POWERLEVEL9K_PACKAGE_FOREGROUND=117
1021 # Package format. The following parameters are available within the expansion.
1022 #
1023 # - P9K_PACKAGE_NAME The value of `name` field in package.json.
1024 # - P9K_PACKAGE_VERSION The value of `version` field in package.json.
1025 #
1026 # typeset -g POWERLEVEL9K_PACKAGE_CONTENT_EXPANSION='${P9K_PACKAGE_NAME//\%/%%}@${P9K_PACKAGE_VERSION//\%/%%}'
1027 # Custom icon.
1028 # typeset -g POWERLEVEL9K_PACKAGE_VISUAL_IDENTIFIER_EXPANSION='⭐'
1029
1030 #############[ rbenv: ruby version from rbenv (https://github.com/rbenv/rbenv) ]##############
1031 # Rbenv color.
1032 typeset -g POWERLEVEL9K_RBENV_FOREGROUND=168
1033 # Hide ruby version if it doesn't come from one of these sources.
1034 typeset -g POWERLEVEL9K_RBENV_SOURCES=(shell local global)
1035 # If set to false, hide ruby version if it's the same as global:
1036 # $(rbenv version-name) == $(rbenv global).
1037 typeset -g POWERLEVEL9K_RBENV_PROMPT_ALWAYS_SHOW=false
1038 # If set to false, hide ruby version if it's equal to "system".
1039 typeset -g POWERLEVEL9K_RBENV_SHOW_SYSTEM=true
1040 # Custom icon.
1041 # typeset -g POWERLEVEL9K_RBENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1042
1043 #######################[ rvm: ruby version from rvm (https://rvm.io) ]########################
1044 # Rvm color.
1045 typeset -g POWERLEVEL9K_RVM_FOREGROUND=168
1046 # Don't show @gemset at the end.
1047 typeset -g POWERLEVEL9K_RVM_SHOW_GEMSET=false
1048 # Don't show ruby- at the front.
1049 typeset -g POWERLEVEL9K_RVM_SHOW_PREFIX=false
1050 # Custom icon.
1051 # typeset -g POWERLEVEL9K_RVM_VISUAL_IDENTIFIER_EXPANSION='⭐'
1052
1053 ###########[ fvm: flutter version management (https://github.com/leoafarias/fvm) ]############
1054 # Fvm color.
1055 typeset -g POWERLEVEL9K_FVM_FOREGROUND=38
1056 # Custom icon.
1057 # typeset -g POWERLEVEL9K_FVM_VISUAL_IDENTIFIER_EXPANSION='⭐'
1058
1059 ##########[ luaenv: lua version from luaenv (https://github.com/cehoffman/luaenv) ]###########
1060 # Lua color.
1061 typeset -g POWERLEVEL9K_LUAENV_FOREGROUND=32
1062 # Hide lua version if it doesn't come from one of these sources.
1063 typeset -g POWERLEVEL9K_LUAENV_SOURCES=(shell local global)
1064 # If set to false, hide lua version if it's the same as global:
1065 # $(luaenv version-name) == $(luaenv global).
1066 typeset -g POWERLEVEL9K_LUAENV_PROMPT_ALWAYS_SHOW=false
1067 # If set to false, hide lua version if it's equal to "system".
1068 typeset -g POWERLEVEL9K_LUAENV_SHOW_SYSTEM=true
1069 # Custom icon.
1070 # typeset -g POWERLEVEL9K_LUAENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1071
1072 ###############[ jenv: java version from jenv (https://github.com/jenv/jenv) ]################
1073 # Java color.
1074 typeset -g POWERLEVEL9K_JENV_FOREGROUND=32
1075 # Hide java version if it doesn't come from one of these sources.
1076 typeset -g POWERLEVEL9K_JENV_SOURCES=(shell local global)
1077 # If set to false, hide java version if it's the same as global:
1078 # $(jenv version-name) == $(jenv global).
1079 typeset -g POWERLEVEL9K_JENV_PROMPT_ALWAYS_SHOW=false
1080 # If set to false, hide java version if it's equal to "system".
1081 typeset -g POWERLEVEL9K_JENV_SHOW_SYSTEM=true
1082 # Custom icon.
1083 # typeset -g POWERLEVEL9K_JENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1084
1085 ###########[ plenv: perl version from plenv (https://github.com/tokuhirom/plenv) ]############
1086 # Perl color.
1087 typeset -g POWERLEVEL9K_PLENV_FOREGROUND=67
1088 # Hide perl version if it doesn't come from one of these sources.
1089 typeset -g POWERLEVEL9K_PLENV_SOURCES=(shell local global)
1090 # If set to false, hide perl version if it's the same as global:
1091 # $(plenv version-name) == $(plenv global).
1092 typeset -g POWERLEVEL9K_PLENV_PROMPT_ALWAYS_SHOW=false
1093 # If set to false, hide perl version if it's equal to "system".
1094 typeset -g POWERLEVEL9K_PLENV_SHOW_SYSTEM=true
1095 # Custom icon.
1096 # typeset -g POWERLEVEL9K_PLENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1097
1098 ############[ phpenv: php version from phpenv (https://github.com/phpenv/phpenv) ]############
1099 # PHP color.
1100 typeset -g POWERLEVEL9K_PHPENV_FOREGROUND=99
1101 # Hide php version if it doesn't come from one of these sources.
1102 typeset -g POWERLEVEL9K_PHPENV_SOURCES=(shell local global)
1103 # If set to false, hide php version if it's the same as global:
1104 # $(phpenv version-name) == $(phpenv global).
1105 typeset -g POWERLEVEL9K_PHPENV_PROMPT_ALWAYS_SHOW=false
1106 # If set to false, hide php version if it's equal to "system".
1107 typeset -g POWERLEVEL9K_PHPENV_SHOW_SYSTEM=true
1108 # Custom icon.
1109 # typeset -g POWERLEVEL9K_PHPENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1110
1111 #######[ scalaenv: scala version from scalaenv (https://github.com/scalaenv/scalaenv) ]#######
1112 # Scala color.
1113 typeset -g POWERLEVEL9K_SCALAENV_FOREGROUND=160
1114 # Hide scala version if it doesn't come from one of these sources.
1115 typeset -g POWERLEVEL9K_SCALAENV_SOURCES=(shell local global)
1116 # If set to false, hide scala version if it's the same as global:
1117 # $(scalaenv version-name) == $(scalaenv global).
1118 typeset -g POWERLEVEL9K_SCALAENV_PROMPT_ALWAYS_SHOW=false
1119 # If set to false, hide scala version if it's equal to "system".
1120 typeset -g POWERLEVEL9K_SCALAENV_SHOW_SYSTEM=true
1121 # Custom icon.
1122 # typeset -g POWERLEVEL9K_SCALAENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1123
1124 ##########[ haskell_stack: haskell version from stack (https://haskellstack.org/) ]###########
1125 # Haskell color.
1126 typeset -g POWERLEVEL9K_HASKELL_STACK_FOREGROUND=172
1127 # Hide haskell version if it doesn't come from one of these sources.
1128 #
1129 # shell: version is set by STACK_YAML
1130 # local: version is set by stack.yaml up the directory tree
1131 # global: version is set by the implicit global project (~/.stack/global-project/stack.yaml)
1132 typeset -g POWERLEVEL9K_HASKELL_STACK_SOURCES=(shell local)
1133 # If set to false, hide haskell version if it's the same as in the implicit global project.
1134 typeset -g POWERLEVEL9K_HASKELL_STACK_ALWAYS_SHOW=true
1135 # Custom icon.
1136 # typeset -g POWERLEVEL9K_HASKELL_STACK_VISUAL_IDENTIFIER_EXPANSION='⭐'
1137
1138 #############[ kubecontext: current kubernetes context (https://kubernetes.io/) ]#############
1139 # Show kubecontext only when the the command you are typing invokes one of these tools.
1140 # Tip: Remove the next line to always show kubecontext.
1141 typeset -g POWERLEVEL9K_KUBECONTEXT_SHOW_ON_COMMAND='kubectl|helm|kubens|kubectx|oc|istioctl|kogito|k9s|helmfile'
1142
1143 # Kubernetes context classes for the purpose of using different colors, icons and expansions with
1144 # different contexts.
1145 #
1146 # POWERLEVEL9K_KUBECONTEXT_CLASSES is an array with even number of elements. The first element
1147 # in each pair defines a pattern against which the current kubernetes context gets matched.
1148 # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below)
1149 # that gets matched. If you unset all POWERLEVEL9K_KUBECONTEXT_*CONTENT_EXPANSION parameters,
1150 # you'll see this value in your prompt. The second element of each pair in
1151 # POWERLEVEL9K_KUBECONTEXT_CLASSES defines the context class. Patterns are tried in order. The
1152 # first match wins.
1153 #
1154 # For example, given these settings:
1155 #
1156 # typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=(
1157 # '*prod*' PROD
1158 # '*test*' TEST
1159 # '*' DEFAULT)
1160 #
1161 # If your current kubernetes context is "deathray-testing/default", its class is TEST
1162 # because "deathray-testing/default" doesn't match the pattern '*prod*' but does match '*test*'.
1163 #
1164 # You can define different colors, icons and content expansions for different classes:
1165 #
1166 # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_FOREGROUND=28
1167 # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐'
1168 # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <'
1169 typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=(
1170 # '*prod*' PROD # These values are examples that are unlikely
1171 # '*test*' TEST # to match your needs. Customize them as needed.
1172 '*' DEFAULT)
1173 typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_FOREGROUND=134
1174 # typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐'
1175
1176 # Use POWERLEVEL9K_KUBECONTEXT_CONTENT_EXPANSION to specify the content displayed by kubecontext
1177 # segment. Parameter expansions are very flexible and fast, too. See reference:
1178 # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion.
1179 #
1180 # Within the expansion the following parameters are always available:
1181 #
1182 # - P9K_CONTENT The content that would've been displayed if there was no content
1183 # expansion defined.
1184 # - P9K_KUBECONTEXT_NAME The current context's name. Corresponds to column NAME in the
1185 # output of `kubectl config get-contexts`.
1186 # - P9K_KUBECONTEXT_CLUSTER The current context's cluster. Corresponds to column CLUSTER in the
1187 # output of `kubectl config get-contexts`.
1188 # - P9K_KUBECONTEXT_NAMESPACE The current context's namespace. Corresponds to column NAMESPACE
1189 # in the output of `kubectl config get-contexts`. If there is no
1190 # namespace, the parameter is set to "default".
1191 # - P9K_KUBECONTEXT_USER The current context's user. Corresponds to column AUTHINFO in the
1192 # output of `kubectl config get-contexts`.
1193 #
1194 # If the context points to Google Kubernetes Engine (GKE) or Elastic Kubernetes Service (EKS),
1195 # the following extra parameters are available:
1196 #
1197 # - P9K_KUBECONTEXT_CLOUD_NAME Either "gke" or "eks".
1198 # - P9K_KUBECONTEXT_CLOUD_ACCOUNT Account/project ID.
1199 # - P9K_KUBECONTEXT_CLOUD_ZONE Availability zone.
1200 # - P9K_KUBECONTEXT_CLOUD_CLUSTER Cluster.
1201 #
1202 # P9K_KUBECONTEXT_CLOUD_* parameters are derived from P9K_KUBECONTEXT_CLUSTER. For example,
1203 # if P9K_KUBECONTEXT_CLUSTER is "gke_my-account_us-east1-a_my-cluster-01":
1204 #
1205 # - P9K_KUBECONTEXT_CLOUD_NAME=gke
1206 # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=my-account
1207 # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east1-a
1208 # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01
1209 #
1210 # If P9K_KUBECONTEXT_CLUSTER is "arn:aws:eks:us-east-1:123456789012:cluster/my-cluster-01":
1211 #
1212 # - P9K_KUBECONTEXT_CLOUD_NAME=eks
1213 # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=123456789012
1214 # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east-1
1215 # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01
1216 typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION=
1217 # Show P9K_KUBECONTEXT_CLOUD_CLUSTER if it's not empty and fall back to P9K_KUBECONTEXT_NAME.
1218 POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${P9K_KUBECONTEXT_CLOUD_CLUSTER:-${P9K_KUBECONTEXT_NAME}}'
1219 # Append the current context's namespace if it's not "default".
1220 POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${${:-/$P9K_KUBECONTEXT_NAMESPACE}:#/default}'
1221
1222 # Custom prefix.
1223 # typeset -g POWERLEVEL9K_KUBECONTEXT_PREFIX='%fat '
1224
1225 ################[ terraform: terraform workspace (https://www.terraform.io) ]#################
1226 # Don't show terraform workspace if it's literally "default".
1227 typeset -g POWERLEVEL9K_TERRAFORM_SHOW_DEFAULT=false
1228 # POWERLEVEL9K_TERRAFORM_CLASSES is an array with even number of elements. The first element
1229 # in each pair defines a pattern against which the current terraform workspace gets matched.
1230 # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below)
1231 # that gets matched. If you unset all POWERLEVEL9K_TERRAFORM_*CONTENT_EXPANSION parameters,
1232 # you'll see this value in your prompt. The second element of each pair in
1233 # POWERLEVEL9K_TERRAFORM_CLASSES defines the workspace class. Patterns are tried in order. The
1234 # first match wins.
1235 #
1236 # For example, given these settings:
1237 #
1238 # typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=(
1239 # '*prod*' PROD
1240 # '*test*' TEST
1241 # '*' OTHER)
1242 #
1243 # If your current terraform workspace is "project_test", its class is TEST because "project_test"
1244 # doesn't match the pattern '*prod*' but does match '*test*'.
1245 #
1246 # You can define different colors, icons and content expansions for different classes:
1247 #
1248 # typeset -g POWERLEVEL9K_TERRAFORM_TEST_FOREGROUND=28
1249 # typeset -g POWERLEVEL9K_TERRAFORM_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐'
1250 # typeset -g POWERLEVEL9K_TERRAFORM_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <'
1251 typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=(
1252 # '*prod*' PROD # These values are examples that are unlikely
1253 # '*test*' TEST # to match your needs. Customize them as needed.
1254 '*' OTHER)
1255 typeset -g POWERLEVEL9K_TERRAFORM_OTHER_FOREGROUND=38
1256 # typeset -g POWERLEVEL9K_TERRAFORM_OTHER_VISUAL_IDENTIFIER_EXPANSION='⭐'
1257
1258 #[ aws: aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) ]#
1259 # Show aws only when the the command you are typing invokes one of these tools.
1260 # Tip: Remove the next line to always show aws.
1261 typeset -g POWERLEVEL9K_AWS_SHOW_ON_COMMAND='aws|awless|terraform|pulumi|terragrunt'
1262
1263 # POWERLEVEL9K_AWS_CLASSES is an array with even number of elements. The first element
1264 # in each pair defines a pattern against which the current AWS profile gets matched.
1265 # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below)
1266 # that gets matched. If you unset all POWERLEVEL9K_AWS_*CONTENT_EXPANSION parameters,
1267 # you'll see this value in your prompt. The second element of each pair in
1268 # POWERLEVEL9K_AWS_CLASSES defines the profile class. Patterns are tried in order. The
1269 # first match wins.
1270 #
1271 # For example, given these settings:
1272 #
1273 # typeset -g POWERLEVEL9K_AWS_CLASSES=(
1274 # '*prod*' PROD
1275 # '*test*' TEST
1276 # '*' DEFAULT)
1277 #
1278 # If your current AWS profile is "company_test", its class is TEST
1279 # because "company_test" doesn't match the pattern '*prod*' but does match '*test*'.
1280 #
1281 # You can define different colors, icons and content expansions for different classes:
1282 #
1283 # typeset -g POWERLEVEL9K_AWS_TEST_FOREGROUND=28
1284 # typeset -g POWERLEVEL9K_AWS_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐'
1285 # typeset -g POWERLEVEL9K_AWS_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <'
1286 typeset -g POWERLEVEL9K_AWS_CLASSES=(
1287 # '*prod*' PROD # These values are examples that are unlikely
1288 # '*test*' TEST # to match your needs. Customize them as needed.
1289 '*' DEFAULT)
1290 typeset -g POWERLEVEL9K_AWS_DEFAULT_FOREGROUND=208
1291 # typeset -g POWERLEVEL9K_AWS_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐'
1292
1293 #[ aws_eb_env: aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) ]#
1294 # AWS Elastic Beanstalk environment color.
1295 typeset -g POWERLEVEL9K_AWS_EB_ENV_FOREGROUND=70
1296 # Custom icon.
1297 # typeset -g POWERLEVEL9K_AWS_EB_ENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1298
1299 ##########[ azure: azure account name (https://docs.microsoft.com/en-us/cli/azure) ]##########
1300 # Show azure only when the the command you are typing invokes one of these tools.
1301 # Tip: Remove the next line to always show azure.
1302 typeset -g POWERLEVEL9K_AZURE_SHOW_ON_COMMAND='az|terraform|pulumi|terragrunt'
1303 # Azure account name color.
1304 typeset -g POWERLEVEL9K_AZURE_FOREGROUND=32
1305 # Custom icon.
1306 # typeset -g POWERLEVEL9K_AZURE_VISUAL_IDENTIFIER_EXPANSION='⭐'
1307
1308 ##########[ gcloud: google cloud account and project (https://cloud.google.com/) ]###########
1309 # Show gcloud only when the the command you are typing invokes one of these tools.
1310 # Tip: Remove the next line to always show gcloud.
1311 typeset -g POWERLEVEL9K_GCLOUD_SHOW_ON_COMMAND='gcloud|gcs'
1312 # Google cloud color.
1313 typeset -g POWERLEVEL9K_GCLOUD_FOREGROUND=32
1314
1315 # Google cloud format. Change the value of POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION and/or
1316 # POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION if the default is too verbose or not informative
1317 # enough. You can use the following parameters in the expansions. Each of them corresponds to the
1318 # output of `gcloud` tool.
1319 #
1320 # Parameter | Source
1321 # -------------------------|--------------------------------------------------------------------
1322 # P9K_GCLOUD_CONFIGURATION | gcloud config configurations list --format='value(name)'
1323 # P9K_GCLOUD_ACCOUNT | gcloud config get-value account
1324 # P9K_GCLOUD_PROJECT_ID | gcloud config get-value project
1325 # P9K_GCLOUD_PROJECT_NAME | gcloud projects describe $P9K_GCLOUD_PROJECT_ID --format='value(name)'
1326 #
1327 # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced with '%%'.
1328 #
1329 # Obtaining project name requires sending a request to Google servers. This can take a long time
1330 # and even fail. When project name is unknown, P9K_GCLOUD_PROJECT_NAME is not set and gcloud
1331 # prompt segment is in state PARTIAL. When project name gets known, P9K_GCLOUD_PROJECT_NAME gets
1332 # set and gcloud prompt segment transitions to state COMPLETE.
1333 #
1334 # You can customize the format, icon and colors of gcloud segment separately for states PARTIAL
1335 # and COMPLETE. You can also hide gcloud in state PARTIAL by setting
1336 # POWERLEVEL9K_GCLOUD_PARTIAL_VISUAL_IDENTIFIER_EXPANSION and
1337 # POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION to empty.
1338 typeset -g POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_ID//\%/%%}'
1339 typeset -g POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_NAME//\%/%%}'
1340
1341 # Send a request to Google (by means of `gcloud projects describe ...`) to obtain project name
1342 # this often. Negative value disables periodic polling. In this mode project name is retrieved
1343 # only when the current configuration, account or project id changes.
1344 typeset -g POWERLEVEL9K_GCLOUD_REFRESH_PROJECT_NAME_SECONDS=60
1345
1346 # Custom icon.
1347 # typeset -g POWERLEVEL9K_GCLOUD_VISUAL_IDENTIFIER_EXPANSION='⭐'
1348
1349 #[ google_app_cred: google application credentials (https://cloud.google.com/docs/authentication/production) ]#
1350 # Show google_app_cred only when the the command you are typing invokes one of these tools.
1351 # Tip: Remove the next line to always show google_app_cred.
1352 typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_SHOW_ON_COMMAND='terraform|pulumi|terragrunt'
1353
1354 # Google application credentials classes for the purpose of using different colors, icons and
1355 # expansions with different credentials.
1356 #
1357 # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES is an array with even number of elements. The first
1358 # element in each pair defines a pattern against which the current kubernetes context gets
1359 # matched. More specifically, it's P9K_CONTENT prior to the application of context expansion
1360 # (see below) that gets matched. If you unset all POWERLEVEL9K_GOOGLE_APP_CRED_*CONTENT_EXPANSION
1361 # parameters, you'll see this value in your prompt. The second element of each pair in
1362 # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES defines the context class. Patterns are tried in order.
1363 # The first match wins.
1364 #
1365 # For example, given these settings:
1366 #
1367 # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=(
1368 # '*:*prod*:*' PROD
1369 # '*:*test*:*' TEST
1370 # '*' DEFAULT)
1371 #
1372 # If your current Google application credentials is "service_account deathray-testing x@y.com",
1373 # its class is TEST because it doesn't match the pattern '* *prod* *' but does match '* *test* *'.
1374 #
1375 # You can define different colors, icons and content expansions for different classes:
1376 #
1377 # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_FOREGROUND=28
1378 # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐'
1379 # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_CONTENT_EXPANSION='$P9K_GOOGLE_APP_CRED_PROJECT_ID'
1380 typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=(
1381 # '*:*prod*:*' PROD # These values are examples that are unlikely
1382 # '*:*test*:*' TEST # to match your needs. Customize them as needed.
1383 '*' DEFAULT)
1384 typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_FOREGROUND=32
1385 # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐'
1386
1387 # Use POWERLEVEL9K_GOOGLE_APP_CRED_CONTENT_EXPANSION to specify the content displayed by
1388 # google_app_cred segment. Parameter expansions are very flexible and fast, too. See reference:
1389 # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion.
1390 #
1391 # You can use the following parameters in the expansion. Each of them corresponds to one of the
1392 # fields in the JSON file pointed to by GOOGLE_APPLICATION_CREDENTIALS.
1393 #
1394 # Parameter | JSON key file field
1395 # ---------------------------------+---------------
1396 # P9K_GOOGLE_APP_CRED_TYPE | type
1397 # P9K_GOOGLE_APP_CRED_PROJECT_ID | project_id
1398 # P9K_GOOGLE_APP_CRED_CLIENT_EMAIL | client_email
1399 #
1400 # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced by '%%'.
1401 typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_CONTENT_EXPANSION='${P9K_GOOGLE_APP_CRED_PROJECT_ID//\%/%%}'
1402
1403 ###############################[ public_ip: public IP address ]###############################
1404 # Public IP color.
1405 typeset -g POWERLEVEL9K_PUBLIC_IP_FOREGROUND=94
1406 # Custom icon.
1407 # typeset -g POWERLEVEL9K_PUBLIC_IP_VISUAL_IDENTIFIER_EXPANSION='⭐'
1408
1409 ########################[ vpn_ip: virtual private network indicator ]#########################
1410 # VPN IP color.
1411 typeset -g POWERLEVEL9K_VPN_IP_FOREGROUND=81
1412 # When on VPN, show just an icon without the IP address.
1413 # Tip: To display the private IP address when on VPN, remove the next line.
1414 typeset -g POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION=
1415 # Regular expression for the VPN network interface. Run `ifconfig` or `ip -4 a show` while on VPN
1416 # to see the name of the interface.
1417 typeset -g POWERLEVEL9K_VPN_IP_INTERFACE='(gpd|wg|(.*tun))[0-9]*'
1418 # If set to true, show one segment per matching network interface. If set to false, show only
1419 # one segment corresponding to the first matching network interface.
1420 # Tip: If you set it to true, you'll probably want to unset POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION.
1421 typeset -g POWERLEVEL9K_VPN_IP_SHOW_ALL=false
1422 # Custom icon.
1423 # typeset -g POWERLEVEL9K_VPN_IP_VISUAL_IDENTIFIER_EXPANSION='⭐'
1424
1425 ###########[ ip: ip address and bandwidth usage for a specified network interface ]###########
1426 # IP color.
1427 typeset -g POWERLEVEL9K_IP_FOREGROUND=38
1428 # The following parameters are accessible within the expansion:
1429 #
1430 # Parameter | Meaning
1431 # ----------------------+---------------
1432 # P9K_IP_IP | IP address
1433 # P9K_IP_INTERFACE | network interface
1434 # P9K_IP_RX_BYTES | total number of bytes received
1435 # P9K_IP_TX_BYTES | total number of bytes sent
1436 # P9K_IP_RX_RATE | receive rate (since last prompt)
1437 # P9K_IP_TX_RATE | send rate (since last prompt)
1438 typeset -g POWERLEVEL9K_IP_CONTENT_EXPANSION='$P9K_IP_IP${P9K_IP_RX_RATE:+ %70F⇣$P9K_IP_RX_RATE}${P9K_IP_TX_RATE:+ %215F⇡$P9K_IP_TX_RATE}'
1439 # Show information for the first network interface whose name matches this regular expression.
1440 # Run `ifconfig` or `ip -4 a show` to see the names of all network interfaces.
1441 typeset -g POWERLEVEL9K_IP_INTERFACE='e.*'
1442 # Custom icon.
1443 # typeset -g POWERLEVEL9K_IP_VISUAL_IDENTIFIER_EXPANSION='⭐'
1444
1445 #########################[ proxy: system-wide http/https/ftp proxy ]##########################
1446 # Proxy color.
1447 typeset -g POWERLEVEL9K_PROXY_FOREGROUND=68
1448 # Custom icon.
1449 # typeset -g POWERLEVEL9K_PROXY_VISUAL_IDENTIFIER_EXPANSION='⭐'
1450
1451 ################################[ battery: internal battery ]#################################
1452 # Show battery in red when it's below this level and not connected to power supply.
1453 typeset -g POWERLEVEL9K_BATTERY_LOW_THRESHOLD=20
1454 typeset -g POWERLEVEL9K_BATTERY_LOW_FOREGROUND=160
1455 # Show battery in green when it's charging or fully charged.
1456 typeset -g POWERLEVEL9K_BATTERY_{CHARGING,CHARGED}_FOREGROUND=70
1457 # Show battery in yellow when it's discharging.
1458 typeset -g POWERLEVEL9K_BATTERY_DISCONNECTED_FOREGROUND=178
1459 # Battery pictograms going from low to high level of charge.
1460 typeset -g POWERLEVEL9K_BATTERY_STAGES='\uf58d\uf579\uf57a\uf57b\uf57c\uf57d\uf57e\uf57f\uf580\uf581\uf578'
1461 # Don't show the remaining time to charge/discharge.
1462 typeset -g POWERLEVEL9K_BATTERY_VERBOSE=false
1463
1464 #####################################[ wifi: wifi speed ]#####################################
1465 # WiFi color.
1466 typeset -g POWERLEVEL9K_WIFI_FOREGROUND=68
1467 # Custom icon.
1468 # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='⭐'
1469
1470 # Use different colors and icons depending on signal strength ($P9K_WIFI_BARS).
1471 #
1472 # # Wifi colors and icons for different signal strength levels (low to high).
1473 # typeset -g my_wifi_fg=(68 68 68 68 68) # <-- change these values
1474 # typeset -g my_wifi_icon=('WiFi' 'WiFi' 'WiFi' 'WiFi' 'WiFi') # <-- change these values
1475 #
1476 # typeset -g POWERLEVEL9K_WIFI_CONTENT_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}$P9K_WIFI_LAST_TX_RATE Mbps'
1477 # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}${my_wifi_icon[P9K_WIFI_BARS+1]}'
1478 #
1479 # The following parameters are accessible within the expansions:
1480 #
1481 # Parameter | Meaning
1482 # ----------------------+---------------
1483 # P9K_WIFI_SSID | service set identifier, a.k.a. network name
1484 # P9K_WIFI_LINK_AUTH | authentication protocol such as "wpa2-psk" or "none"; empty if unknown
1485 # P9K_WIFI_LAST_TX_RATE | wireless transmit rate in megabits per second
1486 # P9K_WIFI_RSSI | signal strength in dBm, from -120 to 0
1487 # P9K_WIFI_NOISE | noise in dBm, from -120 to 0
1488 # P9K_WIFI_BARS | signal strength in bars, from 0 to 4 (derived from P9K_WIFI_RSSI and P9K_WIFI_NOISE)
1489
1490 ####################################[ time: current time ]####################################
1491 # Current time color.
1492 typeset -g POWERLEVEL9K_TIME_FOREGROUND=66
1493 # Format for the current time: 09:51:02. See `man 3 strftime`.
1494 typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}'
1495 # If set to true, time will update when you hit enter. This way prompts for the past
1496 # commands will contain the start times of their commands as opposed to the default
1497 # behavior where they contain the end times of their preceding commands.
1498 typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false
1499 # Custom icon.
1500 # typeset -g POWERLEVEL9K_TIME_VISUAL_IDENTIFIER_EXPANSION='⭐'
1501 # Custom prefix.
1502 # typeset -g POWERLEVEL9K_TIME_PREFIX='%fat '
1503
1504 # Example of a user-defined prompt segment. Function prompt_example will be called on every
1505 # prompt if `example` prompt segment is added to POWERLEVEL9K_LEFT_PROMPT_ELEMENTS or
1506 # POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS. It displays an icon and orange text greeting the user.
1507 #
1508 # Type `p10k help segment` for documentation and a more sophisticated example.
1509 function prompt_example() {
1510 p10k segment -f 208 -i '⭐' -t 'hello, %n'
1511 }
1512
1513 # User-defined prompt segments may optionally provide an instant_prompt_* function. Its job
1514 # is to generate the prompt segment for display in instant prompt. See
1515 # https://github.com/romkatv/powerlevel10k/blob/master/README.md#instant-prompt.
1516 #
1517 # Powerlevel10k will call instant_prompt_* at the same time as the regular prompt_* function
1518 # and will record all `p10k segment` calls it makes. When displaying instant prompt, Powerlevel10k
1519 # will replay these calls without actually calling instant_prompt_*. It is imperative that
1520 # instant_prompt_* always makes the same `p10k segment` calls regardless of environment. If this
1521 # rule is not observed, the content of instant prompt will be incorrect.
1522 #
1523 # Usually, you should either not define instant_prompt_* or simply call prompt_* from it. If
1524 # instant_prompt_* is not defined for a segment, the segment won't be shown in instant prompt.
1525 function instant_prompt_example() {
1526 # Since prompt_example always makes the same `p10k segment` calls, we can call it from
1527 # instant_prompt_example. This will give us the same `example` prompt segment in the instant
1528 # and regular prompts.
1529 prompt_example
1530 }
1531
1532 # User-defined prompt segments can be customized the same way as built-in segments.
1533 # typeset -g POWERLEVEL9K_EXAMPLE_FOREGROUND=208
1534 # typeset -g POWERLEVEL9K_EXAMPLE_VISUAL_IDENTIFIER_EXPANSION='⭐'
1535
1536 # Transient prompt works similarly to the builtin transient_rprompt option. It trims down prompt
1537 # when accepting a command line. Supported values:
1538 #
1539 # - off: Don't change prompt when accepting a command line.
1540 # - always: Trim down prompt when accepting a command line.
1541 # - same-dir: Trim down prompt when accepting a command line unless this is the first command
1542 # typed after changing current working directory.
1543 typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=always
1544
1545 # Instant prompt mode.
1546 #
1547 # - off: Disable instant prompt. Choose this if you've tried instant prompt and found
1548 # it incompatible with your zsh configuration files.
1549 # - quiet: Enable instant prompt and don't print warnings when detecting console output
1550 # during zsh initialization. Choose this if you've read and understood
1551 # https://github.com/romkatv/powerlevel10k/blob/master/README.md#instant-prompt.
1552 # - verbose: Enable instant prompt and print a warning when detecting console output during
1553 # zsh initialization. Choose this if you've never tried instant prompt, haven't
1554 # seen the warning, or if you are unsure what this all means.
1555 typeset -g POWERLEVEL9K_INSTANT_PROMPT=quiet
1556
1557 # Hot reload allows you to change POWERLEVEL9K options after Powerlevel10k has been initialized.
1558 # For example, you can type POWERLEVEL9K_BACKGROUND=red and see your prompt turn red. Hot reload
1559 # can slow down prompt by 1-2 milliseconds, so it's better to keep it turned off unless you
1560 # really need it.
1561 typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true
1562
1563 # If p10k is already loaded, reload configuration.
1564 # This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true.
1565 (( ! $+functions[p10k] )) || p10k reload
1566}
1567
1568# Tell `p10k configure` which file it should overwrite.
1569typeset -g POWERLEVEL9K_CONFIG_FILE=${${(%):-%x}:a}
1570
1571(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]}
1572'builtin' 'unset' 'p10k_config_opts'
diff --git a/users/gkleen/authorized-keys/gkleen-sif.pub b/users/gkleen/authorized-keys/gkleen-sif.pub
new file mode 100644
index 00000000..e9aaf215
--- /dev/null
+++ b/users/gkleen/authorized-keys/gkleen-sif.pub
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKrHPERae+OUTNOzNf9d2767ljFCm5hgmQw48Dj4RrlU gkleen@sif.midgard.yggdrasil
diff --git a/users/gkleen/default.nix b/users/gkleen/default.nix
new file mode 100644
index 00000000..d98ad82c
--- /dev/null
+++ b/users/gkleen/default.nix
@@ -0,0 +1,47 @@
1{ flake, userName, pkgs, customUtils, lib, ... }:
2{
3 imports = with flake.nixosModules.userProfiles.${userName}; [
4 zsh tmux utils direnv
5 ];
6
7 users.users.${userName} = {
8 description = "Gregor Kleen";
9 extraGroups = [ "wheel" "networkmanager" "lp" "dialout" "audio" "video" "xmpp" "mail" "ssh" "vboxusers" "libvirtd" "wireshark" "games"];
10 group = "users";
11 uid = 1000;
12 createHome = true;
13 home = "/home/${userName}";
14 shell = "${pkgs.zsh}/bin/zsh";
15 isNormalUser = true;
16 openssh.authorizedKeys.keyFiles = lib.attrValues (customUtils.recImport rec { dir = ./authorized-keys; _import = name: _base: dir + "/${name}"; });
17 hashedPassword = "$6$rounds=500000$dOMgCU7DAk$yQFYGOURTEt12387LIYBnFKSWmtwXMUk1LJWnV0m7OFt.y2TnxQn2abdGA5dhwG9EmMB5wZGXf4J5F71c746C/";
18 };
19
20 home-manager.users.${userName} = {
21 programs = {
22 git = {
23 enable = true;
24 userEmail = "gkleen@yggdrasil.li";
25 userName = "Gregor Kleen";
26 delta.enable = true;
27 extraConfig = {
28 pull.rebase = false;
29 };
30 };
31
32 ssh = {
33 enable = true;
34 controlMaster = "auto";
35 controlPersist = "30m";
36 serverAliveInterval = 6;
37 hashKnownHosts = true;
38 extraConfig = ''
39 IdentitiesOnly true
40 ServerAliveCountMax 10
41 '';
42 };
43
44 gpg.enable = true;
45 };
46 };
47}
diff --git a/users/root.nix b/users/root.nix
new file mode 100644
index 00000000..be331141
--- /dev/null
+++ b/users/root.nix
@@ -0,0 +1,52 @@
1{ flake, lib, config, hostName, userName, pkgs, ... }:
2let
3 haveGKleen = flake.nixosModules.accounts ? "gkleen@${hostName}";
4in {
5 imports = with flake.nixosModules.userProfiles.${userName}; [
6 zsh tmux direnv utils
7 ];
8
9 users.users.${userName} = lib.mkIf haveGKleen {
10 inherit (config.users.users."gkleen") hashedPassword shell;
11 openssh.authorizedKeys.keyFiles = config.users.users."gkleen".openssh.authorizedKeys.keyFiles;
12 };
13
14 home-manager.users.${userName} = {
15 programs = {
16 git = {
17 enable = true;
18 userEmail = "gkleen@yggdrasil.li";
19 userName = "Gregor Kleen";
20 delta.enable = true;
21 extraConfig = {
22 pull.rebase = false;
23 };
24 };
25
26 ssh = {
27 enable = true;
28 controlMaster = "auto";
29 controlPersist = "30m";
30 serverAliveInterval = 6;
31 hashKnownHosts = true;
32 extraConfig = ''
33 IdentitiesOnly true
34 ServerAliveCountMax 10
35 '';
36 };
37
38 gpg.enable = true;
39 };
40
41 services = {
42 gpg-agent = {
43 enable = true;
44 enableSshSupport = true;
45 extraConfig = ''
46 pinentry-program ${pkgs.pinentry-curses}/bin/pinentry
47 grab
48 '';
49 };
50 };
51 };
52}