dotfiles/manjaro/arch_custom/CLAUDE (1).md

17 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Overview

This is an Ansible playbook repository for automating Arch Linux development environment setup. It configures a local workstation with development tools, window managers, shells, and productivity applications. The playbooks are designed to run locally (not against remote hosts) using localhost as the target.

The project has evolved from monolithic playbooks to a modular, role-based architecture with feature flags for flexible deployments.

Project Structure

arch_custom/
├── playbooks/
│   ├── local_setup.yml          # MAIN PLAYBOOK (production)
│   └── debug/                   # Archived legacy playbooks
│       ├── playbook.yml         # Original monolithic version
│       ├── playbook-2.yml       # Iteration attempt
│       └── local_setups.yml     # Incomplete test version
├── roles/
│   ├── custom/                  # Core system setup (modular)
│   │   ├── tasks/
│   │   │   ├── main.yml         # Base packages, yay, UV, Hyprland, productivity tools
│   │   │   ├── capps.yml        # Development apps (PyCharm, R/RStudio, databases)
│   │   │   └── terms.yml        # Terminal setup (Alacritty, TMUX with TPM)
│   │   └── files/
│   │       └── alacritty.yml    # Alacritty configuration file
│   ├── manage_zsh/              # Zsh with optional Powerlevel10k (modular)
│   │   ├── tasks/
│   │   │   ├── main.yml         # Install packages via pacman, copy .zshrc, fastfetch
│   │   │   ├── zsh-plugin.yml   # Git clone method for plugins (conditional)
│   │   │   └── zsh-p10k.yml     # Oh My Zsh + Powerlevel10k (conditional)
│   │   ├── files/
│   │   │   ├── .zshrc           # Zsh configuration for Powerlevel10k
│   │   │   └── .p10k.zsh        # Powerlevel10k theme configuration
│   │   ├── defaults/
│   │   ├── handlers/
│   │   └── vars/
│   ├── starship/                # Starship prompt (production-ready)
│   │   ├── tasks/
│   │   │   └── main.yml         # Install Starship + modern CLI tools
│   │   └── files/
│   │       ├── starship.toml    # Starship prompt configuration
│   │       └── zshrc            # Zsh config optimized for Starship
│   └── geerlingguy.docker/      # External Galaxy role for Docker
├── inventories/
│   └── hosts                    # Localhost inventory with groups
├── requirement.yml              # Ansible Galaxy role dependencies
├── ansible.cfg                  # Ansible configuration (local connection, logging, facts cache)
├── pyproject.toml               # Python dependencies (ansible, ansible-lint, yamllint)
├── README.md                    # User-facing documentation
└── CLAUDE.md                    # This file (developer documentation)

Key Architecture Decisions

Execution Model

  • All playbooks target localhost with connection: local
  • Uses privilege escalation (become: yes) for system package installation
  • Switches to non-root (become: false or become_user: "{{ user }}") for user-specific configurations
  • Variables username and user_home are derived from the executing user's environment

Modular Role Architecture

The project has evolved significantly:

  • Legacy: Monolithic playbooks (now in playbooks/debug/)
  • Current: Modular roles with sub-task files and feature flags

Custom Role Modularity:

  • main.yml: Core system setup (packages, yay, UV, system tools)
  • capps.yml: Development applications (PyCharm, R, RStudio, databases)
  • terms.yml: Terminal emulators and multiplexers (Alacritty, TMUX with TPM)

Manage_zsh Role Modularity:

  • main.yml: Package installation via pacman, .zshrc deployment, fastfetch setup
  • zsh-plugin.yml: Git clone method for plugins (used when not using pacman)
  • zsh-p10k.yml: Oh My Zsh + Powerlevel10k installation (conditional on USE_P10K)

Shell Configuration Strategy:

  • Two mutually exclusive shell prompt options controlled by feature flags
  • USE_STARSHIP: true → Modern, fast, Rust-based prompt (current default)
  • USE_P10K: true → Feature-rich, customizable prompt with Oh My Zsh
  • Only one should be enabled at a time to avoid conflicts

Feature Flags

The playbook uses boolean variables for conditional component installation:

