From f554eb890197c09e55243cf9a317ab6bb1b2400f Mon Sep 17 00:00:00 2001 From: Travis Herbranson Date: Thu, 23 Apr 2026 12:40:21 -0400 Subject: [PATCH] Phase 2: Chezmoi dotfile orchestration + fastfetch --- CLAUDE.md | 51 ++++++++++-- roles/cli_modern/defaults/main.yml | 13 ++- roles/cli_modern/tasks/chezmoi.yml | 81 +++++++++++++++++++ roles/cli_modern/tasks/main.yml | 5 ++ roles/cli_modern/tasks/packages-archlinux.yml | 3 +- roles/cli_modern/tasks/packages-debian.yml | 3 +- roles/cli_modern/templates/config.fish.j2 | 9 +++ 7 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 roles/cli_modern/tasks/chezmoi.yml diff --git a/CLAUDE.md b/CLAUDE.md index 1448b5b..78ed874 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,10 +1,11 @@ # CLI Standardization Ansible project to deploy a modern CLI environment (Fish + Starship + modern coreutils) -to Arch and Ubuntu/Debian hosts. +to Arch and Ubuntu/Debian hosts. Includes optional Chezmoi dotfile orchestration. ## What it installs +**Phase 1 — packages and config templates:** - **Fish shell** 4.x (PPA on Ubuntu, pacman on Arch) - **Starship** prompt (official binary installer) - **fzf** — fuzzy finder @@ -13,8 +14,14 @@ to Arch and Ubuntu/Debian hosts. - **eza** — modern ls - **fd** — modern find - **ripgrep** — fast grep +- **fastfetch** — system info on login - **Fisher** plugin manager + plugins +**Phase 2 — Chezmoi dotfile sync (opt-in):** +- **Chezmoi** — dotfile manager synced from Gitea +- Manages: Fish config, Starship, Ghostty, Zellij +- Machine-type aware: servers skip Ghostty/Zellij configs + ## Dev workflow ```bash @@ -27,9 +34,11 @@ ansible-playbook playbook.yml --syntax-check # Dry run ansible-playbook playbook.yml -i inventory.yml --check --diff -# Deploy +# Deploy (Phase 1 only — default) ansible-playbook playbook.yml -i inventory.yml -ansible-playbook playbook.yml -i inventory.yml --limit manjaro-dev + +# Deploy with Chezmoi (Phase 2) +ansible-playbook playbook.yml -i inventory.yml -e cli_modern_install_chezmoi=true -e cli_modern_chezmoi_repo=git@gitea:herbygitea/dotfiles.git ``` ## Role structure @@ -43,15 +52,47 @@ roles/cli_modern/ │ ├── packages-debian.yml # Ubuntu/Debian packages + Fish PPA │ ├── starship.yml # Starship binary install │ ├── fisher.yml # Fisher + plugin sync -│ ├── config.yml # Deploy config templates -│ └── default-shell.yml # Set Fish as login shell +│ ├── config.yml # Deploy config templates (Phase 1) +│ ├── default-shell.yml # Set Fish as login shell +│ └── chezmoi.yml # Chezmoi install + apply (Phase 2) └── templates/ ├── config.fish.j2 # Fish shell config ├── starship.toml.j2 # Starship prompt config └── fish_plugins.j2 # Fisher plugin list ``` +## Dotfiles repo (Phase 2) + +Separate repo at `/opt/projects/pbs-workshop/dotfiles/`, to be pushed to Gitea. +Contains placeholder configs — replace with real dotfiles before enabling Chezmoi. + +``` +dotfiles/ +├── .chezmoi.toml.tmpl # Prompted config (name, email, machine type) +├── .chezmoiignore # Skips Ghostty/Zellij on servers +├── dot_config/ +│ ├── fish/config.fish # PLACEHOLDER +│ ├── fish/fish_plugins # Fisher plugin list +│ ├── ghostty/config # PLACEHOLDER (workstations only) +│ ├── starship.toml # PLACEHOLDER +│ └── zellij/config.kdl # PLACEHOLDER (workstations only) +``` + ## Configuration All options are in `roles/cli_modern/defaults/main.yml`. Override per-host in `host_vars/` or per-group in `group_vars/`. + +### Phase 1 vs Phase 2 config management + +Phase 1 deploys Fish/Starship configs via Ansible templates. Phase 2 uses +Chezmoi from a Git repo. When enabling Chezmoi, set the Phase 1 template +flags to false to avoid conflicts: + +```yaml +cli_modern_deploy_fish_config: false +cli_modern_deploy_starship_config: false +cli_modern_install_chezmoi: true +cli_modern_chezmoi_repo: "git@gitea:herbygitea/dotfiles.git" +cli_modern_chezmoi_machine_type: "workstation" # or "server" +``` diff --git a/roles/cli_modern/defaults/main.yml b/roles/cli_modern/defaults/main.yml index f52729a..33592c4 100644 --- a/roles/cli_modern/defaults/main.yml +++ b/roles/cli_modern/defaults/main.yml @@ -37,6 +37,9 @@ cli_modern_install_fd: true # ripgrep — fast grep cli_modern_install_ripgrep: true +# fastfetch — system info on login (neofetch successor) +cli_modern_install_fastfetch: true + # Fisher plugin manager — auto-installed on first Fish launch cli_modern_install_fisher: true @@ -48,6 +51,14 @@ cli_modern_fisher_plugins: - "meaningful-ooo/sponge" - "jorgebucaran/autopair.fish" -# Deploy config templates +# Deploy config templates (Phase 1 — Ansible-managed templates) +# Set to false when using Chezmoi (Phase 2) to avoid conflicts cli_modern_deploy_fish_config: true cli_modern_deploy_starship_config: true + +# ── Phase 2: Chezmoi dotfile orchestration ────────────────── +cli_modern_install_chezmoi: false +# Gitea repo URL for dotfiles (SSH format) +cli_modern_chezmoi_repo: "" +# Machine type passed to chezmoi init ("workstation" or "server") +cli_modern_chezmoi_machine_type: "server" diff --git a/roles/cli_modern/tasks/chezmoi.yml b/roles/cli_modern/tasks/chezmoi.yml new file mode 100644 index 0000000..750bafd --- /dev/null +++ b/roles/cli_modern/tasks/chezmoi.yml @@ -0,0 +1,81 @@ +--- +# Phase 2: Install Chezmoi and apply dotfiles from Gitea repo. + +- name: Check if Chezmoi is already installed + ansible.builtin.command: chezmoi --version + register: cli_modern_chezmoi_check + changed_when: false + failed_when: false + +# --- Install Chezmoi (Arch) --- + +- name: Install Chezmoi via pacman + become: true + community.general.pacman: + name: chezmoi + state: present + when: + - cli_modern_chezmoi_check.rc != 0 + - ansible_os_family == "Archlinux" + +# --- Install Chezmoi (Debian/Ubuntu) --- + +- name: Download Chezmoi install script + ansible.builtin.get_url: + url: https://get.chezmoi.io + dest: /tmp/chezmoi-install.sh + mode: "0755" + when: + - cli_modern_chezmoi_check.rc != 0 + - ansible_os_family == "Debian" + +- name: Install Chezmoi via official installer + become: true + ansible.builtin.command: /tmp/chezmoi-install.sh -b /usr/local/bin + args: + creates: /usr/local/bin/chezmoi + when: + - cli_modern_chezmoi_check.rc != 0 + - ansible_os_family == "Debian" + +- name: Clean up Chezmoi installer + ansible.builtin.file: + path: /tmp/chezmoi-install.sh + state: absent + when: + - cli_modern_chezmoi_check.rc != 0 + - ansible_os_family == "Debian" + +# --- Initialize and apply dotfiles --- + +- name: Check if Chezmoi is already initialized + ansible.builtin.stat: + path: "{{ cli_modern_user_home }}/.local/share/chezmoi" + register: cli_modern_chezmoi_dir + +- name: Initialize Chezmoi from dotfiles repo + become: true + become_user: "{{ cli_modern_user }}" + ansible.builtin.command: > + chezmoi init {{ cli_modern_chezmoi_repo }} + --promptString "Your name={{ cli_modern_user }}" + --promptString "Your email=" + --promptChoice "Machine type={{ cli_modern_chezmoi_machine_type }}" + --no-tty + args: + chdir: "{{ cli_modern_user_home }}" + creates: "{{ cli_modern_user_home }}/.local/share/chezmoi/.git" + when: + - cli_modern_chezmoi_repo | length > 0 + - not cli_modern_chezmoi_dir.stat.exists + +- name: Apply Chezmoi dotfiles + become: true + become_user: "{{ cli_modern_user }}" + ansible.builtin.command: chezmoi apply --no-tty + args: + chdir: "{{ cli_modern_user_home }}" + register: cli_modern_chezmoi_apply + changed_when: cli_modern_chezmoi_apply.rc == 0 + when: + - cli_modern_chezmoi_repo | length > 0 diff --git a/roles/cli_modern/tasks/main.yml b/roles/cli_modern/tasks/main.yml index a6296dc..0784289 100644 --- a/roles/cli_modern/tasks/main.yml +++ b/roles/cli_modern/tasks/main.yml @@ -24,3 +24,8 @@ - name: Set Fish as default shell ansible.builtin.include_tasks: default-shell.yml when: cli_modern_set_default_shell + +# Phase 2: Chezmoi dotfile orchestration +- name: Install Chezmoi and apply dotfiles + ansible.builtin.include_tasks: chezmoi.yml + when: cli_modern_install_chezmoi diff --git a/roles/cli_modern/tasks/packages-archlinux.yml b/roles/cli_modern/tasks/packages-archlinux.yml index d3d45e6..2a67d05 100644 --- a/roles/cli_modern/tasks/packages-archlinux.yml +++ b/roles/cli_modern/tasks/packages-archlinux.yml @@ -17,6 +17,7 @@ (['bat'] if cli_modern_install_bat else []) + (['eza'] if cli_modern_install_eza else []) + (['fd'] if cli_modern_install_fd else []) + - (['ripgrep'] if cli_modern_install_ripgrep else []) + (['ripgrep'] if cli_modern_install_ripgrep else []) + + (['fastfetch'] if cli_modern_install_fastfetch else []) }} when: cli_modern_arch_packages | length > 0 diff --git a/roles/cli_modern/tasks/packages-debian.yml b/roles/cli_modern/tasks/packages-debian.yml index 4f96734..74a3541 100644 --- a/roles/cli_modern/tasks/packages-debian.yml +++ b/roles/cli_modern/tasks/packages-debian.yml @@ -63,7 +63,8 @@ (['bat'] if cli_modern_install_bat else []) + (['eza'] if cli_modern_install_eza else []) + (['fd-find'] if cli_modern_install_fd else []) + - (['ripgrep'] if cli_modern_install_ripgrep else []) + (['ripgrep'] if cli_modern_install_ripgrep else []) + + (['fastfetch'] if cli_modern_install_fastfetch else []) }} when: cli_modern_deb_packages | length > 0 diff --git a/roles/cli_modern/templates/config.fish.j2 b/roles/cli_modern/templates/config.fish.j2 index 5e439b8..1ada878 100644 --- a/roles/cli_modern/templates/config.fish.j2 +++ b/roles/cli_modern/templates/config.fish.j2 @@ -54,4 +54,13 @@ if status is-interactive # Suppress Fish greeting set -g fish_greeting +{% if cli_modern_install_fastfetch %} + # ── System Info ───────────────────────────────────────── + # Show machine identity on first shell in a login session. + # Skips nested shells and non-login to keep things snappy. + if status is-login + fastfetch + end +{% endif %} + end