527 lines
17 KiB
Markdown
527 lines
17 KiB
Markdown
# 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:
|
|
|
|
```yaml
|
|
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:
|
|
```yaml
|
|
# 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)
|
|
```yaml
|
|
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
|
|
```yaml
|
|
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
|
|
```bash
|
|
# 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
|
|
```bash
|
|
# 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
|
|
```bash
|
|
# 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
|
|
```bash
|
|
# 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`:
|
|
```yaml
|
|
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**
|
|
```yaml
|
|
# 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/`:
|
|
|
|
```yaml
|
|
# 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:
|
|
```yaml
|
|
- 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:
|
|
```yaml
|
|
- 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:
|
|
```yaml
|
|
- 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:
|
|
```yaml
|
|
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:
|
|
```yaml
|
|
# 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:
|
|
```bash
|
|
# 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:
|
|
```yaml
|
|
# 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
|
|
```yaml
|
|
# 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
|
|
```bash
|
|
# 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
|
|
```bash
|
|
# 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:
|
|
```yaml
|
|
- 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 |