From 764115fa499e03f40087765011f48a2c7be366c7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 31 Dec 2025 06:20:13 +0000 Subject: [PATCH 1/3] Enhance bootstrap playbook with desktop detection and normalized installs Add automatic desktop vs server detection using systemd default target, with macOS always treated as desktop. This enables installing full desktop apps (VS Code, 1Password) on desktops while using CLI-only versions on servers. Changes: - Add is_desktop variable with auto-detection and manual override support - Normalize macOS installation to prefer Homebrew for all available tools (bat, eza, starship, atuin, zsh plugins, 1Password CLI) - Add missing tools from zsh config audit: - zsh-autosuggestions and zsh-syntax-highlighting - 1Password CLI (op) with proper apt/brew setup - pnpm via corepack - VS Code CLI for server environments - Skip cargo installation of tools available via Homebrew on macOS - Add desktop-only casks: visual-studio-code, 1password - Update documentation with new options --- .config/dotfiles/playbook.yml | 183 +++++++++++++++++++++++++++++++--- 1 file changed, 169 insertions(+), 14 deletions(-) diff --git a/.config/dotfiles/playbook.yml b/.config/dotfiles/playbook.yml index 4592fcf..664c242 100644 --- a/.config/dotfiles/playbook.yml +++ b/.config/dotfiles/playbook.yml @@ -7,7 +7,17 @@ # Update tools: uvx --from ansible-core ansible-playbook playbook.yml # Check mode: uvx --from ansible-core ansible-playbook playbook.yml --check # Dev tools: uvx --from ansible-core ansible-playbook playbook.yml --extra-vars "dev_mode=true" +# Force server mode: uvx --from ansible-core ansible-playbook playbook.yml --extra-vars "is_desktop=false" # Specific category: uvx --from ansible-core ansible-playbook playbook.yml --tags homebrew +# +# Variables: +# dev_mode: Install additional development tools (default: false) +# is_desktop: Install desktop apps vs CLI-only tools (auto-detected, override with true/false) +# +# Desktop detection: +# - macOS: Always treated as desktop +# - Linux: Checks systemd default target (graphical.target = desktop) +# - Override with --extra-vars "is_desktop=true" or "is_desktop=false" - name: Manage development environment hosts: localhost @@ -17,6 +27,26 @@ vars: dev_mode: false force: false + # Desktop detection: macOS is always desktop, Linux checks systemd target + # Override with --extra-vars "is_desktop=true" or "is_desktop=false" + _linux_desktop: "{{ ansible_service_mgr == 'systemd' and (ansible_facts['default_target'] | default('multi-user.target')) == 'graphical.target' }}" + is_desktop: "{{ is_desktop | default(true if ansible_os_family == 'Darwin' else _linux_desktop) }}" + + pre_tasks: + - name: Gather systemd default target (Linux only) + command: systemctl get-default + register: systemd_target + changed_when: false + failed_when: false + when: ansible_system == "Linux" and ansible_service_mgr == "systemd" + + - name: Set desktop fact from systemd target + set_fact: + is_desktop: "{{ is_desktop | default(true if ansible_os_family == 'Darwin' else (systemd_target.stdout | default('multi-user.target')) == 'graphical.target') }}" + + - name: Display system type + debug: + msg: "System type: {{ 'Desktop' if is_desktop else 'Server' }} ({{ ansible_os_family }})" tasks: # ======================================== @@ -44,28 +74,56 @@ register: brew_update changed_when: "'Already up-to-date' not in brew_update.stdout" - - name: Install/upgrade essential Homebrew packages + # Essential CLI tools - installed on all macOS systems + - name: Install/upgrade essential Homebrew formulae shell: brew install {{ item }} || brew upgrade {{ item }} || true loop: - - fzf + - bat + - eza - fd - - ripgrep - - mise + - fzf - jq - - bat + - mise + - ripgrep + - starship + - atuin + - zsh-autosuggestions + - zsh-syntax-highlighting register: brew_install changed_when: "'Upgrading' in brew_install.stdout or 'Installing' in brew_install.stdout" - - name: Install/upgrade dev Homebrew packages + # Dev tools - only in dev mode + - name: Install/upgrade dev Homebrew formulae shell: brew install {{ item }} || brew upgrade {{ item }} || true loop: + - bat-extras - gh - yq - - bat-extras register: brew_dev_install changed_when: "'Upgrading' in brew_dev_install.stdout or 'Installing' in brew_dev_install.stdout" when: dev_mode | bool + # 1Password CLI - requires special tap + - name: Tap 1Password CLI + shell: brew tap 1password/tap + changed_when: false + failed_when: false + + - name: Install 1Password CLI + shell: brew install 1password-cli || brew upgrade 1password-cli || true + register: op_install + changed_when: "'Upgrading' in op_install.stdout or 'Installing' in op_install.stdout" + + # Desktop-only casks + - name: Install desktop Homebrew casks + shell: brew install --cask {{ item }} || brew upgrade --cask {{ item }} || true + loop: + - visual-studio-code + - 1password + register: brew_cask_install + changed_when: "'Upgrading' in brew_cask_install.stdout or 'Installing' in brew_cask_install.stdout" + when: is_desktop | bool + - name: Clean up Homebrew shell: brew cleanup register: brew_cleanup @@ -90,29 +148,83 @@ become: yes register: apt_upgrade + # Essential CLI tools for all Linux systems - name: Install essential apt packages apt: name: - - fzf - fd-find + - fzf + - jq - ripgrep + - zsh-autosuggestions + - zsh-syntax-highlighting state: latest become: yes + # Dev tools - only in dev mode - name: Install dev apt packages apt: name: - - jq - gh state: latest become: yes when: dev_mode | bool + # Desktop-only packages + - name: Install desktop apt packages + apt: + name: + - code + state: latest + become: yes + when: is_desktop | bool + failed_when: false + + # ======================================== + # 1PASSWORD CLI (Linux) + # ======================================== + - name: Install 1Password CLI on Linux + tags: [setup, tools] + when: ansible_pkg_mgr == "apt" + block: + - name: Check if 1Password CLI is installed + command: which op + register: op_check + changed_when: false + failed_when: false + + - name: Add 1Password apt repository key + shell: | + curl -sS https://downloads.1password.com/linux/keys/1password.asc | \ + gpg --dearmor --output /usr/share/keyrings/1password-archive-keyring.gpg + args: + creates: /usr/share/keyrings/1password-archive-keyring.gpg + become: yes + when: op_check.rc != 0 + + - name: Add 1Password apt repository + shell: | + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/1password-archive-keyring.gpg] https://downloads.1password.com/linux/debian/$(dpkg --print-architecture) stable main" | \ + tee /etc/apt/sources.list.d/1password.list + args: + creates: /etc/apt/sources.list.d/1password.list + become: yes + when: op_check.rc != 0 + + - name: Install 1Password CLI via apt + apt: + name: 1password-cli + state: latest + update_cache: yes + become: yes + # ======================================== # MISE # ======================================== - - name: Install and update mise + # On macOS, mise is installed via Homebrew. On Linux, use the installer script. + - name: Install mise (Linux only) tags: [mise, setup] + when: ansible_os_family != "Darwin" block: - name: Check if mise is installed stat: @@ -177,8 +289,10 @@ changed_when: "'Updating' in cargo_updates.stdout" failed_when: false - - name: Install essential Rust tools + # On Linux, install eza and bat via cargo (on macOS these come from Homebrew) + - name: Install essential Rust tools (Linux only) tags: [setup, rust] + when: ansible_os_family != "Darwin" shell: "{{ ansible_env.HOME }}/.cargo/bin/cargo install {{ item }}" args: creates: "{{ ansible_env.HOME }}/.cargo/bin/{{ item }}" @@ -188,8 +302,9 @@ - name: Build bat cache tags: [setup, rust] - shell: "{{ ansible_env.HOME }}/.cargo/bin/bat cache --build" + shell: bat cache --build changed_when: false + failed_when: false - name: Install Rust dev tools tags: [setup, rust, dev] @@ -289,8 +404,10 @@ creates: "{{ ansible_env.HOME }}/.local/bin/claude" when: not claude_check.stat.exists - - name: Install Starship prompt + # On macOS, starship is installed via Homebrew. On Linux, use the installer script. + - name: Install Starship prompt (Linux only) tags: [setup, tools] + when: ansible_os_family != "Darwin" block: - name: Check if Starship is installed stat: @@ -303,8 +420,10 @@ creates: "{{ ansible_env.HOME }}/.local/bin/starship" when: not starship_check.stat.exists - - name: Install/update Atuin + # On macOS, atuin is installed via Homebrew. On Linux, use the installer script. + - name: Install/update Atuin (Linux only) tags: [setup, tools] + when: ansible_os_family != "Darwin" block: - name: Check if Atuin is installed command: which atuin @@ -323,6 +442,42 @@ changed_when: "'Updated' in atuin_update.stdout or 'updated' in atuin_update.stdout" failed_when: false + # pnpm - installed via corepack (comes with Node.js via mise) + - name: Enable pnpm via corepack + tags: [setup, tools] + block: + - name: Check if corepack is available + command: which corepack + register: corepack_check + changed_when: false + failed_when: false + + - name: Enable pnpm via corepack + shell: corepack enable pnpm + when: corepack_check.rc == 0 + changed_when: false + failed_when: false + + # VS Code CLI for server environments (desktop gets full VS Code via package manager) + - name: Install VS Code CLI (server only) + tags: [setup, tools] + when: not is_desktop | bool + block: + - name: Check if code CLI is installed + command: which code + register: code_cli_check + changed_when: false + failed_when: false + + - name: Download and install VS Code CLI + shell: | + curl -Lk 'https://code.visualstudio.com/sha/download?build=stable&os=cli-alpine-x64' -o /tmp/vscode_cli.tar.gz + tar -xf /tmp/vscode_cli.tar.gz -C {{ ansible_env.HOME }}/.local/bin + rm /tmp/vscode_cli.tar.gz + args: + creates: "{{ ansible_env.HOME }}/.local/bin/code" + when: code_cli_check.rc != 0 + - name: Setup tmux plugin manager tags: [setup, tools] block: From 12983ca8ed5934dc64eba8c836be0d2afb2d9253 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 31 Dec 2025 06:28:43 +0000 Subject: [PATCH 2/3] Add system path fallback for zsh-syntax-highlighting The plugin now checks both Homebrew and APT paths, consistent with how zsh-autosuggestions is loaded. This ensures the plugin works on Linux systems where it's installed via apt. --- .config/zsh/tools.zsh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.config/zsh/tools.zsh b/.config/zsh/tools.zsh index f8e8ba8..842ab18 100644 --- a/.config/zsh/tools.zsh +++ b/.config/zsh/tools.zsh @@ -41,8 +41,12 @@ ZSH_AUTO_SCRIPT="zsh-autosuggestions/zsh-autosuggestions.zsh" [[ -e /usr/share/$ZSH_AUTO_SCRIPT ]] && source /usr/share/$ZSH_AUTO_SCRIPT # Zsh syntax highlighting -[[ -d "$BREW_PREFIX/share/zsh-syntax-highlighting" ]] && { - source "$BREW_PREFIX/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" +ZSH_HL_SCRIPT="zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" +ZSH_HL_PATH="" +[[ -e "$BREW_PREFIX/share/$ZSH_HL_SCRIPT" ]] && ZSH_HL_PATH="$BREW_PREFIX/share/$ZSH_HL_SCRIPT" +[[ -z "$ZSH_HL_PATH" && -e "/usr/share/$ZSH_HL_SCRIPT" ]] && ZSH_HL_PATH="/usr/share/$ZSH_HL_SCRIPT" +[[ -n "$ZSH_HL_PATH" ]] && { + source "$ZSH_HL_PATH" # Disable underline (( ${+ZSH_HIGHLIGHT_STYLES} )) || typeset -A ZSH_HIGHLIGHT_STYLES ZSH_HIGHLIGHT_STYLES[path]=none From 0afbf16a79d4a5a634fcb43163bef0e3472cfebd Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Jan 2026 05:52:11 +0000 Subject: [PATCH 3/3] Document desktop/server detection in tool management Update README.md and bin/README.md with: - New --extra-vars flag for overriding desktop detection - Explanation of auto-detection behavior (macOS=desktop, Linux=systemd) - Desktop vs server tool differences (GUI apps vs CLI-only) - Additional tool-update flags (--no-sudo, --extra-vars) --- README.md | 10 ++++++++++ bin/README.md | 12 +++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b5030a..450cd79 100644 --- a/README.md +++ b/README.md @@ -104,8 +104,18 @@ tool-update --tags mise # Include dev tools in update tool-update --dev + +# Force server mode (CLI-only tools, no desktop apps) +tool-update --extra-vars "is_desktop=false" ``` +**Desktop vs Server Detection:** +- macOS is always treated as a desktop system +- Linux auto-detects based on systemd target (`graphical.target` = desktop) +- Desktop systems get full GUI apps (VS Code, 1Password app) +- Server systems get CLI-only tools (VS Code CLI, 1Password CLI) +- Override with `--extra-vars "is_desktop=true"` or `"is_desktop=false"` + To add new tools, edit `.config/dotfiles/playbook.yml`. To change runtime versions (node, python, etc.), edit `.mise.toml`. ### Shell Script Linting diff --git a/bin/README.md b/bin/README.md index e26ba0b..5409bbd 100644 --- a/bin/README.md +++ b/bin/README.md @@ -23,10 +23,20 @@ This directory contains personal utility scripts that are part of my dotfiles se - `--check`: Show what would be updated (dry-run) - `--diff`: Show changes that would be made -- `--tags `: Update only specific tools (e.g., `homebrew`, `mise`) +- `--tags `: Update only specific tools (e.g., `homebrew`, `mise`, `rust`, `uv`) - `--dev`: Include dev tools in updates +- `--no-sudo`: Skip sudo password prompt (for passwordless sudo) +- `--extra-vars "key=value"`: Pass variables to Ansible (e.g., `is_desktop=false`) - `-v`: Verbose output +**Desktop vs Server Mode:** + +The playbook auto-detects desktop vs server environments: +- macOS: Always treated as desktop +- Linux: Detects via systemd target (`graphical.target` = desktop) + +Desktop systems install full GUI apps (VS Code, 1Password). Server systems install CLI-only versions. Override with `--extra-vars "is_desktop=false"`. + **Description:** Wrapper around Ansible playbook for convenient daily/weekly tool updates. ## File and System Utilities