Compare commits

...

217 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
98 changed files with 4654 additions and 1084 deletions

12
.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
@@ -58,3 +52,9 @@
[submodule "pkgbuilds/python-rst2ansi"] [submodule "pkgbuilds/python-rst2ansi"]
path = pkgbuilds/python-rst2ansi path = pkgbuilds/python-rst2ansi
url = https://aur.archlinux.org/python-rst2ansi.git 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 --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

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_KEEPASSXC: "true" 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_KEEPASSXC: "false" MACHINE_HAS_KEEPASSXC: "false"
repositories:
- personal_projects
screen: screen:
1: DP-4-1-6 1: DP-4-1-6

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

@@ -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

@@ -0,0 +1,8 @@
[Unit]
BindsTo=autostart.target
After=windowmanager.target
[Service]
Type=simple
ExecStart=/usr/bin/screencfg watch --best
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

@@ -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

View File

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

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
@@ -47,8 +46,6 @@ dotfiles:
dir: true 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
@@ -102,9 +99,13 @@ dotfiles:
to: .config/screencfg.toml to: .config/screencfg.toml
- from: cargo/config.toml - from: cargo/config.toml
to: .local/state/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

@@ -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
@@ -92,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]
@@ -121,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 = main 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

@@ -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,7 +110,6 @@ 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="^KeePassXC$"] $workspace8
# See https://github.com/i3/i3/issues/2060 # See https://github.com/i3/i3/issues/2060
@@ -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
@@ -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

View File

@@ -3,7 +3,7 @@
set -o nounset set -o nounset
set -o errexit set -o errexit
sudo pacman -Syu sudo bash -c "pacman -Sy --needed --noconfirm archlinux-keyring && pacman -Su"
./update-aur-pkgs.sh ./update-aur-pkgs.sh

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"]
@@ -76,7 +77,7 @@ font-libertine:
- libertinus-font - libertinus-font
font-awesome: font-awesome:
archlinux: archlinux:
- ttf-font-awesome - woff2-font-awesome
font-noto: font-noto:
archlinux: archlinux:
- noto-fonts - noto-fonts
@@ -109,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:
@@ -162,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:
@@ -218,7 +217,7 @@ ruby:
acpi: acpi:
archlinux: ["acpi", "acpid"] archlinux: ["acpi", "acpid"]
nodejs: nodejs:
archlinux: ["nodejs", "npm", "yarn"] archlinux: ["nodejs-lts-jod", "npm", "yarn"]
xdg: xdg:
archlinux: ["xdg-utils"] archlinux: ["xdg-utils"]
dunst: dunst:
@@ -227,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:
@@ -240,7 +237,7 @@ inotify:
rclone: rclone:
archlinux: ["rclone"] archlinux: ["rclone"]
dnf: dnf:
archlinux: ["dnf"] archlinux: ["dnf5"]
rust: rust:
archlinux: archlinux:
- rustup - rustup
@@ -254,10 +251,9 @@ rust:
- cargo-hack - cargo-hack
- cargo-dist - cargo-dist
- cargo-binstall - cargo-binstall
- rust-script
musescore: musescore:
archlinux: ["musescore"] archlinux: ["musescore"]
sipcalc:
archlinux: ["sipcalc"]
rofi: rofi:
archlinux: ["rofi"] archlinux: ["rofi"]
imv: imv:
@@ -322,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:
@@ -331,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"]
@@ -380,6 +374,7 @@ json:
markdown: markdown:
archlinux: archlinux:
- marksman - marksman
- mdformat
lldb: lldb:
archlinux: archlinux:
- lldb - lldb
@@ -451,6 +446,9 @@ mold:
archlinux: archlinux:
- clang - clang
- mold - mold
wild:
archlinux:
- wild
arch-packaging: arch-packaging:
archlinux: archlinux:
- namcap - namcap
@@ -472,6 +470,7 @@ watchexec:
postgresql: postgresql:
archlinux: archlinux:
- postgresql - postgresql
- pgformatter
tokei: tokei:
archlinux: archlinux:
- tokei - tokei
@@ -552,3 +551,42 @@ prettier:
sccache: sccache:
archlinux: archlinux:
- sccache - 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/claude-code Submodule

Submodule pkgbuilds/claude-code added at 8e4e33f722

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"
}

View File