USE_STARSHIP: true           # Enable Starship prompt (recommended)
USE_P10K: false              # Enable Powerlevel10k with Oh My Zsh
USE_ALACRITTY: false         # Set Alacritty as default terminal
INSTALL_CUSTOM_APPS: false   # Install development apps (PyCharm, R, databases)
install_tiling_wm: true      # Install tiling window manager

These allow users to customize their installation without modifying role code.

Package Management Strategy

  • Official Arch packages: community.general.pacman module
  • AUR packages:
    • Method 1: ansible.builtin.command with yay -S --noconfirm <package>
    • Method 2: community.general.pacman with executable: yay
  • Yay installation: Handled conditionally (checks if already installed before building)
  • Ansible Galaxy roles: Installed via ansible-galaxy install -r requirement.yml

Zsh Plugin Management:

  • Default approach (manage_zsh): Install via pacman system packages
    • zsh-autosuggestions, zsh-syntax-highlighting, zsh-completions, zsh-transient-prompt
    • Source from /usr/share/zsh/plugins/ paths
    • Benefits: Automatic updates with pacman -Syu, clean uninstall
  • Alternative approach (starship): Git clone to ~/.zsh/ directory
    • Used for cross-distro compatibility in dotfiles
    • Manual updates required

User Context Switching Pattern

Tasks frequently switch between root and user contexts:

# System package installation (as root)
- name: Install packages
  community.general.pacman:
    name: package-name
    state: present
  # Implicit: become: yes (from playbook level)

# User-specific configuration (as non-root)
- name: Copy config file
  become: false
  ansible.builtin.copy:
    src: config.yml
    dest: "{{ user_home }}/.config/app/config.yml"

# Explicit user context
- name: Install AUR package
  become: false
  ansible.builtin.command: yay -S --noconfirm package-name

External Role Dependencies

The project uses geerlingguy.docker role from Ansible Galaxy:

  • Professional, well-maintained role for Docker installation
  • Handles Docker service management and user group setup
  • Version pinned in requirement.yml: 7.3.0
  • Install before running playbook: ansible-galaxy install -r requirement.yml

Configuration Variables

Required Variables (set in playbooks/local_setup.yml)

username: "{{ lookup('env', 'USER') }}"     # Auto-detected from environment
user_home: "/home/{{ username }}"           # User's home directory
user: "{{ username }}"                      # Alias for role compatibility
home: "{{ user_home }}"                     # Alias for role compatibility

Feature Flags

USE_STARSHIP: true           # Enable Starship shell prompt
USE_P10K: false              # Enable Powerlevel10k (don't enable with Starship)
USE_ALACRITTY: false         # Set Alacritty as default terminal emulator
INSTALL_CUSTOM_APPS: false   # Install PyCharm, R/RStudio, databases
install_tiling_wm: true      # Install tiling window manager
tiling_wm: "i3-gaps"         # Which tiling WM (i3-gaps, sway, awesome, qtile, bspwm)
git_server_port: 3000        # Port for local Git server (if enabled)

Common Commands

First-Time Setup

# Install Ansible on Arch Linux
sudo pacman -S ansible

# Set up Python environment for linting (optional)
uv sync
source .venv/bin/activate

# Install Galaxy role dependencies
ansible-galaxy install -r requirement.yml

# Run the main playbook
ansible-playbook playbooks/local_setup.yml

Run with Specific Tags

# Only setup core packages and tools
ansible-playbook playbooks/local_setup.yml --tags custom

# Only setup Zsh environment
ansible-playbook playbooks/local_setup.yml --tags manage_zsh

# Only setup Starship prompt
ansible-playbook playbooks/local_setup.yml --tags starship

# Only setup Docker
ansible-playbook playbooks/local_setup.yml --tags docker

# Run all base setup tasks (custom + manage_zsh + starship + docker)
ansible-playbook playbooks/local_setup.yml --tags base_setup

# Skip Docker installation
ansible-playbook playbooks/local_setup.yml --skip-tags docker

Validation and Testing

# Syntax check
ansible-playbook playbooks/local_setup.yml --syntax-check

# Dry run (check mode)
ansible-playbook playbooks/local_setup.yml --check

