Here's how such a role-based Ansible dotfiles configuration would work: ## Directory Structure ``` dotfiles/ ├── playbook.yml # Main playbook ├── group_vars/ │ └── all.yml # Your config with default_roles ├── roles/ │ ├── system/ │ │ ├── tasks/main.yml │ │ ├── vars/main.yml │ │ └── files/ │ ├── git/ │ │ ├── tasks/main.yml │ │ ├── templates/ │ │ │ └── .gitconfig.j2 │ │ └── vars/main.yml │ ├── neovim/ │ │ ├── tasks/main.yml │ │ └── files/ │ └── zsh/ │ ├── tasks/main.yml │ ├── files/ │ │ └── .zshrc │ └── vars/main.yml └── inventory ``` ## Main Configuration (`group_vars/all.yml`) ```yaml # Your system configuration default_roles: - system - git - neovim - zsh - docker # Global variables dotfiles_dir: "{{ ansible_env.HOME }}/dotfiles" config_dir: "{{ ansible_env.HOME }}/.config" # Git configuration git_user_name: "Johannes" git_user_email: "johannes@example.com" git_editor: "nvim" # System packages to install system_packages: - curl - wget - unzip - tree ``` ## Main Playbook (`playbook.yml`) ```yaml --- - name: Setup development environment hosts: localhost connection: local become: yes become_method: sudo pre_tasks: - name: Create btrfs snapshot before changes shell: | sudo btrfs subvolume snapshot / /.snapshots/before-dotfiles-$(date +%Y%m%d-%H%M%S) ignore_errors: yes tags: [snapshot] roles: "{{ default_roles }}" post_tasks: - name: Summary of installed roles debug: msg: "Completed setup for: {{ default_roles | join(', ') }}" ``` ## Example Roles ### System Role (`roles/system/tasks/main.yml`) ```yaml --- - name: Install system packages package: name: "{{ system_packages }}" state: present - name: Ensure .config directory exists file: path: "{{ config_dir }}" state: directory mode: '0755' - name: Set up shell as default user: name: "{{ ansible_env.USER }}" shell: /usr/bin/zsh when: "'zsh' in default_roles" ``` ### Git Role (`roles/git/tasks/main.yml`) ```yaml --- - name: Install git package: name: git state: present - name: Check if custom gitconfig exists stat: path: "{{ dotfiles_dir }}/git/.gitconfig" register: custom_gitconfig - name: Use custom gitconfig if available file: src: "{{ dotfiles_dir }}/git/.gitconfig" dest: "{{ ansible_env.HOME }}/.gitconfig" state: link force: yes when: custom_gitconfig.stat.exists - name: Generate gitconfig from template if no custom config template: src: .gitconfig.j2 dest: "{{ ansible_env.HOME }}/.gitconfig" mode: '0644' when: not custom_gitconfig.stat.exists ``` ### Git Template (`roles/git/templates/.gitconfig.j2`) ```ini [user] name = {{ git_user_name }} email = {{ git_user_email }} [core] editor = {{ git_editor }} autocrlf = input [init] defaultBranch = main [push] default = simple ``` ### Neovim Role (`roles/neovim/tasks/main.yml`) ```yaml --- - name: Install neovim package: name: neovim state: present - name: Check for custom neovim config stat: path: "{{ dotfiles_dir }}/nvim" register: nvim_config - name: Symlink neovim config file: src: "{{ dotfiles_dir }}/nvim" dest: "{{ config_dir }}/nvim" state: link force: yes when: nvim_config.stat.exists - name: Create basic neovim config if none exists copy: content: | -- Basic Neovim configuration vim.opt.number = true vim.opt.expandtab = true vim.opt.tabstop = 2 vim.opt.shiftwidth = 2 dest: "{{ config_dir }}/nvim/init.lua" mode: '0644' when: not nvim_config.stat.exists ``` ### ZSH Role (`roles/zsh/tasks/main.yml`) ```yaml --- - name: Install zsh package: name: zsh state: present - name: Install oh-my-zsh shell: | sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" --unattended args: creates: "{{ ansible_env.HOME }}/.oh-my-zsh" - name: Check for custom zshrc stat: path: "{{ dotfiles_dir }}/zsh/.zshrc" register: custom_zshrc - name: Symlink custom zshrc file: src: "{{ dotfiles_dir }}/zsh/.zshrc" dest: "{{ ansible_env.HOME }}/.zshrc" state: link force: yes when: custom_zshrc.stat.exists ``` ## Usage **To install everything:** ```bash ansible-playbook -i inventory playbook.yml --ask-become-pass ``` **To install only specific roles:** ```bash ansible-playbook -i inventory playbook.yml --ask-become-pass --tags "git,neovim" ``` **To override roles temporarily:** ```bash ansible-playbook -i inventory playbook.yml --ask-become-pass -e "default_roles=['system','git']" ``` ## Benefits of This Approach 1. **Modular**: Each tool is a separate role 2. **Configurable**: Easy to enable/disable tools in `all.yml` 3. **Reusable**: Roles can be shared across different machines 4. **Flexible**: Can override variables per host/group 5. **Fallback configs**: Generates basic configs when custom ones don't exist 6. **Scalable**: Easy to add new tools without touching existing code This approach lets you manage your entire development environment as code while keeping it organized and maintainable!