From dae2470e67cf2f66d7bdad11d7c3898b0a724136 Mon Sep 17 00:00:00 2001 From: Martin Bauer Date: Wed, 9 Sep 2026 08:17:48 +0200 Subject: [PATCH] use same shell setup as in nix home manager --- full.yml | 3 +- roles/pi_dhtsensor/tasks/main.yml | 5 +- roles/pi_modern_shell_env/README.md | 10 +- roles/pi_modern_shell_env/defaults/main.yml | 6 +- roles/pi_modern_shell_env/files/ohmyposh.json | 113 ++++++++++++++++++ roles/pi_modern_shell_env/tasks/main.yml | 26 +++- .../templates/config.fish.j2 | 48 +++++++- .../files/raspi-leds-off.service | 2 + .../pi_standard_setup/files/raspi-leds-off.sh | 12 +- 9 files changed, 207 insertions(+), 18 deletions(-) create mode 100644 roles/pi_modern_shell_env/files/ohmyposh.json diff --git a/full.yml b/full.yml index 4410340..0e84b05 100644 --- a/full.yml +++ b/full.yml @@ -18,8 +18,7 @@ - pi_squeezelite_custom - pi_shairport - pi_irserver - # - pi_dhtsensor - - pi_squeezeserver + - pi_dhtsensor - name: Kitchen pi hosts: kitchenpi diff --git a/roles/pi_dhtsensor/tasks/main.yml b/roles/pi_dhtsensor/tasks/main.yml index 3ebcdf8..7f872cd 100644 --- a/roles/pi_dhtsensor/tasks/main.yml +++ b/roles/pi_dhtsensor/tasks/main.yml @@ -18,7 +18,10 @@ ansible.builtin.pip: name: adafruit-dht executable: pip3 - extra_args: "--break-system-packages" + # Modern Raspberry Pi OS kernels no longer expose a "Hardware" line in + # /proc/cpuinfo, which breaks this package's built-in Pi auto-detection. + # Force it explicitly since we only ever target Raspberry Pis. + extra_args: '--break-system-packages --global-option="--force-pi"' notify: Restart dht22_sensing - name: Install script config ansible.builtin.template: diff --git a/roles/pi_modern_shell_env/README.md b/roles/pi_modern_shell_env/README.md index e0d42f9..d049403 100644 --- a/roles/pi_modern_shell_env/README.md +++ b/roles/pi_modern_shell_env/README.md @@ -1,11 +1,19 @@ # pi_modern_shell_env Installs modern CLI tooling (fish, ripgrep, fd, bat, fzf, zoxide, lsd, -tmux) plus a starship prompt for the login user on `iot` Pi hosts, and +tmux) plus an oh-my-posh prompt for the login user on `iot` Pi hosts, and makes fish the default login shell. Purely a convenience layer for interactive SSH sessions into otherwise single-purpose appliance boxes — does not affect any of the audio/sensor services these hosts run. +The oh-my-posh theme (`files/ohmyposh.json`) and its fish vi-mode +integration (`templates/config.fish.j2`) are copied from the same setup +used in `~/nixconfig`'s home-manager config +(`homeManagerModules/cli/modern-cli.nix`/`ohmyposh.json`), so the prompt +looks and behaves the same on the desktop and on these Pis. If that +nixconfig theme changes, re-sync `files/ohmyposh.json` here by hand — the +two aren't linked. + **Key vars:** - `pi_modern_shell_env_target_user` — user to configure (defaults to diff --git a/roles/pi_modern_shell_env/defaults/main.yml b/roles/pi_modern_shell_env/defaults/main.yml index 8fff01e..9e66c53 100644 --- a/roles/pi_modern_shell_env/defaults/main.yml +++ b/roles/pi_modern_shell_env/defaults/main.yml @@ -12,9 +12,9 @@ pi_modern_shell_env_packages: - tmux - curl -# Bookworm's apt coverage for starship is inconsistent across releases/ -# arches, so it's installed via the official install script instead. -pi_modern_shell_env_starship_install_url: https://starship.rs/install.sh +# oh-my-posh isn't packaged in apt; installed via the official script +# instead, matching the prompt used in ~/nixconfig's home-manager setup. +pi_modern_shell_env_ohmyposh_install_url: https://ohmyposh.dev/install.sh pi_modern_shell_env_target_user: "{{ main_user | default('root') }}" diff --git a/roles/pi_modern_shell_env/files/ohmyposh.json b/roles/pi_modern_shell_env/files/ohmyposh.json new file mode 100644 index 0000000..081a6be --- /dev/null +++ b/roles/pi_modern_shell_env/files/ohmyposh.json @@ -0,0 +1,113 @@ +{ + "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", + "blocks": [ + { + "alignment": "left", + "segments": [ + { + "background_templates": [ + "{{ if eq .Env.FISH__BIND_MODE \"insert\" }}#735CDD{{ else }}#78E425{{ end }}" + ], + "foreground": "#ffffff", + "powerline_symbol": "\ue0b8", + "style": "diamond", + "leading_diamond": "\ue0b6", + "type": "text", + "template": "{{ if eq .Env.FISH__BIND_MODE \"default\" }}N {{ else if eq .Env.FISH__BIND_MODE \"insert\" }}I {{ else if eq .Env.FISH__BIND_MODE \"replace_one\" }}R {{ else if eq .Env.FISH__BIND_MODE \"visual\"}}V {{ else }}[?]{{ end }}" + }, + { + "type": "session", + "style": "powerline", + "powerline_symbol": "\ue0b0", + "background": "#735CDD", + "foreground": "#bbbbbb", + "template": "{{ if .SSHSession }}{{ .HostName }} {{ end }}", + "properties": { + "display_host": true, + "ssh_icon": "\ueb03" + } + }, + { + "type": "python", + "style": "powerline", + "powerline_symbol": "", + "foreground": "#100e23", + "background": "#906cff", + "template": "  {{ .Venv }} " + }, + { + "type": "text", + "style": "powerline", + "powerline_symbol": "\ue0b0", + "foreground": "#100e23", + "background": "#7EBAE4", + "template": "{{ if .Env.IN_NIX_SHELL }} \uf313 {{ .Env.IN_NIX_SHELL }} {{ end }}" + }, + { + "background": "#DA627D", + "foreground": "#ffffff", + "powerline_symbol": "\ue0b0", + "properties": { + "folder_icon": "\uf115", + "home_icon": "\ueb06", + "style": "full", + "mapped_locations": { + "/workspaces/sgrid": "󰋁" + } + }, + "template": " {{ .Path }} ", + "type": "path", + "style": "diamond", + "trailing_diamond": "" + } + ], + "type": "prompt" + }, + { + "alignment": "right", + "segments": [ + { + "background": "#ee79d1", + "foreground": "#000000", + "style": "diamond", + "leading_diamond": "", + "trailing_diamond": "", + "properties": { + "branch_icon": "\ue725 ", + "fetch_stash_count": true, + "fetch_status": true, + "fetch_upstream_icon": true, + "fetch_worktree_count": true + }, + "template": " {{ .UpstreamIcon }}{{ .HEAD }}{{if .BranchStatus }} {{ .BranchStatus }}{{ end }}{{ if .Working.Changed }} \uf044 {{ .Working.String }}{{ end }}{{ if and (.Working.Changed) (.Staging.Changed) }} |{{ end }}{{ if .Staging.Changed }} \uf046 {{ .Staging.String }}{{ end }}{{ if gt .StashCount 0 }} \ueb4b {{ .StashCount }}{{ end }} ", + "type": "git" + }, + { + "background": "#db5f32", + "foreground": "#cdf3f4", + "style": "diamond", + "leading_diamond": "", + "trailing_diamond": "", + "properties": { + "threshold": 500, + "style": "austin" + }, + "template": " \ueba2 {{ .FormattedMs }} ", + "type": "executiontime" + }, + { + "background": "#db5f32", + "foreground": "#ffffff", + "template": " \uf421 {{.Code}}", + "style": "diamond", + "leading_diamond": "", + "trailing_diamond": "", + "type": "status" + } + ], + "type": "rprompt" + } + ], + "final_space": true, + "version": 3 +} diff --git a/roles/pi_modern_shell_env/tasks/main.yml b/roles/pi_modern_shell_env/tasks/main.yml index 67d290f..be6dd7a 100644 --- a/roles/pi_modern_shell_env/tasks/main.yml +++ b/roles/pi_modern_shell_env/tasks/main.yml @@ -17,21 +17,35 @@ dest: /usr/local/bin/bat state: link -- name: Download starship install script +- name: Download oh-my-posh install script ansible.builtin.get_url: - url: "{{ pi_modern_shell_env_starship_install_url }}" - dest: /tmp/starship-install.sh + url: "{{ pi_modern_shell_env_ohmyposh_install_url }}" + dest: /tmp/ohmyposh-install.sh mode: "0755" -- name: Run starship install script +- name: Run oh-my-posh install script ansible.builtin.command: - cmd: /tmp/starship-install.sh --yes - creates: /usr/local/bin/starship + cmd: /tmp/ohmyposh-install.sh -d /usr/local/bin + creates: /usr/local/bin/oh-my-posh - name: Determine target user's home directory ansible.builtin.set_fact: pi_modern_shell_env_target_home: "{{ '/root' if pi_modern_shell_env_target_user == 'root' else '/home/' + pi_modern_shell_env_target_user }}" +- name: Ensure config directory exists + ansible.builtin.file: + path: "{{ pi_modern_shell_env_target_home }}/.config" + state: directory + owner: "{{ pi_modern_shell_env_target_user }}" + mode: "0700" + +- name: Deploy oh-my-posh theme + ansible.builtin.copy: + src: ohmyposh.json + dest: "{{ pi_modern_shell_env_target_home }}/.config/ohmyposh.json" + owner: "{{ pi_modern_shell_env_target_user }}" + mode: "0644" + - name: Ensure fish config directory exists ansible.builtin.file: path: "{{ pi_modern_shell_env_target_home }}/.config/fish" diff --git a/roles/pi_modern_shell_env/templates/config.fish.j2 b/roles/pi_modern_shell_env/templates/config.fish.j2 index 89cac5c..6f3542f 100644 --- a/roles/pi_modern_shell_env/templates/config.fish.j2 +++ b/roles/pi_modern_shell_env/templates/config.fish.j2 @@ -1,6 +1,52 @@ {{ ansible_managed | comment }} -starship init fish | source +set -g fish_key_bindings fish_vi_key_bindings + +# Debian's fzf package ships its fish integration as loose scripts rather +# than auto-loading them; source them so fzf-file-widget/fzf-cd-widget +# exist for fish_user_key_bindings to bind below. +if test -f /usr/share/doc/fzf/examples/key-bindings.fish + source /usr/share/doc/fzf/examples/key-bindings.fish +end +if test -f /usr/share/doc/fzf/examples/completion.fish + source /usr/share/doc/fzf/examples/completion.fish +end + +function fish_user_key_bindings + # Accept the current autosuggestion regardless of vi mode (by default + # vi-mode only binds this in insert mode). + for mode in insert default visual + bind -M $mode \cf forward-char + end + + # Esc in visual mode returns to normal mode instead of leaving vi mode + # bindings altogether. + bind -M visual escape 'set fish_bind_mode default; commandline -f repaint' + + # Replace fzf's ctrl-t/alt-c defaults with alt-f/alt-d for the file and + # cd widgets, bound in both insert and normal mode. + for mode in insert default + bind -e -M $mode \ct + bind -e -M $mode \ec + bind -M $mode alt-f 'fzf-file-widget; commandline -f repaint' + bind -M $mode alt-d 'fzf-cd-widget; commandline -f repaint' + end +end + +# oh-my-posh's vi-mode segment reads $FISH__BIND_MODE (set here on every +# mode change) rather than fish's own $fish_bind_mode, and needs the +# built-in mode prompt silenced so it isn't drawn twice. +function rerender_on_bind_mode_change --on-variable fish_bind_mode + if test "$fish_bind_mode" != paste -a "$fish_bind_mode" != "$FISH__BIND_MODE" + set -gx FISH__BIND_MODE $fish_bind_mode + omp_repaint_prompt + end +end +function fish_default_mode_prompt; end +function fish_mode_prompt; end +set -x VIRTUAL_ENV_DISABLE_PROMPT 1 + +oh-my-posh init fish --config ~/.config/ohmyposh.json | source abbr --add ls lsd abbr --add ll 'lsd -l' diff --git a/roles/pi_standard_setup/files/raspi-leds-off.service b/roles/pi_standard_setup/files/raspi-leds-off.service index f02399f..4790c72 100644 --- a/roles/pi_standard_setup/files/raspi-leds-off.service +++ b/roles/pi_standard_setup/files/raspi-leds-off.service @@ -2,6 +2,8 @@ Description=Turn Raspi LEDs off at boot [Service] +Type=oneshot +RemainAfterExit=yes ExecStart=/usr/sbin/raspi-leds-off.sh [Install] diff --git a/roles/pi_standard_setup/files/raspi-leds-off.sh b/roles/pi_standard_setup/files/raspi-leds-off.sh index 62bd8a2..bcbfd45 100644 --- a/roles/pi_standard_setup/files/raspi-leds-off.sh +++ b/roles/pi_standard_setup/files/raspi-leds-off.sh @@ -1,7 +1,11 @@ #!/bin/bash -echo none > /sys/class/leds/led0/trigger -echo none > /sys/class/leds/led1/trigger +for led in led0 led1 ACT PWR; do + path="/sys/class/leds/$led" + if [ -d "$path" ]; then + echo none > "$path/trigger" 2>/dev/null + echo 0 > "$path/brightness" 2>/dev/null + fi +done -echo 0 >/sys/class/leds/led0/brightness -echo 0 >/sys/class/leds/led1/brightness +exit 0