Compare commits

...

296 Commits

Author SHA1 Message Date
227b97479c Use aws-cli v2 2026-02-27 19:16:34 +01:00
6322f56ac2 aur: Update terraform-ls-bin 2026-02-27 19:15:21 +01:00
96dcc37947 aur: Update spotify 2026-02-27 19:15:20 +01:00
007a100b40 aur: Update python-rst2ansi 2026-02-27 19:15:19 +01:00
c21801707f aur: Update portfolio-performance-bin 2026-02-27 19:15:17 +01:00
b8c59ec3e3 aur: Update nodejs-intelephense 2026-02-27 19:15:16 +01:00
7db5362770 aur: Update claude-code 2026-02-27 19:15:15 +01:00
d89c6ea0ce Remove weather from taskbar 2026-02-27 19:14:52 +01:00
21788882d3 Update journal shortcut 2026-02-27 19:14:52 +01:00
ce0bf90370 Remove signal 2026-01-17 14:21:08 +01:00
7ebb2c63c1 aur: Add aws-session-manager-plugin 2026-01-17 14:03:23 +01:00
caa3a7bede aur: Update terraform-ls-bin 2026-01-17 14:03:10 +01:00
9055522e85 aur: Update spotify 2026-01-17 14:03:10 +01:00
a90cfbea83 aur: Update slack-desktop 2026-01-17 14:03:10 +01:00
793a9cd47f aur: Update python-vdf 2026-01-17 14:03:10 +01:00
3405eb0918 aur: Update python-class-registry 2026-01-17 14:03:10 +01:00
e225b14e7e aur: Update protontricks 2026-01-17 14:03:10 +01:00
cdfe3d03e7 aur: Update portfolio-performance-bin 2026-01-17 14:03:10 +01:00
e59f636af6 aur: Update nodejs-intelephense 2026-01-17 14:03:10 +01:00
57bf30107c aur: Update claude-code 2026-01-17 14:03:10 +01:00
ac1522920d aur: Update backblaze-b2 2026-01-17 14:03:10 +01:00
8de9b29147 rust: Use wild 2026-01-17 13:59:38 +01:00
15a350869d spotify: Update GPG key 2026-01-17 13:59:38 +01:00
c624a91839 Fix firefox desktop entry 2026-01-17 13:59:38 +01:00
50b71eeb9f Do not prompt when updating keyring 2025-11-19 15:40:34 +01:00
f00b3ba481 git: Use https to pull from github 2025-11-19 15:39:14 +01:00
c54fbaf36f Add explicit profile override for firefox desktop entry 2025-11-18 22:21:57 +01:00
c216210f37 aur: Update protontricks 2025-11-18 22:21:57 +01:00
b806b475b5 aur: Update nodejs-intelephense 2025-11-18 22:21:57 +01:00
6ebc8280d6 aur: Update claude-code 2025-11-18 22:21:57 +01:00
1f19452d9d Configure firefox properly via policies 2025-11-17 09:00:52 +01:00
f47d26f4dd hera: Install additional packages 2025-11-17 09:00:52 +01:00
966760bff5 Fix all the deprecation warnings 2025-11-11 15:08:33 +01:00
a6f9893fac aur: Update screencfg-git 2025-11-07 18:28:26 +01:00
71469bab93 Install mdformat 2025-11-07 18:28:26 +01:00
0e56890ee4 Add screencfg service 2025-11-07 18:28:26 +01:00
15de59d8cc aur: Fix build script 2025-11-07 18:28:26 +01:00
90c92e2159 Configure reflector properly 2025-11-07 18:28:26 +01:00
c6d23aa138 aur: Update claude-code 2025-11-07 18:28:26 +01:00
bc0a99c883 Do not sync twice during pacman updates 2025-11-04 16:58:43 +01:00
6577f26cd1 Remove vim configuration 2025-11-04 16:58:43 +01:00
2787b90948 aur: Update portfolio-performance-bin 2025-11-04 16:58:43 +01:00
fcf7d99318 aur: Update claude-code 2025-11-04 16:58:43 +01:00
1a758dd8d8 firefox: Remove privacy badger 2025-11-04 16:58:43 +01:00
0adb685741 Remove orphaned packages 2025-11-04 16:58:43 +01:00
a918636dd4 ares: Remove TV mode 2025-11-04 16:58:43 +01:00
c5c697eefd Use new fontawesome package 2025-11-04 16:58:43 +01:00
a049d72379 hera: Fix backup command 2025-10-30 20:09:41 +01:00
44a1c5e6e3 hera: Change restic backup path 2025-10-30 19:00:33 +01:00
55ab0d203b aur: Update claude-code 2025-10-30 18:52:04 +01:00
7c6c8f8bff Add yazi 2025-10-22 10:53:31 +02:00
ead2b2fc41 Add backup configuration 2025-10-22 09:02:33 +02:00
933b232a18 Fix primary user group 2025-10-22 09:01:30 +02:00
f5b31c948d aur: Update claude-code 2025-10-22 09:01:30 +02:00
6381872f80 Try to fix update failures for local AUR packages 2025-10-22 09:01:30 +02:00
c343e3211e Remove debugging tag 2025-10-22 09:01:30 +02:00
5d6ce575d4 Move sddm into separate block 2025-10-22 09:01:30 +02:00
10708409c2 Update gpg keys first during autoupdate 2025-10-22 09:01:30 +02:00
e6307e5881 Install and enable reflector 2025-10-22 00:15:39 +02:00
3da38b1708 Resize /boot to 1GiB 2025-10-22 00:15:39 +02:00
c79811541b Update and configure mkinitcpio hooks 2025-10-22 00:15:39 +02:00
75d5875cac aur: Update spotify 2025-10-22 00:15:39 +02:00
55d91079eb aur: Update claude-code 2025-10-22 00:15:39 +02:00
ae8d83c52f Remove virtualbox 2025-10-22 00:15:39 +02:00
fc2e556a03 Add microcode to initramfs 2025-10-20 10:05:54 +02:00
1a22209bc6 aur: Update claude-code 2025-10-19 23:17:01 +02:00
c9a1f49f91 Fix doc of bootstrap command 2025-10-19 23:06:46 +02:00
aa6f2eee8c Add intel GPU packages 2025-10-19 23:06:26 +02:00
e09a1210fa Move bash_history into XDG directories 2025-10-14 12:28:30 +02:00
4cec4765b1 vim: Remove invalid setting 2025-10-14 12:25:55 +02:00
0d4c7ed4d4 Move python stuff into XDG directories 2025-10-14 12:23:23 +02:00
ee84e3248e Move redis stuff into XDG directories 2025-10-14 12:22:28 +02:00
dae07a30c8 Move postgres stuff into XDG directories 2025-10-14 12:22:10 +02:00
804c0019db Add claude-code 2025-10-14 10:37:30 +02:00
db92e8982a Support machine-specific packages 2025-10-11 17:05:02 +02:00
c91584541a Add hera-specific tasks 2025-10-11 17:05:02 +02:00
440eb42404 Enable machine-specific tasks 2025-10-11 09:52:10 +02:00
da4f5bc228 aur: Update workstation-mgr 2025-10-11 01:39:01 +02:00
c80c40e57a Make aur update more robust 2025-10-11 01:37:44 +02:00
85c81b5a1c aur: Update terraform-ls-bin 2025-10-11 01:37:11 +02:00
28d155400f aur: Update slack-desktop 2025-10-11 01:37:10 +02:00
e322bf65fc test: Add support for non-luks and seprate home 2025-10-11 01:33:49 +02:00
2b0ab9651e Add hera 2025-10-11 01:33:49 +02:00
f97b03919b Add dionysus 2025-10-10 23:27:58 +02:00
660aa2e2fb Make autologin configurable 2025-10-10 23:27:58 +02:00
79ffc5858e Make systemd default target configurable 2025-10-10 23:27:58 +02:00
21e015a0c2 Standardize locale on all runs 2025-10-10 23:27:58 +02:00
b82c598b53 mgr: Fix build 2025-10-10 23:27:58 +02:00
3d733195d6 Make sure pacman database exists on install 2025-10-10 23:27:58 +02:00
81c8483689 Print proper error message in test.sh 2025-10-10 17:38:21 +02:00
a0ff050d0e Remove hades 2025-10-10 17:38:21 +02:00
a6e792bd6b Move user-related tasks together 2025-10-10 17:38:21 +02:00
8447343233 mgr: Add sleep action 2025-10-10 17:38:21 +02:00
65bfa84566 mgr: Fix empty request on wrong usage 2025-10-10 17:38:21 +02:00
f058ea45c0 aur: Update terraform-ls-bin 2025-10-10 17:38:21 +02:00
96b91f8d2c aur: Update portfolio-performance-bin 2025-10-10 17:38:21 +02:00
d44ed4165e Apply ansible-lint 2025-10-10 17:38:21 +02:00
1bff7ad4fe Clean up Makefile 2025-10-10 17:38:21 +02:00
b646d6d730 aur: Update slack-desktop 2025-10-10 17:38:21 +02:00
959ac7825d aur: Update portfolio-performance-bin 2025-10-10 17:38:21 +02:00
a6ce2ad88f mgr: Parse all possible systemd unit states 2025-10-10 17:38:21 +02:00
e4a22a1b89 mgr: Do not fail locking if spotify is not running 2025-10-10 17:38:21 +02:00
1c544e8902 aur: Update terraform-ls-bin 2025-10-10 17:38:21 +02:00
f8e68062bd aur: Update slack-desktop 2025-10-10 17:38:21 +02:00
ccdbef4bb3 aur: Update protontricks 2025-10-10 17:38:21 +02:00
6ed6ede0d8 aur: Update portfolio-performance-bin 2025-10-10 17:38:21 +02:00
e9e17eceb5 aur: Update google-earth-pro 2025-10-10 17:38:21 +02:00
d31d39473b Replace common functionality with rust implementation 2025-10-10 17:38:21 +02:00
d0d162f3e9 Do not update packages via ansible 2025-09-26 10:35:04 +02:00
f7915cdbff packages: Install pgformatter 2025-09-26 09:18:05 +02:00
b069b349b2 git: Skip main during branch cleanup 2025-09-26 09:18:05 +02:00
bc1bbb2a5c Remove unnecessary whitespace 2025-09-04 14:30:54 +02:00
29d882829f packages: Remove games 2025-09-02 22:46:18 +02:00
f9ba92bd3d packages: Install rust-script 2025-09-02 22:46:05 +02:00
2a69ae05df aur: Update terraform-ls-bin 2025-09-02 22:44:58 +02:00
2d5e56e725 aur: Update slack-desktop 2025-09-02 22:44:57 +02:00
0b404f31dd aur: Update screencfg-git 2025-09-02 22:44:57 +02:00
762a4a08f2 aur: Update protontricks 2025-09-02 22:44:54 +02:00
8314ad3387 aur: Update portfolio-performance-bin 2025-09-02 22:44:54 +02:00
d26d37cb7b aur: Update backblaze-b2 2025-09-02 22:44:53 +02:00
29175ac617 aur: Update portfolio-performance-bin 2025-08-08 13:56:45 +02:00
59b773a438 aur: Update portfolio-performance-bin 2025-08-03 21:52:47 +02:00
27cc4e8a86 packages: Add pulumi 2025-07-29 09:14:37 +02:00
fd30e9d7ef packages: Add podman 2025-07-29 09:14:32 +02:00
6c78ae24f4 aur: Update slack-desktop 2025-07-28 09:05:27 +02:00
ff57670936 aur: Update portfolio-performance-bin 2025-07-16 17:42:05 +02:00
53ca90c0b1 aur: Update terraform-ls-bin 2025-06-23 16:55:24 +02:00
fb52961120 aur: Update portfolio-performance-bin 2025-06-23 16:55:21 +02:00
f3785233c1 aur: Update portfolio-performance-bin 2025-06-17 09:13:33 +02:00
838e99cb19 Do not fail on missing variables in PKGBUILDs 2025-06-10 10:00:57 +02:00
6b0c74b674 aur: Update slack-desktop 2025-06-10 09:53:33 +02:00
0befa8b6f5 aur: Update portfolio-performance-bin 2025-06-10 09:53:30 +02:00
ea465fc075 aur: Update backblaze-b2 2025-06-10 09:53:29 +02:00
bc414939e6 aur: Update spotify 2025-05-21 07:25:19 +02:00
b1db300ae8 aur: Update slack-desktop 2025-05-21 07:25:19 +02:00
0489a0e856 aur: Update portfolio-performance-bin 2025-05-21 07:25:16 +02:00
9348ebce7c aur: Update backblaze-b2 2025-05-21 07:25:15 +02:00
a307940bf4 aur: Update aws-sam-cli 2025-05-21 07:25:15 +02:00
c8413c975f packages: Install kolourpaint 2025-05-08 15:49:47 +02:00
7a71f28303 Fix brightnessctl shortcuts 2025-05-08 15:49:40 +02:00
399294ae65 aur: Update slack-desktop 2025-05-08 15:37:26 +02:00
e5b8ded7fa aur: Update python-aws-lambda-builders 2025-05-08 15:37:24 +02:00
da6db3276c aur: Update portfolio-performance-bin 2025-05-08 15:37:23 +02:00
5d4e3ac606 aur: Update nodejs-intelephense 2025-05-08 15:37:23 +02:00
e095b487dc aur: Update google-earth-pro 2025-05-08 15:37:23 +02:00
1ca7f09ea4 aur: Update aws-sam-cli 2025-05-08 15:37:22 +02:00
205f284c47 Replace xbacklight with brightnessctl 2025-04-26 11:22:58 +02:00
394e4686f3 packages: Install yq 2025-04-25 15:20:39 +02:00
4ed570c7a2 packages: Remove sipcalc 2025-04-24 17:48:43 +02:00
78504a3b1b packages: Use new xbacklight package 2025-04-24 17:44:48 +02:00
be6aa9f66f packages: Remove pinta 2025-04-24 17:39:49 +02:00
0fd006d6c3 Add test for hades 2025-04-24 17:12:00 +02:00
9d5783db39 packages: Install hexyl 2025-04-24 17:11:07 +02:00
c75cdb4473 packages: Replace dnf with dnf5 2025-04-24 17:10:51 +02:00
61b6f85ef3 git: Fix config 2025-04-24 17:10:39 +02:00
1d4597c7db Remove confusing remote_tmp configuration 2025-04-24 17:10:34 +02:00
897280fc3d aur: Update vim-plug 2025-04-14 10:23:49 +02:00
9bd326a7cb aur: Update spotify 2025-04-14 10:23:48 +02:00
ce316547aa aur: Update slack-desktop 2025-04-14 10:23:48 +02:00
7712c0b153 aur: Update portfolio-performance-bin 2025-04-14 10:23:45 +02:00
d086320dfb git: Enable rerere 2025-04-11 10:54:20 +02:00
59f5146df8 Add hades 2025-03-15 09:53:54 +01:00
c7a8e1fb31 firefox: Enable smooth scroll 2025-03-15 08:33:18 +01:00
97ef143f53 garmin: Sync more folders 2025-03-15 08:32:58 +01:00
31f16cfe52 packages: Install nodejs LTS 2025-03-15 08:32:51 +01:00
73c5582e7f aur: Update spotify 2025-03-14 09:04:55 +01:00
cdaefbd74f aur: Update slack-desktop 2025-03-12 08:53:42 +01:00
3ebc46f069 aur: Update protontricks 2025-03-12 08:53:39 +01:00
91d5dd1186 aur: Update portfolio-performance-bin 2025-03-12 08:53:38 +01:00
908fd619e7 aur: Update nodejs-intelephense 2025-03-12 08:53:38 +01:00
f052e65a16 aur: Update spotify 2025-02-16 10:14:08 +01:00
bd7620d0c4 Rename reserved variable 2025-01-31 19:45:44 +01:00
0f75a28af9 packages: Install dua 2025-01-31 19:45:33 +01:00
5506d8e4ae packages: Install composer 2025-01-31 19:45:28 +01:00
32c1e8eac9 aur: Update terraform-ls-bin 2025-01-31 19:35:03 +01:00
f08caa5659 aur: Update portfolio-performance-bin 2025-01-31 19:34:59 +01:00
ea289f1f8d aur: Update backblaze-b2 2025-01-31 19:34:58 +01:00
fc52b828a5 Install supertuxkart 2025-01-20 09:22:58 +01:00
38448a8194 terraform-get-targets: Report errors to stderr 2025-01-16 12:06:13 +01:00
b7fc0c6e3d aur: Update spotify 2025-01-15 10:14:22 +01:00
427da325c4 aur: Update google-earth-pro 2025-01-14 13:58:05 +01:00
12fc9737f7 Change location 2025-01-09 17:27:41 +01:00
133aa0fa44 aur: Fix preexec conditional execution 2025-01-09 17:24:58 +01:00
d431c74ed2 spotify: Update GPG key 2025-01-09 17:24:46 +01:00
f98e9c1351 Install hedgewars 2025-01-09 17:24:36 +01:00
b85059b2fc aur: Update terraform-ls-bin 2025-01-04 11:21:05 +01:00
dac6a48a38 aur: Update spotify 2025-01-04 11:21:04 +01:00
90c69533a4 aur: Update slack-desktop 2025-01-04 11:21:04 +01:00
b5fb06b977 aur: Update python-vdf 2025-01-04 11:21:03 +01:00
43042e489f aur: Update portfolio-performance-bin 2025-01-04 11:21:00 +01:00
2766d90b27 aur: Update google-earth-pro 2025-01-04 11:20:59 +01:00
12d984a840 aur: Update terraform-ls-bin 2024-12-11 22:57:27 +01:00
a954a44abc aur: Update portfolio-performance-bin 2024-11-30 21:33:21 +01:00
4f2a9e67eb Add script to sync garmin 2024-11-30 21:33:10 +01:00
d9dd5a581e aur: Update python-aws-lambda-builders 2024-11-30 21:33:10 +01:00
e816019383 aur: Update portfolio-performance-bin 2024-11-30 21:33:10 +01:00
9eb9433349 aur: Update aws-sam-cli 2024-11-30 21:33:10 +01:00
016cb009cf aur: Update slack-desktop 2024-11-30 21:33:10 +01:00
0e5155d447 aur: Update python-aws-lambda-builders 2024-11-30 21:33:10 +01:00
5f4817d1f2 aur: Update backblaze-b2 2024-11-30 21:33:10 +01:00
a266112070 aur: Update terraform-ls-bin 2024-11-30 21:33:10 +01:00
873406ade5 aur: Update spotify 2024-11-30 21:33:10 +01:00
77d9901077 aur: Update python-vdf 2024-11-30 21:33:10 +01:00
0bddfee932 Use eza instead of exa 2024-11-30 21:32:42 +01:00
19f0fb0ddb Add btop 2024-11-30 21:32:42 +01:00
0901f946bc cargo: Use mold 2024-11-30 21:32:42 +01:00
9772e381ce aur: Update slack-desktop 2024-11-30 21:32:42 +01:00
2b86d66dca Fix include of the gitcfg file 2024-11-30 21:32:42 +01:00
2fef62c2dd Mark all submodules as trusted 2024-11-30 21:32:42 +01:00
ca4070f9e1 packages: Install ansible-lint 2024-11-10 16:10:19 +01:00
1e075ffb13 Formatter run 2024-11-10 16:09:03 +01:00
1429eaf34f make: Add fmt target 2024-11-10 16:08:21 +01:00
4ec1268b2e Update README 2024-11-10 16:07:58 +01:00
89ae314939 aur: Update screencfg-git 2024-11-10 15:56:36 +01:00
162c72aa1b Disable git gpg signing unconditionally 2024-11-10 15:56:36 +01:00
faed023d89 Remove unused machine config for repos 2024-11-10 15:56:36 +01:00
a9a4a1e288 Remove unused machine config for pass 2024-11-10 15:56:36 +01:00
ee824be6fa Fix neptune install script 2024-11-10 13:34:53 +01:00
c6c5812245 test: Fix paths to firmware 2024-11-10 12:52:25 +01:00
3f2cfc65c8 aur: Update grm-git 2024-11-10 12:50:13 +01:00
8514e52850 Enable pcscd socket instead of service 2024-11-09 19:05:21 +01:00
8b9475681a aur: Remove dateparser
It's now available in the official repos
2024-11-09 18:23:48 +01:00
cf2d9274d4 Add screencfg config 2024-11-09 18:01:46 +01:00
3d3890affe Add cargo config 2024-11-09 18:01:33 +01:00
c4ed7b9b9b Always copy files and directories, never symlink 2024-11-09 18:01:19 +01:00
81553524a6 Create XDG_ dirs on startup 2024-11-09 17:37:08 +01:00
e725fdea2a Print packages during update check 2024-11-09 12:44:24 +01:00
c13d4a4d2c grm: Use SSH for my repositories 2024-11-09 12:44:24 +01:00
4d818cd32b git: Automatically set up tracking 2024-11-09 12:44:24 +01:00
30f04074b7 packages: Add cargo-binstall 2024-11-09 12:44:24 +01:00
bc4665d629 packages: Add cargo-dist 2024-11-09 12:44:23 +01:00
bb90ad407e packages: Add cargo-hack 2024-11-09 12:41:18 +01:00
c80add037c packages: Add sccache 2024-11-02 12:00:58 +01:00
980ec8f5eb aur: Add backblaze-b2 2024-11-02 11:26:08 +01:00
c9b7711a95 packages: Add prettier 2024-11-02 11:01:42 +01:00
cebbd6ddbd packages: Add cargo-expand 2024-11-02 11:01:42 +01:00
4d09162f39 alacritty: Migrate config 2024-11-02 11:01:42 +01:00
a726a4de02 aur: Update slack-desktop 2024-11-02 11:01:42 +01:00
ecdf14ae5f aur: Update python-boto3-stubs 2024-11-02 11:01:42 +01:00
e0bf00ce13 aur: Update aws-sam-cli 2024-11-02 11:01:42 +01:00
c508c795bd aur: Update spotify 2024-11-02 11:01:42 +01:00
60cf14d327 aur: Update slack-desktop 2024-11-02 11:01:42 +01:00
7d5af14d21 aur: Update terraform-ls-bin 2024-11-02 11:01:42 +01:00
b259a11a3e aur: Update python-vdf 2024-11-02 11:01:42 +01:00
12b644378a Install slack 2024-11-02 11:01:42 +01:00
f38c3612df packages: Add taplo 2024-11-02 11:01:42 +01:00
7bcfa38026 Use "main" as default git branch 2024-11-02 11:01:42 +01:00
ddff48eef2 packages: Install uv 2024-11-02 11:01:42 +01:00
58e538bb4f aur: Update portfolio-performance-bin 2024-10-05 11:05:38 +02:00
37e6fafac0 dotfiles-directory: Ignore changes in .git/modules/ 2024-10-02 23:54:25 +02:00
146cbf39b7 Configure libvirt properly 2024-10-02 23:45:53 +02:00
59d9c95224 services: Make unit suffix explicit 2024-10-02 23:45:53 +02:00
9330040e00 dotfiles-directory: Rework permission assertion 2024-10-02 23:45:53 +02:00
34ad7579f1 Remove unused conditional 2024-10-02 23:18:18 +02:00
a34ab937e5 aur: Also install checkdepends 2024-10-02 23:01:15 +02:00
1e91752dc6 Fix missing submodules 2024-10-02 22:50:42 +02:00
8f5e821c13 zsh: Add alias for python shell 2024-10-02 22:37:21 +02:00
dafdbb3498 packages: Install yarn 2024-10-02 22:37:16 +02:00
f2ad356272 packages: Install noto fonts 2024-10-02 22:37:14 +02:00
82e9856e68 Fix steam workspace assignment 2024-09-30 17:28:35 +02:00
5a1f1cfa14 Install protontricks 2024-09-30 17:28:32 +02:00
b4c771cc59 aur: Update spotify 2024-09-28 00:06:21 +02:00
6ec26a64fc Update gothic installer 2024-09-24 19:03:22 +02:00
e989b91979 Update AUR updater script 2024-09-24 19:02:38 +02:00
5cc03d302e aur: Update terraform-ls-bin 2024-09-24 18:58:42 +02:00
8314fa6b72 aur: Update spotify 2024-09-24 18:58:42 +02:00
2309b35ed8 aur: Update python-boto3-stubs 2024-09-24 18:58:41 +02:00
49ed1861e0 aur: Update portfolio-performance-bin 2024-09-24 18:58:41 +02:00
6de36ffba2 aur: Update nodejs-intelephense 2024-09-24 18:58:41 +02:00
e575e03f92 aur: Update aws-sam-cli 2024-09-24 18:58:40 +02:00
ca0cee6398 Fix steam UI 2024-08-16 13:08:37 +02:00
b66fe2a155 Install some utils 2024-08-16 13:08:37 +02:00
9c660f82ed Add aws-sam 2024-08-16 09:58:05 +02:00
b3248a0fca aur: Update 2024-08-15 09:50:26 +02:00
f2c651da7c Migrate to keepassxc 2024-08-15 09:50:13 +02:00
7208028023 aur: Update 2024-08-12 21:20:39 +02:00
7c6078d59d firefox: Make restore behave non-idiotic 2024-07-15 11:32:37 +02:00
009b8ff5ce firefox: Remove ad tracking garbage 2024-07-15 11:32:13 +02:00
1fb5713d41 portfolio: Update to v0.69.1 2024-07-08 19:27:14 +02:00
9307929381 Add projector script 2024-07-07 17:14:58 +02:00
ff5897e3b2 Add maintenance script 2024-07-07 17:14:35 +02:00
bfdb956341 Install google earth 2024-07-07 17:14:26 +02:00
6e2b5ddd78 Update PKGBUILDs 2024-07-07 17:14:10 +02:00
1ed0157ffb Install picom 2024-07-07 17:13:43 +02:00
00fd98e873 Use xfce4-screenshooter 2024-07-07 17:13:43 +02:00
b421fb0c95 Move from libertine (deprecated) to libertinus 2024-07-07 17:13:43 +02:00
d8e0f3e78c Install apache 2024-07-07 17:13:43 +02:00
4c7e4918c9 dunst: Use fontawesome 2024-07-07 17:13:43 +02:00
a3d9e6c715 Do not use global ansible config 2024-07-07 17:13:43 +02:00
680aeac317 Drop explicit ansible python interpreter 2024-07-07 17:13:43 +02:00
2dccc21d33 Install fontawesome 2024-07-07 17:13:43 +02:00
bf77835d30 Make pacman-cache-clean timer enableable 2024-05-29 23:20:16 +02:00
eb122a79cc grm: Update github config 2024-05-28 23:56:12 +02:00
b2a428f1f7 Fix steamapps 2024-05-28 17:06:21 +02:00
d9841e5858 Remove project list 2024-05-28 17:06:15 +02:00
4e1f060c42 Prefer ~/bin to default PATH components 2024-05-28 16:54:18 +02:00
112 changed files with 4973 additions and 1461 deletions