@@ -1,108 +1,105 @@
---
- name: configure system - name: configure system
hosts: localhost hosts: localhost
connection: local connection: local
become: false become: false
tasks: tasks:
- name: read machine-specific variables - name: Read machine-specific variables
include_vars: ansible.builtin.include_vars:
file: _machines/{{ ansible_hostname }}.yml file: _machines/{{ ansible_facts['hostname'] }}.yml
name: machine name: machine
tags: tags:
- always - always
- set_fact: - ansible.builtin.set_fact:
distro: "{{ ansible_distribution|lower }}" distro: "{{ ansible_facts['distribution'] | lower }}"
tags: tags:
- always - always
- name: check for valid distro - name: Check for valid distro
assert: ansible.builtin.assert:
that: distro in ('archlinux') that: distro in ('archlinux')
- block: - block:
- name: install ansible requirements - name: Install ansible requirements
package: ansible.builtin.package:
name: "{{ packages[distro] }}" name: "{{ packages[distro] }}"
state: present state: present
become: true become: true
vars: vars:
packages: packages:
archlinux: archlinux:
- python-jmespath - python-jmespath
- name: pacman - name: Pacman
tags: tags:
- pacman - pacman
block: block:
- name: enable multilib repository - name: Enable multilib repository
blockinfile: ansible.builtin.blockinfile:
path: /etc/pacman.conf path: /etc/pacman.conf
block: | block: |
[multilib] [multilib]
Include = /etc/pacman.d/mirrorlist Include = /etc/pacman.d/mirrorlist
marker: "# {mark} ANSIBLE MANAGED multilib" marker: "# {mark} ANSIBLE MANAGED multilib"
notify:
- refresh package lists
become: true become: true
- name: enable parallel download - name: Make sure that package lists are refreshed if necessary
blockinfile: ansible.builtin.meta: flush_handlers
- name: Enable parallel download
ansible.builtin.blockinfile:
path: /etc/pacman.conf path: /etc/pacman.conf
insertafter: '\[options\]' insertafter: "\\[options\\]"
block: | block: |
ParallelDownloads = 5 ParallelDownloads = 5
marker: "# {mark} ANSIBLE MANAGED parallel_download" marker: "# {mark} ANSIBLE MANAGED parallel_download"
become: true become: true
- block: - name: Install pacman-contrib for paccache
- name: upgrade system ansible.builtin.package:
pacman:
upgrade: true
update_cache: true
become: true
changed_when: false
tags: [system-update]
- name: install pacman-contrib for paccache
package:
name: pacman-contrib name: pacman-contrib
state: present state: present
become: true become: true
- block: - block:
- name: install pacman cache clean service - name: Install pacman cache clean service
copy: ansible.builtin.copy:
dest: /etc/systemd/system/pacman-cache-cleanup.service dest: /etc/systemd/system/pacman-cache-cleanup.service
owner: root owner: root
group: root group: root
mode: '0644' mode: "0644"
content: | content: |
[Service] [Service]
Type=oneshot Type=oneshot
ExecStart=/bin/sh -c '/usr/bin/paccache -rk1 && /usr/bin/paccache -ruk0' ExecStart=/bin/sh -c '/usr/bin/paccache -rk1 && /usr/bin/paccache -ruk0'
RemainAfterExit=true RemainAfterExit=true
become: true become: true
- name: install pacman cache clean timer - name: Install pacman cache clean timer
copy: ansible.builtin.copy:
dest: /etc/systemd/system/pacman-cache-cleanup.timer dest: /etc/systemd/system/pacman-cache-cleanup.timer
owner: root owner: root
group: root group: root
mode: '0644' mode: "0644"
content: | content: |
[Timer] [Timer]
OnCalendar=daily OnCalendar=daily
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
become: true become: true
- name: enable pacman cache clean timer - ansible.builtin.systemd:
systemd: name: pacman-cache-cleanup.timer
name: pacman-cache-cleanup.timer enabled: true
enabled: true state: started
state: started daemon_reload: true
daemon_reload: true become: true
become: true
name: Enable pacman cache clean timer
- name: dotfiles directory - name: dotfiles directory
tags: tags:
@@ -132,7 +129,7 @@
path: /var/lib/dotfiles path: /var/lib/dotfiles
owner: dotfiles owner: dotfiles
group: dotfiles group: dotfiles
mode: '0775' # group needs write access! mode: "0775" # group needs write access!
become: true become: true
become_user: root become_user: root
@@ -191,11 +188,17 @@
- name: install packages - name: install packages
package: package:
name: "{{ defined_packages|json_query(query) }}" name: "{{ defined_packages|json_query(pkg_query) }}"
state: present state: present
become: true become: true
vars: vars:
query: "{{ '*.%s[]'|format(distro) }}" pkg_query: "{{ '*.%s[]'|format(distro) }}"
- name: install additional packages
package:
name: "{{ machine.additional_packages|default([]) }}"
state: present
become: true
- name: remove unconfigured packages - name: remove unconfigured packages
script: script:
@@ -205,6 +208,30 @@
changed_when: unconfigured_packages_cmd.rc == 123 changed_when: unconfigured_packages_cmd.rc == 123
become: true become: true
- name: reflector
block:
- name: Configure reflector
ansible.builtin.copy:
dest: /etc/xdg/reflector/reflector.conf
owner: root
group: root
mode: "0644"
content: |
--save /etc/pacman.d/mirrorlist
--protocol https
--country Germany
--latest 5
--sort age
become: true
- name: Enable reflector timer
ansible.builtin.systemd:
name: reflector.timer
enabled: true
state: started
daemon_reload: true
become: true
- name: aur - name: aur
tags: tags:
- aur - aur
@@ -220,6 +247,9 @@
- set_fact: - set_fact:
aur_packages: aur_packages:
# local packages:
- name: workstation-mgr
- name: portfolio-performance-bin - name: portfolio-performance-bin
preexec: | preexec: |
#!/usr/bin/env bash #!/usr/bin/env bash
@@ -230,10 +260,10 @@
preexec: | preexec: |
#!/usr/bin/env bash #!/usr/bin/env bash
source ./env source ./env
curl -sSf --proto '=https' https://download.spotify.com/debian/pubkey_6224F9941A8AA6D1.gpg | gpg --import - echo lel
curl -sSf --proto '=https' https://download.spotify.com/debian/pubkey_5384CE82BA52C83A.gpg | gpg --import -
- name: nodejs-intelephense - name: nodejs-intelephense
- name: vim-plug
- name: terraform-ls-bin - name: terraform-ls-bin
- name: grm-git - name: grm-git
- name: screencfg-git - name: screencfg-git
@@ -263,6 +293,9 @@
# dependency of # dependency of
- name: backblaze-b2 - name: backblaze-b2
# ===
- name: claude-code
- set_fact: - set_fact:
aur_packages: "{{ aur_packages|map(attribute='dependencies', default=[]) | flatten + aur_packages }}" aur_packages: "{{ aur_packages|map(attribute='dependencies', default=[]) | flatten + aur_packages }}"
@@ -313,7 +346,7 @@
file: file:
path: "/var/lib/makepkg/{{ item.name }}/" path: "/var/lib/makepkg/{{ item.name }}/"
state: directory state: directory
mode: '0700' mode: "0700"
owner: makepkg owner: makepkg
group: makepkg group: makepkg
become_user: makepkg become_user: makepkg
@@ -326,7 +359,7 @@
file: file:
path: "/var/lib/makepkg/{{ item.name }}/gnupg" path: "/var/lib/makepkg/{{ item.name }}/gnupg"
state: directory state: directory
mode: '0700' mode: "0700"
owner: makepkg owner: makepkg
group: makepkg group: makepkg
become_user: makepkg become_user: makepkg
@@ -391,9 +424,7 @@
chdir: "{{ item.1.stat.path | dirname }}" chdir: "{{ item.1.stat.path | dirname }}"
become_user: makepkg become_user: makepkg
become: true become: true
when: when: not item[0].stat.exists or (item[0].stat.checksum|default('') != item[1].stat.checksum)
- not item[0].stat.exists
- item[0].stat.checksum|default('') != item[1].stat.checksum
loop: "{{ preexec_before.results| reject('skipped')|zip(preexec_after.results| reject('skipped')) }}" loop: "{{ preexec_before.results| reject('skipped')|zip(preexec_after.results| reject('skipped')) }}"
loop_control: loop_control:
label: "{{ item.1.stat.path }}" label: "{{ item.1.stat.path }}"
@@ -418,13 +449,14 @@
source ./PKGBUILD source ./PKGBUILD
for arch in "${arch[@]}" ; do for a in "${arch[@]}" ; do
if [[ "${arch}" == "any" ]] ; then if [[ "${a}" == "any" ]] ; then
arch="any" arch="any"
break break
fi fi
if [[ "${arch}" == "x86_64" ]] ; then if [[ "${a}" == "x86_64" ]] ; then
arch="x86_64" arch="x86_64"
break
fi fi
done done
@@ -441,9 +473,7 @@
filename="${PKGDEST%/}/${pkgname}-${version}-${arch}${PKGEXT}" filename="${PKGDEST%/}/${pkgname}-${version}-${arch}${PKGEXT}"
needed_build=0
if [[ ! -e "${filename}" ]] ; then if [[ ! -e "${filename}" ]] ; then
needed_build=1
makepkg \ makepkg \
--clean \ --clean \
--nosign || exit 1 --nosign || exit 1
@@ -469,8 +499,8 @@
filename="$(</var/lib/makepkg/{{ item.name }}/pkgname)" filename="$(</var/lib/makepkg/{{ item.name }}/pkgname)"
name=$(pacman -Qi --file "${filename}" | grep '^Name' | awk '{print $3}') name=$(pacman -Qi --file "${filename}" | grep '^Name' | awk '{print $3}')
version=$(pacman -Qi --file "${filename}" | grep '^Version' | awk '{print $3}') version=$(pacman -Qi --file "${filename}" | grep '^Version' | awk '{print $3}')
if [[ "$(pacman -Q "${name}")" == "${name} ${version}" ]] ; then if [[ "$(pacman -Q "${name}")" == "${name} ${version}" ]] ; then
exit 0 exit 0
@@ -501,8 +531,7 @@
become: true become: true
with_nested: with_nested:
- "{{ aur_packages }}" - "{{ aur_packages }}"
- - - build
- build
- src - src
loop_control: loop_control:
label: "{{ item[0].name }}/{{ item[1] }}" label: "{{ item[0].name }}/{{ item[1] }}"
@@ -525,10 +554,29 @@
state: present state: present
become: true become: true
- name: set mkinitcpio hooks
set_fact:
mkinitcpio_hooks: "base udev autodetect microcode modconf kms keyboard keymap consolefont block encrypt lvm2 filesystems resume fsck"
when: machine.encrypted_root|bool
- name: set mkinitcpio hooks
set_fact:
mkinitcpio_hooks: "base udev autodetect microcode modconf kms keyboard keymap consolefont block filesystems resume fsck"
when: not machine.encrypted_root|bool
- name: configure mkinitcpio hooks
lineinfile:
path: /etc/mkinitcpio.conf
regexp: "^#?HOOKS=.*$"
line: 'HOOKS=({{ mkinitcpio_hooks }})'
become: true
notify:
- rebuild initrd
- name: use vz4 for mkinitcpio compression - name: use vz4 for mkinitcpio compression
lineinfile: lineinfile:
path: /etc/mkinitcpio.conf path: /etc/mkinitcpio.conf
regexp: '^#?COMPRESSION=.*$' regexp: "^#?COMPRESSION=.*$"
line: 'COMPRESSION="lz4"' line: 'COMPRESSION="lz4"'
become: true become: true
notify: notify:
@@ -631,8 +679,9 @@
changed_when: false changed_when: false
check_mode: false check_mode: false
- set_fact: - name: define systemd default target
default_target: multi-user.target set_fact:
default_target: "{{ machine.system_default_target|default('multi-user.target') }}"
- name: set systemd boot target - name: set systemd boot target
command: systemctl set-default {{ default_target }} command: systemctl set-default {{ default_target }}
@@ -642,22 +691,22 @@
- name: handle lid switch - name: handle lid switch
lineinfile: lineinfile:
path: /etc/systemd/logind.conf path: /etc/systemd/logind.conf
regexp: '^HandleLidSwitch=' regexp: "^HandleLidSwitch="
line: 'HandleLidSwitch=ignore' line: "HandleLidSwitch=ignore"
become: true become: true
- name: handle power key - name: handle power key
lineinfile: lineinfile:
path: /etc/systemd/logind.conf path: /etc/systemd/logind.conf
regexp: '^HandlePowerKey=' regexp: "^HandlePowerKey="
line: 'HandlePowerKey=suspend' line: "HandlePowerKey=suspend"
become: true become: true
- name: limit journald size - name: limit journald size
lineinfile: lineinfile:
path: /etc/systemd/journald.conf path: /etc/systemd/journald.conf
regexp: '^#?SystemMaxUse=.*$' regexp: "^#?SystemMaxUse=.*$"
line: 'SystemMaxUse=50M' line: "SystemMaxUse=50M"
become: true become: true
notify: notify:
- restart journald - restart journald
@@ -755,41 +804,45 @@
name: "{{ drivers.gpu.nvidia }}" name: "{{ drivers.gpu.nvidia }}"
state: present state: present
become: true become: true
- name: Intel configuration
when: machine.gpu == 'intel'
block:
- name: install intel packages
package:
name: "{{ drivers.gpu.intel }}"
state: present
become: true
when: when:
- machine.gpu is defined - machine.gpu is defined
- set_fact:
users: "{{ machine.users }}"
tags:
- always
# See https://bbs.archlinux.org/viewtopic.php?id=259764 # See https://bbs.archlinux.org/viewtopic.php?id=259764
- block: - block:
- name: configure pacman to skip installing nextcloud dbus file - name: configure pacman to skip installing nextcloud dbus file
blockinfile: blockinfile:
path: /etc/pacman.conf path: /etc/pacman.conf
insertafter: '^#NoExtract' insertafter: "^#NoExtract"
block: | block: |
NoExtract = usr/share/dbus-1/services/com.nextcloudgmbh.Nextcloud.service NoExtract = usr/share/dbus-1/services/com.nextcloudgmbh.Nextcloud.service
marker: "# {mark} ANSIBLE MANAGED noextract nextcloud" marker: "# {mark} ANSIBLE MANAGED noextract nextcloud"
become: true become: true
- name: remove nextcloud dbus file - name: remove nextcloud dbus file
file: file:
path: /usr/share/dbus-1/services/com.nextcloudgmbh.Nextcloud.service path: /usr/share/dbus-1/services/com.nextcloudgmbh.Nextcloud.service
state: absent state: absent
become: true become: true
- name: try to make gpg agent behave - name: try to make gpg agent behave
block: block:
- name: configure pacman to skip installing gpg user units - name: configure pacman to skip installing gpg user units
blockinfile: blockinfile:
path: /etc/pacman.conf path: /etc/pacman.conf
insertafter: '^#NoExtract' insertafter: "^#NoExtract"
block: | block: |
NoExtract = usr/lib/systemd/user/gpg-agent* NoExtract = usr/lib/systemd/user/gpg-agent*
marker: "# {mark} ANSIBLE MANAGED noextract gpg-agent" marker: "# {mark} ANSIBLE MANAGED noextract gpg-agent"
become: true become: true
- name: backlight configuration - name: backlight configuration
tags: tags:
@@ -801,11 +854,185 @@
dest: /etc/udev/rules.d/backlight.rules dest: /etc/udev/rules.d/backlight.rules
owner: root owner: root
group: root group: root
mode: '0644' mode: "0644"
content: | content: |
ACTION=="add", SUBSYSTEM=="backlight", RUN+="/bin/chgrp video $sys$devpath/brightness", RUN+="/bin/chmod g+w $sys$devpath/brightness" ACTION=="add", SUBSYSTEM=="backlight", RUN+="/bin/chgrp video $sys$devpath/brightness", RUN+="/bin/chmod g+w $sys$devpath/brightness"
become: true become: true
- name: Firefox
tags:
- firefox
block:
- name: create firefox directories
file:
state: directory
path: "{{ item }}"
owner: root
group: root
mode: "0775"
become: true
become_user: root
loop:
- /etc/firefox
- /etc/firefox/policies
- set_fact:
firefox_policy:
policies:
AutofillAddressEnabled: false
AutofillCreditCardEnabled: false
DefaultDownloadDirectory: "${home}/download"
DisableFeedbackCommands: true
DisableFirefoxAccounts: true
DisableFirefoxStudies: true
DisableForgetButton: true
DisableMasterPasswordCreation: true
DisableProfileImport: true
DisableProfileRefresh: true
DisableSafeMode: true
DisableSetDesktopBackground: true
DisableTelemetry: true
DisplayBookmarksToolbar: "always"
DisplayMenuBar: "default-off"
DontCheckDefaultBrowser: true
EnableTrackingProtection:
Value: true
Locked: false
Category: "strict"
BaselineExceptions: true
ConvenienceExceptions: false
ExtensionSettings:
"*":
allowed_types:
- extension
"jid1-KKzOGWgsW3Ao4Q@jetpack": # I don't care about cookies
installation_mode: "normal_installed"
install_url: "https://addons.mozilla.org/firefox/downloads/file/4202634/i_dont_care_about_cookies.xpi"
default_area: "menupanel"
private_browsing: true
updates_disabled: false
"uBlock0@raymondhill.net": # Ublock origin
installation_mode: "normal_installed"
install_url: "https://addons.mozilla.org/firefox/downloads/file/4598854/ublock_origin-1.67.0.xpi"
default_area: "navbar"
private_browsing: true
updates_disabled: false
"treestyletab@piro.sakura.ne.jp": # I don't care about cookies
installation_mode: "normal_installed"
install_url: "https://addons.mozilla.org/firefox/downloads/file/4602712/tree_style_tab-4.2.7.xpi"
default_area: "navbar"
private_browsing: true
updates_disabled: false
"{9063c2e9-e07c-4c2c-9646-cfe7ca8d0498}": # Old Reddit redirect
installation_mode: "normal_installed"
install_url: "https://addons.mozilla.org/firefox/downloads/file/4526031/old_reddit_redirect-2.0.9.xpi"
default_area: "menupanel"
private_browsing: true
updates_disabled: false
FirefoxHome:
Search: false
TopSites: false
SponsoredTopSites: false
Highlights: false
Pocket: false
Stories: false
SponsoredPocket: false
SponsoredStories: false
Snippets: false
Locked: true
GenerativeAI:
Enabled: false
Chatbot: false
LinkPreviews: false
TabGroups: false
Homepage:
URL: "about:newtab"
StartPage: "previous-session"
MicrosoftEntraSSO: false
NewTabPage: false
NoDefaultBookmarks: true
OfferToSaveLogins: false
OverrideFirstRunPage: ""
PasswordManagerEnabled: false
Preferences:
"browser.translations.automaticallyPopup":
Value: false
Status: "default"
Type: "boolean"
"browser.aboutConfig.showWarning":
Value: false
Status: "default"
Type: "boolean"
"general.smoothScroll":
Value: true
Status: "default"
Type: "boolean"
# "Play DRM-controlled content"
"media.eme.enabled":
Value: true
Status: "default"
Type: "boolean"
# Restore last session on startup
# https://support.mozilla.org/de/questions/1235263
"browser.startup.page":
Value: 3
Status: "default"
Type: "number"
# reload the tabs properly when restoring
"browser.sessionstore.restore_on_demand":
Value: false
Status: "default"
Type: "boolean"
# "Check spelling as you type"
"layout.spellcheckDefault":
Value: 0
Status: "default"
Type: "number"
# remove ad tracking garbage
"dom.private-attribution.submission.enabled":
Value: false
Status: "default"
Type: "boolean"
# (Try to) disable automatic update, as firefox is pulling a Windows
"app.update.auto":
Value: false
Status: "default"
Type: "boolean"
"app.update.service.enabled":
Value: false
Status: "default"
Type: "boolean"
PromptForDownloadLocation: false
RequestedLocales:
- en-US
- de
SearchSuggestEnabled: false
ShowHomeButton: false
SkipTermsOfUse: true
UserMessaging:
ExtensionRecommendations: false
FeatureRecommendations: false
UrlbarInterventions: false
SkipOnboarding: true
MoreFromMozilla: false
FirefoxLabs: false
VisualSearchEnabled: false
- name: Firefox global policies
ansible.builtin.copy:
dest: "/etc/firefox/policies/policies.json"
owner: root
group: root
mode: "0644"
content: "{{ firefox_policy | to_nice_json }}"
become: true
become_user: root
- set_fact:
users: "{{ machine.users }}"
tags:
- always
- include_tasks: user.yml - include_tasks: user.yml
args: args:
apply: apply:
@@ -814,13 +1041,26 @@
tags: tags:
- user - user
with_items: "{{ users }}" with_items: "{{ users }}"
no_log: True # less spam no_log: true # less spam
loop_control: loop_control:
loop_var: user loop_var: user
tags: tags:
- always - always
- include_tasks: "{{ item }}"
with_first_found:
- files:
- "_machines/{{ ansible_facts['hostname'] }}-tasks.yml"
skip: true
tags:
- always
handlers: handlers:
- name: refresh package lists
community.general.pacman:
update_cache: true
become: true
- name: rebuild initrd - name: rebuild initrd
command: mkinitcpio -P command: mkinitcpio -P
become: true become: true