# Run with verbose output
ansible-playbook playbooks/local_setup.yml -vv

# Linting (requires uv environment)
uv sync && source .venv/bin/activate
ansible-lint playbooks/local_setup.yml
yamllint .

Inventory Management

# View inventory structure
ansible-inventory --list

# View inventory graph
ansible-inventory --graph

Development Workflow

Adding New Roles

  1. Create role structure: ansible-galaxy init roles/<role-name>
  2. Add tasks to roles/<role-name>/tasks/main.yml
  3. Define defaults in roles/<role-name>/defaults/main.yml if needed
  4. Store config files in roles/<role-name>/files/ for the copy module
  5. Include role in playbooks/local_setup.yml:
    roles:
      - role: <role-name>
        tags:
          - <role-name>
          - base_setup  # Optional: include in base setup
    

Modularizing Existing Roles

When a role's main.yml becomes too large, split into focused sub-task files:

Example: Custom role structure

# roles/custom/tasks/main.yml
---
- name: Base system setup
  # Core tasks...

- name: Include development apps
  include_tasks: capps.yml
  when: INSTALL_CUSTOM_APPS

- name: Include terminal setup
  include_tasks: terms.yml

Sub-task files:

  • capps.yml: Development applications (PyCharm, R, databases)
  • terms.yml: Terminal emulators and configurations

This pattern keeps files focused (< 200 lines) and improves readability.

Adding Configuration Files

Store configuration files in roles/<role-name>/files/:

# Reference file in copy task
- name: Configure application
  ansible.builtin.copy:
    src: config.yml              # Looks in roles/<role-name>/files/config.yml
    dest: "{{ user_home }}/.config/app/config.yml"
    mode: '0644'

For inline content, use content parameter:

- name: Create config file
  ansible.builtin.copy:
    dest: "{{ user_home }}/.config/app/config.yml"
    content: |
      key: value
      setting: true      
    mode: '0644'

Handling AUR Packages

Standard pattern for AUR packages:

- name: Install AUR package
  become: false
  ansible.builtin.command: yay -S --noconfirm <package-name>
  register: result
  changed_when: result.rc == 0
  failed_when: result.rc != 0 and 'error' in result.stderr

Alternative using pacman module:

- name: Install AUR package
  become: false
  community.general.pacman:
    name: <package-name>
    state: present
    executable: yay
    extra_args: "--builddir /tmp/yay"

Managing Shell Plugins

For Powerlevel10k (manage_zsh role):

  • Plugins installed via pacman: Edit roles/manage_zsh/tasks/main.yml
  • Add package to the pacman task list
  • Source in .zshrc from /usr/share/zsh/plugins/<plugin-name>/

For Starship (starship role):

  • Plugins cloned via git: Edit roles/starship/tasks/main.yml
  • Add git clone task for the plugin
  • Source in zshrc from ~/.zsh/<plugin-name>/

Conditional Role Execution

Use when clauses with feature flags:

roles:
  - role: starship
    when: USE_STARSHIP
    tags:
      - starship
      - base_setup

  - role: manage_zsh
    when: USE_P10K
    tags:
      - manage_zsh
      - base_setup

Internal conditionals in roles:

# In manage_zsh/tasks/main.yml
roles:
  - role: zsh-plugin
    when: USE_P10K
  - role: zsh-p10k
    when: USE_P10K

Facts Caching

Facts are cached to ./facts_cache/ with a 2-hour timeout:

# Clear cache if needed
rm -rf facts_cache/*

# Disable cache for testing
ansible-playbook playbooks/local_setup.yml --flush-cache

Important Patterns and Conventions

Variable Naming

  • Feature flags: USE_<FEATURE> or INSTALL_<FEATURE> (boolean)
  • User variables: username, user_home, user, home
  • Role-specific: Prefix with role name (manage_zsh_user)

Task Naming

  • Prefix with role name: Manage_zsh | Install required packages
  • Use descriptive, action-oriented names
  • Follow pattern: <Role> | <Action> <Target>

File Organization

  • Keep main.yml under 200 lines
  • Split into logical sub-task files when needed
  • Use include_tasks for conditional sub-tasks
  • Store static files in files/ directory
  • Use templates/ for Jinja2 templates (if needed)

Idempotency

Ensure tasks can be run multiple times safely:

# Check before creating
- name: Check if exists
  ansible.builtin.stat:
    path: /path/to/resource
  register: resource_check

- name: Create resource
  when: not resource_check.stat.exists
  # ... creation task

# Use creates argument
- name: Build and install
  ansible.builtin.command: makepkg -si --noconfirm
  args:
    creates: /usr/bin/executable

Error Handling

# Allow task to fail without stopping playbook
- name: Optional task
  ansible.builtin.command: some-command
  failed_when: false

# Define specific failure conditions
- name: Install package
  ansible.builtin.command: yay -S --noconfirm package
  register: result
  failed_when: result.rc != 0 and 'error' in result.stderr

# Ignore specific return codes
- name: Check for resource
  ansible.builtin.command: which program
  register: check
  changed_when: false
  failed_when: check.rc not in [0, 1]

Logs and Debugging

Log Files

  • Ansible logs: ./ansible.log (configured in ansible.cfg)
  • Location: Project root directory
  • Persistent across runs (appends)

Verbosity Levels

# Standard output
ansible-playbook playbooks/local_setup.yml

# Show task results (-v)
ansible-playbook playbooks/local_setup.yml -v

# Show task inputs and outputs (-vv)
ansible-playbook playbooks/local_setup.yml -vv

# Debug level (-vvv)
ansible-playbook playbooks/local_setup.yml -vvv

# Connection debug level (-vvvv)
ansible-playbook playbooks/local_setup.yml -vvvv

Debugging Specific Tasks

# Start at specific task
ansible-playbook playbooks/local_setup.yml --start-at-task="Task Name"

# Run single task using tags
ansible-playbook playbooks/local_setup.yml --tags custom -vv

# Step through tasks interactively
ansible-playbook playbooks/local_setup.yml --step

Debug Tasks

Add debug tasks to troubleshoot:

- name: Debug user variables
  ansible.builtin.debug:
    msg:
      - "Username: {{ username }}"
      - "Home: {{ user_home }}"
      - "Variable: {{ my_var | default('NOT SET') }}"

Post-Installation Manual Steps

Document these for users in README.md:

  1. Docker group: Log out and back in for group changes
  2. TMUX plugins: Run tmux then press Ctrl+Space + I
  3. Starship config: Edit ~/.config/starship.toml for customization
  4. Powerlevel10k: Run p10k configure for initial setup
  5. Python venvs: Set up per-project environments with uv venv

Testing Recommendations

  1. Test in VM: Use Arch Linux VM for testing before deploying to main system
  2. Use check mode: Always run with --check first
  3. Tag-based testing: Test individual roles before full run
  4. Incremental approach: Add new features behind feature flags
  5. Version control: Commit working states before major changes

Common Issues and Solutions

Yay build failures

  • Ensure base-devel is installed
  • Check disk space for build directory
  • Clear yay cache: yay -Sc

Plugin sourcing errors

  • Verify plugin path exists: ls -la ~/.zsh/ or /usr/share/zsh/plugins/
  • Check .zshrc syntax: zsh -n ~/.zshrc
  • Ensure plugin installed before sourcing

Permission errors

  • Verify become: false for user-specific tasks
  • Check file ownership: ls -la ~/.config/
  • Use become_user for explicit user context

Role not found

  • Install Galaxy dependencies: ansible-galaxy install -r requirement.yml
  • Check role path in ansible.cfg: roles_path = ./roles
  • Verify role directory structure exists

Migration Notes

If working with older versions of this playbook:

  1. Legacy playbooks are in playbooks/debug/ (archived, not deleted)
  2. Old monolithic custom role has been split into modular sub-tasks
  3. Plugin installation migrated from git clone to pacman packages (where possible)
  4. Feature flags introduced for conditional installations
  5. geerlingguy.docker replaced inline Docker tasks

When updating from old versions:

  • Review feature flags in playbooks/local_setup.yml
  • Run ansible-galaxy install -r requirement.yml for new dependencies
  • Check README.md for updated configuration variables
  • to memorize