48
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "ansible_roles/firefox"]
path = ansible_roles/firefox
url = https://github.com/staticdev/ansible-role-firefox
[submodule "pkgbuilds/spotify"] [submodule "pkgbuilds/spotify"]
path = pkgbuilds/spotify path = pkgbuilds/spotify
url = https://aur.archlinux.org/spotify.git url = https://aur.archlinux.org/spotify.git
@@ -10,9 +7,6 @@
[submodule "pkgbuilds/portfolio-performance-bin"] [submodule "pkgbuilds/portfolio-performance-bin"]
path = pkgbuilds/portfolio-performance-bin path = pkgbuilds/portfolio-performance-bin
url = https://aur.archlinux.org/portfolio-performance-bin.git url = https://aur.archlinux.org/portfolio-performance-bin.git
[submodule "pkgbuilds/vim-plug"]
path = pkgbuilds/vim-plug
url = https://aur.archlinux.org/vim-plug.git
[submodule "pkgbuilds/terraform-ls-bin"] [submodule "pkgbuilds/terraform-ls-bin"]
path = pkgbuilds/terraform-ls-bin path = pkgbuilds/terraform-ls-bin
url = https://aur.archlinux.org/terraform-ls-bin.git url = https://aur.archlinux.org/terraform-ls-bin.git
@@ -22,3 +16,45 @@
[submodule "pkgbuilds/screencfg-git"] [submodule "pkgbuilds/screencfg-git"]
path = pkgbuilds/screencfg-git path = pkgbuilds/screencfg-git
url = https://aur.archlinux.org/screencfg-git.git url = https://aur.archlinux.org/screencfg-git.git
[submodule "pkgbuilds/google-earth-pro"]
path = pkgbuilds/google-earth-pro
url = https://aur.archlinux.org/google-earth-pro.git
[submodule "pkgbuilds/aws-sam-cli"]
path = pkgbuilds/aws-sam-cli
url = https://aur.archlinux.org/aws-sam-cli.git
[submodule "pkgbuilds/python-boto3-stubs"]
path = pkgbuilds/python-boto3-stubs
url = https://aur.archlinux.org/python-boto3-stubs.git
[submodule "pkgbuilds/python-botocore-stubs"]
path = pkgbuilds/python-botocore-stubs
url = https://aur.archlinux.org/python-botocore-stubs.git
[submodule "pkgbuilds/python-chevron"]
path = pkgbuilds/python-chevron
url = https://aur.archlinux.org/python-chevron.git
[submodule "pkgbuilds/python-aws-lambda-builders"]
path = pkgbuilds/python-aws-lambda-builders
url = https://aur.archlinux.org/python-aws-lambda-builders.git
[submodule "pkgbuilds/python-vdf"]
path = pkgbuilds/python-vdf
url = https://aur.archlinux.org/python-vdf.git
[submodule "pkgbuilds/protontricks"]
path = pkgbuilds/protontricks
url = https://aur.archlinux.org/protontricks.git
[submodule "pkgbuilds/slack-desktop"]
path = pkgbuilds/slack-desktop
url = https://aur.archlinux.org/slack-desktop.git
[submodule "pkgbuilds/backblaze-b2"]
path = pkgbuilds/backblaze-b2
url = https://aur.archlinux.org/backblaze-b2.git
[submodule "pkgbuilds/python-class-registry"]
path = pkgbuilds/python-class-registry
url = https://aur.archlinux.org/python-class-registry.git
[submodule "pkgbuilds/python-rst2ansi"]
path = pkgbuilds/python-rst2ansi
url = https://aur.archlinux.org/python-rst2ansi.git
[submodule "pkgbuilds/claude-code"]
path = pkgbuilds/claude-code
url = https://aur.archlinux.org/claude-code.git
[submodule "pkgbuilds/aws-session-manager-plugin"]
path = pkgbuilds/aws-session-manager-plugin
url = https://aur.archlinux.org/aws-session-manager-plugin.git

View File

@@ -1,36 +1,24 @@
ansible_run = ansible-playbook -e ansible_python_interpreter=/usr/bin/python3 --inventory localhost, --diff ./playbook.yml ${ANSIBLE_EXTRA_ARGS} # Make sure to standardize locale, regardless of the machine config
#
# Having a different locale broke "yes | pacman -S" to force-install
# iptables, for example
export LC_ALL = en_US.UTF-8
.PHONY: all ansible_run = ansible-playbook --inventory localhost, --diff ./playbook.yml ${ANSIBLE_EXTRA_ARGS}
all:
$(ansible_run)
.PHONY: config .PHONY: config
config: config:
$(ansible_run) --skip-tags system-update $(ansible_run)
.PHONY: system-update .PHONY: maintenance
system-update: maintenance:
$(ansible_run) --tags system-update ./maintenance.sh
.PHONY: reboot
reboot:
sudo reboot
.PHONY: poweroff
poweroff:
sudo poweroff
.PHONY: weekend
weekend: | update poweroff
.PHONY: packages
packages:
$(ansible_run) --tags packages
.PHONY: dotfiles
dotfiles:
$(ansible_run) --tags dotfiles
.PHONY: test .PHONY: test
test: test:
./test-in-docker.sh ./test-in-docker.sh
.PHONY: fmt
fmt:
git ls-files -z '*.md' | xargs -0 prettier --print-width 80 --prose-wrap always --write
git ls-files -z '*.toml' | xargs -0 taplo format

View File

@@ -1,27 +1,45 @@
# dotfiles # dotfiles
My configuration files. My configuration files for my systems. Uses Ansible for local configuration.
# Installation ## Supported OS
Only Arch Linux is supported
## Bootstrapping
Bootstrapping is specific to the exact machine that is installed. See
`_machines/` for machine-specific configuration, and `install_scripts/` for the
machine install scripts.
They are keyed by hostname.
For easier installation, the install scripts are available via shortlinks. To
(re)install a new machine from a Arch live environment:
```
curl --proto '=https' -O -sSfL https://s.hkoerber.de/i/bootstrap.sh && bash bootstrap.sh {host}
```
## Manual Installation
Because it manages multiple users on the system, the directory is supposed to be Because it manages multiple users on the system, the directory is supposed to be
at `/var/lib/dotfiles`. at `/var/lib/dotfiles`.
To setup the dotfiles: To set up the dotfiles:
1. `git clone https://github.com/hakoerber/dotfiles.git ~/dotfiles` 1. `git clone https://github.com/hakoerber/dotfiles.git ~/dotfiles`
2. `cd ~/dotfiles && ./install.sh` 2. `cd ~/dotfiles && ./install.sh`
# Partial application ## Partial application
To apply only a subset of the changes, use ansible tags that are available via To apply only a subset of the changes, use ansible tags that are available via
the Makefile: the Makefile:
| Command | Description | | Command | Description |
| --- | --- | | --------------- | -------------------------------------------------- |
| `make update` | Updates the system with the latest packages |
| `make packages` | Installs all defined packages (see `packages.yml`) | | `make packages` | Installs all defined packages (see `packages.yml`) |
| `make dotfiles` | Manages the users' dotfiles | | `make dotfiles` | Manages the users' dotfiles |
Note that these are not supported on a first bootstrap run. Only use them after Note that these are not supported on a first bootstrap run. Only use them after
the bootstrap to update existing configuration. the bootstrap to update existing configuration.

View File

@@ -2,6 +2,7 @@ font_size: 11
gpu: amd gpu: amd
cpu: amd cpu: amd
encrypted_root: true
users: users:
- name: hannes - name: hannes
@@ -11,22 +12,11 @@ users:
extensions: extensions:
- ublock-origin - ublock-origin
- passff - passff
- privacy-badger17
- tree-style-tab - tree-style-tab
- i-dont-care-about-cookies - i-dont-care-about-cookies
- floccus - floccus
manage_css: true manage_css: true
media:
extensions:
- ublock-origin
- passff
- privacy-badger17
- tree-style-tab
- i-dont-care-about-cookies
manage_css: true
bigger_font: true
mail: hannes@hkoerber.de mail: hannes@hkoerber.de
git_gpg_sign: false
ssh_agent: false ssh_agent: false
gpg_agent: true gpg_agent: true
gpg_agent_for_ssh: true gpg_agent_for_ssh: true
@@ -34,11 +24,8 @@ users:
email: hannes@hkoerber.de email: hannes@hkoerber.de
id: "0xB5C002530C6A2053" id: "0xB5C002530C6A2053"
fingerprint: "973AE48D71B76735C4712B5BB5C002530C6A2053" fingerprint: "973AE48D71B76735C4712B5BB5C002530C6A2053"
enable_passwordstore: true
environment: environment:
MACHINE_HAS_NEXTCLOUD: "true" MACHINE_HAS_NEXTCLOUD: "true"
repositories:
- personal_projects
screen: screen:
1: DisplayPort-0 1: DisplayPort-0
@@ -59,7 +46,7 @@ workspace:
environment: environment:
MACHINE_TYPE: "workstation" MACHINE_TYPE: "workstation"
MACHINE_HAS_KEEPASSX: "false" MACHINE_HAS_KEEPASSXC: "false"
MACHINE_HAS_NEXTCLOUD: "true" MACHINE_HAS_NEXTCLOUD: "true"
MACHINE_HAS_STEAM: "true" MACHINE_HAS_STEAM: "true"
MACHINE_RESOLUTION_X: "2560" MACHINE_RESOLUTION_X: "2560"

61
_machines/dionysus.yml Normal file
View File

@@ -0,0 +1,61 @@
font_size: 11
gpu: intel
cpu: intel
encrypted_root: true
users:
- name: hannes
vt: 1
firefox_profiles:
default:
extensions:
- ublock-origin
- passff
- tree-style-tab
- i-dont-care-about-cookies
- floccus
manage_css: true
media:
extensions:
- ublock-origin
- passff
- tree-style-tab
- i-dont-care-about-cookies
manage_css: true
bigger_font: true
mail: hannes@hkoerber.de
ssh_agent: false
gpg_agent: true
gpg_agent_for_ssh: true
gpg_key:
email: hannes@hkoerber.de
id: "0xB5C002530C6A2053"
fingerprint: "973AE48D71B76735C4712B5BB5C002530C6A2053"
environment:
MACHINE_HAS_NEXTCLOUD: "true"
screen:
1: HDMI-1
2: HDMI-1
3: HDMI-1
4: HDMI-1
5: HDMI-1
6: HDMI-1
7: HDMI-1
8: HDMI-1
9: HDMI-1
0: HDMI-1
workspace:
1: ""
2: ""
3: ""
environment:
MACHINE_TYPE: "tv"
MACHINE_HAS_KEEPASSXC: "false"
MACHINE_HAS_NEXTCLOUD: "true"
MACHINE_HAS_STEAM: "false"
MACHINE_RESOLUTION_X: "1920"
MACHINE_RESOLUTION_Y: "1080"

265
_machines/hera-tasks.yml Normal file
View File

