Compare commits

..

5 Commits

100 changed files with 1093 additions and 4719 deletions

15
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[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
@@ -7,6 +10,9 @@
[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
@@ -28,6 +34,9 @@
[submodule "pkgbuilds/python-botocore-stubs"] [submodule "pkgbuilds/python-botocore-stubs"]
path = pkgbuilds/python-botocore-stubs path = pkgbuilds/python-botocore-stubs
url = https://aur.archlinux.org/python-botocore-stubs.git url = https://aur.archlinux.org/python-botocore-stubs.git
[submodule "pkgbuilds/python-dateparser"]
path = pkgbuilds/python-dateparser
url = https://aur.archlinux.org/python-dateparser.git
[submodule "pkgbuilds/python-chevron"] [submodule "pkgbuilds/python-chevron"]
path = pkgbuilds/python-chevron path = pkgbuilds/python-chevron
url = https://aur.archlinux.org/python-chevron.git url = https://aur.archlinux.org/python-chevron.git
@@ -52,9 +61,3 @@
[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,24 +1,36 @@
# 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
ansible_run = ansible-playbook --inventory localhost, --diff ./playbook.yml ${ANSIBLE_EXTRA_ARGS} ansible_run = ansible-playbook --inventory localhost, --diff ./playbook.yml ${ANSIBLE_EXTRA_ARGS}
.PHONY: all
all:
$(ansible_run)
.PHONY: config .PHONY: config
config: config:
$(ansible_run) $(ansible_run) --skip-tags system-update
.PHONY: maintenance .PHONY: system-update
maintenance: system-update:
./maintenance.sh $(ansible_run) --tags system-update
.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,8 @@
# dotfiles # dotfiles
My configuration files for my systems. Uses Ansible for local configuration. My configuration files.
## Supported OS # Installation
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`.
@@ -31,13 +12,14 @@ 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 |

View File

@@ -2,7 +2,6 @@ font_size: 11
gpu: amd gpu: amd
cpu: amd cpu: amd
encrypted_root: true
users: users:
- name: hannes - name: hannes
@@ -12,11 +11,22 @@ 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
@@ -24,8 +34,11 @@ 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

View File

@@ -1,61 +0,0 @@
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"

View File

@@ -1,265 +0,0 @@
---
- 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

View File

@@ -1,81 +0,0 @@
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,7 +2,6 @@ font_size: 11
gpu: nvidia gpu: nvidia
cpu: intel cpu: intel
encrypted_root: true
users: users:
- name: hannes-work - name: hannes-work
@@ -11,6 +10,7 @@ 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,12 +19,14 @@ 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
@@ -33,11 +35,13 @@ 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
@@ -45,9 +49,12 @@ 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

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

View File

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

1
ansible_roles/firefox Submodule

Submodule ansible_roles/firefox added at 2151dce632

View File

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

View File

@@ -30,5 +30,3 @@ 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 --profile %h/.mozilla/firefox/profile-%i ExecStart=/usr/bin/env firefox --setDefaultBrowser -P %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 --profile %h/.mozilla/firefox/profile-%i ExecStart=/usr/bin/env firefox --setDefaultBrowser -P %i
PassEnvironment=DISPLAY PassEnvironment=DISPLAY
Restart=always Restart=always

View File

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

View File

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

View File

@@ -1,3 +0,0 @@
#!/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" >&2 echo "Cannot detect terraform resource and module in $FILE"
exit 1 exit 1
fi fi

View File

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

View File

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

View File

@@ -26,9 +26,3 @@ 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,6 +2,9 @@
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]
@@ -41,7 +44,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 main -e '^*' | xargs --no-run-if-empty git branch -d'" branch-clean = "!sh -c 'git branch --merged | grep -v -e master -e develop -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
@@ -89,7 +92,7 @@
commitBeforeMerge = false commitBeforeMerge = false
detachedHead = false detachedHead = false
[commit] [commit]
gpgSign = false gpgSign = {{ user.git_gpg_sign|bool }}
cleanup = strip cleanup = strip
status = true status = true
[status] [status]
@@ -118,19 +121,11 @@
[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
directory = /var/lib/dotfiles/* [includeIf "gitdir:/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
@@ -138,5 +133,3 @@
# 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,7 +5,12 @@ idle-timeout = 0
completion-trigger-len = 2 completion-trigger-len = 2
[editor.statusline] [editor.statusline]
left = ["mode", "separator", "file-name", "file-modification-indicator"] left = [
"mode",
"separator",
"file-name",
"file-modification-indicator",
]
right = [ right = [
"spinner", "spinner",

View File

@@ -95,6 +95,7 @@
set $screenshot o set $screenshot o
################################################################################ ################################################################################
### WORKSPACE ASSIGNMENTS ###################################################### ### WORKSPACE ASSIGNMENTS ######################################################
################################################################################ ################################################################################
@@ -110,6 +111,7 @@ workspace $workspace8 output {{ machine.screen.8 }}
workspace $workspace9 output {{ machine.screen.9 }} workspace $workspace9 output {{ machine.screen.9 }}
workspace $workspace10 output {{ machine.screen.0 }} workspace $workspace10 output {{ machine.screen.0 }}
assign [class="^KeePassXC$"] $workspace8 assign [class="^KeePassXC$"] $workspace8
# See https://github.com/i3/i3/issues/2060 # See https://github.com/i3/i3/issues/2060
@@ -204,18 +206,22 @@ assign [class="^Wine$"] $workspace10
### START APPLICATIONS ##################################################### ### START APPLICATIONS #####################################################
bindsym $mod+d exec --no-startup-id rofi -show combi -combi-modi run -display-combi "run" bindsym $mod+d exec --no-startup-id $scriptdir/appmenu
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 workstation-client power menu bindsym F1 exec --no-startup-id $scriptdir/shutdown-menu
bindsym F2 exec --no-startup-id $scriptdir/screenmenu
bindsym $mod+F1 exec --no-startup-id workstation-client power lock bindsym $mod+F1 exec --no-startup-id $scriptdir/i3exit 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
################################################################################ ################################################################################
@@ -308,22 +314,22 @@ bindsym $mod+F9 exec --no-startup-id evolution
### SPECIAL KEYBINDS ########################################################### ### SPECIAL KEYBINDS ###########################################################
################################################################################ ################################################################################
bindsym XF86Sleep exec --no-startup-id workstation-client power lock bindsym XF86Sleep exec --no-startup-id $scriptdir/i3exit suspend
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 XF86AudioMute exec --no-startup-id workstation-client pulseaudio output toggle bindsym XF86AudioPlay exec --no-startup-id playerctl -p spotify play-pause
bindsym XF86AudioRaiseVolume exec --no-startup-id workstation-client pulseaudio output inc bindsym XF86AudioNext exec --no-startup-id playerctl -p spotify next
bindsym XF86AudioLowerVolume exec --no-startup-id workstation-client pulseaudio output dec bindsym XF86AudioPrev exec --no-startup-id playerctl -p spotify previous
bindsym XF86AudioPlay exec --no-startup-id workstation-client spotify toggle # keys seemingly switched
bindsym XF86AudioNext exec --no-startup-id workstation-client spotify next bindsym XF86MonBrightnessUp exec --no-startup-id xbacklight -inc 8 ; exec --no-startup-id $scriptdir/update-status
bindsym XF86AudioPrev exec --no-startup-id workstation-client spotify previous bindsym XF86MonBrightnessDown exec --no-startup-id xbacklight -dec 8 ; exec --no-startup-id $scriptdir/update-status
bindsym XF86MonBrightnessUp exec --no-startup-id workstation-client brightness inc bindsym $mod+m exec --no-startup-id pactl set-source-mute '@DEFAULT_SOURCE@' toggle
bindsym XF86MonBrightnessDown exec --no-startup-id workstation-client brightness dec bindsym $mod+space exec --no-startup-id pactl set-source-mute '@DEFAULT_SOURCE@' toggle
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 = "workstation-client pulseaudio output toggle" cmd = "pactl set-sink-mute '@DEFAULT_SINK@' 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 = "workstation-client pulseaudio input toggle" cmd = "pactl set-source-mute '@DEFAULT_SOURCE@' toggle"
update = true update = true
[[block]] [[block]]
@@ -67,32 +67,31 @@ missing_format = ""
[[block]] [[block]]
block = "toggle" block = "toggle"
format = "  $icon " format = "  $icon "
signal = 1 command_on = "$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_off = "$XDG_CONFIG_HOME/i3/scripts/presentation-mode toggle ; pkill -SIGRTMIN+0 i3status-rs"
command_off = "workstation-client present toggle ; pkill -SIGRTMIN+1 i3status-rs" command_state = "[[ $($XDG_CONFIG_HOME/i3/scripts/presentation-mode status) == on ]] && echo active"
command_state = "[[ $(workstation-client present status) == on ]] && echo active"
[[block]] [[block]]
block = "toggle" block = "toggle"
format = "  $icon " format = "  $icon "
command_on = "workstation-client theme light" command_on = "systemctl --user start color-theme-light"
command_off = "workstation-client theme dark" command_off = "systemctl --user start color-theme-dark"
command_state = "[[ $(workstation-client theme status) == light ]] && echo 1" command_state = "[[ $(systemctl --user is-active color-theme-light) == active ]] && echo active"
[[block]] [[block]]
block = "toggle" block = "toggle"
format = "  $icon " format = "  $icon "
command_on = "workstation-client redshift start" command_on = "systemctl --user start redshift"
command_off = "workstation-client redshift stop" command_off = "systemctl --user stop redshift"
command_state = "[[ $(workstation-client redshift status) == active ]] && echo 1" command_state = "[[ $(systemctl --user is-active redshift) == active ]] && echo active"
signal = 0 signal = 0
[[block]] [[block]]
block = "toggle" block = "toggle"
format = "  $icon " format = "  $icon "
command_on = "workstation-client spotify start" command_on = "systemctl --user start spotify"
command_off = "workstation-client spotify stop" command_off = "systemctl --user stop spotify"
command_state = "[[ $(workstation-client spotify status) == active ]] && echo 1" command_state = "[[ $(systemctl --user is-active spotify) == active ]] && echo active"
signal = 0 signal = 0
[[block]] [[block]]
@@ -100,6 +99,11 @@ 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

@@ -1 +0,0 @@

2
i3/scripts/appmenu Executable file
View File

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

84
i3/scripts/i3exit Executable file
View File

@@ -0,0 +1,84 @@
#!/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

47
i3/scripts/presentation-mode Executable file
View File

@@ -0,0 +1,47 @@
#!/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

19
i3/scripts/shutdown-menu Executable file
View File

@@ -0,0 +1,19 @@
#!/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,12 +8,6 @@
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=1G , type=linux ${DEVICE}2 : name=boot , size=512M , 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 microcode modconf kms keyboard keymap consolefont block encrypt lvm2 filesystems resume fsck)/' /etc/mkinitcpio.conf sed -i 's/^HOOKS=.*$/HOOKS=(base udev autodetect keyboard keymap consolefont modconf block encrypt lvm2 filesystems resume fsck)/' /etc/mkinitcpio.conf
mkinitcpio -P mkinitcpio -P

View File

@@ -1,137 +0,0 @@
#!/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

View File

@@ -1,130 +0,0 @@
#!/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=1G , type=linux ${DEVICE}p2 : name=boot , size=512M , type=linux
${DEVICE}p3 : name=cryptpart , type=linux ${DEVICE}p3 : name=cryptpart , type=linux
EOF EOF
@@ -33,8 +33,8 @@ EOF
sleep 1 sleep 1
while : ; do while : ; do
cryptsetup --batch-mode luksFormat --iter-time 1000 ${DEVICE}p3 cryptsetup --batch-mode luksFormat --iter-time 1000 ${DEVICE}3
cryptsetup --batch-mode open --tries 1 ${DEVICE}p3 cryptpart && break cryptsetup --batch-mode open --tries 1 ${DEVICE}3 cryptpart && break
done done
pvcreate /dev/mapper/cryptpart pvcreate /dev/mapper/cryptpart
@@ -89,7 +89,7 @@ cat <<EOF > /etc/hosts
127.0.1.1 neptune 127.0.1.1 neptune
EOF EOF
sed -i 's/^HOOKS=.*$/HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block encrypt lvm2 filesystems resume fsck)/' /etc/mkinitcpio.conf sed -i 's/^HOOKS=.*$/HOOKS=(base udev autodetect keyboard keymap consolefont modconf 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 bash -c "pacman -Sy --needed --noconfirm archlinux-keyring && pacman -Su" sudo pacman -Syu
./update-aur-pkgs.sh ./update-aur-pkgs.sh

1
mgr/.gitignore vendored
View File

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

503
mgr/Cargo.lock generated
View File

@@ -1,503 +0,0 @@
# 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"

View File

@@ -1,184 +0,0 @@
[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"

View File

@@ -1,87 +0,0 @@
#![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),
}
}

View File

@@ -1,105 +0,0 @@
#![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),
}
}

View File

@@ -1,77 +0,0 @@
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)
}
}

View File

@@ -1,23 +0,0 @@
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;
}

View File

@@ -1,183 +0,0 @@
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,
}
}

View File

@@ -1,29 +0,0 @@
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"),
})
}

View File

@@ -1,66 +0,0 @@
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 })?,
))
}
}

View File

@@ -1,40 +0,0 @@
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 }),
}
}

View File

@@ -1,28 +0,0 @@
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 })
}

View File

@@ -1,205 +0,0 @@
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()?),
}
}
}

View File

@@ -1,227 +0,0 @@
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(())
}

View File

@@ -1,154 +0,0 @@
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 })
}
}
}

View File

@@ -1,112 +0,0 @@
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)
}
}

View File

@@ -1,116 +0,0 @@
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",
],
)?)
}

View File

@@ -1,104 +0,0 @@
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 })
}
}
}

View File

@@ -1,173 +0,0 @@
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")
}

View File

@@ -1,40 +0,0 @@
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(),
}),
}
}
}

View File

@@ -1,101 +0,0 @@
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())
},
),
}
}
}

View File

@@ -1,198 +0,0 @@
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),
}
}

View File

@@ -1,20 +0,0 @@
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())?)
}
}

View File

@@ -1,11 +0,0 @@
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>;
}

View File

@@ -1,90 +0,0 @@
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!()
}

View File

@@ -1,35 +0,0 @@
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,7 +49,6 @@ ansible:
archlinux: archlinux:
- ansible - ansible
- ansible-language-server - ansible-language-server
- ansible-lint
- python-jmespath - python-jmespath
xdotool: xdotool:
archlinux: ["xdotool"] archlinux: ["xdotool"]
@@ -77,7 +76,7 @@ font-libertine:
- libertinus-font - libertinus-font
font-awesome: font-awesome:
archlinux: archlinux:
- woff2-font-awesome - ttf-font-awesome
font-noto: font-noto:
archlinux: archlinux:
- noto-fonts - noto-fonts
@@ -110,6 +109,8 @@ 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:
@@ -161,8 +162,8 @@ python-modules:
- python-semver - python-semver
black: black:
archlinux: ["python-black"] archlinux: ["python-black"]
brightnessctl: xbacklight:
archlinux: ["brightnessctl"] archlinux: ["acpilight"]
wireshark: wireshark:
archlinux: ["wireshark-cli", "wireshark-qt"] archlinux: ["wireshark-cli", "wireshark-qt"]
nmap: nmap:
@@ -217,7 +218,7 @@ ruby:
acpi: acpi:
archlinux: ["acpi", "acpid"] archlinux: ["acpi", "acpid"]
nodejs: nodejs:
archlinux: ["nodejs-lts-jod", "npm", "yarn"] archlinux: ["nodejs", "npm", "yarn"]
xdg: xdg:
archlinux: ["xdg-utils"] archlinux: ["xdg-utils"]
dunst: dunst:
@@ -226,6 +227,8 @@ 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:
@@ -237,7 +240,7 @@ inotify:
rclone: rclone:
archlinux: ["rclone"] archlinux: ["rclone"]
dnf: dnf:
archlinux: ["dnf5"] archlinux: ["dnf"]
rust: rust:
archlinux: archlinux:
- rustup - rustup
@@ -248,12 +251,12 @@ rust:
- cargo-watch - cargo-watch
- cargo-release - cargo-release
- cargo-sort - cargo-sort
- 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:
@@ -318,6 +321,8 @@ 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:
@@ -325,12 +330,12 @@ helix:
keepassxc: keepassxc:
archlinux: ["keepassxc"] archlinux: ["keepassxc"]
awscli: awscli:
archlinux: ["aws-cli-v2"] archlinux: ["aws-cli"]
mariadb-client: mariadb-client:
archlinux: ["mariadb-clients"] archlinux: ["mariadb-clients"]
php: php:
archlinux: ["php", "composer"] archlinux: ["php"]
eza: exa:
archlinux: ["eza"] archlinux: ["eza"]
just: just:
archlinux: ["just"] archlinux: ["just"]
@@ -374,7 +379,6 @@ json:
markdown: markdown:
archlinux: archlinux:
- marksman - marksman
- mdformat
lldb: lldb:
archlinux: archlinux:
- lldb - lldb
@@ -446,9 +450,6 @@ mold:
archlinux: archlinux:
- clang - clang
- mold - mold
wild:
archlinux:
- wild
arch-packaging: arch-packaging:
archlinux: archlinux:
- namcap - namcap
@@ -470,7 +471,6 @@ watchexec:
postgresql: postgresql:
archlinux: archlinux:
- postgresql - postgresql
- pgformatter
tokei: tokei:
archlinux: archlinux:
- tokei - tokei
@@ -548,45 +548,3 @@ taplo:
prettier: prettier:
archlinux: archlinux:
- prettier - prettier
sccache:
archlinux:
- sccache
btop:
archlinux:
- btop
dua:
archlinux:
- dua-cli
hexyl:
archlinux:
- hexyl
yq:
archlinux:
- go-yq
kolourpaint:
archlinux:
- kolourpaint
podman:
archlinux:
- podman
- podman-compose
pulumi:
archlinux:
- pulumi
reflector:
archlinux:
- reflector
yazi:
archlinux:
- yazi
- ffmpeg
- 7zip
- jq
- poppler
- fd
- ripgrep
- fzf
- xsel
- zoxide
- resvg
- imagemagick

1
pkgbuilds/vim-plug Submodule

Submodule pkgbuilds/vim-plug added at 6edef0322a

View File

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

View File

@@ -1,42 +0,0 @@
# 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,28 +1,27 @@
---
- 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
ansible.builtin.include_vars: include_vars:
file: _machines/{{ ansible_facts['hostname'] }}.yml file: _machines/{{ ansible_hostname }}.yml
name: machine name: machine
tags: tags:
- always - always
- ansible.builtin.set_fact: - set_fact:
distro: "{{ ansible_facts['distribution'] | lower }}" distro: "{{ ansible_distribution|lower }}"
tags: tags:
- always - always
- name: Check for valid distro - name: check for valid distro
ansible.builtin.assert: assert:
that: distro in ('archlinux') that: distro in ('archlinux')
- block: - block:
- name: Install ansible requirements - name: install ansible requirements
ansible.builtin.package: package:
name: "{{ packages[distro] }}" name: "{{ packages[distro] }}"
state: present state: present
become: true become: true
@@ -31,46 +30,51 @@
archlinux: archlinux:
- python-jmespath - python-jmespath
- name: Pacman - name: pacman
tags: tags:
- pacman - pacman
block: block:
- name: Enable multilib repository - name: enable multilib repository
ansible.builtin.blockinfile: 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: Make sure that package lists are refreshed if necessary - name: enable parallel download
ansible.builtin.meta: flush_handlers blockinfile:
- 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
- name: Install pacman-contrib for paccache - block:
ansible.builtin.package: - name: upgrade system
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
ansible.builtin.copy: 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
@@ -78,12 +82,12 @@
RemainAfterExit=true RemainAfterExit=true
become: true become: true
- name: Install pacman cache clean timer - name: install pacman cache clean timer
ansible.builtin.copy: 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
@@ -92,15 +96,14 @@
WantedBy=multi-user.target WantedBy=multi-user.target
become: true become: true
- ansible.builtin.systemd: - name: enable pacman cache clean timer
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:
- dotfiles-directory - dotfiles-directory
@@ -129,7 +132,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
@@ -188,17 +191,11 @@
- name: install packages - name: install packages
package: package:
name: "{{ defined_packages|json_query(pkg_query) }}" name: "{{ defined_packages|json_query(query) }}"
state: present state: present
become: true become: true
vars: vars:
pkg_query: "{{ '*.%s[]'|format(distro) }}" 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:
@@ -208,30 +205,6 @@
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
@@ -247,9 +220,6 @@
- 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
@@ -260,10 +230,10 @@
preexec: | preexec: |
#!/usr/bin/env bash #!/usr/bin/env bash
source ./env source ./env
echo lel curl -sSf --proto '=https' https://download.spotify.com/debian/pubkey_6224F9941A8AA6D1.gpg | gpg --import -
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
@@ -274,6 +244,7 @@
# dependency of # dependency of
- name: python-boto3-stubs - name: python-boto3-stubs
- name: python-dateparser
- name: python-chevron - name: python-chevron
- name: python-aws-lambda-builders - name: python-aws-lambda-builders
# === dependencies of # === dependencies of
@@ -293,9 +264,6 @@
# 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 }}"
@@ -346,7 +314,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
@@ -359,7 +327,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
@@ -424,7 +392,9 @@
chdir: "{{ item.1.stat.path | dirname }}" chdir: "{{ item.1.stat.path | dirname }}"
become_user: makepkg become_user: makepkg
become: true become: true
when: not item[0].stat.exists or (item[0].stat.checksum|default('') != item[1].stat.checksum) when:
- 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 }}"
@@ -449,14 +419,13 @@
source ./PKGBUILD source ./PKGBUILD
for a in "${arch[@]}" ; do for arch in "${arch[@]}" ; do
if [[ "${a}" == "any" ]] ; then if [[ "${arch}" == "any" ]] ; then
arch="any" arch="any"
break break
fi fi
if [[ "${a}" == "x86_64" ]] ; then if [[ "${arch}" == "x86_64" ]] ; then
arch="x86_64" arch="x86_64"
break
fi fi
done done
@@ -473,7 +442,9 @@
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
@@ -531,7 +502,8 @@
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] }}"
@@ -554,29 +526,10 @@
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:
@@ -655,6 +608,7 @@
- NetworkManager - NetworkManager
- docker - docker
- systemd-timesyncd - systemd-timesyncd
- pcscd
- name: enable services - name: enable services
service: service:
@@ -664,24 +618,14 @@
with_items: "{{ enable_services }}" with_items: "{{ enable_services }}"
become: true become: true
- name: enable sockets
service:
state: started
enabled: true
name: "{{ item }}.socket"
loop:
- pcscd
become: true
- name: get systemd boot target - name: get systemd boot target
command: systemctl get-default command: systemctl get-default
register: systemd_target register: systemd_target
changed_when: false changed_when: false
check_mode: false check_mode: false
- name: define systemd default target - set_fact:
set_fact: default_target: multi-user.target
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 }}
@@ -691,22 +635,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
@@ -804,24 +748,20 @@
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"
@@ -838,7 +778,7 @@
- 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"
@@ -854,185 +794,11 @@
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:
@@ -1041,26 +807,13 @@
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

12
projector.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/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,10 +21,8 @@ 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
@@ -32,7 +30,6 @@ 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
@@ -62,29 +59,12 @@ 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 +0,0 @@
post_commands = [
"systemctl --user restart keyboard.service",
"systemctl --user restart touchpad.service",
]

View File

@@ -1,53 +0,0 @@
#!/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

72
test.sh
View File

@@ -79,6 +79,8 @@ qemuopts=(
"-accel" "kvm" "-accel" "kvm"
"-drive" "if=pflash,format=raw,readonly=true,file=/usr/share/ovmf/x64/OVMF_CODE.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"
@@ -127,10 +129,6 @@ 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:?}"/*
@@ -147,7 +145,7 @@ install_from_iso() {
EOF EOF
) )
cp /usr/share/edk2/x64/OVMF_VARS.4m.fd "${tmpdir}/efivars.fd" cp /usr/share/ovmf/x64/OVMF_VARS.fd "${tmpdir}/efivars.fd"
mkisofs \ mkisofs \
-uid 0 \ -uid 0 \
-gid 0 \ -gid 0 \
@@ -190,23 +188,10 @@ EOF
mkdir /repo/ mkdir /repo/
mount /dev/disk/by-label/REPO /repo/ mount /dev/disk/by-label/REPO /repo/
if [[ "${lukscfg}" == "luks" ]] ; then
printf 'lukspw\nlukspw\nrootpw\nrootpw\n' | \ printf 'lukspw\nlukspw\nrootpw\nrootpw\n' | \
/repo/install_scripts/"${hostname}".sh /repo/install_scripts/"${hostname}".sh
mount /dev/mapper/vgbase-root /mnt 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
cat << SPECIALS > /tmp/specials.sh cat << SPECIALS > /tmp/specials.sh
if [[ "\\\$(tty)" == "/dev/tty1" ]] ; then if [[ "\\\$(tty)" == "/dev/tty1" ]] ; then
@@ -226,7 +211,7 @@ SPECIALS
rsync -rl /repo/ /mnt/var/lib/dotfiles/ rsync -rl /repo/ /mnt/var/lib/dotfiles/
umount --recursive /mnt umount /mnt
poweroff poweroff
EOF EOF
@@ -237,10 +222,6 @@ 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=(
@@ -252,71 +233,36 @@ configure_new_system() {
qemu-system-x86_64 -name "${hostname}" "${qemuopts[@]}" "${hostqemuopts[@]}" "${opts[@]}" & qemu-system-x86_64 -name "${hostname}" "${qemuopts[@]}" "${hostqemuopts[@]}" "${opts[@]}" &
if [[ "${lukscfg}" == "luks" ]] ; then
# 5s for grub timeout, 5s for kernel boot # 5s for grub timeout, 5s for kernel boot
echo waiting for luks password prompt ... echo waiting for luks password prompt ...
sleep 10s sleep 10s
echo 'lukspw' | send_mon "${mon_sock}" echo 'lukspw' | send_mon "${mon_sock}"
fi
echo waiting for boot ... echo waiting for boot ...
sleep 10s sleep 10s
wait wait
} }
declare -A machinecfg machines=(ares neptune)
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=( hostqemuopts=("-device" "ide-hd,drive=root")
"-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=( hostqemuopts=("-device" "nvme,serial=rootnvme,drive=root")
"-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}" "${lukscfg}" "${homecfg}" "${hostqemuopts[@]}" install_from_iso "${hostname}" "${hostqemuopts[@]}"
configure_new_system "${hostname}" "${lukscfg}" "${homecfg}" "${hostqemuopts[@]}" configure_new_system "${hostname}" "${hostqemuopts[@]}"
done done

View File

@@ -1,19 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -o nounset
set -o errexit
for pkg in pkgbuilds/* ; do for pkg in pkgbuilds/* ; do
if [[ -n "$(builtin cd "${pkg}" && git rev-parse --show-superproject-working-tree)" ]] ; then printf "checking %s\n" "${pkg}"
printf "checking git submodule %s\n" "${pkg}"
git submodule update --remote "${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}")"

349
user.yml
View File

@@ -1,11 +1,11 @@
--- - name: base user configuration
- name: Base user configuration
tags: [user:base] tags: [user:base]
block: block:
- ansible.builtin.set_fact: - set_fact:
user_groups: user_groups:
- libvirt - libvirt
- wheel - wheel
- vboxusers
- wireshark - wireshark
- docker - docker
- sudonopw - sudonopw
@@ -13,28 +13,27 @@
- kvm - kvm
- video - video
- name: Create user group - name: create user group
ansible.builtin.group: 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
ansible.builtin.user: 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
group: "{{ user.name }}" groups: "{{ [user.name, 'dotfiles'] + user_groups }}"
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
ansible.builtin.file: file:
state: directory state: directory
path: "{{ item }}" path: "{{ item }}"
owner: "{{ user.name }}" owner: "{{ user.name }}"
@@ -44,25 +43,22 @@
- "/home/{{ user.name }}/.config/systemd/" - "/home/{{ user.name }}/.config/systemd/"
- "/home/{{ user.name }}/.config/systemd/user/" - "/home/{{ user.name }}/.config/systemd/user/"
- name: Configure autologin - name: create directory for getty autologin
when: user.autologin|default(true) is sameas True file:
block:
- name: Create directory for getty autologin
ansible.builtin.file:
state: directory state: directory
path: /etc/systemd/system/getty@tty{{ user.vt }}.service.d path: /etc/systemd/system/getty@tty{{ user.vt }}.service.d
owner: root owner: root
group: root group: root
mode: "0755" mode: '0755'
become: true become: true
become_user: root become_user: root
- name: Enable getty autologin - name: enable getty autologin
ansible.builtin.copy: 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=
@@ -70,16 +66,16 @@
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
ansible.builtin.include_vars: include_vars:
file: dotfiles.yml file: dotfiles.yml
- name: Get state of empty directories - name: get state of empty directories
ansible.builtin.stat: stat:
path: ~/{{ item.name }} path: ~/{{ item.name }}
register: empty_dir_stat register: empty_dir_stat
with_items: "{{ empty_directories }}" with_items: "{{ empty_directories }}"
@@ -87,8 +83,8 @@
loop_control: loop_control:
label: "{{ item.name }}" label: "{{ item.name }}"
- name: Remove symlinks - name: remove symlinks
ansible.builtin.file: 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
@@ -96,8 +92,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
ansible.builtin.file: file:
state: directory state: directory
path: ~/{{ item.name }} path: ~/{{ item.name }}
mode: "{{ item.mode | default('0755') }}" mode: "{{ item.mode | default('0755') }}"
@@ -105,8 +101,8 @@
loop_control: loop_control:
label: "{{ item.name }}" label: "{{ item.name }}"
- name: Link this folder to ~/.dotfiles - name: link this folder to ~/.dotfiles
ansible.builtin.file: file:
state: link state: link
force: true force: true
follow: false follow: false
@@ -117,8 +113,8 @@
become: true become: true
become_user: root become_user: root
- name: Get state of copy targets - name: get state of copy targets
ansible.builtin.stat: 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)
@@ -127,20 +123,20 @@
loop_control: loop_control:
label: "{{ item.to }}" label: "{{ item.to }}"
- name: Remove invalid copy target (symlinks) - name: remove invalid copy target (directories)
ansible.builtin.file: file:
path: "{{ item.stat.path }}" path: "{{ item.stat.path }}"
state: absent state: absent
when: when:
- not item.skipped is defined or not item.skipped - not item.skipped is defined or not item.skipped
- item.stat.exists - item.stat.exists
- item.stat.islnk - item.stat.isdir
with_items: "{{ copy_stat.results }}" with_items: "{{ copy_stat.results }}"
loop_control: loop_control:
label: "{{ item.item.from }}" label: "{{ item.item.from }}"
- name: Make sure target directories exist - name: make sure target directories exist
ansible.builtin.file: 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 }}"
@@ -151,54 +147,24 @@
loop_control: loop_control:
label: "{{ item.to }}" label: "{{ item.to }}"
- name: Copy dotfiles - name: link dotfiles
ansible.builtin.copy: file:
dest: "/home/{{ user.name }}/{{ item.to }}" state: link
force: true
follow: false
path: "/home/{{ user.name }}/{{ item.to }}"
src: /var/lib/dotfiles/{{ item.from }} src: /var/lib/dotfiles/{{ item.from }}
owner: "{{ user.name }}" owner: "{{ user.name }}"
group: "{{ user.name }}" group: "{{ user.name }}"
when: not item.template|default(false) and not item.dir|default(false) when: not item.template|default(false)
with_items: "{{ dotfiles }}" with_items: "{{ dotfiles }}"
become: true become: true
become_user: root become_user: root
loop_control: loop_control:
label: "{{ item.to }}" label: "{{ item.to }}"
- name: Copy directories - name: get state of template targets
ansible.posix.synchronize: stat:
dest: "/home/{{ user.name }}/{{ item.to }}/"
src: /var/lib/dotfiles/{{ item.from }}/
archive: false
owner: false
group: false
links: true
perms: false
times: false
recursive: true
checksum: true
delete: true
when: item.dir|default(false)
with_items: "{{ dotfiles }}"
become: true
become_user: root
loop_control:
label: "{{ item.to }}"
- name: Apply directory permissions
ansible.builtin.file:
dest: "/home/{{ user.name }}/{{ item.to }}/"
owner: "{{ user.name }}"
group: "{{ user.name }}"
recurse: true
when: item.dir|default(false)
with_items: "{{ dotfiles }}"
become: true
become_user: root
loop_control:
label: "{{ item.to }}"
- name: Get state of template targets
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)
@@ -207,8 +173,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)
ansible.builtin.file: file:
path: "{{ item.stat.path }}" path: "{{ item.stat.path }}"
state: absent state: absent
when: when:
@@ -219,8 +185,8 @@
loop_control: loop_control:
label: "{{ item.item.to }}" label: "{{ item.item.to }}"
- name: Deploy dotfiles templates - name: deploy dotfiles templates
ansible.builtin.template: 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 }}"
@@ -233,35 +199,35 @@
loop_control: loop_control:
label: "{{ item.to }}" label: "{{ item.to }}"
- name: Remove dotfiles - name: remove dotfiles
ansible.builtin.file: 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
ansible.builtin.file: file:
state: directory state: directory
path: "{{ item }}" path: "{{ item }}"
with_items: with_items:
- ~/tmp - ~/tmp
- name: Stat ~/bin - name: stat ~/bin
ansible.builtin.stat: 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
ansible.builtin.file: 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
ansible.builtin.file: file:
state: link state: link
force: true force: true
follow: false follow: false
@@ -270,90 +236,124 @@
owner: "{{ user.name }}" owner: "{{ user.name }}"
group: "{{ user.name }}" group: "{{ user.name }}"
- name: Firefox - name: vim
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 base directories - name: create firefox directories
ansible.builtin.file: firefox_profile:
path: "{{ item }}" name: "{{ item.key }}"
state: directory
mode: "0755"
loop:
- "~/.mozilla/"
- "~/.mozilla/firefox/"
- name: Create firefox profile directories
ansible.builtin.file:
path: "~/.mozilla/firefox/profile-{{ item.key }}"
state: directory
mode: "0755"
loop: "{{ user.firefox_profiles | dict2items }}" loop: "{{ user.firefox_profiles | dict2items }}"
loop_control: check_mode: false
label: "{{ item.key }}" register: firefox_profile_names
- name: Create chrome directory - set_fact:
ansible.builtin.file: firefox_preferences:
path: "~/.mozilla/firefox/profile-{{ item.key }}/chrome/" browser.aboutConfig.showWarning: false
state: directory extensions.pocket.enabled: false
mode: "0755" 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 }}" loop: "{{ user.firefox_profiles | dict2items }}"
loop_control: when: not ansible_check_mode
label: "{{ item.key }}"
- name: Configure firefox custom css - name: firefox - create chrome directory
ansible.builtin.copy: file:
dest: "~/.mozilla/firefox/profile-{{ item.key }}/chrome/userChrome.css" path: "{{ item.profile_path }}/chrome/"
# from https://www.kvakil.me/posts/2023-09-12-my-tree-style-tab-configuration.html state: directory
mode: '0755'
with_items: "{{ firefox_profile_names.results }}"
when: not ansible_check_mode
loop_control:
label: "{{ item.profile_path }}"
- name: firefox - configure firefox custom css
copy:
dest: "{{ item.profile_path }}/chrome/userChrome.css"
content: | content: |
// Hide the title bar. #TabsToolbar {
#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;
} }
#titlebar {
// Hide the side toolbar noise. visibility: collapse !important;
#TabsToolbar {
min-width: 0 !important;
min-height: 0 !important;
} }
#sidebar-header {
#TabsToolbar > .titlebar-buttonbox-container { visibility: collapse !important;
display: block;
position: absolute;
top: 12px;
left: 0px;
}
#sidebar-box[sidebarcommand="treestyletab_piro_sakura_ne_jp-sidebar-action"] #sidebar-header {
display: none;
} }
when: when:
- item.value.manage_css is sameas True - not ansible_check_mode
loop: "{{ user.firefox_profiles | dict2items }}" - user.firefox_profiles[item.profile_name].manage_css is sameas True
with_items: "{{ firefox_profile_names.results }}"
loop_control: loop_control:
label: "{{ item.key }}" label: "{{ item.profile_path }}"
- 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
ansible.builtin.file: file:
state: link state: link
force: true force: true
follow: false follow: false
@@ -363,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
ansible.builtin.file: file:
state: directory state: directory
path: ~/{{ item }} path: ~/{{ item }}
loop: loop:
@@ -376,8 +376,8 @@
- .config/systemd/ - .config/systemd/
- .config/systemd/user/ - .config/systemd/user/
- name: Link autostart service files - name: link autostart service files
ansible.builtin.file: file:
state: link state: link
force: true force: true
follow: false follow: false
@@ -387,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
ansible.builtin.stat: 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
ansible.builtin.file: 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
ansible.builtin.template: 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 }}"
@@ -409,19 +409,20 @@
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
ansible.builtin.command: gpg --import ./gpgkeys/{{ user.gpg_key.email }}.gpg.asc 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
ansible.builtin.shell: "gpg --import-ownertrust <<< {{ user.gpg_key.fingerprint }}:6" 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 Normal file
View File

@@ -0,0 +1,320 @@
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-default" export BROWSER="firefox"
export PAGER="less" export PAGER="less"
export LESS="FRX" export LESS="FRX"
@@ -21,13 +21,8 @@ export XDG_CONFIG_HOME="$HOME/.config"
export XDG_CACHE_HOME="$HOME/.cache" export XDG_CACHE_HOME="$HOME/.cache"
export XDG_DATA_HOME="$HOME/.local/share" export XDG_DATA_HOME="$HOME/.local/share"
export XDG_STATE_HOME="$HOME/.local/state" export XDG_STATE_HOME="$HOME/.local/state"
export XDG_DOWNLOAD_DIR="$HOME/download"
mkdir -p "${XDG_CONFIG_HOME}" XDG_DOWNLOAD_DIR="$HOME/download"
mkdir -p "${XDG_CACHE_HOME}"
mkdir -p "${XDG_DATA_HOME}"
mkdir -p "${XDG_STATE_HOME}"
mkdir -p "${XDG_DOWNLOAD_DIR}"
export XAUTHORITY="$XDG_RUNTIME_DIR"/Xauthority export XAUTHORITY="$XDG_RUNTIME_DIR"/Xauthority
@@ -69,20 +64,6 @@ 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) %}

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="eza --oneline --icons --group-directories-first" alias ls="exa --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,7 +333,12 @@ embiggen() {
} }
journal() { journal() {
$EDITOR ~/sync/journal/"$(date +%Y-%m-%d).md" journaldir=~/sync/journal/
file="$journaldir/$(date +%Y-%m-%d).md"
if [[ ! -e $file ]] ; then
cp $journaldir/template.md $file || return
fi
$EDITOR $file
} }
prefix() { prefix() {
@@ -374,16 +379,6 @@ 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