View File

@@ -1,12 +0,0 @@
#!/usr/bin/env bash
switch_back() {
screencfg --setup laptop-only
}
trap switch_back EXIT
xrandr --output eDP-1 --off --output DP-1 --auto # --mode 1920x1080
printf 'press ENTER or CTRL+C to switch back'
read -r _

View File

@@ -21,8 +21,10 @@ proctected=(
) )
for pkgbuild in pkgbuilds/*/PKGBUILD ; do for pkgbuild in pkgbuilds/*/PKGBUILD ; do
set +o nounset
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${pkgbuild}" source "${pkgbuild}"
set -o nounset
aurdeps+=("${depends[@]%%[<=>]*}" "${makedepends[@]%%[<=>]*}" "${checkdepends[@]%%[<=>]*}" "${pkgname}") aurdeps+=("${depends[@]%%[<=>]*}" "${makedepends[@]%%[<=>]*}" "${checkdepends[@]%%[<=>]*}" "${pkgname}")
done done
@@ -30,6 +32,7 @@ declare -a packages_to_remove=()
readarray -d $'\0' -t packages_to_remove < <(comm --zero-terminated -13 \ readarray -d $'\0' -t packages_to_remove < <(comm --zero-terminated -13 \
<(cat \ <(cat \
<(<_machines/"$(hostname --short)".yml yaml2json | jq --raw-output0 '(.additional_packages // [])[]') \
<(<packages.yml yaml2json | jq --raw-output0 'map(.archlinux) | flatten[]') \ <(<packages.yml yaml2json | jq --raw-output0 'map(.archlinux) | flatten[]') \
<(for dep in "${aurdeps[@]}" "${cpu_packages[@]}" "${gpu_packages[@]}" ; do printf '%s\0' "${dep}" ; done) \ <(for dep in "${aurdeps[@]}" "${cpu_packages[@]}" "${gpu_packages[@]}" ; do printf '%s\0' "${dep}" ; done) \
| while IFS= read -r -d $'\0' package; do | while IFS= read -r -d $'\0' package; do
@@ -59,12 +62,29 @@ readarray -d $'\0' -t packages_to_remove < <(comm --zero-terminated -13 \
printf '%s\0' "${package}" printf '%s\0' "${package}"
done) done)
packages_removed=0
if (( "${#packages_to_remove[@]}" > 0 )) ; then if (( "${#packages_to_remove[@]}" > 0 )) ; then
echo "found the following explicitly installed packages that are not configured:" echo "found the following explicitly installed packages that are not configured:"
for pkg in "${packages_to_remove[@]}" ; do for pkg in "${packages_to_remove[@]}" ; do
echo "${pkg}" echo "${pkg}"
done done
sudo pacman -Rcns "${packages_to_remove[@]}" "${@}" || exit $? sudo pacman -Rcns "${packages_to_remove[@]}" "${@}" || exit $?
packages_removed=1
fi
readarray -t orphans < <(pacman -Qdtq)
if (( "${#orphans[@]}" > 0 )) ; then
echo "found the following orphaned packages:"
for pkg in "${orphans[@]}" ; do
echo "${pkg}"
done
sudo pacman -Rcns "${orphans[@]}" "${@}" || exit $?
packages_removed=1
fi
if (( packages_removed)) ; then
exit 123 exit 123
fi fi

View File

@@ -1,4 +1,4 @@
post_commands = [ post_commands = [
"systemctl --user restart keyboard.service", "systemctl --user restart keyboard.service",
"systemctl --user restart touchpad.service", "systemctl --user restart touchpad.service",
] ]

53
scripts/garmin.sh Executable file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env bash
set -o nounset
gid=$(id -g)
uid=$(id -u)
DEV=/dev/disk/by-label/GARMIN
MOUNTPOINT=/mnt
MOUNTOPTS="uid=${uid},gid=${gid}"
SYNC_FOLDERS=(
Activity
Settings
Courses
Records
Totals
Workouts
)
RSYNCOPTS=(
--verbose
--recursive
--checksum
--itemize-changes
)
if [[ ! -e "${DEV}" ]]; then
printf "no garmin device present\n" >&2
exit 1
fi
mnt=$(findmnt --noheadings --first-only --options "${MOUNTOPTS}" --output TARGET "${DEV}")
if [[ -n "${mnt}" ]]; then
if [[ "${mnt}" == "${MOUNTPOINT}" ]]; then
printf '%s already correctly mounted\n' "${DEV}"
else
printf '%s already mounted somewhere else, aborting\n' "${DEV}" >&2
exit 1
fi
else
if findmnt --mountpoint "${MOUNTPOINT}" >/dev/null; then
printf "%s already in use\n" "${MOUNTPOINT}" >&2
exit 1
else
sudo mount -o "${MOUNTOPTS}" "${DEV}" "${MOUNTPOINT}"
fi
fi
for folder in "${SYNC_FOLDERS[@]}"; do
rsync "${RSYNCOPTS[@]}" "${MOUNTPOINT}/GARMIN/${folder}/" "${HOME}/sync/garmin/${folder}/"
done

84
test.sh
View File

@@ -79,8 +79,6 @@ qemuopts=(
"-accel" "kvm" "-accel" "kvm"
"-drive" "if=pflash,format=raw,readonly=true,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd"
"-drive" "if=pflash,format=raw,file=${tmpdir}/efivars.fd"
"-machine" "q35,smm=on,acpi=on" "-machine" "q35,smm=on,acpi=on"
"-smp" "cpus=8,sockets=1,cores=8,threads=1" "-smp" "cpus=8,sockets=1,cores=8,threads=1"
"-cpu" "host" "-cpu" "host"
@@ -129,6 +127,10 @@ send_mon() {
install_from_iso() { install_from_iso() {
local hostname="${1}" local hostname="${1}"
shift shift
local lukscfg="${1}"
shift
local homecfg="${1}"
shift
local hostqemuopts=("$@") local hostqemuopts=("$@")
rm -rf "${tmpdir:?}"/* rm -rf "${tmpdir:?}"/*
@@ -188,10 +190,23 @@ EOF
mkdir /repo/ mkdir /repo/
mount /dev/disk/by-label/REPO /repo/ mount /dev/disk/by-label/REPO /repo/
printf 'lukspw\nlukspw\nrootpw\nrootpw\n' | \ if [[ "${lukscfg}" == "luks" ]] ; then
/repo/install_scripts/"${hostname}".sh printf 'lukspw\nlukspw\nrootpw\nrootpw\n' | \
/repo/install_scripts/"${hostname}".sh
mount /dev/mapper/vgbase-root /mnt
if [[ "${homecfg}" == "separate" ]] ; then
mount /dev/mapper/vgbase-home /mnt/home
fi
else
printf 'rootpw\nrootpw\n' | \
/repo/install_scripts/"${hostname}".sh
mount /dev/disk/by-partlabel/root /mnt
if [[ "${homecfg}" == "separate" ]] ; then
mount /dev/disk/by-partlabel/home /mnt/home
fi
fi
mount /dev/mapper/vgbase-root /mnt
cat << SPECIALS > /tmp/specials.sh cat << SPECIALS > /tmp/specials.sh
if [[ "\\\$(tty)" == "/dev/tty1" ]] ; then if [[ "\\\$(tty)" == "/dev/tty1" ]] ; then
@@ -211,7 +226,7 @@ SPECIALS
rsync -rl /repo/ /mnt/var/lib/dotfiles/ rsync -rl /repo/ /mnt/var/lib/dotfiles/
umount /mnt umount --recursive /mnt
poweroff poweroff
EOF EOF
@@ -222,6 +237,10 @@ EOF
configure_new_system() { configure_new_system() {
local hostname="${1}" local hostname="${1}"
shift shift
local lukscfg="${1}"
shift
local homecfg="${1}"
shift
local hostqemuopts=("${@}") local hostqemuopts=("${@}")
opts=( opts=(
@@ -233,36 +252,71 @@ configure_new_system() {
qemu-system-x86_64 -name "${hostname}" "${qemuopts[@]}" "${hostqemuopts[@]}" "${opts[@]}" & qemu-system-x86_64 -name "${hostname}" "${qemuopts[@]}" "${hostqemuopts[@]}" "${opts[@]}" &
# 5s for grub timeout, 5s for kernel boot if [[ "${lukscfg}" == "luks" ]] ; then
echo waiting for luks password prompt ... # 5s for grub timeout, 5s for kernel boot
sleep 10s echo waiting for luks password prompt ...
echo 'lukspw' | send_mon "${mon_sock}" sleep 10s
echo 'lukspw' | send_mon "${mon_sock}"
fi
echo waiting for boot ... echo waiting for boot ...
sleep 10s sleep 10s
wait wait
} }
machines=(ares neptune) declare -A machinecfg
machinecfg["ares"]="luks|combined"
machinecfg["neptune"]="luks|combined"
machinecfg["dionysus"]="luks|combined"
machinecfg["hera"]="noluks|separate"
if (($# > 0)); then if (($# > 0)); then
machines=("${@}") machines=("${@}")
else
machines=("${!machinecfg[@]}")
fi fi
download_iso download_iso
for hostname in "${machines[@]}"; do for hostname in "${machines[@]}"; do
IFS='|' read -r -a cfg <<< "${machinecfg[${hostname}]}"
lukscfg="${cfg[0]}"
homecfg="${cfg[1]}"
case "${hostname}" in case "${hostname}" in
ares) ares)
hostqemuopts=("-device" "ide-hd,drive=root") hostqemuopts=(
"-device" "ide-hd,drive=root"
"-drive" "if=pflash,format=raw,readonly=true,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd"
"-drive" "if=pflash,format=raw,file=${tmpdir}/efivars.fd"
)
;; ;;
neptune) neptune)
hostqemuopts=("-device" "nvme,serial=rootnvme,drive=root") hostqemuopts=(
"-device" "nvme,serial=rootnvme,drive=root"
"-drive" "if=pflash,format=raw,readonly=true,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd"
"-drive" "if=pflash,format=raw,file=${tmpdir}/efivars.fd"
)
;;
dionysus)
hostqemuopts=(
"-device" "nvme,serial=rootnvme,drive=root"
"-drive" "if=pflash,format=raw,readonly=true,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd"
"-drive" "if=pflash,format=raw,file=${tmpdir}/efivars.fd"
)
;;
hera)
hostqemuopts=(
"-device" "nvme,serial=rootnvme,drive=root"
"-drive" "if=pflash,format=raw,readonly=true,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd"
"-drive" "if=pflash,format=raw,file=${tmpdir}/efivars.fd"
)
;; ;;
*) *)
printf "unknown hostname: %s\n" "${hostname}" >&2
exit 1 exit 1
;; ;;
esac esac
[[ ! "${hostqemuopts[*]}" ]] && exit 1 [[ ! "${hostqemuopts[*]}" ]] && exit 1
install_from_iso "${hostname}" "${hostqemuopts[@]}" install_from_iso "${hostname}" "${lukscfg}" "${homecfg}" "${hostqemuopts[@]}"
configure_new_system "${hostname}" "${hostqemuopts[@]}" configure_new_system "${hostname}" "${lukscfg}" "${homecfg}" "${hostqemuopts[@]}"
done done

View File

@@ -1,8 +1,19 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -o nounset
set -o errexit
for pkg in pkgbuilds/* ; do for pkg in pkgbuilds/* ; do
printf "checking %s\n" "${pkg}" if [[ -n "$(builtin cd "${pkg}" && git rev-parse --show-superproject-working-tree)" ]] ; then
git submodule update --remote "${pkg}" printf "checking git submodule %s\n" "${pkg}"
git submodule update --remote "${pkg}"
else
printf "checking local package %s\n" "${pkg}"
(
builtin cd "${pkg}" || exit 1
makepkg --nodeps --nobuild --noextract --cleanbuild
)
fi
if git status --porcelain "${pkg}" | grep -q . ; then if git status --porcelain "${pkg}" | grep -q . ; then
git add "${pkg}" git add "${pkg}"
git commit -m "aur: Update $(basename "${pkg}")" git commit -m "aur: Update $(basename "${pkg}")"

351
user.yml
View File

@@ -1,11 +1,11 @@
- name: base user configuration ---
- name: Base user configuration
tags: [user:base] tags: [user:base]
block: block:
- set_fact: - ansible.builtin.set_fact:
user_groups: user_groups:
- libvirt - libvirt
- wheel - wheel
- vboxusers
- wireshark - wireshark
- docker - docker
- sudonopw - sudonopw
@@ -13,27 +13,28 @@
- kvm - kvm
- video - video
- name: create user group - name: Create user group
group: ansible.builtin.group:
name: "{{ user.name }}" name: "{{ user.name }}"
state: present state: present
become: true become: true
become_user: root become_user: root
- name: create user - name: Create user
user: ansible.builtin.user:
name: "{{ user.name }}" name: "{{ user.name }}"
state: present state: present
home: "/home/{{ user.name }}" home: "/home/{{ user.name }}"
create_home: true create_home: true
groups: "{{ [user.name, 'dotfiles'] + user_groups }}" group: "{{ user.name }}"
groups: "{{ ['dotfiles'] + user_groups }}"
shell: /usr/bin/zsh shell: /usr/bin/zsh
skeleton: /dev/null skeleton: /dev/null
become: true become: true
become_user: root become_user: root
- name: create systemd directory - name: Create systemd directory
file: ansible.builtin.file:
state: directory state: directory
path: "{{ item }}" path: "{{ item }}"
owner: "{{ user.name }}" owner: "{{ user.name }}"
@@ -43,39 +44,42 @@
- "/home/{{ user.name }}/.config/systemd/" - "/home/{{ user.name }}/.config/systemd/"
- "/home/{{ user.name }}/.config/systemd/user/" - "/home/{{ user.name }}/.config/systemd/user/"
- name: create directory for getty autologin - name: Configure autologin
file: when: user.autologin|default(true) is sameas True
state: directory block:
path: /etc/systemd/system/getty@tty{{ user.vt }}.service.d - name: Create directory for getty autologin
owner: root ansible.builtin.file:
group: root state: directory
mode: '0755' path: /etc/systemd/system/getty@tty{{ user.vt }}.service.d
become: true owner: root
become_user: root group: root
mode: "0755"
become: true
become_user: root
- name: enable getty autologin - name: Enable getty autologin
copy: ansible.builtin.copy:
dest: /etc/systemd/system/getty@tty{{ user.vt }}.service.d/override.conf dest: /etc/systemd/system/getty@tty{{ user.vt }}.service.d/override.conf
owner: root owner: root
group: root group: root
mode: '0644' mode: "0644"
content: | content: |
[Service] [Service]
ExecStart= ExecStart=
ExecStart=-/sbin/agetty --autologin {{ user.name }} --noclear %I $TERM ExecStart=-/sbin/agetty --autologin {{ user.name }} --noclear %I $TERM
become: true become: true
become_user: root become_user: root
- name: configure dotfiles - name: Configure dotfiles
tags: tags:
- user:dotfiles - user:dotfiles
block: block:
- name: load dotfile list - name: Load dotfile list
include_vars: ansible.builtin.include_vars:
file: dotfiles.yml file: dotfiles.yml
- name: get state of empty directories - name: Get state of empty directories
stat: ansible.builtin.stat:
path: ~/{{ item.name }} path: ~/{{ item.name }}
register: empty_dir_stat register: empty_dir_stat
with_items: "{{ empty_directories }}" with_items: "{{ empty_directories }}"
@@ -83,8 +87,8 @@
loop_control: loop_control:
label: "{{ item.name }}" label: "{{ item.name }}"
- name: remove symlinks - name: Remove symlinks
file: ansible.builtin.file:
path: "{{ item.stat.path }}" path: "{{ item.stat.path }}"
state: absent state: absent
when: item.stat.exists and item.stat.islnk when: item.stat.exists and item.stat.islnk
@@ -92,8 +96,8 @@
loop_control: loop_control:
label: "{{ item.item.name }}" label: "{{ item.item.name }}"
- name: create empty directories for dotfiles - name: Create empty directories for dotfiles
file: ansible.builtin.file:
state: directory state: directory
path: ~/{{ item.name }} path: ~/{{ item.name }}
mode: "{{ item.mode | default('0755') }}" mode: "{{ item.mode | default('0755') }}"
@@ -101,8 +105,8 @@
loop_control: loop_control:
label: "{{ item.name }}" label: "{{ item.name }}"
- name: link this folder to ~/.dotfiles - name: Link this folder to ~/.dotfiles
file: ansible.builtin.file:
state: link state: link
force: true force: true
follow: false follow: false
@@ -113,8 +117,8 @@
become: true become: true
become_user: root become_user: root
- name: get state of copy targets - name: Get state of copy targets
stat: ansible.builtin.stat:
path: ~/{{ item.to }} path: ~/{{ item.to }}
register: copy_stat register: copy_stat
when: not item.template|default(false) when: not item.template|default(false)
@@ -123,8 +127,8 @@
loop_control: loop_control:
label: "{{ item.to }}" label: "{{ item.to }}"
- name: remove invalid copy target (symlinks) - name: Remove invalid copy target (symlinks)
file: ansible.builtin.file:
path: "{{ item.stat.path }}" path: "{{ item.stat.path }}"
state: absent state: absent
when: when:
@@ -135,10 +139,10 @@
loop_control: loop_control:
label: "{{ item.item.from }}" label: "{{ item.item.from }}"
- name: make sure target directories exist - name: Make sure target directories exist
file: ansible.builtin.file:
state: directory state: directory
path: "{{ (['/home', user.name, item.to]|join('/')) | dirname }}" path: "{{ (['/home', user.name, item.to] | join('/')) | dirname }}"
owner: "{{ user.name }}" owner: "{{ user.name }}"
group: "{{ user.name }}" group: "{{ user.name }}"
with_items: "{{ dotfiles }}" with_items: "{{ dotfiles }}"
@@ -147,8 +151,8 @@
loop_control: loop_control:
label: "{{ item.to }}" label: "{{ item.to }}"
- name: copy dotfiles - name: Copy dotfiles
copy: ansible.builtin.copy:
dest: "/home/{{ user.name }}/{{ item.to }}" dest: "/home/{{ user.name }}/{{ item.to }}"
src: /var/lib/dotfiles/{{ item.from }} src: /var/lib/dotfiles/{{ item.from }}
owner: "{{ user.name }}" owner: "{{ user.name }}"
@@ -160,8 +164,8 @@
loop_control: loop_control:
label: "{{ item.to }}" label: "{{ item.to }}"
- name: copy directories - name: Copy directories
synchronize: ansible.posix.synchronize:
dest: "/home/{{ user.name }}/{{ item.to }}/" dest: "/home/{{ user.name }}/{{ item.to }}/"
src: /var/lib/dotfiles/{{ item.from }}/ src: /var/lib/dotfiles/{{ item.from }}/
archive: false archive: false
@@ -180,8 +184,8 @@
loop_control: loop_control:
label: "{{ item.to }}" label: "{{ item.to }}"
- name: apply directory permissions - name: Apply directory permissions
file: ansible.builtin.file:
dest: "/home/{{ user.name }}/{{ item.to }}/" dest: "/home/{{ user.name }}/{{ item.to }}/"
owner: "{{ user.name }}" owner: "{{ user.name }}"
group: "{{ user.name }}" group: "{{ user.name }}"
@@ -193,8 +197,8 @@
loop_control: loop_control:
label: "{{ item.to }}" label: "{{ item.to }}"
- name: get state of template targets - name: Get state of template targets
stat: ansible.builtin.stat:
path: ~/{{ item.to }} path: ~/{{ item.to }}
register: template_stat register: template_stat
when: item.template|default(false) when: item.template|default(false)
@@ -203,8 +207,8 @@
loop_control: loop_control:
label: "{{ item.to }}" label: "{{ item.to }}"
- name: remove invalid template target (directory or symlink) - name: Remove invalid template target (directory or symlink)
file: ansible.builtin.file:
path: "{{ item.stat.path }}" path: "{{ item.stat.path }}"
state: absent state: absent
when: when:
@@ -215,8 +219,8 @@
loop_control: loop_control:
label: "{{ item.item.to }}" label: "{{ item.item.to }}"
- name: deploy dotfiles templates - name: Deploy dotfiles templates
template: ansible.builtin.template:
src: /var/lib/dotfiles/{{ item.from }}.j2 src: /var/lib/dotfiles/{{ item.from }}.j2
dest: "/home/{{ user.name }}/{{ item.to }}" dest: "/home/{{ user.name }}/{{ item.to }}"
owner: "{{ user.name }}" owner: "{{ user.name }}"
@@ -229,35 +233,35 @@
loop_control: loop_control:
label: "{{ item.to }}" label: "{{ item.to }}"
- name: remove dotfiles - name: Remove dotfiles
file: ansible.builtin.file:
state: absent state: absent
path: "/home/{{ user.name }}/{{ item }}" path: "/home/{{ user.name }}/{{ item }}"
loop: "{{ dotfiles_remove }}" loop: "{{ dotfiles_remove }}"
- name: create directories - name: Create directories
file: ansible.builtin.file:
state: directory state: directory
path: "{{ item }}" path: "{{ item }}"
with_items: with_items:
- ~/tmp - ~/tmp
- name: stat ~/bin - name: Stat ~/bin
stat: ansible.builtin.stat:
path: "/home/{{ user.name }}/bin" path: "/home/{{ user.name }}/bin"
register: bin_stat register: bin_stat
check_mode: false check_mode: false
- name: remove ~/bin if not a link - name: Remove ~/bin if not a link
file: ansible.builtin.file:
state: absent state: absent
path: "/home/{{ user.name }}/bin" path: "/home/{{ user.name }}/bin"
when: when:
- bin_stat.stat.exists - bin_stat.stat.exists
- not bin_stat.stat.islnk - not bin_stat.stat.islnk
- name: link bin directory - name: Link bin directory
file: ansible.builtin.file:
state: link state: link
force: true force: true
follow: false follow: false
@@ -266,124 +270,90 @@
owner: "{{ user.name }}" owner: "{{ user.name }}"
group: "{{ user.name }}" group: "{{ user.name }}"
- name: vim - name: Firefox
tags:
- user:vim
block:
- name: install vim plugins
command: nvim --headless +PlugInstall +qall
register: vim_plugin_install
changed_when: vim_plugin_install.stderr != ""
- name: update vim plugins
command: nvim --headless +PlugUpdate +qall
register: vim_plugin_update
changed_when: vim_plugin_update.stderr != ""
- name: firefox
tags: tags:
- user:firefox - user:firefox
block: block:
- name: create firefox directories - name: Create firefox base directories
firefox_profile: ansible.builtin.file:
name: "{{ item.key }}" path: "{{ item }}"
loop: "{{ user.firefox_profiles | dict2items }}"
check_mode: false
register: firefox_profile_names
- set_fact:
firefox_preferences:
browser.aboutConfig.showWarning: false
extensions.pocket.enabled: false
toolkit.legacyUserProfileCustomizations.stylesheets: true
browser.contentblocking.category: "strict"
browser.newtabpage.enabled: false
browser.startup.homepage: "about:blank"
privacy.trackingprotection.enabled: true
privacy.trackingprotection.socialtracking.enabled: true
general.smoothScroll: false
# Restore last session on startup
# https://support.mozilla.org/de/questions/1235263
browser.startup.page: 3
# reload the tabs properly when restoring
browser.sessionstore.restore_on_demand: false
# "Play DRM-controlled content"
media.eme.enabled: true
# "Recommend (extensions|features) as you browse"
browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons: false
browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features: false
# "Ask to save logins and passwords for websites"
signon.rememberSignons: false
# "Allow Firefox to make personalized extension recommendations"
browser.discovery.enabled: false
# "Allow Firefox to install and run studies"
app.shield.optoutstudies.enabled: false
# "Check spelling as you type"
layout.spellcheckDefault: 0
# Ask for download directory
browser.download.useDownloadDir: false
# (Try to) disable automatic update, as firefox is pulling a Windows
app.update.auto: false
app.update.service.enabled: false
# remove this camera / microphone overlay when in calls or similar
privacy.webrtc.legacyGlobalIndicator: false
# remove ad tracking garbage
dom.private-attribution.submission.enabled: false
- include_role:
name: firefox
vars:
firefox_profiles: "{{ {item.key: item.value} | combine({item.key: {'preferences': firefox_preferences}}, recursive=True) }}"
loop: "{{ user.firefox_profiles | dict2items }}"
when: not ansible_check_mode
- name: firefox - create chrome directory
file:
path: "{{ item.profile_path }}/chrome/"
state: directory state: directory
mode: '0755' mode: "0755"
with_items: "{{ firefox_profile_names.results }}" loop:
when: not ansible_check_mode - "~/.mozilla/"
loop_control: - "~/.mozilla/firefox/"
label: "{{ item.profile_path }}"
- name: firefox - configure firefox custom css - name: Create firefox profile directories
copy: ansible.builtin.file:
dest: "{{ item.profile_path }}/chrome/userChrome.css" path: "~/.mozilla/firefox/profile-{{ item.key }}"
state: directory
mode: "0755"
loop: "{{ user.firefox_profiles | dict2items }}"
loop_control:
label: "{{ item.key }}"
- name: Create chrome directory
ansible.builtin.file:
path: "~/.mozilla/firefox/profile-{{ item.key }}/chrome/"
state: directory
mode: "0755"
loop: "{{ user.firefox_profiles | dict2items }}"
loop_control:
label: "{{ item.key }}"
- name: Configure firefox custom css
ansible.builtin.copy:
dest: "~/.mozilla/firefox/profile-{{ item.key }}/chrome/userChrome.css"
# from https://www.kvakil.me/posts/2023-09-12-my-tree-style-tab-configuration.html
content: | content: |
#TabsToolbar { // Hide the title bar.
visibility: collapse !important;
}
#titlebar { #titlebar {
appearance: none !important;
height: 0px;
}
#titlebar > #toolbar-menubar {
margin-top: 0px;
}
// Hide regular tab toolbar.
#main-window[tabsintitlebar="true"]:not([extradragspace="true"]) #TabsToolbar > .toolbar-items {
opacity: 0;
pointer-events: none;
}
#main-window:not([tabsintitlebar="true"]) #TabsToolbar {
visibility: collapse !important; visibility: collapse !important;
} }
#sidebar-header {
visibility: collapse !important; // Hide the side toolbar noise.
#TabsToolbar {
min-width: 0 !important;
min-height: 0 !important;
}
#TabsToolbar > .titlebar-buttonbox-container {
display: block;
position: absolute;
top: 12px;
left: 0px;
}
#sidebar-box[sidebarcommand="treestyletab_piro_sakura_ne_jp-sidebar-action"] #sidebar-header {
display: none;
} }
when: when:
- not ansible_check_mode - item.value.manage_css is sameas True
- user.firefox_profiles[item.profile_name].manage_css is sameas True loop: "{{ user.firefox_profiles | dict2items }}"
with_items: "{{ firefox_profile_names.results }}"
loop_control: loop_control:
label: "{{ item.profile_path }}" label: "{{ item.key }}"
- name: handle user units - name: Handle user units
tags: tags:
- user:units - user:units
block: block:
- name: link user service files - name: Link user service files
file: ansible.builtin.file:
state: link state: link
force: true force: true
follow: false follow: false
@@ -393,12 +363,12 @@
group: "{{ user.name }}" group: "{{ user.name }}"
with_fileglob: /var/lib/dotfiles/services/* with_fileglob: /var/lib/dotfiles/services/*
- name: handle autostart units - name: Handle autostart units
tags: tags:
- user:autostart - user:autostart
block: block:
- name: create systemd user directory - name: Create systemd user directory
file: ansible.builtin.file:
state: directory state: directory
path: ~/{{ item }} path: ~/{{ item }}
loop: loop:
@@ -406,8 +376,8 @@
- .config/systemd/ - .config/systemd/
- .config/systemd/user/ - .config/systemd/user/
- name: link autostart service files - name: Link autostart service files
file: ansible.builtin.file:
state: link state: link
force: true force: true
follow: false follow: false
@@ -417,21 +387,21 @@
group: "{{ user.name }}" group: "{{ user.name }}"
with_fileglob: /var/lib/dotfiles/autostart/services/* with_fileglob: /var/lib/dotfiles/autostart/services/*
- name: get state of autostart.target - name: Get state of autostart.target
stat: ansible.builtin.stat:
path: "/home/{{ user.name }}/.config/systemd/user/autostart.target" path: "/home/{{ user.name }}/.config/systemd/user/autostart.target"
register: autostart_target_stat register: autostart_target_stat
- name: remove invalid autostart.target - name: Remove invalid autostart.target
file: ansible.builtin.file:
path: "/home/{{ user.name }}/.config/systemd/user/autostart.target" path: "/home/{{ user.name }}/.config/systemd/user/autostart.target"
state: absent state: absent
when: when:
- autostart_target_stat.stat.exists - autostart_target_stat.stat.exists
- not autostart_target_stat.stat.isreg - not autostart_target_stat.stat.isreg
- name: deploy autostart.target - name: Deploy autostart.target
template: ansible.builtin.template:
src: ./autostart/autostart.target.j2 src: ./autostart/autostart.target.j2
dest: "/home/{{ user.name }}/.config/systemd/user/autostart.target" dest: "/home/{{ user.name }}/.config/systemd/user/autostart.target"
owner: "{{ user.name }}" owner: "{{ user.name }}"
@@ -439,20 +409,19 @@
force: true force: true
follow: false follow: false
- name: gpg - name: Gpg
tags: tags:
- user:gpg - user:gpg
when: user.gpg_key is defined
block: block:
- name: import gpg key - name: Import gpg key
command: gpg --import ./gpgkeys/{{ user.gpg_key.email }}.gpg.asc ansible.builtin.command: gpg --import ./gpgkeys/{{ user.gpg_key.email }}.gpg.asc
register: gpg_import_output register: gpg_import_output
changed_when: not ("unchanged" in gpg_import_output.stderr) changed_when: not ("unchanged" in gpg_import_output.stderr)
- name: trust gpg key - name: Trust gpg key
shell: "gpg --import-ownertrust <<< {{ user.gpg_key.fingerprint }}:6" ansible.builtin.shell: "gpg --import-ownertrust <<< {{ user.gpg_key.fingerprint }}:6"
args: args:
executable: /bin/bash # required for <<< executable: /bin/bash # required for <<<
register: gpg_trust_output register: gpg_trust_output
changed_when: gpg_trust_output.stderr_lines|length > 0 changed_when: gpg_trust_output.stderr_lines|length > 0
when: user.gpg_key is defined

320
vim/vimrc
View File

@@ -1,320 +0,0 @@
set nocompatible
filetype off
call plug#begin('~/.local/share/nvim/plugged')
" === plugins ===
function! Cond(Cond, ...)
let opts = get(a:000, 0, {})
return a:Cond ? opts : extend(opts, { 'on': [], 'for': [] })
endfunction
" editing plugins
Plug 'godlygeek/tabular', Cond(!exists('g:vscode'))
Plug 'nathanaelkane/vim-indent-guides', Cond(!exists('g:vscode'))
Plug 'tpope/vim-commentary', Cond(!exists('g:vscode'))
Plug 'airblade/vim-gitgutter', Cond(!exists('g:vscode'))
" ui
Plug 'sickill/vim-monokai', Cond(!exists('g:vscode'))
Plug 'itchyny/lightline.vim', Cond(!exists('g:vscode'))
" lang integrations
Plug 'lepture/vim-jinja', Cond(!exists('g:vscode'))
Plug 'fatih/vim-go', Cond(!exists('g:vscode'))
Plug 'hashivim/vim-terraform', Cond(!exists('g:vscode'))
Plug 'editorconfig/editorconfig-vim', Cond(!exists('g:vscode'))
Plug 'rust-lang/rust.vim', Cond(!exists('g:vscode'))
Plug 'rodjek/vim-puppet', Cond(!exists('g:vscode'))
" helpers
"" distraction free writing
Plug 'junegunn/limelight.vim', Cond(!exists('g:vscode'))
Plug 'junegunn/goyo.vim', Cond(!exists('g:vscode'))
Plug 'reedes/vim-pencil', Cond(!exists('g:vscode'))
"" markdown
Plug 'suan/vim-instant-markdown', Cond(!exists('g:vscode'))
Plug 'dense-analysis/ale', Cond(!exists('g:vscode'))
Plug 'neoclide/coc.nvim', Cond(!exists('g:vscode'), {'branch': 'release'})
call plug#end()
filetype plugin indent on
" == formatting ==
set tabstop=4
set smarttab
set softtabstop=4
set shiftround
set shiftwidth=4
set autoindent
set expandtab
set smartindent
set formatoptions=tcqjron
"set formatoptions=
" == ui ==
set cursorline
set showcmd
set number
set wildmode=list:longest
set lazyredraw
set wildmenu
set noshowmatch
set colorcolumn=80
set laststatus=2
set matchtime=5
set mouse=a
set mousehide
set noerrorbells
set noshowmode
set numberwidth=2
set relativenumber
set shortmess=rtiF
set ruler
set scrolloff=7
set title
set titlestring=""
set ttyfast
" == searching ==
set hlsearch
set incsearch
set gdefault
set ignorecase
set magic
set smartcase
" == folding ==
set foldenable
set foldmethod=indent
set foldnestmax=2
set foldlevelstart=2
" == backups ==
set nobackup
set backupcopy=no
set nowritebackup
" == swap ==
set swapfile
set updatecount=200
set updatetime=300
" == undo ==
set undolevels=1000
set undoreload=10000
set undodir=~/.vim/undo
set undofile
" == environment / directories ==
set autochdir
set directory=/var/tmp,/tmp
set viewdir=~/.vim/view
" == misc ==
set autoread
set confirm
set encoding=utf-8
set history=1000
set modeline
set modelines=5
set notildeop
set wildignore=*.swp,*.bak,*.pyc,*~,*.o
set hidden
" == editing ==
set backspace=indent,eol,start
" set esckeys
set matchpairs=(:),{:},[:],<:>
set notimeout
set ttimeout
set timeoutlen=1000
set ttimeoutlen=0
set virtualedit=block
set whichwrap=b,s
" == line breaking ==
set linebreak
set wrap
set wrapscan
" == to use guicolors in terminal ==
set termguicolors
" === keybinds ===
set pastetoggle=<F11>
set signcolumn=yes
let maplocalleader = "ö"
let mapleader = "\<Space>"
" map <leader>w: w!<cr>
" nnoremap <leader>w :w<CR>
nmap <leader><leader> za
map , :
vnoremap <silent> y y`]
vnoremap <silent> p p`]
nnoremap <silent> p p`]
nnoremap <leader>, :nohlsearch<CR>
noremap gV `[v`]
map Y y$
map j gj
map k gk
map N Nzz
map n nzz
inoremap jj <ESC>
" no more ex mode
nnoremap Q <nop>
" Use // in visual mode to search for selection
" https://vim.fandom.com/wiki/Search_for_visually_selected_text
vnoremap // y/\V<C-R>=escape(@",'/\')<CR><CR>
if exists('g:vscode')
xmap gc <Plug>VSCodeCommentary
nmap gc <Plug>VSCodeCommentary
omap gc <Plug>VSCodeCommentary
nmap gcc <Plug>VSCodeCommentaryLine
else
nnoremap <C-h> <C-w>h
nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-l> <C-w>l
nmap <C-n> :bnext<CR>
nmap <C-p> :bprev<CR>
nnoremap <leader>m :InstantMarkdownPreview<CR>
nnoremap <leader>u :GundoToggle<CR>
nnoremap <leader>d :diffupdate<CR>
nmap <F9> :Goyo<CR>:TogglePencil<CR>
nmap <leader>w :Goyo<CR>:TogglePencil<CR>:set colorcolumn=<CR>
nmap <leader>c :%w !xclip -selection clipboard<CR>
nmap <leader>x :r !xclip -out -selection -clipboard<CR><CR>
nmap <leader>f :Autoformat<CR>
nnoremap <leader>v <C-w>v<C-w>l
syntax enable
silent! colorscheme monokai
highlight Comment guifg=#64d86b
highlight SpecialComment guifg=#64d86b
highlight Todo guibg=#a9ebad
let g:lightline = {
\ 'colorscheme': 'wombat',
\ 'active': {
\ 'left': [ [ 'mode', 'paste' ],
\ [ 'readonly', 'filename', 'modified', 'helloworld' ] ],
\ 'right': [ [ 'gitbranch' ],
\ [ 'lineinfo' ],
\ [ 'percent' ],
\ [ 'fileformat', 'fileencoding', 'filetype', 'charvaluehex' ],
\ [ 'directory' ] ],
\ },
\ 'component_function': {
\ 'gitbranch': 'fugitive#head',
\ 'directory': 'LightLineFilename',
\ },
\ 'component': {
\ },
\ }
function! LightLineFilename()
return fnamemodify(expand('%F'), ":~:h")
endfunction
" == pencil ==
let g:pencil#textwidth = 80
let g:pencil#autoformat = 1
let g:pencil#wrapModeDefault = 'hard'
let g:pencil#map#suspend_af = 'K'
" == goyo ==
let g:goyo_width = 100
let g:goyo_height = "90%"
let g:goyo_linenr = 0
autocmd! User GoyoEnter Limelight
autocmd! User GoyoLeave Limelight!
" == limelight ==
let g:limelight_default_coefficient = 0.5
let g:ale_linters = {'rust': ['rust-analyzer']}
" === functions ===
function! DeleteTrailingWS()
exe "normal mz"
%s/\s\+$//e
exe "normal `z"
endfunction
autocmd BufWritePre * :call DeleteTrailingWS()
autocmd FileType yaml set shiftwidth=2
autocmd FileType toml set shiftwidth=2
autocmd FileType html setl shiftwidth=2
let g:instant_markdown_autostart = 0
let g:terraform_align = 1
let g:terraform_fmt_on_save=1
let g:rustfmt_autosave = 1
inoremap <silent><expr> <TAB>
\ coc#pum#visible() ? coc#pum#next(1) :
\ CheckBackspace() ? "\<Tab>" :
\ coc#refresh()
inoremap <expr><S-TAB> coc#pum#visible() ? coc#pum#prev(1) : "\<C-h>"
" Make <CR> to accept selected completion item or notify coc.nvim to format
" <C-g>u breaks current undo, please make your own choice.
inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm()
\: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
function! CheckBackspace() abort
let col = col('.') - 1
return !col || getline('.')[col - 1] =~# '\s'
endfunction
" Use <c-space> to trigger completion.
if has('nvim')
inoremap <silent><expr> <c-space> coc#refresh()
else
inoremap <silent><expr> <c-@> coc#refresh()
endif
if has('nvim')
inoremap <silent><expr> <c-space> coc#refresh()
else
inoremap <silent><expr> <c-@> coc#refresh()
endif
" https://stackoverflow.com/a/8585343
map <leader>q :bp<bar>sp<bar>bn<bar>bd<CR>
nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)
endif

View File

@@ -8,7 +8,7 @@ export PATH="${HOME}/bin:${PATH}"
export EDITOR="helix" export EDITOR="helix"
export VISUAL="helix" export VISUAL="helix"
export BROWSER="firefox" export BROWSER="firefox-default"
export PAGER="less" export PAGER="less"
export LESS="FRX" export LESS="FRX"
@@ -69,6 +69,20 @@ export AWS_CONFIG_FILE="$XDG_CONFIG_HOME"/aws/config
export XINITRC="$XDG_CONFIG_HOME"/xinitrc export XINITRC="$XDG_CONFIG_HOME"/xinitrc
export PSQLRC="$XDG_CONFIG_HOME/psqlrc"
export PSQL_HISTORY="$XDG_STATE_HOME/psql_history"
export PGPASSFILE="$XDG_CONFIG_HOME/pgpass"
export PGSERVICEFILE="$XDG_CONFIG_HOME/pg_service.conf"
export REDISCLI_HISTFILE="$XDG_DATA_HOME"/rediscli_history
export REDISCLI_RCFILE="$XDG_CONFIG_HOME"/redisclirc
export PYTHON_HISTORY=$XDG_STATE_HOME/python_history
export PYTHONPYCACHEPREFIX=$XDG_CACHE_HOME/python
# bash-specific
export HISTFILE="$XDG_STATE_HOME"/bash_history
umask 0022 umask 0022
{% set env = machine.environment | combine(user.environment) %} {% set env = machine.environment | combine(user.environment) %}
@@ -80,9 +94,9 @@ feature_dir="${XDG_RUNTIME_DIR}/features/"
rm -rf "${feature_dir}"/ rm -rf "${feature_dir}"/
mkdir -p "${feature_dir}" mkdir -p "${feature_dir}"
[[ $MACHINE_HAS_NEXTCLOUD == "true" ]] && touch "${feature_dir}"/nextcloud [[ $MACHINE_HAS_NEXTCLOUD == "true" ]] && touch "${feature_dir}"/nextcloud
[[ $MACHINE_HAS_KEEPASSXC == "true" ]] && touch "${feature_dir}"/keepassxc [[ $MACHINE_HAS_KEEPASSXC == "true" ]] && touch "${feature_dir}"/keepassxc
[[ $MACHINE_HAS_STEAM == "true" ]] && touch "${feature_dir}"/steam [[ $MACHINE_HAS_STEAM == "true" ]] && touch "${feature_dir}"/steam
[[ $MACHINE_TYPE == "laptop" ]] && touch "${feature_dir}"/machine_is_laptop [[ $MACHINE_TYPE == "laptop" ]] && touch "${feature_dir}"/machine_is_laptop

View File

@@ -63,7 +63,7 @@ compinit -d "$XDG_CACHE_HOME"/zsh/zcompdump-$ZSH_VERSION
alias vim="nvim" alias vim="nvim"
### BETTER COMMANDS ### BETTER COMMANDS
alias ls="exa --oneline --icons --group-directories-first" alias ls="eza --oneline --icons --group-directories-first"
alias ll='ls --all --long --classify --group --modified --time-style=long-iso --git' alias ll='ls --all --long --classify --group --modified --time-style=long-iso --git'
alias la='ls --all' alias la='ls --all'
@@ -333,12 +333,7 @@ embiggen() {
} }
journal() { journal() {
journaldir=~/sync/journal/ $EDITOR ~/sync/journal/"$(date +%Y-%m-%d).md"
file="$journaldir/$(date +%Y-%m-%d).md"
if [[ ! -e $file ]] ; then
cp $journaldir/template.md $file || return
fi
$EDITOR $file
} }
prefix() { prefix() {
@@ -379,6 +374,16 @@ tmp() {
fi fi
} }
# taken verbatim from https://yazi-rs.github.io/docs/quick-start, extended with "command" in
# the last line to not use aliased `rm`
function y() {
local tmp="$(mktemp -t "yazi-cwd.XXXXXX")" cwd
yazi "$@" --cwd-file="$tmp"
IFS= read -r -d '' cwd < "$tmp"
[ -n "$cwd" ] && [ "$cwd" != "$PWD" ] && builtin cd -- "$cwd"
command rm -f -- "$tmp"
}
setopt PROMPT_SUBST setopt PROMPT_SUBST
autoload -Uz vcs_info autoload -Uz vcs_info