@@ -0,0 +1,265 @@
---
- name: Autoupdate
block:
- name: Deploy autoupdate script
copy:
owner: root
group: root
mode: "0755"
dest: /usr/local/bin/pacman-autoupdate
content: |
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
# Prevent failures when not battery present
shopt -s nullglob
for battery in /sys/class/power_supply/*/capacity ; do
capacity="$(< "$battery")"
if (( "${capacity}" < 40 )) ; then
printf "Battery at %s%%, exiting\n" "${capacity}" >&2
exit 0
fi
done
if nmcli --terse --fields GENERAL.METERED dev show 2>/dev/null | grep -q "yes" ; then
printf "Detected metered connection, exiting\n" >&2
exit 0
fi
# Make sure that keys are up to date, otherwise sig checks may fail
pacman --sync --noprogressbar --noconfirm --refresh --needed archlinux-keyring
pacman --noprogressbar --noconfirm --sysupgrade
- name: Install pacman autoupdate service
ansible.builtin.copy:
dest: /etc/systemd/system/pacman-autoupdate.service
owner: root
group: root
mode: "0644"
content: |
[Service]
Type=oneshot
ExecStart=/usr/local/bin/pacman-autoupdate
become: true
- name: Install pacman autoupdate timer
ansible.builtin.copy:
dest: /etc/systemd/system/pacman-autoupdate.timer
owner: root
group: root
mode: "0644"
content: |
[Timer]
OnCalendar=daily
OnBootSec=5min
OnUnitInactiveSec=120min
[Install]
WantedBy=multi-user.target
- name: Enable pacman autoupdate timer
ansible.builtin.systemd:
name: pacman-autoupdate.timer
enabled: true
state: started
daemon_reload: true
become: true
become: true
- name: User configuration
block:
- name: Create user group
ansible.builtin.group:
name: "herta"
state: present
become: true
- name: Create user
ansible.builtin.user:
name: "herta"
state: present
home: "/home/herta"
create_home: true
groups:
- dotfiles
- libvirt
- wheel
- wireshark
- docker
- sudonopw
- games
- kvm
- video
shell: /usr/bin/zsh
skeleton: /dev/null
become: true
- name: Display Manager
block:
- name: Enable sddm
ansible.builtin.systemd:
name: sddm.service
enabled: true
daemon_reload: true
become: true
- name: Create sddm config folder
ansible.builtin.file:
state: directory
path: /etc/sddm.conf.d/
owner: root
group: root
mode: "0755"
- name: Enable autologin
ansible.builtin.copy:
dest: /etc/sddm.conf.d/autologin.conf
owner: root
group: root
mode: "0644"
content: |
[Autologin]
User=herta
Session=plasma
- name: Lock on startup
ansible.builtin.copy:
dest: /etc/xdg/kscreenlockerrc
owner: root
group: root
mode: "0644"
content: |
[Daemon]
LockOnStart=true
- name: Backup
block:
- name: create restic config directory
file:
path: /etc/restic
state: directory
owner: root
group: root
mode: "0755"
become: true
- name: create restic exclude file
copy:
dest: /etc/restic/exclude.lst
content: |
/home/*/.cache/**
/home/*/.mozilla/firefox/*/Cache/**
owner: root
group: root
mode: "0755"
become: true
- name: create restic cache directory
file:
path: /var/cache/restic
state: directory
owner: root
group: root
mode: "0700"
become: true
- name: create restic wrapper script
copy:
owner: root
group: root
mode: "0700"
dest: /usr/local/bin/restic-cmd
content: |
#!/usr/bin/env bash
source /etc/restic/env
set -o nounset
set -o errexit
set -o pipefail
export B2_ACCOUNT_ID
export B2_ACCOUNT_KEY
export RESTIC_PASSWORD_FILE=/etc/restic/repopassword
restic \
--cache-dir=/var/cache/restic/ \
--repo="b2:${BUCKET_NAME}:hera" \
--password-file=/etc/restic/repopassword \
--verbose \
"${@}"
become: true
- name: add backup script
copy:
owner: root
group: root
mode: "0700"
dest: /usr/local/bin/restic-backup
content: |
#!/usr/bin/env bash
set -o nounset
set -o errexit
set -o pipefail
run() {
name="${1}" ; shift
printf '[%s] %s - start\n' "${name}" "$(date --utc --iso-8601=seconds)"
"${@}"
printf '[%s] %s - end\n' "${name}" "$(date --utc --iso-8601=seconds)"
}
run backup restic-cmd \
backup \
--exclude-file /etc/restic/exclude.lst \
/home/
run forget restic-cmd \
forget \
--prune \
--keep-daily 30 \
--keep-monthly 12 \
--keep-yearly 3
become: true
- name: Install restic backup service
ansible.builtin.copy:
dest: /etc/systemd/system/restic-backup.service
owner: root
group: root
mode: "0644"
content: |
[Service]
Type=oneshot
ExecStart=systemd-inhibit /usr/local/bin/restic-backup
become: true
- name: Install restic backup timer
ansible.builtin.copy:
dest: /etc/systemd/system/restic-backup.timer
owner: root
group: root
mode: "0644"
content: |
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=multi-user.target
become: true
- name: Enable restic backup timer
ansible.builtin.systemd:
name: restic-backup.timer
enabled: true
state: started
daemon_reload: true
become: true

81
_machines/hera.yml Normal file
View File

@@ -0,0 +1,81 @@
font_size: 11
gpu: intel
cpu: intel
encrypted_root: false
# make sure that display manager works
system_default_target: "graphical.target"
additional_packages:
- plasma-desktop
- konsole
- dolphin
- kdeplasma-addons
- plasma-nm
- plasma-pa
- plasma-systemmonitor
- sddm
- sddm-kcm
- thunderbird
# kde archive manager
- ark
# kde image viewer
- gwenview
# german language packs
- hunspell-de
- thunderbird-i18n-de
- firefox-i18n-de
users:
- name: hannes
vt: 1
firefox_profiles:
default:
extensions:
- ublock-origin
- passff
- tree-style-tab
- i-dont-care-about-cookies
- floccus
manage_css: true
media:
extensions:
- ublock-origin
- passff
- tree-style-tab
- i-dont-care-about-cookies
manage_css: true
bigger_font: true
mail: hannes@hkoerber.de
autologin: false
ssh_agent: false
gpg_agent: true
gpg_agent_for_ssh: true
gpg_key:
email: hannes@hkoerber.de
id: "0xB5C002530C6A2053"
fingerprint: "973AE48D71B76735C4712B5BB5C002530C6A2053"
environment: {}
screen:
1: HDMI-1
2: HDMI-1
3: HDMI-1
4: HDMI-1
5: HDMI-1
6: HDMI-1
7: HDMI-1
8: HDMI-1
9: HDMI-1
0: HDMI-1
workspace: []
environment:
MACHINE_TYPE: "workstation"
MACHINE_HAS_KEEPASSXC: "false"
MACHINE_HAS_NEXTCLOUD: "false"
MACHINE_HAS_STEAM: "false"
MACHINE_RESOLUTION_X: "1920"
MACHINE_RESOLUTION_Y: "1080"

View File

@@ -2,6 +2,7 @@ font_size: 11
gpu: nvidia gpu: nvidia
cpu: intel cpu: intel
encrypted_root: true
users: users:
- name: hannes-work - name: hannes-work
@@ -10,7 +11,6 @@ users:
default: default:
extensions: extensions:
- ublock-origin - ublock-origin
- privacy-badger17
- tree-style-tab - tree-style-tab
- i-dont-care-about-cookies - i-dont-care-about-cookies
manage_css: true manage_css: true
@@ -19,14 +19,12 @@ users:
- ublock-origin - ublock-origin
manage_css: false manage_css: false
mail: h.koerber@clipmyhorse.tv mail: h.koerber@clipmyhorse.tv
git_gpg_sign: false
ssh_agent: true ssh_agent: true
gpg_agent: false gpg_agent: false
gpg_agent_for_ssh: false gpg_agent_for_ssh: false
environment: environment:
MACHINE_HAS_NEXTCLOUD: "false" MACHINE_HAS_NEXTCLOUD: "false"
MACHINE_HAS_KEEPASSX: "false" MACHINE_HAS_KEEPASSXC: "true"
repositories: []
- name: hannes-private - name: hannes-private
vt: 2 vt: 2
@@ -35,13 +33,11 @@ users:
extensions: extensions:
- ublock-origin - ublock-origin
- passff - passff
- privacy-badger17
- tree-style-tab - tree-style-tab
- i-dont-care-about-cookies - i-dont-care-about-cookies
- floccus - floccus
manage_css: true manage_css: true
mail: hannes@hkoerber.de mail: hannes@hkoerber.de
git_gpg_sign: false
ssh_agent: false ssh_agent: false
gpg_agent: true gpg_agent: true
gpg_agent_for_ssh: true gpg_agent_for_ssh: true
@@ -49,12 +45,9 @@ users:
email: hannes@hkoerber.de email: hannes@hkoerber.de
id: "0xB5C002530C6A2053" id: "0xB5C002530C6A2053"
fingerprint: "973AE48D71B76735C4712B5BB5C002530C6A2053" fingerprint: "973AE48D71B76735C4712B5BB5C002530C6A2053"
enable_passwordstore: true
environment: environment:
MACHINE_HAS_NEXTCLOUD: "true" MACHINE_HAS_NEXTCLOUD: "true"
MACHINE_HAS_KEEPASSX: "false" MACHINE_HAS_KEEPASSXC: "false"
repositories:
- personal_projects
screen: screen:
1: DP-4-1-6 1: DP-4-1-6

View File

@@ -1,3 +1,4 @@
[general]
live_config_reload = true live_config_reload = true
import = [ import = [

View File

@@ -7,25 +7,25 @@ foreground = '#24292f'
# Normal colors # Normal colors
[colors.normal] [colors.normal]
black = '#24292e' black = '#24292e'
red = '#d73a49' red = '#d73a49'
green = '#28a745' green = '#28a745'
yellow = '#dbab09' yellow = '#dbab09'
blue = '#0366d6' blue = '#0366d6'
magenta = '#5a32a3' magenta = '#5a32a3'
cyan = '#0598bc' cyan = '#0598bc'
white = '#6a737d' white = '#6a737d'
# Bright colors # Bright colors
[colors.bright] [colors.bright]
black = '#959da5' black = '#959da5'
red = '#cb2431' red = '#cb2431'
green = '#22863a' green = '#22863a'
yellow = '#b08800' yellow = '#b08800'
blue = '#005cc5' blue = '#005cc5'
magenta = '#5a32a3' magenta = '#5a32a3'
cyan = '#3192aa' cyan = '#3192aa'
white = '#d1d5da' white = '#d1d5da'
[[colors.indexed_colors]] [[colors.indexed_colors]]
index = 16 index = 16

View File

@@ -21,4 +21,3 @@ blue = "#66d9ef"
magenta = "#ae81ff" magenta = "#ae81ff"
cyan = "#a1efe4" cyan = "#a1efe4"
white = "#f9f8f5" white = "#f9f8f5"

View File

@@ -2,5 +2,4 @@
retry_files_enabled = False retry_files_enabled = False
nocows = 1 nocows = 1
roles_path = ./ansible_roles roles_path = ./ansible_roles
library = ./ansible_roles/firefox/library interpreter_python = "auto_silent"
remote_tmp = ${XDG_CONFIG_HOME}/ansible/tmp

View File

@@ -0,0 +1,4 @@
[Desktop Entry]
Type=Application
Name=Firefox
Exec=firefox-default --new-tab %u

View File

@@ -16,7 +16,7 @@ Wants=firefox-gtk-override-bigger-font@{{ profile }}.service
{% endfor %} {% endfor %}
Wants=gpg-agent.service Wants=gpg-agent.service
Wants=gnome-keyring.service Wants=gnome-keyring.service
Wants=keepassx.service Wants=keepassxc.service
Wants=keyboard.service Wants=keyboard.service
Wants=laptop-lid.service Wants=laptop-lid.service
Wants=nextcloud.service Wants=nextcloud.service
@@ -30,3 +30,5 @@ Wants=xresources.service
Wants=yubikey-touch-detector.service Wants=yubikey-touch-detector.service
Wants=kdeconnect.service Wants=kdeconnect.service
Wants=color-theme-dark.service Wants=color-theme-dark.service
Wants=workstation-mgr.service
Wants=screencfg.service

View File

@@ -3,7 +3,7 @@ BindsTo=autostart.target
After=windowmanager.target After=windowmanager.target
[Service] [Service]
ExecStart=/usr/bin/env firefox --setDefaultBrowser -P %i ExecStart=/usr/bin/env firefox --profile %h/.mozilla/firefox/profile-%i
PassEnvironment=DISPLAY PassEnvironment=DISPLAY
Environment=XDG_CONFIG_HOME=%h/.config/gtk-3.0-overrides/bigger-font/ Environment=XDG_CONFIG_HOME=%h/.config/gtk-3.0-overrides/bigger-font/
Restart=always Restart=always

View File

@@ -3,6 +3,6 @@ BindsTo=autostart.target
After=windowmanager.target After=windowmanager.target
[Service] [Service]
ExecStart=/usr/bin/env firefox --setDefaultBrowser -P %i ExecStart=/usr/bin/env firefox --profile %h/.mozilla/firefox/profile-%i
PassEnvironment=DISPLAY PassEnvironment=DISPLAY
Restart=always Restart=always

View File

@@ -1,9 +0,0 @@
[Unit]
BindsTo=autostart.target
After=windowmanager.target
ConditionPathExists=%t/features/keepassx
[Service]
ExecStart=/usr/bin/env keepassx --keyfile %h/.secret/main.key %h/.secret/main.kdbx
PassEnvironment=DISPLAY
Restart=always

View File

@@ -0,0 +1,9 @@
[Unit]
BindsTo=autostart.target
After=windowmanager.target
ConditionPathExists=%t/features/keepassxc
[Service]
ExecStart=/usr/bin/env keepassxc --keyfile %h/.secrets/main.keyx %h/.secrets/main.kdbx
PassEnvironment=DISPLAY
Restart=always

View File

@@ -0,0 +1,8 @@
[Unit]
BindsTo=autostart.target
After=windowmanager.target
[Service]
Type=simple
ExecStart=/usr/bin/screencfg watch --best
Restart=always

View File

@@ -6,6 +6,7 @@ After=i3.service
ConditionPathExists=%t/features/steam ConditionPathExists=%t/features/steam
[Service] [Service]
ExecStart=/usr/bin/env steam # `-system-composer`: https://github.com/ValveSoftware/steam-for-linux/issues/10806
ExecStart=/usr/bin/env steam -system-composer
PassEnvironment=DISPLAY PassEnvironment=DISPLAY
Restart=always Restart=always

View File

@@ -0,0 +1,8 @@
[Unit]
BindsTo=autostart.target
After=windowmanager.target
[Service]
Type=simple
ExecStart=/usr/bin/workstation-mgr serve
Restart=always

3
bin/firefox-default Executable file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env bash
exec /usr/bin/firefox --profile "$HOME/.mozilla/firefox/profile-default" "${@}"

View File

@@ -43,7 +43,7 @@ pandoc \
--variable papersize=a4 \ --variable papersize=a4 \
--variable date=$(date --iso-8601=date) \ --variable date=$(date --iso-8601=date) \
--variable fontsize=12pt \ --variable fontsize=12pt \
--variable fontfamily=libertine \ --variable fontfamily=libertinus \
--variable familydefault=sfdefault \ --variable familydefault=sfdefault \
--variable documentclass=scrartcl \ --variable documentclass=scrartcl \
--variable fontfamilyoptions= \ --variable fontfamilyoptions= \

View File

@@ -11,7 +11,7 @@ do
RESOURCE=$(sed -n 's/^resource "\([^"]*\)" "\([^"]*\)".*/-target=\1.\2 /gp' "$FILE") RESOURCE=$(sed -n 's/^resource "\([^"]*\)" "\([^"]*\)".*/-target=\1.\2 /gp' "$FILE")
MODULE=$(sed -n 's/^module "\([^"]*\)".*/-target=module.\1 /gp' "$FILE") MODULE=$(sed -n 's/^module "\([^"]*\)".*/-target=module.\1 /gp' "$FILE")
if [[ -z "$RESOURCE" ]] && [[ -z "$MODULE" ]]; then if [[ -z "$RESOURCE" ]] && [[ -z "$MODULE" ]]; then
echo "Cannot detect terraform resource and module in $FILE" echo "Cannot detect terraform resource and module in $FILE" >&2
exit 1 exit 1
fi fi

6
cargo/config.toml Normal file
View File

@@ -0,0 +1,6 @@
[build]
rustc-wrapper = "sccache"
[target.x86_64-unknown-linux-gnu]
linker = "/usr/bin/clang"
rustflags = ["-Clink-arg=--ld-path=/usr/bin/wild"]

View File

@@ -1,3 +0,0 @@
#!/usr/bin/env bash
git submodule update --remote pkgbuilds/*

View File

@@ -1,5 +1,4 @@
empty_directories: empty_directories:
- name: .config/nvim
- name: .config/rofi - name: .config/rofi
- name: .config/gtk-3.0 - name: .config/gtk-3.0
- name: .config/gtk-3.0-overrides - name: .config/gtk-3.0-overrides
@@ -17,6 +16,7 @@ empty_directories:
- name: .config/alacritty - name: .config/alacritty
- name: .local/state/gnupg/ - name: .local/state/gnupg/
mode: '0700' mode: '0700'
- name: .local/state/cargo/
dotfiles: dotfiles:
- from: git/gitconfig - from: git/gitconfig
to: .config/git/config to: .config/git/config
@@ -43,10 +43,9 @@ dotfiles:
to: .config/i3status-rust/icons/awesome.toml to: .config/i3status-rust/icons/awesome.toml
- from: i3/scripts - from: i3/scripts
to: .config/i3/scripts to: .config/i3/scripts
dir: true
- from: tmux/tmux.conf - from: tmux/tmux.conf
to: .config/tmux/tmux.conf to: .config/tmux/tmux.conf
- from: vim/vimrc
to: .config/nvim/init.vim
- from: x/Xresources - from: x/Xresources
to: .config/Xresources to: .config/Xresources
- from: x/xinitrc - from: x/xinitrc
@@ -75,6 +74,7 @@ dotfiles:
template: true template: true
- from: alacritty/themes - from: alacritty/themes
to: .config/alacritty/themes to: .config/alacritty/themes
dir: true
- from: rofi/config - from: rofi/config
to: .config/rofi/config to: .config/rofi/config
- from: gtk/gtk-3.0.ini - from: gtk/gtk-3.0.ini
@@ -90,13 +90,22 @@ dotfiles:
to: .config/qt5ct/qt5ct.conf to: .config/qt5ct/qt5ct.conf
- from: scripts - from: scripts
to: scripts to: scripts
dir: true
- from: helix/config.toml - from: helix/config.toml
to: .config/helix/config.toml to: .config/helix/config.toml
- from: helix/languages.toml - from: helix/languages.toml
to: .config/helix/languages.toml to: .config/helix/languages.toml
- from: screencfg/screencfg.toml
to: .config/screencfg.toml
- from: cargo/config.toml
to: .local/state/cargo/config.toml
- from: applications
to: .local/share/applications
dir: true
dotfiles_remove: dotfiles_remove:
- .gitconfig - .gitconfig
- .vimrc - .vimrc
- .config/nvim/init.vim
- .tmux.conf - .tmux.conf
- .i3 - .i3
- .gtkrc-2.0 - .gtkrc-2.0

View File

@@ -26,3 +26,9 @@ gpu:
- lib32-vulkan-nouveau - lib32-vulkan-nouveau
- vulkan-headers - vulkan-headers
- vulkan-tools - vulkan-tools
intel:
- mesa
- mesa-utils
- lib32-mesa
- vulkan-intel
- lib32-vulkan-intel

View File

@@ -84,7 +84,7 @@
### Text ### ### Text ###
font = JetBrains Mono {{ machine.font_size }} font = JetBrainsMono, Fontawesome {{ machine.font_size }}
# The spacing between lines. If the height is smaller than the # The spacing between lines. If the height is smaller than the
# font height, it will get raised to the font height. # font height, it will get raised to the font height.

View File

@@ -2,9 +2,6 @@
name = Hannes Körber name = Hannes Körber
email = {{ user.mail }} email = {{ user.mail }}
useConfigOnly = true useConfigOnly = true
{% if user.git_gpg_sign|bool %}
signingkey = {{ user.gpg_key.id }}
{% endif %}
[github] [github]
user = hakoerber user = hakoerber
[alias] [alias]
@@ -36,7 +33,7 @@
k = "!gitk --all" k = "!gitk --all"
serve = !git daemon --reuseaddr --verbose --base-path=. --export-all ./.git serve = !git daemon --reuseaddr --verbose --base-path=. --export-all ./.git
last = "log -1 HEAD" last = "log -1 HEAD"
@@ -44,7 +41,7 @@
pushall = "!bash -c 'for r in $(git remote) ; do [[ "$r" != "upstream" ]] && { echo \"--- [$r] ---\" ; git push $r \"$@\" ; } ; done' -" pushall = "!bash -c 'for r in $(git remote) ; do [[ "$r" != "upstream" ]] && { echo \"--- [$r] ---\" ; git push $r \"$@\" ; } ; done' -"
branch-clean = "!sh -c 'git branch --merged | grep -v -e master -e develop -e '^*' | xargs --no-run-if-empty git branch -d'" branch-clean = "!sh -c 'git branch --merged | grep -v -e master -e develop -e main -e '^*' | xargs --no-run-if-empty git branch -d'"
brc = "!git branch-clean" brc = "!git branch-clean"
graph = log --graph --pretty=format:'%C(yellow)%h%Creset%C(bold red)% D%Creset %C(green)(%cr) %C(blue)%an<%ae>%Creset%n %C(bold white)%s%Creset' --all graph = log --graph --pretty=format:'%C(yellow)%h%Creset%C(bold red)% D%Creset %C(green)(%cr) %C(blue)%an<%ae>%Creset%n %C(bold white)%s%Creset' --all
@@ -80,6 +77,7 @@
ui = auto ui = auto
[push] [push]
default = simple default = simple
autoSetupRemote = true
[merge] [merge]
conflictstyle = diff3 conflictstyle = diff3
[gc] [gc]
@@ -91,7 +89,7 @@
commitBeforeMerge = false commitBeforeMerge = false
detachedHead = false detachedHead = false
[commit] [commit]
gpgSign = {{ user.git_gpg_sign|bool }} gpgSign = false
cleanup = strip cleanup = strip
status = true status = true
[status] [status]
@@ -120,15 +118,25 @@
[url "ssh://git@code.hkoerber.de:2222/"] [url "ssh://git@code.hkoerber.de:2222/"]
insteadOf = https://code.hkoerber.de/ insteadOf = https://code.hkoerber.de/
# https://stackoverflow.com/a/71971739
[url "https://github.com/"]
insteadOf = "git@github.com:"
[url "git@github.com:"]
pushInsteadOf = "https://github.com/"
pushInsteadOf = "git@github.com:"
[init] [init]
defaultBranch = master defaultBranch = main
[safe] [safe]
directory = /var/lib/dotfiles directory = /var/lib/dotfiles
[includeIf "gitdir:/var/lib/dotfiles"] directory = /var/lib/dotfiles/*
[includeIf "gitdir:/var/lib/dotfiles/.git"]
path = /var/lib/dotfiles/gitcfg path = /var/lib/dotfiles/gitcfg
[delta] [delta]
navigate = true # use n and N to move between diff sections navigate = true # use n and N to move between diff sections
# delta detects terminal colors automatically; set one of these to disable auto-detection # delta detects terminal colors automatically; set one of these to disable auto-detection
# dark = true # dark = true
# light = true # light = true
[rerere]
enabled = true

View File

@@ -1,9 +1,8 @@
provider = "github" provider = "github"
token_command = "pass show github | pf '.personal_access_token.grm.value'" token_command = "pass show github | ~/bin/yaml2json | jq -r '.personal_access_token.grm.value'"
root = "~/projects/github" root = "~/code/github.com"
force_ssh = true
worktree = true worktree = true
force_ssh = true
[filters] [filters]
# owner = true owner = true
# groups = ["hi"]

View File

@@ -1,326 +0,0 @@
[[trees]]
root = "~/projects"
[[trees.repos]]
name = "misc/rbackupd"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/rbackupd.git"
type = "ssh"
[[trees.repos]]
name = "misc/cobbler-kickstart"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/cobbler-kickstart.git"
type = "ssh"
[[trees.repos]]
name = "misc/postfix-grok-patterns"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@github.com/hakoerber/postfix-grok-patterns.git"
type = "ssh"
[[trees.repos.remotes]]
name = "upstream"
url = "https://github.com/whyscream/postfix-grok-patterns.git"
type = "https"
[[trees.repos]]
name = "misc/syncrepo"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@github.com/hakoerber/syncrepo.git"
type = "ssh"
[[trees.repos]]
name = "misc/pkgbuilds"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@github.com/hakoerber/pkgbuilds.git"
type = "ssh"
[[trees.repos]]
name = "misc/openvpn-helper"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/openvpn-helper.git"
type = "ssh"
[[trees.repos]]
name = "misc/wifiqr"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/wifiqr.git"
type = "ssh"
[[trees.repos]]
name = "misc/checkconn"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/checkconn.git"
type = "ssh"
[[trees.repos]]
name = "misc/xftwidth"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/xftwidth.git"
type = "ssh"
[[trees.repos.remotes]]
name = "upstream"
url = "https://github.com/vixus0/xftwidth"
type = "https"
[[trees.repos]]
name = "git-repo-manager"
worktree_setup = true
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/git-repo-manager.git"
type = "ssh"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/hannes/git-repo-manager.git"
type = "ssh"
[[trees.repos]]
name = "talks"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@github.com/hakoerber/talks.git"
type = "ssh"
[[trees.repos]]
name = "container/openresty-oidc"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/container/openresty-oidc.git"
type = "ssh"
[[trees.repos]]
name = "container/acimaker"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/acimaker.git"
type = "ssh"
[[trees.repos]]
name = "container/drone-kaniko"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/container/drone-kaniko.git"
type = "ssh"
[[trees.repos.remotes]]
name = "upstream"
url = "https://github.com/banzaicloud/drone-kaniko"
type = "https"
[[trees.repos]]
name = "container/roundcube"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/container/roundcube.git"
type = "ssh"
[[trees.repos]]
name = "container/mycloud-homer"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/container/mycloud-homer.git"
type = "ssh"
[[trees.repos]]
name = "projects/misc/cobbler-kickstart"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@github.com/hakoerber/cobbler-kickstart.git"
type = "ssh"
[[trees.repos]]
name = "finance-auto-import"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/hannes/finance-auto-import.git"
type = "ssh"
[[trees.repos]]
name = "guitar_practice"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@github.com/hakoerber/guitar-practice.git"
type = "ssh"
[[trees.repos]]
name = "picture-cleaner"
remotes = []
[[trees.repos]]
name = "mycloud"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/hannes/mycloud.git"
type = "ssh"
[[trees.repos]]
name = "resume"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/hannes/resume.git"
type = "ssh"
[[trees.repos]]
name = "lea-michael-hochzeit"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/hannes/lea-michi-hochzeit.git"
type = "ssh"
[[trees.repos]]
name = "builddoc"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/builddoc.git"
type = "ssh"
[[trees.repos]]
name = "blog"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/blog.hkoerber.de.git"
type = "ssh"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/hannes/blog.git"
type = "ssh"
[[trees.repos]]
name = "fizzbuzz"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/fizzbuzz.git"
type = "ssh"
[[trees.repos]]
name = "ggj/2018-the-lost-son"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@github.com/niklas-heer/the-lost-son.git"
type = "ssh"
[[trees.repos]]
name = "ggj/2019-claim-your-world"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@github.com/theintroverts/claim-your-world.git"
type = "ssh"
[[trees.repos]]
name = "prometheus-restic-backblaze"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/prometheus-restic-backblaze.git"
type = "ssh"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/container/prometheus-restic-backblaze.git"
type = "ssh"
[[trees.repos]]
name = "dotfiles"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/dotfiles.git"
type = "ssh"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/hannes/dotfiles.git"
type = "ssh"
[[trees.repos]]
name = "packager"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/packager.git"
type = "ssh"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/hannes/packager.git"
type = "ssh"
[[trees.repos]]
name = "time-tracking"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/time-tracking.git"
type = "ssh"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/hannes/time-tracking.git"
type = "ssh"
[[trees.repos]]
name = "aws-glacier-backup"
[[trees.repos.remotes]]
name = "github"
url = "ssh://git@github.com/hakoerber/aws-glacier-backup.git"
type = "ssh"
[[trees.repos.remotes]]
name = "origin"
url = "ssh://git@code.hkoerber.de:2222/hannes/aws-glacier-backup.git"
type = "ssh"
[[trees.repos]]
name = "unclutter-xfixes"
[[trees.repos.remotes]]
name = "github"
url = "git@github.com:hakoerber/unclutter-xfixes.git"
type = "ssh"
[[trees.repos.remotes]]
name = "upstream"
url = "https://github.com/Airblader/unclutter-xfixes"
type = "https"

View File

@@ -5,12 +5,7 @@ idle-timeout = 0
completion-trigger-len = 2 completion-trigger-len = 2
[editor.statusline] [editor.statusline]
left = [ left = ["mode", "separator", "file-name", "file-modification-indicator"]
"mode",
"separator",
"file-name",
"file-modification-indicator",
]
right = [ right = [
"spinner", "spinner",

View File

@@ -1,15 +1,15 @@
[[language]] [[language]]
name = "bash" name = "bash"
indent = { unit = " " , tab-width = 4 } indent = { unit = " ", tab-width = 4 }
[language-server.pylsp.config.pylsp.plugins] [language-server.pylsp.config.pylsp.plugins]
flake8 = {enabled = false} flake8 = { enabled = false }
[[language]] [[language]]
name = "python" name = "python"
indent = { unit = " " , tab-width = 4 } indent = { unit = " ", tab-width = 4 }
[[language]] [[language]]
name = "dockerfile" name = "dockerfile"
indent = { unit = " " , tab-width = 4 } indent = { unit = " ", tab-width = 4 }

View File

@@ -64,8 +64,8 @@
set $up k set $up k
set $right l set $right l
set $splith v set $splith v
set $splitv c set $splitv c
set $split_toggle x set $split_toggle x
set $fullscreen f set $fullscreen f
@@ -95,7 +95,6 @@
set $screenshot o set $screenshot o
################################################################################ ################################################################################
### WORKSPACE ASSIGNMENTS ###################################################### ### WORKSPACE ASSIGNMENTS ######################################################
################################################################################ ################################################################################
@@ -111,8 +110,7 @@ workspace $workspace8 output {{ machine.screen.8 }}
workspace $workspace9 output {{ machine.screen.9 }} workspace $workspace9 output {{ machine.screen.9 }}
workspace $workspace10 output {{ machine.screen.0 }} workspace $workspace10 output {{ machine.screen.0 }}
assign [class="^KeePassXC$"] $workspace8
assign [class="^Keepassx$"] $workspace8
# See https://github.com/i3/i3/issues/2060 # See https://github.com/i3/i3/issues/2060
for_window [class="^Spotify$"] move to workspace $workspace9 for_window [class="^Spotify$"] move to workspace $workspace9
@@ -120,8 +118,8 @@ assign [class="^Spotify$"] $workspace9
assign [class="^Google-chrome$"] $workspace7 assign [class="^Google-chrome$"] $workspace7
assign [class="^Chromium$"] $workspace7 assign [class="^Chromium$"] $workspace7
assign [class="^Steam"] $workspace5 assign [class="^[Ss]team$"] $workspace5
for_window [class="^Steam$"] move to workspace $workspace5 for_window [class="^[Ss]team$"] move to workspace $workspace5
assign [class="^dota2$"] $workspace10 assign [class="^dota2$"] $workspace10
assign [class="^Wine$"] $workspace10 assign [class="^Wine$"] $workspace10
@@ -206,22 +204,18 @@ assign [class="^Wine$"] $workspace10
### START APPLICATIONS ##################################################### ### START APPLICATIONS #####################################################
bindsym $mod+d exec --no-startup-id $scriptdir/appmenu bindsym $mod+d exec --no-startup-id rofi -show combi -combi-modi run -display-combi "run"
bindsym $mod+Return exec $terminal
bindsym $mod+Return exec $terminal
bindsym $mod+Shift+Return exec $calc bindsym $mod+Shift+Return exec $calc
bindsym F1 exec --no-startup-id $scriptdir/shutdown-menu bindsym F1 exec --no-startup-id workstation-client power menu
bindsym F2 exec --no-startup-id $scriptdir/screenmenu
bindsym $mod+F1 exec --no-startup-id $scriptdir/i3exit lock bindsym $mod+F1 exec --no-startup-id workstation-client power lock
bindsym $mod+F4 exec --no-startup-id $scriptdir/i3exit suspend
bindsym $mod+Home exec --no-startup-id $scriptdir/shutdown-menu
bindsym $mod+$screenshot exec --no-startup-id sh -c 'maim | xclip -selection clipboard -t image/png' bindsym $mod+$screenshot exec --no-startup-id sh -c 'maim | xclip -selection clipboard -t image/png'
bindsym $mod+Shift+$screenshot exec --no-startup-id sh -c 'maim --select | xclip -selection clipboard -t image/png' bindsym $mod+Shift+$screenshot exec --no-startup-id sh -c 'maim --select | xclip -selection clipboard -t image/png'
bindsym $mod+Shift+v exec --no-startup-id redshift-toggle
bindsym $mod+$pim_toggle exec --no-startup-id $scriptdir/swap-from-workspace $workspace10 bindsym $mod+$pim_toggle exec --no-startup-id $scriptdir/swap-from-workspace $workspace10
################################################################################ ################################################################################
@@ -314,22 +308,22 @@ bindsym $mod+F9 exec --no-startup-id evolution
### SPECIAL KEYBINDS ########################################################### ### SPECIAL KEYBINDS ###########################################################
################################################################################ ################################################################################
bindsym XF86Sleep exec --no-startup-id $scriptdir/i3exit suspend bindsym XF86Sleep exec --no-startup-id workstation-client power lock
bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute '@DEFAULT_SINK@' toggle
bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume '@DEFAULT_SINK@' +5%
bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume '@DEFAULT_SINK@' -5%
bindsym XF86AudioPlay exec --no-startup-id playerctl -p spotify play-pause bindsym XF86AudioMute exec --no-startup-id workstation-client pulseaudio output toggle
bindsym XF86AudioNext exec --no-startup-id playerctl -p spotify next bindsym XF86AudioRaiseVolume exec --no-startup-id workstation-client pulseaudio output inc
bindsym XF86AudioPrev exec --no-startup-id playerctl -p spotify previous bindsym XF86AudioLowerVolume exec --no-startup-id workstation-client pulseaudio output dec
# keys seemingly switched bindsym XF86AudioPlay exec --no-startup-id workstation-client spotify toggle
bindsym XF86MonBrightnessUp exec --no-startup-id xbacklight -inc 8 ; exec --no-startup-id $scriptdir/update-status bindsym XF86AudioNext exec --no-startup-id workstation-client spotify next
bindsym XF86MonBrightnessDown exec --no-startup-id xbacklight -dec 8 ; exec --no-startup-id $scriptdir/update-status bindsym XF86AudioPrev exec --no-startup-id workstation-client spotify previous
bindsym $mod+m exec --no-startup-id pactl set-source-mute '@DEFAULT_SOURCE@' toggle bindsym XF86MonBrightnessUp exec --no-startup-id workstation-client brightness inc
bindsym $mod+space exec --no-startup-id pactl set-source-mute '@DEFAULT_SOURCE@' toggle bindsym XF86MonBrightnessDown exec --no-startup-id workstation-client brightness dec
bindsym KP_Enter exec --no-startup-id pactl set-source-mute '@DEFAULT_SOURCE@' toggle
bindsym $mod+m exec --no-startup-id workstation-client pulseaudio input toggle
bindsym $mod+space exec --no-startup-id workstation-client pulseaudio input toggle
bindsym KP_Enter exec --no-startup-id workstation-client pulseaudio input toggle
############################################################################## ##############################################################################
### BARS ####################################################################### ### BARS #######################################################################

View File

@@ -33,7 +33,7 @@ format = " $icon{ $volume.eng(w:2)|} "
[[block.click]] [[block.click]]
button = "left" button = "left"
cmd = "pactl set-sink-mute '@DEFAULT_SINK@' toggle" cmd = "workstation-client pulseaudio output toggle"
update = true update = true
[[block]] [[block]]
@@ -50,7 +50,7 @@ idle_bg = { link = "warning_bg" }
[[block.click]] [[block.click]]
button = "left" button = "left"
cmd = "pactl set-source-mute '@DEFAULT_SOURCE@' toggle" cmd = "workstation-client pulseaudio input toggle"
update = true update = true
[[block]] [[block]]
@@ -67,31 +67,32 @@ missing_format = ""
[[block]] [[block]]
block = "toggle" block = "toggle"
format = "  $icon " format = "  $icon "
command_on = "$XDG_CONFIG_HOME/i3/scripts/presentation-mode toggle ; pkill -SIGRTMIN+0 i3status-rs" signal = 1
command_off = "$XDG_CONFIG_HOME/i3/scripts/presentation-mode toggle ; pkill -SIGRTMIN+0 i3status-rs" command_on = "workstation-client present toggle ; pkill -SIGRTMIN+1 i3status-rs"
command_state = "[[ $($XDG_CONFIG_HOME/i3/scripts/presentation-mode status) == on ]] && echo active" command_off = "workstation-client present toggle ; pkill -SIGRTMIN+1 i3status-rs"
command_state = "[[ $(workstation-client present status) == on ]] && echo active"
[[block]] [[block]]
block = "toggle" block = "toggle"
format = "  $icon " format = "  $icon "
command_on = "systemctl --user start color-theme-light" command_on = "workstation-client theme light"
command_off = "systemctl --user start color-theme-dark" command_off = "workstation-client theme dark"
command_state = "[[ $(systemctl --user is-active color-theme-light) == active ]] && echo active" command_state = "[[ $(workstation-client theme status) == light ]] && echo 1"
[[block]] [[block]]
block = "toggle" block = "toggle"
format = "  $icon " format = "  $icon "
command_on = "systemctl --user start redshift" command_on = "workstation-client redshift start"
command_off = "systemctl --user stop redshift" command_off = "workstation-client redshift stop"
command_state = "[[ $(systemctl --user is-active redshift) == active ]] && echo active" command_state = "[[ $(workstation-client redshift status) == active ]] && echo 1"
signal = 0 signal = 0
[[block]] [[block]]
block = "toggle" block = "toggle"
format = "  $icon " format = "  $icon "
command_on = "systemctl --user start spotify" command_on = "workstation-client spotify start"
command_off = "systemctl --user stop spotify" command_off = "workstation-client spotify stop"
command_state = "[[ $(systemctl --user is-active spotify) == active ]] && echo active" command_state = "[[ $(workstation-client spotify status) == active ]] && echo 1"
signal = 0 signal = 0
[[block]] [[block]]
@@ -99,11 +100,6 @@ block = "custom"
json = true json = true
command = "ping -n -q -w 2 -c 1 8.8.8.8 >/dev/null 2>/dev/null && printf '{\"text\":\"\",\"state\":\"Info\"}' || printf '{\"text\":\"\",\"state\":\"Critical\"}'" command = "ping -n -q -w 2 -c 1 8.8.8.8 >/dev/null 2>/dev/null && printf '{\"text\":\"\",\"state\":\"Info\"}' || printf '{\"text\":\"\",\"state\":\"Critical\"}'"
[[block]]
block = "custom"
command = "curl -s 'https://wttr.in/Stockholm?m&T&format=%c%t' | sed 's/ / /g'"
interval = 3600
[[block]] [[block]]
block = "time" block = "time"
interval = 1 interval = 1

View File

@@ -14,81 +14,81 @@ backlight_10 = "\U0001f312"
backlight_11 = "\U0001f312" backlight_11 = "\U0001f312"
backlight_12 = "\U0001f312" backlight_12 = "\U0001f312"
backlight_13 = "\U0001f312" backlight_13 = "\U0001f312"
bat_charging = "\uf1e6" # fa-plug bat_charging = "\uf1e6" # fa-plug
bat_discharging = "\uf242" # fa-battery-half bat_discharging = "\uf242" # fa-battery-half
bat_10 = "\uf244" # fa-battery-empty bat_10 = "\uf244" # fa-battery-empty
bat_20 = "\uf243" # fa-battery-quarter bat_20 = "\uf243" # fa-battery-quarter
bat_30 = "\uf243" # fa-battery-quarter bat_30 = "\uf243" # fa-battery-quarter
bat_40 = "\uf243" # fa-battery-quarter bat_40 = "\uf243" # fa-battery-quarter
bat_50 = "\uf242" # fa-battery-half bat_50 = "\uf242" # fa-battery-half
bat_60 = "\uf242" # fa-battery-half bat_60 = "\uf242" # fa-battery-half
bat_70 = "\uf241" # fa-battery-three-quarters bat_70 = "\uf241" # fa-battery-three-quarters
bat_80 = "\uf241" # fa-battery-three-quarters bat_80 = "\uf241" # fa-battery-three-quarters
bat_90 = "\uf241" # fa-battery-three-quarters bat_90 = "\uf241" # fa-battery-three-quarters
bat_full = "\uf240" # fa-battery-full bat_full = "\uf240" # fa-battery-full
bat_not_available = "\uf244" # fa-battery-empty bat_not_available = "\uf244" # fa-battery-empty
bell = "\uf0f3" # fa-bell bell = "\uf0f3" # fa-bell
bell-slash = "\uf1f7" # fa-bell-slash-o bell-slash = "\uf1f7" # fa-bell-slash-o
bluetooth = "\uf294" # fa-bluetooth-b bluetooth = "\uf294" # fa-bluetooth-b
calendar = "\uf073" # fa-calendar calendar = "\uf073" # fa-calendar
cogs = "\uf085" # fa-cogs cogs = "\uf085" # fa-cogs
cpu = "\uf0e4" # fa-dashboard cpu = "\uf0e4" # fa-dashboard
cpu_boost_off = "\uf204" # fa-toggle-off cpu_boost_off = "\uf204" # fa-toggle-off
cpu_boost_on = "\uf205" # fa-toggle-on cpu_boost_on = "\uf205" # fa-toggle-on
disk_drive = "\uf0a0" # fa-hdd-o disk_drive = "\uf0a0" # fa-hdd-o
docker = "\uf21a" # fa-ship docker = "\uf21a" # fa-ship
github = "\uf09b" # fa-github github = "\uf09b" # fa-github
gpu = "\uf26c" # fa-television gpu = "\uf26c" # fa-television
headphones = "\uf025" # fa-headphones headphones = "\uf025" # fa-headphones
joystick = "\uf11b" # fa-gamepad joystick = "\uf11b" # fa-gamepad
keyboard = "\uf11c" # fa-keyboard-o keyboard = "\uf11c" # fa-keyboard-o
mail = "\uf0e0" # fa-envelope mail = "\uf0e0" # fa-envelope
memory_mem = "\uf2db" # fa-microchip memory_mem = "\uf2db" # fa-microchip
memory_swap = "\uf0a0" # fa-hdd-o memory_swap = "\uf0a0" # fa-hdd-o
mouse = "\uf245" # fa-mouse-pointer mouse = "\uf245" # fa-mouse-pointer
music = "\uf001" # fa-music music = "\uf001" # fa-music
music_next = "\uf061" # fa-arrow-right music_next = "\uf061" # fa-arrow-right
music_pause = "\uf04c" # fa-pause music_pause = "\uf04c" # fa-pause
music_play = "\uf04b" # fa-play music_play = "\uf04b" # fa-play
music_prev = "\uf060" # fa-arrow-left music_prev = "\uf060" # fa-arrow-left
net_bridge = "\uf0e8" # fa-sitemap net_bridge = "\uf0e8" # fa-sitemap
net_down = "\u2b07" net_down = "\u2b07"
net_loopback = "LO" net_loopback = "LO"
net_modem = "\uf095" # fa-phone net_modem = "\uf095" # fa-phone
net_up = "\u2b06" net_up = "\u2b06"
net_vpn = "\uf023" # fa-lock net_vpn = "\uf023" # fa-lock
net_wired = "\uf0ac" # fa-globe net_wired = "\uf0ac" # fa-globe
net_wireless = "\uf1eb" # fa-wifi net_wireless = "\uf1eb" # fa-wifi
notification = "\uf0a2" # fa-bell-o notification = "\uf0a2" # fa-bell-o
phone = "\uf10b" # fa-mobile phone = "\uf10b" # fa-mobile
phone_disconnected = "\U0001f4f5" # https://unicode-table.com/en/1F4F5/ phone_disconnected = "\U0001f4f5" # https://unicode-table.com/en/1F4F5/
ping = "\u21ba" ping = "\u21ba"
pomodoro = "\U0001f345" pomodoro = "\U0001f345"
pomodoro_break = "\uf0f4" # fa-coffee pomodoro_break = "\uf0f4" # fa-coffee
pomodoro_paused = "\uf04c" # fa-pause pomodoro_paused = "\uf04c" # fa-pause
pomodoro_started = "\uf04b" # fa-play pomodoro_started = "\uf04b" # fa-play
pomodoro_stopped = "\uf04d" # fa-stop pomodoro_stopped = "\uf04d" # fa-stop
resolution = "\uf096" # fa-square-o resolution = "\uf096" # fa-square-o
tasks = "\uf0ae" # fa-tasks tasks = "\uf0ae" # fa-tasks
thermometer = "\uf2c8" # fa-thermometer-3 thermometer = "\uf2c8" # fa-thermometer-3
time = "\uf017" # fa-clock-o time = "\uf017" # fa-clock-o
toggle_off = "\uf204" # fa-toggle-off toggle_off = "\uf204" # fa-toggle-off
toggle_on = "\uf205" # fa-toggle-on toggle_on = "\uf205" # fa-toggle-on
unknown = "\uf128" # fa-question unknown = "\uf128" # fa-question
update = "\uf062" # fa-arrow-up update = "\uf062" # fa-arrow-up
uptime = "\uf017" # fa-clock-o uptime = "\uf017" # fa-clock-o
volume_empty = "\uf026" # fa-volume-off volume_empty = "\uf026" # fa-volume-off
volume_full = "\uf028" # fa-volume-up volume_full = "\uf028" # fa-volume-up
volume_half = "\uf027" # fa-volume-down volume_half = "\uf027" # fa-volume-down
volume_muted = "\uf026 \uf00d" volume_muted = "\uf026 \uf00d"
microphone_empty = "\uf130" # fa-microphone microphone_empty = "\uf130" # fa-microphone
microphone_full = "\uf130" # fa-microphone microphone_full = "\uf130" # fa-microphone
microphone_half = "\uf130" # fa-microphone microphone_half = "\uf130" # fa-microphone
microphone_muted = "\uf131" # fa-microphone-slash microphone_muted = "\uf131" # fa-microphone-slash
weather_clouds = "\uf0c2" # fa-cloud weather_clouds = "\uf0c2" # fa-cloud
weather_default = "\uf0c2" # fa-cloud weather_default = "\uf0c2" # fa-cloud
weather_rain = "\uf043" # fa-tint weather_rain = "\uf043" # fa-tint
weather_snow = "\uf2dc" # fa-snowflake-o weather_snow = "\uf2dc" # fa-snowflake-o
weather_sun = "\uf185" # fa-sun-o weather_sun = "\uf185" # fa-sun-o
weather_thunder = "\uf0e7" # fa-bolt weather_thunder = "\uf0e7" # fa-bolt
xrandr = "\uf26c" # fa-television xrandr = "\uf26c" # fa-television

View File

@@ -0,0 +1 @@

View File

@@ -1,2 +0,0 @@
#!/usr/bin/env bash
rofi -show combi -combi-modi run -display-combi "run"

View File

@@ -1,84 +0,0 @@
#!/bin/bash
### From http://www.archlinux.org/index.php/i3
_logfile="$XDG_RUNTIME_DIR/i3exit.log"
touch "$_logfile"
log()
{
echo "$*"
echo "[$(date +%FT%T)] $*" >> "$_logfile"
}
lock()
{
set -x
playerctl -p spotify pause
i3lock --nofork --show-failed-attempts --ignore-empty-password \
--color "000000"
}
screen_off() {
xset dpms force off
}
reset_screen() {
systemctl --user restart dpms.service
}
lock_and_screen_off() {
lock &
_pid=$!
dunst_paused=$(dunstctl is-paused)
[[ "${dunst_paused}" != "true" ]] && dunstctl set-paused true
screen_off
wait $_pid
[[ "${dunst_paused}" != "true" ]] && dunstctl set-paused false
reset_screen
}
signal="$1"
log "[I] Received signal \"$signal\"."
case "$signal" in
lock)
log "[I] Locking session."
lock_and_screen_off
;;
logout)
log "[I] Exiting i3."
i3-msg exit
;;
suspend)
log "[I] Suspending."
lock &
sleep 0.1
systemctl suspend
;;
hibernate)
log "[I] Hibernating."
sudo systemctl hibernate
;;
reboot)
log "[I] Rebooting."
systemctl reboot
;;
shutdown)
log "[I] Shutting down."
systemctl poweroff
;;
screen-off)
log "[I] Turning screen off."
screen_off
;;
*)
echo "Usage: $0 {lock|logout|suspend|hibernate|reboot|shutdown}"
log "[E] Signal \"$signal\" unknown. Aborting."
exit 2
esac
log "[I] Done."
exit 0

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env bash
_status_file="${XDG_RUNTIME_DIR}/presentation-mode-on"
is_on() {
[[ -e "${_status_file}" ]]
}
switch_on() {
touch "${_status_file}"
dunstctl set-paused true &
systemctl --user --no-block stop redshift.service
systemctl --user --no-block stop spotify.service
}
switch_off() {
rm -f "${_status_file}"
dunstctl set-paused false &
systemctl --user --no-block start redshift.service
systemctl --user --no-block start spotify.service
}
case "$1" in
status)
if is_on ; then
printf "on\n"
else
printf "off\n"
fi
;;
toggle)
if is_on ; then
switch_off
else
switch_on
fi
;;
off)
switch_off
;;
on)
switch_on
;;
esac

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env bash
options=(
"lock"
"logout"
"suspend"
"hibernate"
"reboot"
"shutdown"
"screen-off")
i=1
output=$(
for option in "${options[@]}"; do
echo "($i) $option"
(( i++ ))
done | rofi -dmenu -p "action" -no-custom)
[[ "$output" ]] && "$(dirname "$0")"/i3exit "${output#(*) }"

View File

@@ -8,6 +8,12 @@
set -o errexit set -o errexit
set -o nounset set -o nounset
# Make sure to standardize locale, regardless of the machine config
#
# Having a different locale broke "yes | pacman -S" to force-install
# iptables, for example
export LC_ALL="en_US.UTF-8"
DOTDIR="/var/lib/dotfiles" DOTDIR="/var/lib/dotfiles"
os_release_file=/etc/os-release os_release_file=/etc/os-release

View File

@@ -25,7 +25,7 @@ sed -e 's/\s*\([^#]*\).*/\1/' << EOF | sfdisk ${DEVICE}
device: ${DEVICE} device: ${DEVICE}
${DEVICE}1 : name=uefi , size=512M , type=uefi ${DEVICE}1 : name=uefi , size=512M , type=uefi
${DEVICE}2 : name=boot , size=512M , type=linux ${DEVICE}2 : name=boot , size=1G , type=linux
${DEVICE}3 : name=cryptpart , type=linux ${DEVICE}3 : name=cryptpart , type=linux
EOF EOF
@@ -89,7 +89,7 @@ cat <<EOF > /etc/hosts
127.0.1.1 ares 127.0.1.1 ares
EOF EOF
sed -i 's/^HOOKS=.*$/HOOKS=(base udev autodetect keyboard keymap consolefont modconf block encrypt lvm2 filesystems resume fsck)/' /etc/mkinitcpio.conf sed -i 's/^HOOKS=.*$/HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block encrypt lvm2 filesystems resume fsck)/' /etc/mkinitcpio.conf
mkinitcpio -P mkinitcpio -P

View File

@@ -3,7 +3,7 @@
set -o nounset set -o nounset
set -o errexit set -o errexit
host="${1}" ; shift host="${1}" ; shift
pacman -Sy --noconfirm git # yes its a partial upgrade, but thats just the live cd pacman -Sy --noconfirm git # yes its a partial upgrade, but thats just the live cd

137
install_scripts/dionysus.sh Executable file
View File

@@ -0,0 +1,137 @@
#!/usr/bin/env bash
set -o xtrace
set -o nounset
set -o errexit
DEVICE="/dev/nvme0n1"
if [[ ! -b "${DEVICE}" ]] ; then
printf '%s does not look like a device\n' "${DEVICE}"
exit 1
fi
if [[ ! -d /sys/firmware/efi/efivars ]] ; then
printf 'efivars does not exist, looks like the system is not booted in EFI mode\n'
exit 1
fi
loadkeys de-latin1
timedatectl set-ntp true
sed -e 's/\s*\([^#]*\).*/\1/' << EOF | sfdisk ${DEVICE}
label: gpt
device: ${DEVICE}
${DEVICE}p1 : name=uefi , size=512M , type=uefi
${DEVICE}p2 : name=boot , size=1G , type=linux
${DEVICE}p3 : name=cryptpart , type=linux
EOF
# might take a bit for the new partion table to be updated in-kernel
sleep 1
while : ; do
cryptsetup --batch-mode luksFormat --iter-time 1000 ${DEVICE}p3
cryptsetup --batch-mode open --tries 1 ${DEVICE}p3 cryptpart && break
done
pvcreate /dev/mapper/cryptpart
vgcreate vgbase /dev/mapper/cryptpart
lvcreate -L 16G vgbase -n swap
lvcreate -l 100%FREE vgbase -n root
yes | mkfs.fat -F32 ${DEVICE}p1
yes | mkfs.ext4 ${DEVICE}p2
yes | mkfs.ext4 /dev/vgbase/swap
yes | mkfs.ext4 /dev/vgbase/root
mount /dev/vgbase/root /mnt
mkdir /mnt/efi
mount ${DEVICE}p1 /mnt/efi
mkdir /mnt/boot
mount ${DEVICE}p2 /mnt/boot
mkswap /dev/vgbase/swap
swapon /dev/vgbase/swap
pacstrap /mnt base linux-zen linux-firmware networkmanager intel-ucode lvm2 grub efibootmgr
genfstab -U /mnt >> /mnt/etc/fstab
cat << CHROOTSCRIPT > /mnt/chroot-script.sh
set -o xtrace
set -o errexit
set -o nounset
ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime
hwclock --systohc
sed -i 's/^#de_DE.UTF-8 UTF-8/de_DE.UTF-8 UTF-8/' /etc/locale.gen
sed -i 's/^#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
locale-gen
printf 'LANG=en_US.UTF-8\n' > /etc/locale.conf
printf 'KEYMAP=de-latin1\nFONT=lat2-16\n' > /etc/vconsole.conf
printf 'dionysus\n' > /etc/hostname
cat <<EOF > /etc/hosts
127.0.0.1 localhost
::1 localhost
127.0.1.1 dionysus
EOF
sed -i 's/^HOOKS=.*$/HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block encrypt lvm2 filesystems resume fsck)/' /etc/mkinitcpio.conf
mkinitcpio -P
grub-install --target=x86_64-efi --efi-directory=/efi --bootloader-id=GRUB
sed -i "s/^GRUB_CMDLINE_LINUX=.*$/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID=\$(blkid -s UUID -o value ${DEVICE}p3):cryptpart root=UUID=\$(blkid -s UUID -o value /dev/vgbase/root)\"/" /etc/default/grub
sed -i "s/^GRUB_CMDLINE_LINUX_DEFAULT=.*$/GRUB_CMDLINE_LINUX_DEFAULT=\"resume=UUID=\$(blkid -s UUID -o value /dev/vgbase/swap)\"/" /etc/default/grub
sed -i 's/^GRUB_DISABLE_RECOVERY=.*$/GRUB_DISABLE_RECOVERY=/' /etc/default/grub
grub-mkconfig -o /boot/grub/grub.cfg
systemctl enable NetworkManager
passwd
# enable root autologin on first boot
mkdir /etc/systemd/system/getty@tty1.service.d/
cat << EOF > /etc/systemd/system/getty@tty1.service.d/autologin.conf
[Service]
ExecStart=
ExecStart=-/sbin/agetty -o '-p -f -- \\u' --noclear --autologin root %I $TERM
EOF
# ExecStartPost=/bin/rm /etc/systemd/system/getty@tty1.service.d/autologin.conf
# ExecStartPost=/bin/rmdir /etc/systemd/system/getty@tty1.service.d/
# Run
cat << 'EOF' > /root/.bash_profile
if [[ "\$(tty)" == "/dev/tty1" ]] ; then
while ! ping -w 3 -c 3 8.8.8.8 ; do
nmtui
sleep 5
done
rm -rf /etc/systemd/system/getty@tty1.service.d/
if /var/lib/dotfiles/install.sh ; then
rm -f /root/.bash_profile
reboot
fi
fi
EOF
CHROOTSCRIPT
chmod +x /mnt/chroot-script.sh
arch-chroot /mnt /chroot-script.sh
rm -f /mnt/chroot-script.sh

130
install_scripts/hera.sh Executable file
View File

@@ -0,0 +1,130 @@
#!/usr/bin/env bash
set -o xtrace
set -o nounset
set -o errexit
DEVICE="/dev/nvme0n1"
if [[ ! -b "${DEVICE}" ]] ; then
printf '%s does not look like a device\n' "${DEVICE}"
exit 1
fi
if [[ ! -d /sys/firmware/efi/efivars ]] ; then
printf 'efivars does not exist, looks like the system is not booted in EFI mode\n'
exit 1
fi
loadkeys de-latin1
timedatectl set-ntp true
sed -e 's/\s*\([^#]*\).*/\1/' << EOF | sfdisk ${DEVICE}
label: gpt
device: ${DEVICE}
${DEVICE}p1 : name=uefi, size=512M , type=uefi
${DEVICE}p2 : name=boot, size=1G , type=linux
${DEVICE}p3 : name=swap, size=16G , type=linux
${DEVICE}p4 : name=root, size=60G , type=linux
${DEVICE}p5 : name=home, type=linux
EOF
# might take a bit for the new partion table to be updated in-kernel
sleep 1
yes | mkfs.fat -F32 /dev/disk/by-partlabel/uefi
yes | mkfs.ext4 /dev/disk/by-partlabel/boot
yes | mkfs.ext4 /dev/disk/by-partlabel/root
yes | mkfs.ext4 /dev/disk/by-partlabel/home
mkswap /dev/disk/by-partlabel/swap
swapon /dev/disk/by-partlabel/swap
mount /dev/disk/by-partlabel/root /mnt
mkdir /mnt/efi
mount /dev/disk/by-partlabel/uefi /mnt/efi
mkdir /mnt/boot
mount /dev/disk/by-partlabel/boot /mnt/boot
mkdir /mnt/home
mount /dev/disk/by-partlabel/home /mnt/home
pacstrap /mnt base linux-zen linux-firmware networkmanager intel-ucode grub efibootmgr
genfstab -U /mnt >> /mnt/etc/fstab
cat << CHROOTSCRIPT > /mnt/chroot-script.sh
set -o xtrace
set -o errexit
set -o nounset
ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime
hwclock --systohc
sed -i 's/^#de_DE.UTF-8 UTF-8/de_DE.UTF-8 UTF-8/' /etc/locale.gen
sed -i 's/^#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
locale-gen
printf 'LANG=de_DE.UTF-8\n' > /etc/locale.conf
printf 'KEYMAP=de-latin1\nFONT=lat2-16\n' > /etc/vconsole.conf
printf 'hera\n' > /etc/hostname
cat <<EOF > /etc/hosts
127.0.0.1 localhost
::1 localhost
127.0.1.1 hera
EOF
sed -i 's/^HOOKS=.*$/HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block filesystems resume fsck)/' /etc/mkinitcpio.conf
mkinitcpio -P
grub-install --target=x86_64-efi --efi-directory=/efi --bootloader-id=GRUB
sed -i 's/^GRUB_DISABLE_RECOVERY=.*$/GRUB_DISABLE_RECOVERY=/' /etc/default/grub
sed -i "s/^GRUB_CMDLINE_LINUX_DEFAULT=.*$/GRUB_CMDLINE_LINUX_DEFAULT=\"resume=UUID=\$(blkid -s UUID -o value /dev/disk/by-partlabel/swap)\"/" /etc/default/grub
grub-mkconfig -o /boot/grub/grub.cfg
systemctl enable NetworkManager
passwd
# enable root autologin on first boot
mkdir /etc/systemd/system/getty@tty1.service.d/
cat << EOF > /etc/systemd/system/getty@tty1.service.d/autologin.conf
[Service]
ExecStart=
ExecStart=-/sbin/agetty -o '-p -f -- \\u' --noclear --autologin root %I $TERM
EOF
# ExecStartPost=/bin/rm /etc/systemd/system/getty@tty1.service.d/autologin.conf
# ExecStartPost=/bin/rmdir /etc/systemd/system/getty@tty1.service.d/
# Run
cat << 'EOF' > /root/.bash_profile
if [[ "\$(tty)" == "/dev/tty1" ]] ; then
while ! ping -w 3 -c 3 8.8.8.8 ; do
nmtui
sleep 5
done
rm -rf /etc/systemd/system/getty@tty1.service.d/
if /var/lib/dotfiles/install.sh ; then
rm -f /root/.bash_profile
reboot
fi
fi
EOF
CHROOTSCRIPT
chmod +x /mnt/chroot-script.sh
arch-chroot /mnt /chroot-script.sh
rm -f /mnt/chroot-script.sh

View File

@@ -25,7 +25,7 @@ sed -e 's/\s*\([^#]*\).*/\1/' << EOF | sfdisk ${DEVICE}
device: ${DEVICE} device: ${DEVICE}
${DEVICE}p1 : name=uefi , size=512M , type=uefi ${DEVICE}p1 : name=uefi , size=512M , type=uefi
${DEVICE}p2 : name=boot , size=512M , type=linux ${DEVICE}p2 : name=boot , size=1G , type=linux
${DEVICE}p3 : name=cryptpart , type=linux ${DEVICE}p3 : name=cryptpart , type=linux
EOF EOF
@@ -33,8 +33,8 @@ EOF
sleep 1 sleep 1
while : ; do while : ; do
cryptsetup --batch-mode luksFormat --iter-time 1000 ${DEVICE}3 cryptsetup --batch-mode luksFormat --iter-time 1000 ${DEVICE}p3
cryptsetup --batch-mode open --tries 1 ${DEVICE}3 cryptpart && break cryptsetup --batch-mode open --tries 1 ${DEVICE}p3 cryptpart && break
done done
pvcreate /dev/mapper/cryptpart pvcreate /dev/mapper/cryptpart
@@ -89,7 +89,7 @@ cat <<EOF > /etc/hosts
127.0.1.1 neptune 127.0.1.1 neptune
EOF EOF
sed -i 's/^HOOKS=.*$/HOOKS=(base udev autodetect keyboard keymap consolefont modconf block encrypt lvm2 filesystems resume fsck)/' /etc/mkinitcpio.conf sed -i 's/^HOOKS=.*$/HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block encrypt lvm2 filesystems resume fsck)/' /etc/mkinitcpio.conf
mkinitcpio -P mkinitcpio -P

13
maintenance.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -o nounset
set -o errexit
sudo bash -c "pacman -Sy --needed --noconfirm archlinux-keyring && pacman -Su"
./update-aur-pkgs.sh
ANSIBLE_DISPLAY_OK_HOSTS=false \
ANSIBLE_DISPLAY_SKIPPED_HOSTS=false \
ANSIBLE_EXTRA_ARGS='' \
make

1
mgr/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

503
mgr/Cargo.lock generated Normal file
View File

@@ -0,0 +1,503 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cc"
version = "1.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "deranged"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc"
dependencies = [
"powerfmt",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "http"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "log"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "mgr"
version = "0.1.0"
dependencies = [
"thiserror",
"time",
"tracing",
"tracing-subscriber",
"ureq",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom",
"libc",
"untrusted",
"windows-sys",
]
[[package]]
name = "rustls"
version = "0.23.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pemfile"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
]
[[package]]
name = "time"
version = "0.3.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031"
dependencies = [
"deranged",
"num-conv",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
[[package]]
name = "time-macros"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tracing"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
dependencies = [
"once_cell",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
dependencies = [
"sharded-slab",
"thread_local",
"tracing-core",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00432f493971db5d8e47a65aeb3b02f8226b9b11f1450ff86bb772776ebadd70"
dependencies = [
"base64",
"log",
"percent-encoding",
"rustls",
"rustls-pemfile",
"rustls-pki-types",
"ureq-proto",
"utf-8",
"webpki-roots",
]
[[package]]
name = "ureq-proto"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe120bb823a0061680e66e9075942fcdba06d46551548c2c259766b9558bc9a"
dependencies = [
"base64",
"http",
"httparse",
"log",
]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "webpki-roots"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"

184
mgr/Cargo.toml Normal file
View File

@@ -0,0 +1,184 @@
[package]
name = "mgr"
version = "0.1.0"
edition = "2024"
[dependencies]
thiserror = { version = "2.0.16", default-features = false }
time = { version = "0.3.43", default-features = false, features = ["formatting", "parsing", "std"] }
tracing = { version = "0.1.41", default-features = false }
tracing-subscriber = { version = "0.3.20", default-features = false, features = ["fmt"] }
ureq = { version = "3.1.0", default-features = false, features = ["rustls"] }
[[bin]]
name = "workstation-mgr"
path = "src/bin/main.rs"
[[bin]]
name = "workstation-client"
path = "src/bin/client.rs"
[profile.release]
strip = true
lto = true
codegen-units = 1
panic = "abort"
[lints.clippy]
# enabled groups
correctness = { level = "deny", priority = -1 }
suspicious = { level = "warn", priority = -1 }
style = { level = "warn", priority = -1 }
complexity = { level = "warn", priority = -1 }
perf = { level = "warn", priority = -1 }
cargo = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
# pedantic overrides
too_many_lines = "allow"
must_use_candidate = "allow"
map_unwrap_or = "allow"
missing_errors_doc = "allow"
if_not_else = "allow"
similar_names = "allow"
redundant_else = "allow"
# nursery overrides
missing_const_for_fn = "allow"
option_if_let_else = "allow"
redundant_pub_crate = "allow"
# complexity overrides
too_many_arguments = "allow"
# style overrides
new_without_default = "allow"
redundant_closure = "allow"
# cargo overrides
multiple_crate_versions = "allow"
cargo_common_metadata = "allow"
# selected restrictions
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
arithmetic_side_effects = "warn"
as_conversions = "warn"
assertions_on_result_states = "warn"
cfg_not_test = "warn"
clone_on_ref_ptr = "warn"
create_dir = "warn"
dbg_macro = "warn"
decimal_literal_representation = "warn"
default_numeric_fallback = "warn"
deref_by_slicing = "warn"
disallowed_script_idents = "warn"
empty_drop = "warn"
empty_enum_variants_with_brackets = "warn"
empty_structs_with_brackets = "warn"
exit = "warn"
filetype_is_file = "warn"
float_arithmetic = "warn"
float_cmp_const = "warn"
fn_to_numeric_cast_any = "warn"
format_push_string = "warn"
get_unwrap = "warn"
indexing_slicing = "warn"
infinite_loop = "warn"
inline_asm_x86_att_syntax = "warn"
inline_asm_x86_intel_syntax = "warn"
integer_division = "warn"
iter_over_hash_type = "warn"
large_include_file = "warn"
let_underscore_must_use = "warn"
let_underscore_untyped = "warn"
little_endian_bytes = "warn"
lossy_float_literal = "warn"
map_err_ignore = "warn"
mem_forget = "warn"
missing_assert_message = "warn"
missing_asserts_for_indexing = "warn"
mixed_read_write_in_expression = "warn"
modulo_arithmetic = "warn"
multiple_inherent_impl = "warn"
multiple_unsafe_ops_per_block = "warn"
mutex_atomic = "warn"
panic = "warn"
partial_pub_fields = "warn"
pattern_type_mismatch = "warn"
print_stderr = "warn"
print_stdout = "warn"
pub_without_shorthand = "warn"
rc_buffer = "warn"
rc_mutex = "warn"
redundant_type_annotations = "warn"
renamed_function_params = "warn"
rest_pat_in_fully_bound_structs = "warn"
same_name_method = "warn"
self_named_module_files = "warn"
semicolon_inside_block = "warn"
str_to_string = "warn"
string_add = "warn"
string_lit_chars_any = "warn"
string_slice = "warn"
implicit_clone = "warn"
suspicious_xor_used_as_pow = "warn"
tests_outside_test_module = "warn"
todo = "warn"
try_err = "warn"
undocumented_unsafe_blocks = "warn"
unimplemented = "warn"
unnecessary_safety_comment = "warn"
unnecessary_safety_doc = "warn"
unnecessary_self_imports = "warn"
unneeded_field_pattern = "warn"
unseparated_literal_suffix = "warn"
unused_result_ok = "warn"
unwrap_used = "warn"
use_debug = "warn"
verbose_file_reads = "warn"
# restrictions explicit allows
absolute_paths = "allow"
alloc_instead_of_core = "allow"
as_underscore = "allow"
big_endian_bytes = "allow"
default_union_representation = "allow"
error_impl_error = "allow"
exhaustive_enums = "allow"
exhaustive_structs = "allow"
expect_used = "allow"
field_scoped_visibility_modifiers = "allow"
host_endian_bytes = "allow"
if_then_some_else_none = "allow"
impl_trait_in_params = "allow"
implicit_return = "allow"
integer_division_remainder_used = "allow"
min_ident_chars = "allow"
missing_docs_in_private_items = "allow"
missing_inline_in_public_items = "allow"
missing_trait_methods = "allow"
mod_module_files = "allow"
needless_raw_strings = "allow"
non_ascii_literal = "allow"
non_zero_suggestions = "allow"
panic_in_result_fn = "allow"
pathbuf_init_then_push = "allow"
pub_use = "allow"
pub_with_shorthand = "allow"
question_mark_used = "allow"
ref_patterns = "allow"
semicolon_outside_block = "allow"
separated_literal_suffix = "allow"
shadow_reuse = "allow"
shadow_same = "allow"
shadow_unrelated = "allow"
single_call_fn = "allow"
single_char_lifetime_names = "allow"
std_instead_of_alloc = "allow"
std_instead_of_core = "allow"
unreachable = "allow"
unused_trait_names = "allow"
unwrap_in_result = "allow"
wildcard_enum_match_arm = "allow"

87
mgr/src/bin/client.rs Normal file
View File

@@ -0,0 +1,87 @@
#![expect(
clippy::print_stderr,
clippy::print_stdout,
reason = "output is fine for cli"
)]
use std::{
env,
io::{self, Read as _},
net,
os::unix::net::UnixStream,
process, str,
};
use thiserror::Error;
use mgr::{
Action,
cli::{self, CliCommand as _, ParseError},
wire::{client, socket},
};
#[derive(Debug, Error)]
enum Error {
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
Socket(#[from] socket::Error),
#[error(transparent)]
Send(#[from] client::SendError),
#[error(transparent)]
CliParse(#[from] cli::ParseError),
#[error("response is not valid utf8: {0}")]
ResponseNonUtf8(#[from] str::Utf8Error),
}
enum MainResult {
Success,
Failure(Error),
}
impl process::Termination for MainResult {
fn report(self) -> process::ExitCode {
match self {
Self::Success => process::ExitCode::SUCCESS,
Self::Failure(e) => {
eprintln!("Error: {e}");
process::ExitCode::FAILURE
}
}
}
}
fn main() -> MainResult {
fn inner() -> Result<(), Error> {
let mut args = env::args().skip(1);
let action =
Action::parse_str(args.next().ok_or(ParseError::MissingAction)?.as_str(), args)?;
let socket = socket::get_socket_path()?;
let mut stream = UnixStream::connect(socket)?;
action.send(&mut stream)?;
stream.shutdown(net::Shutdown::Write)?;
let response = {
let mut buf = Vec::new();
stream.read_to_end(&mut buf)?;
let response = str::from_utf8(&buf)?.to_owned();
drop(stream);
response
};
if !response.is_empty() {
println!("{response}");
}
Ok(())
}
match inner() {
Ok(()) => MainResult::Success,
Err(e) => MainResult::Failure(e),
}
}

105
mgr/src/bin/main.rs Executable file
View File

@@ -0,0 +1,105 @@
#![expect(
clippy::print_stderr,
clippy::print_stdout,
reason = "output is fine for cli"
)]
use std::{env, process};
use thiserror::Error;
use tracing::Level;
use mgr::{
self, Action, Exec as _,
cli::{CliCommand as _, ParseError},
};
#[derive(Debug, Error)]
enum Error {
#[error(transparent)]
Power(#[from] mgr::power::Error),
#[error(transparent)]
Dmenu(#[from] mgr::dmenu::Error),
#[error(transparent)]
Server(#[from] mgr::wire::server::Error),
#[error(transparent)]
Presentation(#[from] mgr::present::Error),
#[error(transparent)]
Exec(#[from] mgr::ExecError),
#[error(transparent)]
ParseParse(#[from] ParseError),
#[error(transparent)]
Tracing(#[from] tracing::dispatcher::SetGlobalDefaultError),
}
enum MainResult {
Success,
Failure(Error),
}
impl process::Termination for MainResult {
fn report(self) -> process::ExitCode {
match self {
Self::Success => process::ExitCode::SUCCESS,
Self::Failure(e) => {
eprintln!("Error: {e}");
process::ExitCode::FAILURE
}
}
}
}
impl From<Error> for MainResult {
fn from(value: Error) -> Self {
Self::Failure(value)
}
}
fn init_tracing() -> Result<(), Error> {
tracing::subscriber::set_global_default(
tracing_subscriber::fmt()
.with_max_level(Level::DEBUG)
.event_format(
tracing_subscriber::fmt::format()
.with_ansi(false)
.with_target(false)
.compact(),
)
.finish(),
)?;
Ok(())
}
fn main() -> MainResult {
fn inner() -> Result<(), Error> {
init_tracing()?;
let mut args = env::args().skip(1);
match args.next().ok_or(ParseError::MissingAction)?.as_str() {
"serve" => {
mgr::wire::server::run()?;
Ok(())
}
"run" => {
let action = Action::parse_str(
args.next().ok_or(ParseError::MissingAction)?.as_str(),
args,
)?;
if let Some(output) = action.execute()? {
println!("{output}");
}
Ok(())
}
input => Err(ParseError::UnknownAction {
action: input.to_owned(),
}
.into()),
}
}
match inner() {
Ok(()) => MainResult::Success,
Err(e) => MainResult::Failure(e),
}
}

77
mgr/src/brightness.rs Normal file
View File

@@ -0,0 +1,77 @@
use thiserror::Error;
use super::{
Exec,
cli::{self, CliCommand},
cmd,
wire::{WireCommand, server},
};
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Cli(#[from] cli::ParseError),
#[error(transparent)]
Cmd(#[from] cmd::Error),
}
#[derive(Debug, Clone, Copy)]
pub enum Action {
Inc,
Dec,
}
impl WireCommand for Action {
fn parse_wire(mut input: impl Iterator<Item = u8>) -> Result<Self, server::ParseError> {
match input.next().ok_or(server::ParseError::Eof)? {
0x01 => Ok(Self::Inc),
0x02 => Ok(Self::Dec),
byte => Err(server::ParseError::Unknown(byte)),
}
}
fn to_wire(&self) -> Vec<u8> {
match *self {
Self::Inc => vec![0x01],
Self::Dec => vec![0x02],
}
}
}
impl CliCommand for Action {
type ExecErr = Error;
fn parse_str(input: &str, rest: impl Iterator<Item = String>) -> Result<Self, cli::ParseError>
where
Self: Sized,
{
let result = match input {
"inc" => Self::Inc,
"dec" => Self::Dec,
s => {
return Err(cli::ParseError::UnknownAction {
action: s.to_owned(),
});
}
};
let rest = rest.collect::<Vec<String>>();
if rest.is_empty() {
Ok(result)
} else {
Err(cli::ParseError::UnexpectedInput { rest })
}
}
}
impl Exec for Action {
type ExecErr = Error;
fn execute(&self) -> Result<Option<String>, Self::ExecErr> {
match *self {
Self::Inc => cmd::command("brightnessctl", &["set", "8%+"])?,
Self::Dec => cmd::command("brightnessctl", &["set", "8%-"])?,
}
Ok(None)
}
}

23
mgr/src/cli.rs Normal file
View File

@@ -0,0 +1,23 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ParseError {
#[error("no action given")]
MissingAction,
#[error("unknown action: {action}")]
UnknownAction { action: String },
#[error("unexpected input: {rest:?}")]
UnexpectedInput { rest: Vec<String> },
#[error("missing argument")]
MissingArgument,
#[error("error parsing argument: {message}")]
ArgumentParse { message: String },
}
pub trait CliCommand {
type ExecErr: From<ParseError>;
fn parse_str(input: &str, rest: impl Iterator<Item = String>) -> Result<Self, ParseError>
where
Self: Sized;
}

183
mgr/src/cmd.rs Normal file
View File

@@ -0,0 +1,183 @@
use std::{io, panic, process, str, thread};
use thiserror::Error;
use tracing::{Level, event};
#[derive(Error, Debug)]
pub enum Error {
#[error("command \"{command}\" failed: {error}")]
CommandInvocation {
command: &'static str,
error: io::Error,
},
#[error("command \"{command}\" was terminated by signal")]
CommandTerminatedBySignal { command: &'static str },
#[error(
"command \"{command}\" failed [{code}]: {stderr}",
code = match *.code {
Some(code) => &.code.to_string(),
_ => "unknown exit code",
},
stderr = if .stderr.is_empty() {
"[stderr empty]"
} else {
.stderr
})]
CommandFailed {
command: &'static str,
code: Option<i32>,
stderr: String,
},
#[error("{command} produced non-utf8 output: {error}")]
CommandOutputNonUtf8 {
command: &'static str,
error: str::Utf8Error,
},
#[error("failed writing to stdin of command \"{command}\": {error}")]
StdinWriteFailed {
command: &'static str,
error: io::Error,
},
}
pub(crate) fn command(command: &'static str, args: &[&str]) -> Result<(), Error> {
let _: FinishedProcess = run_command_checked(command, args)?;
Ok(())
}
pub(crate) fn run_command(command: &'static str, args: &[&str]) -> Result<FinishedProcess, Error> {
event!(Level::DEBUG, "running {command} {args:?}");
let proc = process::Command::new(command)
.args(args)
.output()
.map_err(|error| Error::CommandInvocation { command, error })?;
Ok(FinishedProcess {
exit_code: proc
.status
.code()
.ok_or(Error::CommandTerminatedBySignal { command })?,
stdout: str::from_utf8(&proc.stdout)
.map_err(|error| Error::CommandOutputNonUtf8 { command, error })?
.to_owned(),
stderr: str::from_utf8(&proc.stderr)
.map_err(|error| Error::CommandOutputNonUtf8 { command, error })?
.to_owned(),
})
}
pub(crate) fn run_command_checked(
command: &'static str,
args: &[&str],
) -> Result<FinishedProcess, Error> {
let output = run_command(command, args)?;
if output.exit_code != 0_i32 {
event!(Level::DEBUG, "{command} {args:?} failed");
return Err(Error::CommandFailed {
command,
code: Some(output.exit_code),
stderr: output.stderr,
});
}
Ok(output)
}
pub(crate) fn command_output(command: &'static str, args: &[&str]) -> Result<String, Error> {
let output = run_command_checked(command, args)?;
Ok(output.stdout)
}
#[derive(Debug)]
pub(crate) struct FinishedProcess {
pub exit_code: i32,
pub stdout: String,
pub stderr: String,
}
pub(crate) fn command_output_with_stdin_write(
command: &'static str,
args: &[&str],
input: &[u8],
) -> Result<FinishedProcess, Error> {
use io::Write as _;
let process = process::Command::new(command)
.args(args)
.stdin(process::Stdio::piped())
.stdout(process::Stdio::piped())
.stderr(process::Stdio::null())
.spawn()
.map_err(|error| Error::CommandInvocation { command, error })?;
let mut stdin = process
.stdin
.as_ref()
.expect("stdin handle must be present");
stdin
.write_all(input)
.map_err(|error| Error::StdinWriteFailed { command, error })?;
let output = process
.wait_with_output()
.map_err(|error| Error::CommandInvocation { command, error })?;
let exit_code = output
.status
.code()
.ok_or(Error::CommandTerminatedBySignal { command })?;
let stdout = str::from_utf8(&output.stdout)
.map_err(|error| Error::CommandOutputNonUtf8 { command, error })?
.to_owned();
let stderr = str::from_utf8(&output.stderr)
.map_err(|error| Error::CommandOutputNonUtf8 { command, error })?
.to_owned();
Ok(FinishedProcess {
exit_code,
stdout,
stderr,
})
}
pub(crate) struct RunningProcess {
command: &'static str,
join_handle: thread::JoinHandle<Result<FinishedProcess, Error>>,
}
impl RunningProcess {
pub fn with<F: Fn() -> Result<(), E>, E: From<Error>>(
self,
f: F,
) -> Result<FinishedProcess, E> {
f()?;
event!(
Level::DEBUG,
"waiting for process {} to finish",
self.command
);
let ret = match self.join_handle.join() {
Ok(ret) => ret?,
Err(e) => panic::resume_unwind(e),
};
event!(Level::DEBUG, "process {} finished", self.command);
Ok(ret)
}
}
pub(crate) fn start_command(
command: &'static str,
args: &'static [&'static str],
) -> RunningProcess {
event!(Level::DEBUG, "starting {command} {args:?}");
let join_handle = thread::spawn(move || run_command_checked(command, args));
RunningProcess {
command,
join_handle,
}
}

29
mgr/src/dirs.rs Normal file
View File

@@ -0,0 +1,29 @@
use std::path::PathBuf;
use thiserror::Error;
use super::env;
const ENV_XDG_RUNTIME_DIR: &str = "XDG_RUNTIME_DIR";
const ENV_XDG_CACHE_DIR: &str = "XDG_CACHE_HOME";
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Env(#[from] env::Error),
}
pub(crate) fn xdg_runtime_dir() -> Result<Option<PathBuf>, Error> {
Ok(env::get(ENV_XDG_RUNTIME_DIR)?.map(PathBuf::from))
}
pub(crate) fn require_xdg_runtime_dir() -> Result<PathBuf, Error> {
Ok(PathBuf::from(env::require(ENV_XDG_RUNTIME_DIR)?))
}
pub(crate) fn xdg_cache_dir() -> Result<PathBuf, Error> {
Ok(match env::get(ENV_XDG_CACHE_DIR)? {
Some(value) => PathBuf::from(value),
None => PathBuf::from(env::require("HOME")?).join(".cache"),
})
}

66
mgr/src/dmenu.rs Normal file
View File

@@ -0,0 +1,66 @@
use std::{fmt::Write as _, num};
use thiserror::Error;
use tracing::{Level, event};
use super::cmd;
#[derive(Debug, Error)]
pub enum Error {
#[error("rofi did not return an integer: {error}")]
RofiNonIntOutput { error: num::ParseIntError },
#[error("rofi returned an invalid indexx: {index}")]
RofiInvalidIndex { index: usize },
#[error(transparent)]
Cmd(#[from] cmd::Error),
}
pub(crate) fn get_choice(actions: &[&'static str]) -> Result<Option<&'static str>, Error> {
const ROFI: &str = "rofi";
event!(Level::DEBUG, "starting rofi");
let process = cmd::command_output_with_stdin_write(
ROFI,
&[
"-dmenu",
"-p",
"action",
"-l",
&actions.len().to_string(),
"-no-custom",
"-sync",
"-format",
"i",
],
actions
.iter()
.enumerate()
.fold(String::new(), |mut output, (i, action)| {
writeln!(
output,
"({i}) {action}",
i = i.checked_add(1).expect("too many action")
)
.expect("writing to string cannot fail");
output
})
.as_bytes(),
)?;
if process.exit_code == 1 {
Ok(None)
} else {
let choice = process
.stdout
.trim()
.parse::<usize>()
.map_err(|error| Error::RofiNonIntOutput { error })?;
Ok(Some(
actions
.get(choice)
.ok_or(Error::RofiInvalidIndex { index: choice })?,
))
}
}

40
mgr/src/dunst.rs Normal file
View File

@@ -0,0 +1,40 @@
use thiserror::Error;
use super::cmd;
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Cmd(#[from] cmd::Error),
#[error("dunstctl is-paused returned unknown output: {output}")]
DunstctlIsPausedUnknownOutput { output: String },
}
#[derive(Clone, Copy)]
pub(crate) enum Status {
Paused,
Unpaused,
}
pub(crate) fn set_status(status: Status) -> Result<(), Error> {
Ok(cmd::command(
"dunstctl",
&[
"set-paused",
match status {
Status::Paused => "true",
Status::Unpaused => "false",
},
],
)?)
}
pub(crate) fn is_paused() -> Result<bool, Error> {
let output = cmd::command_output("dunstctl", &["is-paused"])?;
match output.trim() {
"true" => Ok(true),
"false" => Ok(false),
_ => Err(Error::DunstctlIsPausedUnknownOutput { output }),
}
}

28
mgr/src/env.rs Normal file
View File

@@ -0,0 +1,28 @@
use std::{env, ffi::OsString};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error(
"env variable \"{name}\" is not valid unicode: \"{value}\"",
value = value.to_string_lossy()
)]
EnvNotUnicode { name: &'static str, value: OsString },
#[error("env variable \"{name}\" not found")]
EnvNotFound { name: &'static str },
}
pub(crate) fn get(var: &'static str) -> Result<Option<String>, Error> {
match env::var(var) {
Ok(value) => Ok(Some(value)),
Err(e) => match e {
env::VarError::NotPresent => Ok(None),
env::VarError::NotUnicode(value) => Err(Error::EnvNotUnicode { name: var, value }),
},
}
}
pub(crate) fn require(var: &'static str) -> Result<String, Error> {
get(var)?.ok_or(Error::EnvNotFound { name: var })
}

205
mgr/src/lib.rs Normal file
View File

@@ -0,0 +1,205 @@
use thiserror::Error;
pub(crate) mod brightness;
pub mod cli;
pub(crate) mod cmd;
pub(crate) mod dirs;
pub mod dmenu;
pub(crate) mod dunst;
pub(crate) mod env;
pub mod power;
pub mod present;
pub(crate) mod pulseaudio;
pub(crate) mod redshift;
pub(crate) mod sleep;
pub(crate) mod spotify;
pub(crate) mod systemd;
pub(crate) mod theme;
pub(crate) mod weather;
pub mod wire;
#[derive(Debug, Error)]
pub enum ExecError {
#[error(transparent)]
Power(#[from] power::Error),
#[error(transparent)]
Presentation(#[from] present::Error),
#[error(transparent)]
Pulseaudio(#[from] pulseaudio::Error),
#[error(transparent)]
Theme(#[from] theme::Error),
#[error(transparent)]
Spotify(#[from] spotify::Error),
#[error(transparent)]
Redshift(#[from] redshift::Error),
#[error(transparent)]
Weather(#[from] weather::Error),
#[error(transparent)]
Brightness(#[from] brightness::Error),
#[error(transparent)]
Sleep(#[from] sleep::Error),
#[error(transparent)]
Parse(#[from] cli::ParseError),
}
#[derive(Debug)]
pub enum Action {
Power(power::Action),
Present(present::Action),
Pulseaudio(pulseaudio::Action),
Theme(theme::Action),
Spotify(spotify::Action),
Redshift(redshift::Action),
Weather(weather::Action),
Brightness(brightness::Action),
Sleep(sleep::Action),
}
impl wire::WireCommand for Action {
fn parse_wire(mut input: impl Iterator<Item = u8>) -> Result<Self, wire::server::ParseError> {
match input.next().ok_or(wire::server::ParseError::Eof)? {
0x01 => Ok(Self::Power(power::Action::parse_wire(input)?)),
0x02 => Ok(Self::Present(present::Action::parse_wire(input)?)),
0x03 => Ok(Self::Pulseaudio(pulseaudio::Action::parse_wire(input)?)),
0x04 => Ok(Self::Theme(theme::Action::parse_wire(input)?)),
0x05 => Ok(Self::Spotify(spotify::Action::parse_wire(input)?)),
0x06 => Ok(Self::Redshift(redshift::Action::parse_wire(input)?)),
0x07 => Ok(Self::Weather(weather::Action::parse_wire(input)?)),
0x08 => Ok(Self::Brightness(brightness::Action::parse_wire(input)?)),
0x09 => Ok(Self::Sleep(sleep::Action::parse_wire(input)?)),
other => Err(wire::server::ParseError::Unknown(other)),
}
}
fn to_wire(&self) -> Vec<u8> {
match *self {
Self::Power(action) => {
let mut v = vec![0x01];
v.extend_from_slice(&action.to_wire());
v
}
Self::Present(action) => {
let mut v = vec![0x02];
v.extend_from_slice(&action.to_wire());
v
}
Self::Pulseaudio(action) => {
let mut v = vec![0x03];
v.extend_from_slice(&action.to_wire());
v
}
Self::Theme(action) => {
let mut v = vec![0x04];
v.extend_from_slice(&action.to_wire());
v
}
Self::Spotify(action) => {
let mut v = vec![0x05];
v.extend_from_slice(&action.to_wire());
v
}
Self::Redshift(action) => {
let mut v = vec![0x06];
v.extend_from_slice(&action.to_wire());
v
}
Self::Weather(action) => {
let mut v = vec![0x07];
v.extend_from_slice(&action.to_wire());
v
}
Self::Brightness(action) => {
let mut v = vec![0x08];
v.extend_from_slice(&action.to_wire());
v
}
Self::Sleep(action) => {
let mut v = vec![0x09];
v.extend_from_slice(&action.to_wire());
v
}
}
}
}
impl cli::CliCommand for Action {
type ExecErr = ExecError;
fn parse_str(
input: &str,
mut rest: impl Iterator<Item = String>,
) -> Result<Self, cli::ParseError>
where
Self: Sized,
{
match input {
"power" => {
let choice = rest.next().ok_or(cli::ParseError::MissingAction)?;
Ok(Self::Power(power::Action::parse_str(&choice, rest)?))
}
"present" => {
let choice = rest.next().ok_or(cli::ParseError::MissingAction)?;
Ok(Self::Present(present::Action::parse_str(&choice, rest)?))
}
"pulseaudio" => {
let choice = rest.next().ok_or(cli::ParseError::MissingAction)?;
Ok(Self::Pulseaudio(pulseaudio::Action::parse_str(
&choice, rest,
)?))
}
"theme" => {
let choice = rest.next().ok_or(cli::ParseError::MissingAction)?;
Ok(Self::Theme(theme::Action::parse_str(&choice, rest)?))
}
"spotify" => {
let choice = rest.next().ok_or(cli::ParseError::MissingAction)?;
Ok(Self::Spotify(spotify::Action::parse_str(&choice, rest)?))
}
"redshift" => {
let choice = rest.next().ok_or(cli::ParseError::MissingAction)?;
Ok(Self::Redshift(redshift::Action::parse_str(&choice, rest)?))
}
"weather" => {
let choice = rest.next().ok_or(cli::ParseError::MissingAction)?;
Ok(Self::Weather(weather::Action::parse_str(&choice, rest)?))
}
"brightness" => {
let choice = rest.next().ok_or(cli::ParseError::MissingAction)?;
Ok(Self::Brightness(brightness::Action::parse_str(
&choice, rest,
)?))
}
"sleep" => {
let choice = rest.next().ok_or(cli::ParseError::MissingAction)?;
Ok(Self::Sleep(sleep::Action::parse_str(&choice, rest)?))
}
s => Err(cli::ParseError::UnknownAction {
action: s.to_owned(),
}),
}
}
}
pub trait Exec {
type ExecErr: Into<ExecError>;
fn execute(&self) -> Result<Option<String>, Self::ExecErr>;
}
impl Exec for Action {
type ExecErr = ExecError;
fn execute(&self) -> Result<Option<String>, Self::ExecErr> {
match *self {
Self::Power(action) => Ok(action.execute()?),
Self::Present(action) => Ok(action.execute()?),
Self::Pulseaudio(action) => Ok(action.execute()?),
Self::Theme(action) => Ok(action.execute()?),
Self::Spotify(action) => Ok(action.execute()?),
Self::Redshift(action) => Ok(action.execute()?),
Self::Weather(action) => Ok(action.execute()?),
Self::Brightness(action) => Ok(action.execute()?),
Self::Sleep(action) => Ok(action.execute()?),
}
}
}

227
mgr/src/power.rs Normal file
View File

@@ -0,0 +1,227 @@
use thiserror::Error;
use tracing::{Level, event};
use super::{
Exec,
cli::{self, CliCommand},
cmd, dmenu, dunst, spotify,
wire::{WireCommand, server},
};
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Dunst(#[from] dunst::Error),
#[error("unknown action: {action}")]
UnknownAction { action: String },
#[error(transparent)]
Cmd(#[from] cmd::Error),
#[error(transparent)]
Dmenu(#[from] dmenu::Error),
#[error(transparent)]
Cli(#[from] cli::ParseError),
#[error(transparent)]
Spotify(#[from] spotify::Error),
}
#[derive(Debug, Clone, Copy)]
pub enum Action {
Menu,
Lock,
Suspend,
Hibernate,
Reboot,
Poweroff,
}
impl Action {
fn as_str(self) -> &'static str {
match self {
Self::Menu => "menu",
Self::Lock => "lock",
Self::Suspend => "suspend",
Self::Hibernate => "hibernate",
Self::Reboot => "reboot",
Self::Poweroff => "poweroff",
}
}
}
impl WireCommand for Action {
fn parse_wire(mut input: impl Iterator<Item = u8>) -> Result<Self, server::ParseError> {
match input.next().ok_or(server::ParseError::Eof)? {
0x01 => Ok(Self::Menu),
0x02 => Ok(Self::Lock),
0x03 => Ok(Self::Suspend),
0x04 => Ok(Self::Hibernate),
0x05 => Ok(Self::Reboot),
0x06 => Ok(Self::Poweroff),
byte => Err(server::ParseError::Unknown(byte)),
}
}
fn to_wire(&self) -> Vec<u8> {
match *self {
Self::Menu => vec![0x01],
Self::Lock => vec![0x02],
Self::Suspend => vec![0x03],
Self::Hibernate => vec![0x04],
Self::Reboot => vec![0x05],
Self::Poweroff => vec![0x06],
}
}
}
impl Exec for Action {
type ExecErr = Error;
fn execute(&self) -> Result<Option<String>, Self::ExecErr> {
match *self {
Self::Menu => menu()?,
Self::Lock => lock_and_screen_off()?,
Self::Suspend => lock_and_suspend()?,
Self::Hibernate => hibernate()?,
Self::Reboot => reboot()?,
Self::Poweroff => poweroff()?,
}
Ok(None)
}
}
impl CliCommand for Action {
type ExecErr = Error;
fn parse_str(input: &str, rest: impl Iterator<Item = String>) -> Result<Self, cli::ParseError>
where
Self: Sized,
{
let result = match input {
"menu" => Self::Menu,
"lock" => Self::Lock,
"suspend" => Self::Suspend,
"hibernate" => Self::Hibernate,
"reboot" => Self::Reboot,
"shutdown" => Self::Poweroff,
s => {
return Err(cli::ParseError::UnknownAction {
action: s.to_owned(),
});
}
};
let rest = rest.collect::<Vec<String>>();
if rest.is_empty() {
Ok(result)
} else {
Err(cli::ParseError::UnexpectedInput { rest })
}
}
}
const MENU_ACTIONS: &[Action] = &[
Action::Lock,
Action::Suspend,
Action::Hibernate,
Action::Reboot,
Action::Poweroff,
];
fn menu() -> Result<(), Error> {
let choice = dmenu::get_choice(
&MENU_ACTIONS
.iter()
.map(|action| action.as_str())
.collect::<Vec<&str>>(),
)?;
if let Some(choice) = choice {
MENU_ACTIONS
.iter()
.find(|action| action.as_str() == choice)
.copied()
.expect("choice must be one of the valid values")
.execute()?;
} else {
event!(Level::DEBUG, "rofi was cancelled");
}
Ok(())
}
fn screen_off() -> Result<(), Error> {
Ok(cmd::command("xset", &["dpms", "force", "off"])?)
}
fn lock() -> Result<cmd::RunningProcess, Error> {
match spotify::pause() {
Ok(_) => (),
Err(spotify::Error::NotFound) => (),
Err(e) => return Err(e.into()),
}
let lock_handle = cmd::start_command(
"i3lock",
&[
"--nofork",
"--show-failed-attempts",
"--ignore-empty-password",
"--color",
"000000",
],
);
Ok(lock_handle)
}
fn reset_screen() -> Result<(), Error> {
Ok(cmd::command(
"systemctl",
&["--user", "restart", "dpms.service"],
)?)
}
fn lock_and_screen_off() -> Result<(), Error> {
let dunst_paused = dunst::is_paused()?;
if dunst_paused {
dunst::set_status(dunst::Status::Paused)?;
}
lock()?.with(|| -> Result<(), Error> {
screen_off()?;
Ok(())
})?;
if dunst_paused {
dunst::set_status(dunst::Status::Unpaused)?;
}
reset_screen()?;
Ok(())
}
fn suspend() -> Result<(), Error> {
Ok(cmd::command("systemctl", &["suspend"])?)
}
fn hibernate() -> Result<(), Error> {
Ok(cmd::command("systemctl", &["hibernate"])?)
}
fn reboot() -> Result<(), Error> {
Ok(cmd::command("systemctl", &["reboot"])?)
}
fn poweroff() -> Result<(), Error> {
Ok(cmd::command("systemctl", &["poweroff"])?)
}
fn lock_and_suspend() -> Result<(), Error> {
lock()?.with(|| -> Result<(), Error> {
screen_off()?;
suspend()?;
Ok(())
})?;
Ok(())
}

154
mgr/src/present.rs Normal file
View File

@@ -0,0 +1,154 @@
use std::{fs, io, path::PathBuf};
use thiserror::Error;
use super::{
Exec,
cli::{self, CliCommand},
cmd, dirs, dunst, redshift, spotify,
wire::{WireCommand, server},
};
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Io(#[from] io::Error),
#[error("unknown action: {action}")]
UnknownAction { action: String },
#[error(transparent)]
Cli(#[from] cli::ParseError),
#[error(transparent)]
Dirs(#[from] dirs::Error),
#[error(transparent)]
Dunst(#[from] dunst::Error),
#[error(transparent)]
Cmd(#[from] cmd::Error),
#[error(transparent)]
Redshift(#[from] redshift::Error),
#[error(transparent)]
Spotify(#[from] spotify::Error),
}
#[derive(Debug, Clone, Copy)]
pub enum Action {
On,
Off,
Toggle,
Status,
}
impl WireCommand for Action {
fn parse_wire(mut input: impl Iterator<Item = u8>) -> Result<Self, server::ParseError> {
match input.next().ok_or(server::ParseError::Eof)? {
0x01 => Ok(Self::On),
0x02 => Ok(Self::Off),
0x03 => Ok(Self::Toggle),
0x04 => Ok(Self::Status),
byte => Err(server::ParseError::Unknown(byte)),
}
}
fn to_wire(&self) -> Vec<u8> {
match *self {
Self::On => vec![0x01],
Self::Off => vec![0x02],
Self::Toggle => vec![0x03],
Self::Status => vec![0x04],
}
}
}
fn status_file() -> Result<PathBuf, Error> {
Ok(dirs::require_xdg_runtime_dir()?.join("presentation-mode-on"))
}
#[derive(Debug, Clone, Copy)]
enum Status {
On,
Off,
}
fn status() -> Result<Status, Error> {
Ok(if status_file()?.exists() {
Status::On
} else {
Status::Off
})
}
fn on() -> Result<(), Error> {
drop(fs::File::create(status_file()?)?);
dunst::set_status(dunst::Status::Paused)?;
redshift::set(redshift::Status::Off)?;
spotify::set(spotify::Status::Off)?;
Ok(())
}
fn off() -> Result<(), Error> {
fs::remove_file(status_file()?)?;
dunst::set_status(dunst::Status::Unpaused)?;
redshift::set(redshift::Status::On)?;
spotify::set(spotify::Status::On)?;
Ok(())
}
fn toggle() -> Result<(), Error> {
match status()? {
Status::On => off()?,
Status::Off => on()?,
}
Ok(())
}
impl Exec for Action {
type ExecErr = Error;
fn execute(&self) -> Result<Option<String>, Self::ExecErr> {
match *self {
Self::On => {
on()?;
Ok(None)
}
Self::Off => {
off()?;
Ok(None)
}
Self::Toggle => {
toggle()?;
Ok(None)
}
Self::Status => Ok(match status()? {
Status::On => Some("on".to_owned()),
Status::Off => Some("off".to_owned()),
}),
}
}
}
impl CliCommand for Action {
type ExecErr = Error;
fn parse_str(input: &str, rest: impl Iterator<Item = String>) -> Result<Self, cli::ParseError>
where
Self: Sized,
{
let result = match input {
"on" => Self::On,
"off" => Self::Off,
"toggle" => Self::Toggle,
"status" => Self::Status,
s => {
return Err(cli::ParseError::UnknownAction {
action: s.to_owned(),
});
}
};
let rest = rest.collect::<Vec<String>>();
if rest.is_empty() {
Ok(result)
} else {
Err(cli::ParseError::UnexpectedInput { rest })
}
}
}

112
mgr/src/pulseaudio.rs Normal file
View File

@@ -0,0 +1,112 @@
use thiserror::Error;
use super::{
Exec,
cli::{self, CliCommand},
cmd,
wire::{WireCommand, server},
};
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Cli(#[from] cli::ParseError),
#[error(transparent)]
Cmd(#[from] cmd::Error),
}
#[derive(Debug, Clone, Copy)]
pub enum Action {
InputToggle,
OutputToggle,
OutputInc,
OutputDec,
}
impl WireCommand for Action {
fn parse_wire(mut input: impl Iterator<Item = u8>) -> Result<Self, server::ParseError> {
match input.next().ok_or(server::ParseError::Eof)? {
0x01 => Ok(Self::InputToggle),
0x02 => Ok(Self::OutputToggle),
0x03 => Ok(Self::OutputInc),
0x04 => Ok(Self::OutputDec),
byte => Err(server::ParseError::Unknown(byte)),
}
}
fn to_wire(&self) -> Vec<u8> {
match *self {
Self::InputToggle => vec![0x01],
Self::OutputToggle => vec![0x02],
Self::OutputInc => vec![0x03],
Self::OutputDec => vec![0x04],
}
}
}
impl CliCommand for Action {
type ExecErr = Error;
fn parse_str(
input: &str,
mut rest: impl Iterator<Item = String>,
) -> Result<Self, cli::ParseError>
where
Self: Sized,
{
let result = match input {
"input" => match rest.next().ok_or(cli::ParseError::MissingAction)?.as_str() {
"toggle" => Self::InputToggle,
s => {
return Err(cli::ParseError::UnknownAction {
action: s.to_owned(),
});
}
},
"output" => match rest.next().ok_or(cli::ParseError::MissingAction)?.as_str() {
"toggle" => Self::OutputToggle,
"inc" => Self::OutputInc,
"dec" => Self::OutputDec,
s => {
return Err(cli::ParseError::UnknownAction {
action: s.to_owned(),
});
}
},
s => {
return Err(cli::ParseError::UnknownAction {
action: s.to_owned(),
});
}
};
let rest = rest.collect::<Vec<String>>();
if rest.is_empty() {
Ok(result)
} else {
Err(cli::ParseError::UnexpectedInput { rest })
}
}
}
impl Exec for Action {
type ExecErr = Error;
fn execute(&self) -> Result<Option<String>, Self::ExecErr> {
match *self {
Self::InputToggle => {
cmd::command("pactl", &["set-source-mute", "@DEFAULT_SOURCE@", "toggle"])?;
}
Self::OutputToggle => {
cmd::command("pactl", &["set-sink-mute", "@DEFAULT_SINK@", "toggle"])?;
}
Self::OutputInc => {
cmd::command("pactl", &["set-sink-volume", "@DEFAULT_SINK@", "+5%"])?;
}
Self::OutputDec => {
cmd::command("pactl", &["set-sink-volume", "@DEFAULT_SINK@", "-5%"])?;
}
}
Ok(None)
}
}

116
mgr/src/redshift.rs Normal file
View File

@@ -0,0 +1,116 @@
use thiserror::Error;
use super::{
Exec,
cli::{self, CliCommand},
cmd, systemd,
wire::{WireCommand, server},
};
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Cmd(#[from] cmd::Error),
#[error(transparent)]
Cli(#[from] cli::ParseError),
#[error(transparent)]
Systemd(#[from] systemd::Error),
}
#[derive(Debug, Clone, Copy)]
pub enum Action {
Start,
Stop,
Status,
}
impl WireCommand for Action {
fn parse_wire(mut input: impl Iterator<Item = u8>) -> Result<Self, server::ParseError> {
match input.next().ok_or(server::ParseError::Eof)? {
0x01 => Ok(Self::Start),
0x02 => Ok(Self::Stop),
0x03 => Ok(Self::Status),
byte => Err(server::ParseError::Unknown(byte)),
}
}
fn to_wire(&self) -> Vec<u8> {
match *self {
Self::Start => vec![0x01],
Self::Stop => vec![0x02],
Self::Status => vec![0x03],
}
}
}
impl Exec for Action {
type ExecErr = Error;
fn execute(&self) -> Result<Option<String>, Self::ExecErr> {
match *self {
Self::Start => {
set(Status::On)?;
Ok(None)
}
Self::Stop => {
set(Status::Off)?;
Ok(None)
}
Self::Status => Ok(
if systemd::user::unit_status("redshift.service")?.is_active() {
Some("active".to_owned())
} else {
Some("inactive".to_owned())
},
),
}
}
}
impl CliCommand for Action {
type ExecErr = Error;
fn parse_str(input: &str, rest: impl Iterator<Item = String>) -> Result<Self, cli::ParseError>
where
Self: Sized,
{
let result = match input {
"start" => Self::Start,
"stop" => Self::Stop,
"status" => Self::Status,
s => {
return Err(cli::ParseError::UnknownAction {
action: s.to_owned(),
});
}
};
let rest = rest.collect::<Vec<String>>();
if rest.is_empty() {
Ok(result)
} else {
Err(cli::ParseError::UnexpectedInput { rest })
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum Status {
On,
Off,
}
pub(crate) fn set(status: Status) -> Result<(), Error> {
Ok(cmd::command(
"systemctl",
&[
"--user",
"--no-block",
match status {
Status::On => "start",
Status::Off => "stop",
},
"redshift.service",
],
)?)
}

104
mgr/src/sleep.rs Normal file
View File

@@ -0,0 +1,104 @@
use super::{
Exec,
cli::{self, CliCommand},
cmd,
wire::{WireCommand, server},
};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Cmd(#[from] cmd::Error),
#[error(transparent)]
Cli(#[from] cli::ParseError),
}
#[derive(Debug, Clone, Copy)]
pub enum Action {
Sleep(std::time::Duration),
}
impl WireCommand for Action {
fn parse_wire(mut input: impl Iterator<Item = u8>) -> Result<Self, server::ParseError> {
match input.next().ok_or(server::ParseError::Eof)? {
0x01 => {
const BYTES: usize = (u64::BITS / 8) as usize;
let input = input.take(BYTES).collect::<Vec<u8>>();
let input: [u8; BYTES] =
input
.try_into()
.map_err(|vec: Vec<u8>| server::ParseError::MissingBytes {
expected: BYTES,
received: vec.len(),
})?;
let secs = u64::from_le_bytes(input);
let duration = std::time::Duration::from_secs(secs);
Ok(Self::Sleep(duration))
}
byte => Err(server::ParseError::Unknown(byte)),
}
}
fn to_wire(&self) -> Vec<u8> {
match *self {
Self::Sleep(duration) => {
let mut v = vec![0x01];
v.extend(duration.as_secs().to_le_bytes());
v
}
}
}
}
impl Exec for Action {
type ExecErr = Error;
fn execute(&self) -> Result<Option<String>, Self::ExecErr> {
match *self {
Self::Sleep(duration) => {
std::thread::sleep(duration);
Ok(None)
}
}
}
}
impl CliCommand for Action {
type ExecErr = Error;
fn parse_str(
input: &str,
mut rest: impl Iterator<Item = String>,
) -> Result<Self, cli::ParseError>
where
Self: Sized,
{
let result = match input {
"sleep" => {
let input = rest.next().ok_or(cli::ParseError::MissingArgument)?;
let seconds =
input
.parse::<u64>()
.map_err(|err| cli::ParseError::ArgumentParse {
message: err.to_string(),
})?;
Self::Sleep(std::time::Duration::from_secs(seconds))
}
s => {
return Err(cli::ParseError::UnknownAction {
action: s.to_owned(),
});
}
};
let rest = rest.collect::<Vec<String>>();
if rest.is_empty() {
Ok(result)
} else {
Err(cli::ParseError::UnexpectedInput { rest })
}
}
}

173
mgr/src/spotify.rs Normal file
View File

@@ -0,0 +1,173 @@
use thiserror::Error;
use super::{
Exec,
cli::{self, CliCommand},
cmd, systemd,
wire::{WireCommand, server},
};
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Cmd(#[from] cmd::Error),
#[error(transparent)]
Cli(#[from] cli::ParseError),
#[error(transparent)]
Systemd(#[from] systemd::Error),
#[error("spotify does not seem to be running")]
NotFound,
}
#[derive(Debug, Clone, Copy)]
pub enum Action {
Start,
Stop,
Status,
Play,
Pause,
Toggle,
Previous,
Next,
}
impl WireCommand for Action {
fn parse_wire(mut input: impl Iterator<Item = u8>) -> Result<Self, server::ParseError> {
match input.next().ok_or(server::ParseError::Eof)? {
0x01 => Ok(Self::Start),
0x02 => Ok(Self::Stop),
0x03 => Ok(Self::Status),
0x04 => Ok(Self::Play),
0x05 => Ok(Self::Pause),
0x06 => Ok(Self::Toggle),
0x07 => Ok(Self::Previous),
0x08 => Ok(Self::Next),
byte => Err(server::ParseError::Unknown(byte)),
}
}
fn to_wire(&self) -> Vec<u8> {
match *self {
Self::Start => vec![0x01],
Self::Stop => vec![0x02],
Self::Status => vec![0x03],
Self::Play => vec![0x04],
Self::Pause => vec![0x05],
Self::Toggle => vec![0x06],
Self::Previous => vec![0x07],
Self::Next => vec![0x08],
}
}
}
impl Exec for Action {
type ExecErr = Error;
fn execute(&self) -> Result<Option<String>, Self::ExecErr> {
match *self {
Self::Start => {
set(Status::On)?;
Ok(None)
}
Self::Stop => {
set(Status::Off)?;
Ok(None)
}
Self::Status => Ok(
if systemd::user::unit_status("spotify.service")?.is_active() {
Some("active".to_owned())
} else {
Some("inactive".to_owned())
},
),
Self::Play => {
playerctl("play")?;
Ok(None)
}
Self::Pause => {
playerctl("pause")?;
Ok(None)
}
Self::Toggle => {
playerctl("play-pause")?;
Ok(None)
}
Self::Previous => {
playerctl("previous")?;
Ok(None)
}
Self::Next => {
playerctl("next")?;
Ok(None)
}
}
}
}
impl CliCommand for Action {
type ExecErr = Error;
fn parse_str(input: &str, rest: impl Iterator<Item = String>) -> Result<Self, cli::ParseError>
where
Self: Sized,
{
let result = match input {
"start" => Self::Start,
"stop" => Self::Stop,
"status" => Self::Status,
"play" => Self::Play,
"pause" => Self::Pause,
"toggle" => Self::Toggle,
"previous" => Self::Previous,
"next" => Self::Next,
s => {
return Err(cli::ParseError::UnknownAction {
action: s.to_owned(),
});
}
};
let rest = rest.collect::<Vec<String>>();
if rest.is_empty() {
Ok(result)
} else {
Err(cli::ParseError::UnexpectedInput { rest })
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum Status {
On,
Off,
}
pub(crate) fn set(status: Status) -> Result<(), Error> {
Ok(cmd::command(
"systemctl",
&[
"--user",
"--no-block",
match status {
Status::On => "start",
Status::Off => "stop",
},
"spotify.service",
],
)?)
}
fn playerctl(cmd: &str) -> Result<(), Error> {
if cmd::run_command("playerctl", &["-p", "spotify", cmd])?
.stderr
.contains("No players found")
{
Err(Error::NotFound)
} else {
Ok(())
}
}
pub(crate) fn pause() -> Result<(), Error> {
playerctl("pause")
}

40
mgr/src/systemd.rs Normal file
View File

@@ -0,0 +1,40 @@
use thiserror::Error;
use super::cmd;
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Cmd(#[from] cmd::Error),
#[error("unknown status output: \"{output}\"")]
UnknownStatusOutput { output: String },
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum UnitStatus {
Active,
Inactive,
Failed,
}
impl UnitStatus {
pub(crate) fn is_active(self) -> bool {
matches!(self, Self::Active)
}
}
pub(crate) mod user {
use super::{super::cmd, Error, UnitStatus};
pub(crate) fn unit_status(unit: &str) -> Result<UnitStatus, Error> {
let output = cmd::run_command("systemctl", &["--user", "is-active", unit])?;
match output.stdout.as_str().trim() {
"active" | "activating" | "reloading" | "refreshing" => Ok(UnitStatus::Active),
"inactive" | "deactivating" | "maintenance" => Ok(UnitStatus::Inactive),
"failed" => Ok(UnitStatus::Failed),
other => Err(Error::UnknownStatusOutput {
output: other.to_owned(),
}),
}
}
}

101
mgr/src/theme.rs Normal file
View File

@@ -0,0 +1,101 @@
use thiserror::Error;
use super::{
Exec,
cli::{self, CliCommand},
cmd, systemd,
wire::{WireCommand, server},
};
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Cli(#[from] cli::ParseError),
#[error(transparent)]
Cmd(#[from] cmd::Error),
#[error(transparent)]
Systemd(#[from] systemd::Error),
}
#[derive(Debug, Clone, Copy)]
pub enum Action {
Dark = 0x01,
Light = 0x02,
Status = 0x03,
}
impl WireCommand for Action {
fn parse_wire(mut input: impl Iterator<Item = u8>) -> Result<Self, server::ParseError> {
match input.next().ok_or(server::ParseError::Eof)? {
0x01 => Ok(Self::Dark),
0x02 => Ok(Self::Light),
0x03 => Ok(Self::Status),
byte => Err(server::ParseError::Unknown(byte)),
}
}
fn to_wire(&self) -> Vec<u8> {
match *self {
Self::Dark => vec![0x01],
Self::Light => vec![0x02],
Self::Status => vec![0x03],
}
}
}
impl CliCommand for Action {
type ExecErr = Error;
fn parse_str(input: &str, rest: impl Iterator<Item = String>) -> Result<Self, cli::ParseError>
where
Self: Sized,
{
let result = match input {
"dark" => Self::Dark,
"light" => Self::Light,
"status" => Self::Status,
s => {
return Err(cli::ParseError::UnknownAction {
action: s.to_owned(),
});
}
};
let rest = rest.collect::<Vec<String>>();
if rest.is_empty() {
Ok(result)
} else {
Err(cli::ParseError::UnexpectedInput { rest })
}
}
}
impl Exec for Action {
type ExecErr = Error;
fn execute(&self) -> Result<Option<String>, Self::ExecErr> {
match *self {
Self::Dark => {
cmd::command(
"systemctl",
&["--user", "--no-block", "start", "color-theme-dark.service"],
)?;
Ok(None)
}
Self::Light => {
cmd::command(
"systemctl",
&["--user", "--no-block", "start", "color-theme-light.service"],
)?;
Ok(None)
}
Self::Status => Ok(
if systemd::user::unit_status("color-theme-light.service")?.is_active() {
Some("light".to_owned())
} else {
Some("dark".to_owned())
},
),
}
}
}

198
mgr/src/weather.rs Normal file
View File

@@ -0,0 +1,198 @@
use std::{
fs, io,
ops::Sub as _,
path::{Path, PathBuf},
};
use thiserror::Error;
use time::format_description::well_known::Iso8601;
use tracing::{Level, event};
const CACHE_AGE: time::Duration = time::Duration::hours(1);
const CACHE_DELIMITER: char = '|';
use super::{
Exec,
cli::{self, CliCommand},
cmd, dirs, systemd,
wire::{WireCommand, server},
};
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Cmd(#[from] cmd::Error),
#[error(transparent)]
Cli(#[from] cli::ParseError),
#[error(transparent)]
Systemd(#[from] systemd::Error),
#[error(transparent)]
Http(#[from] ureq::Error),
#[error(transparent)]
Dirs(#[from] dirs::Error),
#[error(transparent)]
Io(#[from] io::Error),
#[error("delimiter not found in cache file")]
CacheDelimitedNotFound,
#[error("invalid timestamp \"{input}\" in cache file: {error}")]
CacheTimestampParse {
input: String,
error: time::error::Parse,
},
#[error("cache timestamp ({cache_timestamp}) is from the future (now: {now})")]
CacheTimestampOverflow {
now: time::UtcDateTime,
cache_timestamp: time::UtcDateTime,
},
#[error("formatting cache timestamp failed: {error}")]
CacheTimestampFormat { error: time::error::Format },
}
#[derive(Debug, Clone, Copy)]
pub enum Action {
Get,
}
impl WireCommand for Action {
fn parse_wire(mut input: impl Iterator<Item = u8>) -> Result<Self, server::ParseError> {
match input.next().ok_or(server::ParseError::Eof)? {
0x01 => Ok(Self::Get),
byte => Err(server::ParseError::Unknown(byte)),
}
}
fn to_wire(&self) -> Vec<u8> {
match *self {
Self::Get => vec![0x01],
}
}
}
impl Exec for Action {
type ExecErr = Error;
fn execute(&self) -> Result<Option<String>, Self::ExecErr> {
match *self {
Self::Get => Ok(Some(get()?)),
}
}
}
impl CliCommand for Action {
type ExecErr = Error;
fn parse_str(input: &str, rest: impl Iterator<Item = String>) -> Result<Self, cli::ParseError>
where
Self: Sized,
{
let result = match input {
"get" => Self::Get,
s => {
return Err(cli::ParseError::UnknownAction {
action: s.to_owned(),
});
}
};
let rest = rest.collect::<Vec<String>>();
if rest.is_empty() {
Ok(result)
} else {
Err(cli::ParseError::UnexpectedInput { rest })
}
}
}
#[derive(Debug)]
struct Cache {
timestamp: time::UtcDateTime,
value: String,
}
fn cache_file() -> Result<PathBuf, Error> {
Ok(dirs::xdg_cache_dir()?.join("workstation-mgr.wttr.cache"))
}
fn store_cache(path: &Path, timestamp: &time::UtcDateTime, value: &str) -> Result<(), Error> {
event!(Level::DEBUG, "storing in cache: {timestamp} {value}");
Ok(fs::write(
path,
format!(
"{timestamp}{CACHE_DELIMITER}{value}",
timestamp = timestamp
.format(&Iso8601::DEFAULT)
.map_err(|error| Error::CacheTimestampFormat { error })?,
),
)?)
}
fn get_cache(path: &Path) -> Result<Option<Cache>, Error> {
match fs::read_to_string(path) {
Ok(content) => {
let (timestamp, value) = content
.split_once(CACHE_DELIMITER)
.ok_or(Error::CacheDelimitedNotFound)?;
let cache_timestamp =
time::UtcDateTime::parse(timestamp, &Iso8601::DEFAULT).map_err(|error| {
Error::CacheTimestampParse {
input: timestamp.to_owned(),
error,
}
})?;
Ok(Some(Cache {
timestamp: cache_timestamp,
value: value.to_owned(),
}))
}
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None),
Err(e) => Err(e.into()),
}
}
fn request() -> Result<String, Error> {
Ok(ureq::get("https://wttr.in/Ansbach?m&T&format=%c%t")
.call()?
.body_mut()
.read_to_string()?)
}
fn get_and_update_cache(cache_file: &Path, now: &time::UtcDateTime) -> Result<String, Error> {
event!(Level::DEBUG, "refreshing cache");
let value = request()?;
store_cache(cache_file, now, &value)?;
Ok(value)
}
fn get() -> Result<String, Error> {
let cache_file = cache_file()?;
event!(Level::DEBUG, "using cache file {cache_file:?}");
let cache = get_cache(&cache_file)?;
event!(Level::DEBUG, "read from cache: {cache:?}");
let now = time::UtcDateTime::now();
match cache {
Some(cache) => {
let cache_age = now.sub(cache.timestamp);
event!(Level::DEBUG, "cache age: {cache_age}");
if cache_age.is_negative() {
return Err(Error::CacheTimestampOverflow {
now,
cache_timestamp: cache.timestamp,
});
}
if cache_age <= CACHE_AGE {
event!(Level::DEBUG, "reusing cache");
Ok(cache.value)
} else {
get_and_update_cache(&cache_file, &now)
}
}
None => get_and_update_cache(&cache_file, &now),
}
}

20
mgr/src/wire/client.rs Normal file
View File

@@ -0,0 +1,20 @@
use std::{
io::{self, Write as _},
os::unix::net::UnixStream,
};
use thiserror::Error;
use super::{super::Action, WireCommand as _};
#[derive(Debug, Error)]
pub enum SendError {
#[error(transparent)]
Io(#[from] io::Error),
}
impl Action {
pub fn send(&self, stream: &mut UnixStream) -> Result<(), SendError> {
Ok(stream.write_all(&self.to_wire())?)
}
}

11
mgr/src/wire/mod.rs Normal file
View File

@@ -0,0 +1,11 @@
pub mod client;
pub mod server;
pub mod socket;
pub(crate) trait WireCommand {
fn parse_wire(input: impl Iterator<Item = u8>) -> Result<Self, server::ParseError>
where
Self: Sized;
fn to_wire(&self) -> Vec<u8>;
}

90
mgr/src/wire/server.rs Normal file
View File

@@ -0,0 +1,90 @@
use std::{
io::{self, Read, Write},
os::unix::net::{SocketAddr, UnixListener, UnixStream},
thread,
};
use thiserror::Error;
use tracing::{Level, event};
use super::{
super::{Action, Exec as _},
WireCommand, socket,
};
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
Parse(#[from] ParseError),
#[error(transparent)]
Socket(#[from] socket::Error),
#[error(transparent)]
Exec(#[from] crate::ExecError),
}
#[derive(Debug, Error)]
pub enum ParseError {
#[error("received unexpected eof")]
Eof,
#[error("received unknown byte: {0:#X}")]
Unknown(u8),
#[error("received surplus input: {0:?}")]
Surplus(Vec<u8>),
#[error("expected {expected} bytes, received only {received}")]
MissingBytes { expected: usize, received: usize },
}
fn handle_client(stream: &mut UnixStream) -> Result<(), Error> {
let input = {
let mut buf = Vec::new();
stream.read_to_end(&mut buf)?;
buf
};
event!(Level::DEBUG, "request data: {input:?}");
let action = Action::parse_wire(input.into_iter())?;
event!(Level::DEBUG, "parsed request: {action:?}");
if let Some(output) = action.execute()? {
stream.write_all(output.as_bytes())?;
}
Ok(())
}
pub fn run() -> Result<(), Error> {
event!(Level::DEBUG, "starting server");
let socket_path = socket::get_socket_path()?;
socket::try_remove_socket(&socket_path)?;
let socket_addr = SocketAddr::from_pathname(socket_path)?;
event!(Level::DEBUG, "socket address {socket_addr:?}");
let listener = UnixListener::bind_addr(&socket_addr)?;
for stream in listener.incoming() {
let mut stream = stream?;
thread::spawn(move || {
event!(Level::DEBUG, "received request");
let result = handle_client(&mut stream);
if let Err(e) = result {
let msg = e.to_string();
event!(Level::ERROR, "action failed: {msg}");
if let Err(e) = stream.write_all(msg.as_bytes()) {
event!(Level::ERROR, "sending \"{msg}\" failed: {e}");
}
}
event!(Level::DEBUG, "closing stream");
drop(stream);
});
}
unreachable!()
}

35
mgr/src/wire/socket.rs Normal file
View File

@@ -0,0 +1,35 @@
use std::{
fs, io,
path::{Path, PathBuf},
};
use thiserror::Error;
use super::super::dirs;
#[derive(Debug, Error)]
pub enum Error {
#[error("could not find a suitable socket path")]
NoSocketPathFound,
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
Dirs(#[from] dirs::Error),
}
pub fn get_socket_path() -> Result<PathBuf, Error> {
if let Some(mut dir) = dirs::xdg_runtime_dir()? {
dir.push("workstation-mgr.sock");
return Ok(dir);
}
Err(Error::NoSocketPathFound)
}
pub(crate) fn try_remove_socket(path: &Path) -> Result<(), Error> {
match fs::remove_file(path) {
Ok(()) => Ok(()),
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()),
Err(e) => Err(e.into()),
}
}

View File

@@ -49,6 +49,7 @@ ansible:
archlinux: archlinux:
- ansible - ansible
- ansible-language-server - ansible-language-server
- ansible-lint
- python-jmespath - python-jmespath
xdotool: xdotool:
archlinux: ["xdotool"] archlinux: ["xdotool"]
@@ -73,7 +74,13 @@ font-dejavu:
- ttf-dejavu-nerd - ttf-dejavu-nerd
font-libertine: font-libertine:
archlinux: archlinux:
- ttf-linux-libertine - libertinus-font
font-awesome:
archlinux:
- woff2-font-awesome
font-noto:
archlinux:
- noto-fonts
git: git:
# tk required for gitk # tk required for gitk
archlinux: ["git", "tk", "git-delta", "git-filter-repo"] archlinux: ["git", "tk", "git-delta", "git-filter-repo"]
@@ -103,8 +110,6 @@ pavucontrol:
archlinux: ["pavucontrol-qt"] archlinux: ["pavucontrol-qt"]
pinentry-qt: pinentry-qt:
archlinux: ["pinentry"] archlinux: ["pinentry"]
pinta:
archlinux: ["pinta"]
pass: pass:
archlinux: ["pass", "passff-host", "xclip"] archlinux: ["pass", "passff-host", "xclip"]
urxvt: urxvt:
@@ -148,6 +153,7 @@ python:
- python-pylint - python-pylint
- flake8 - flake8
- mypy - mypy
- uv
python-modules: python-modules:
archlinux: archlinux:
- python-ruamel-yaml - python-ruamel-yaml
@@ -155,8 +161,8 @@ python-modules:
- python-semver - python-semver
black: black:
archlinux: ["python-black"] archlinux: ["python-black"]
xbacklight: brightnessctl:
archlinux: ["acpilight"] archlinux: ["brightnessctl"]
wireshark: wireshark:
archlinux: ["wireshark-cli", "wireshark-qt"] archlinux: ["wireshark-cli", "wireshark-qt"]
nmap: nmap:
@@ -211,7 +217,7 @@ ruby:
acpi: acpi:
archlinux: ["acpi", "acpid"] archlinux: ["acpi", "acpid"]
nodejs: nodejs:
archlinux: ["nodejs", "npm"] archlinux: ["nodejs-lts-jod", "npm", "yarn"]
xdg: xdg:
archlinux: ["xdg-utils"] archlinux: ["xdg-utils"]
dunst: dunst:
@@ -220,8 +226,6 @@ cloc:
archlinux: ["cloc"] archlinux: ["cloc"]
bwm-ng: bwm-ng:
archlinux: ["bwm-ng"] archlinux: ["bwm-ng"]
virtualbox:
archlinux: ["virtualbox"]
ssh: ssh:
archlinux: ["openssh"] archlinux: ["openssh"]
sshfs: sshfs:
@@ -233,20 +237,23 @@ inotify:
rclone: rclone:
archlinux: ["rclone"] archlinux: ["rclone"]
dnf: dnf:
archlinux: ["dnf"] archlinux: ["dnf5"]
rust: rust:
archlinux: archlinux:
- rustup - rustup
- rust-analyzer - rust-analyzer
- cargo-edit - cargo-edit
- cargo-expand
- cargo-msrv - cargo-msrv
- cargo-watch - cargo-watch
- cargo-release - cargo-release
- cargo-sort - cargo-sort
- cargo-hack
- cargo-dist
- cargo-binstall
- rust-script
musescore: musescore:
archlinux: ["musescore"] archlinux: ["musescore"]
sipcalc:
archlinux: ["sipcalc"]
rofi: rofi:
archlinux: ["rofi"] archlinux: ["rofi"]
imv: imv:
@@ -311,8 +318,6 @@ fzf:
archlinux: ["fzf"] archlinux: ["fzf"]
chromium: chromium:
archlinux: ["chromium"] archlinux: ["chromium"]
signal:
archlinux: ["signal-desktop"]
go: go:
archlinux: ["go", "gopls", "delve"] archlinux: ["go", "gopls", "delve"]
helix: helix:
@@ -320,12 +325,12 @@ helix:
keepassxc: keepassxc:
archlinux: ["keepassxc"] archlinux: ["keepassxc"]
awscli: awscli:
archlinux: ["aws-cli"] archlinux: ["aws-cli-v2"]
mariadb-client: mariadb-client:
archlinux: ["mariadb-clients"] archlinux: ["mariadb-clients"]
php: php:
archlinux: ["php"] archlinux: ["php", "composer"]
exa: eza:
archlinux: ["eza"] archlinux: ["eza"]
just: just:
archlinux: ["just"] archlinux: ["just"]
@@ -369,6 +374,7 @@ json:
markdown: markdown:
archlinux: archlinux:
- marksman - marksman
- mdformat
lldb: lldb:
archlinux: archlinux:
- lldb - lldb
@@ -440,6 +446,9 @@ mold:
archlinux: archlinux:
- clang - clang
- mold - mold
wild:
archlinux:
- wild
arch-packaging: arch-packaging:
archlinux: archlinux:
- namcap - namcap
@@ -461,6 +470,7 @@ watchexec:
postgresql: postgresql:
archlinux: archlinux:
- postgresql - postgresql
- pgformatter
tokei: tokei:
archlinux: archlinux:
- tokei - tokei
@@ -514,3 +524,69 @@ geeqie:
xpath: xpath:
archlinux: archlinux:
- perl-xml-xpath - perl-xml-xpath
apache: # for apachebench
archlinux:
- apache
xfce4-screenshooter:
archlinux:
- xfce4-screenshooter
picom:
archlinux:
- picom
fuseiso:
archlinux:
- fuseiso
7zip:
archlinux:
- p7zip
unrar:
archlinux:
- unrar
taplo:
archlinux:
- taplo-cli
prettier:
archlinux:
- prettier
sccache:
archlinux:
- sccache
btop:
archlinux:
- btop
dua:
archlinux:
- dua-cli
hexyl:
archlinux:
- hexyl
yq:
archlinux:
- go-yq
kolourpaint:
archlinux:
- kolourpaint
podman:
archlinux:
- podman
- podman-compose
pulumi:
archlinux:
- pulumi
reflector:
archlinux:
- reflector
yazi:
archlinux:
- yazi
- ffmpeg
- 7zip
- jq
- poppler
- fd
- ripgrep
- fzf
- xsel
- zoxide
- resvg
- imagemagick

1
pkgbuilds/aws-sam-cli Submodule

Submodule pkgbuilds/aws-sam-cli added at 8d4290f258

1
pkgbuilds/claude-code Submodule

Submodule pkgbuilds/claude-code added at 8e4e33f722

1
pkgbuilds/python-vdf Submodule

Submodule pkgbuilds/python-vdf added at 03356b9232

3
pkgbuilds/workstation-mgr/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*
!.gitignore
!PKGBUILD

View File

@@ -0,0 +1,42 @@
# Maintainer: Hannes Körber <hannes@hkoerber.de>
pkgname='workstation-mgr'
pkgver=5
pkgrel=1
pkgdesc=''
arch=('x86_64')
depends=('glibc' 'gcc-libs')
makedepends=('cargo')
options=(!lto)
source=()
sha256sums=()
pkgver() {
cd "/var/lib/dotfiles/mgr/"
git log --oneline . | wc -l
}
prepare() {
cd "/var/lib/dotfiles/mgr/"
export RUSTUP_TOOLCHAIN=stable
cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')"
}
build() {
cd "/var/lib/dotfiles/mgr/"
export RUSTUP_TOOLCHAIN=stable
export CARGO_TARGET_DIR=/var/lib/makepkg/${pkgname}/build/target
cargo build --frozen --release
}
check() {
cd "/var/lib/dotfiles/mgr/"
export RUSTUP_TOOLCHAIN=stable
cargo test --frozen
}
package() {
cd "/var/lib/dotfiles/mgr/"
export CARGO_TARGET_DIR=/var/lib/makepkg/${pkgname}/build/target
install -Dm0755 -t "$pkgdir/usr/bin/" "${CARGO_TARGET_DIR}/release/${pkgname}"
install -Dm0755 -t "$pkgdir/usr/bin/" "${CARGO_TARGET_DIR}/release/workstation-client"
}

Some files were not shown because too many files have changed in this diff Show More