Skip to content

🎯 From zero to hero: Deploy your entire dev paradise in minutes

License

Notifications You must be signed in to change notification settings

igrybkov/dotfiles

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

68 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

dotfiles

Ansible-based macOS environment setup with profile-based configuration management.

License: MIT macOS Ansible

dotfiles demo

Overview

Automated macOS development environment setup powered by Ansible. One command installs Homebrew packages, symlinks dotfiles, configures SSH and Git per profile, manages secrets with Ansible Vault, and applies macOS system settings. The profile system lets you separate work, personal, and private configurations β€” and keep private profiles in their own git repos.

This repo ships with an opinionated set of packages and tools across four topical profiles (shell, neovim, development, macos-desktop). Fork it and make it yours β€” swap in your own packages, dotfiles, and profiles.

Features

  • Profile-based configuration β€” work, personal, private profiles that combine freely
  • Homebrew, Cask, and Mac App Store automation
  • Dotfile symlinking with XDG config directory support
  • Per-profile SSH and Git configuration
  • Secret management with Ansible Vault
  • Private profiles as separate git repos β€” keep sensitive configs out of your public dotfiles
  • CLI with shell completions for fish, bash, and zsh
  • 18 modular Ansible roles β€” use what you need, ignore the rest
  • macOS system settings automation
  • Auto-bootstrapping β€” installs Homebrew, mise, uv, and Ansible on first run

Table of Contents

Prerequisites

  • macOS Ventura (13) or later
  • Xcode Command Line Tools: xcode-select --install

Everything else β€” Homebrew, Python, Ansible, mise, uv β€” is installed automatically on first run.

Quick Start

  1. Fork and clone:

    git clone https://github.com/<your-username>/dotfiles.git ~/.dotfiles
    cd ~/.dotfiles
  2. Configure profiles and settings:

    ./dotfiles config
  3. Install everything:

    ./dotfiles install --all

After installation, dotfiles is available globally from any directory.

What just happened?
  • Homebrew formulae and casks were installed
  • Dotfiles were symlinked to your home directory and ~/.config/
  • SSH config was generated from your profile settings
  • Git config blocks were written per profile
  • macOS system preferences were applied
  • Shell was set (if configured)
  • Mac App Store apps were installed (if mas tag was included)
  • The dotfiles CLI was linked to ~/.local/bin/dotfiles for global access

Customization

This repo is designed to be forked and customized. The built-in profiles (shell, neovim, development, macos-desktop) contain the author's preferred tools β€” replace them with your own.

Adding packages

Edit the appropriate profile's config.yml β€” e.g., profiles/shell/config.yml for CLI tools, profiles/development/config.yml for dev tools, or profiles/{profile}/config.yml for profile-specific packages:

brew_packages:
  - name: ripgrep
  - name: fd

cask_packages:
  - name: visual-studio-code
  - name: firefox

mas_packages:
  - name: Magnet
    id: 441258766

Then run: dotfiles install brew cask mas

Adding dotfiles

Place files in the appropriate profile directory:

Source Destination Method
files/dotfiles/{file} ~/.{file} Symlink
files/dotfiles/config/{dir} ~/.config/{dir} Symlink
files/dotfiles-copy/{file} ~/{file} Copy
files/bin/{script} ~/.local/bin/{script} Symlink

All paths are relative to profiles/{profile}/. Then run: dotfiles install dotfiles

Local overrides

Create config.yml in the repository root to override any profile variables. This file is git-ignored, so your local tweaks won't affect version control.

CLI Commands

Command Description
dotfiles install [TAGS] Install packages and configure system
dotfiles config Interactive profile and settings configuration
dotfiles sync Pull, upgrade dependencies, push
dotfiles upgrade Upgrade mise, Ansible Galaxy, Python packages
dotfiles pull / push Git operations (main repo + profile repos)
dotfiles edit Open dotfiles in $EDITOR
dotfiles secret <cmd> Manage Ansible Vault secrets
dotfiles completion <shell> Generate or install shell completions
dotfiles profile list List all profiles with status and priority
dotfiles profile bootstrap <name> Create a new profile

Install examples

dotfiles install --all                       # Everything
dotfiles install dotfiles brew               # Specific tags
dotfiles install --profile shell,work brew    # Specific profiles + tags
dotfiles install --sync --all                # Sync before installing
dotfiles install -v                          # Verbose (-vv, -vvv for more)
dotfiles install --dry-run                   # Preview changes without applying

Run dotfiles <command> --help for full options on any command.

Profiles

Why Profiles?

  • Privacy: Keep work configs (corporate GitHub orgs, internal tools) in private repos
  • Modularity: Separate concerns between different environments
  • Portability: Share your main dotfiles publicly while keeping sensitive configs private

Profile structure

profiles/
β”œβ”€β”€ shell/                     # Core CLI tools (priority 100)
β”‚   β”œβ”€β”€ config.yml
β”‚   └── files/dotfiles/
β”œβ”€β”€ neovim/                    # Editor configuration (priority 110)
β”‚   β”œβ”€β”€ config.yml
β”‚   └── files/dotfiles/
β”œβ”€β”€ development/               # Dev tools (priority 120)
β”‚   β”œβ”€β”€ config.yml
β”‚   └── files/bin/
β”œβ”€β”€ macos-desktop/             # GUI apps, fonts (priority 130)
β”‚   β”œβ”€β”€ config.yml
β”‚   └── files/dotfiles/
β”œβ”€β”€ work/                      # Work-specific configuration
β”‚   └── config.yml
β”œβ”€β”€ personal/                  # Personal configuration
β”‚   └── config.yml
└── private/                   # Private profiles (git-ignored)
    └── mycompany/             # Separate git repo per company/context
        β”œβ”€β”€ config.yml
        β”œβ”€β”€ files/dotfiles/    # Profile dotfiles to symlink
        β”œβ”€β”€ files/bin/         # Profile scripts
        β”œβ”€β”€ tasks/main.yml     # Custom Ansible tasks
        β”œβ”€β”€ roles/             # Custom Ansible roles
        β”œβ”€β”€ secrets/           # Vault-encrypted secrets
        └── .git/              # Managed as a separate git repo

Configuration example

# profiles/private/mycompany/config.yml
---
host:
  name: mycompany-profile
  priority: 200

brew_packages:
  - name: internal-tool

ssh_client_config:
  - host: "*.internal.company.com"
    identity_file: ~/.ssh/company_key
    remote_user: myuser

Creating a profile

dotfiles profile bootstrap private/mycompany          # Creates private profile with git repo
dotfiles profile bootstrap private/mycompany --no-git  # Without git initialization
dotfiles profile bootstrap mycompany                   # Creates shared (public) profile

Private profile git repos

Private profiles live in profiles/private/ (git-ignored) and can be managed as separate git repositories:

cd profiles/private/mycompany
git remote add origin git@github.com:you/dotfiles-mycompany.git
git push -u origin main

The pull, push, and sync commands automatically discover and sync profile git repos.

See docs/profiles.md for nested profiles, custom tasks, and advanced configuration.

Available Tags

Run specific parts of the setup using tags:

Tag Description
all Run everything
brew Install Homebrew formulae
brew-packages Install Homebrew formulae (alias for brew)
cask Install Homebrew casks
taps Configure Homebrew taps
mas Install Mac App Store apps
dotfiles Symlink dotfiles to home directory
gitconfig Configure git (profile blocks)
ssh Configure SSH
python Python environment setup
pip Install Python packages
pipx Install pipx packages
gem Install Ruby gems
npm Install npm global packages
composer Install PHP Composer packages
docker Docker configuration
mise Install mise-managed tools
fonts Install fonts
chsh Change default shell
gh-extensions Install GitHub CLI extensions
gh-repos Clone GitHub repositories
mcp-servers Configure MCP servers for Claude
coding-agents Configure coding agent tools
cursor-cli Install Cursor CLI
json-config Manage JSON configuration files
yaml-config Manage YAML configuration files

Secret Management

dotfiles secret init                          # Create global vault password
dotfiles secret init -p shell                 # Create profile-specific vault password
dotfiles secret set -p shell mcp.api_key      # Set a secret (prompts for value)
dotfiles secret get -p shell mcp.api_key      # Retrieve a secret

Secrets are encrypted with Ansible Vault. Each profile can have its own secrets file and vault password.

See docs/secrets.md for the full reference including profile secrets, editing, rekeying, and more.

Architecture

The system runs a four-play Ansible playbook:

  1. Gather Facts β€” collect system information (all hosts, linear strategy)
  2. Bootstrap β€” one-time setup: macOS settings, Homebrew installation (localhost)
  3. Per-Profile Setup β€” profile-specific tasks: dotfiles, pipx, MCP servers (all profile hosts)
  4. Finalize β€” aggregation across profiles: brew packages, SSH config, git config, etc. (localhost)

Key design choices:

  • Dynamic inventory β€” profiles are discovered automatically via a custom inventory plugin
  • Aggregation pattern β€” the Finalize play collects variables from all profiles and merges them before executing once
  • Mitogen β€” used as the connection strategy for faster execution

See docs/architecture.md for detailed architecture documentation.

Project Structure

β”œβ”€β”€ dotfiles                  # CLI wrapper script (entry point)
β”œβ”€β”€ playbook.yml              # Main Ansible playbook
β”œβ”€β”€ packages/                 # Python packages (UV workspace)
β”‚   β”œβ”€β”€ dotfiles_cli/         # Main CLI (Click-based)
β”‚   β”œβ”€β”€ dotfiles_profile_discovery/  # Shared profile discovery
β”‚   └── symlink_dotfiles/     # Dotfile symlinking logic
β”œβ”€β”€ ansible_plugins/          # Custom Ansible plugins
β”‚   β”œβ”€β”€ inventory/            # Dynamic profile inventory
β”‚   β”œβ”€β”€ lookup/               # Aggregation lookup
β”‚   β”œβ”€β”€ filter/               # Custom Jinja2 filters
β”‚   └── action/               # Custom action plugins
β”œβ”€β”€ profiles/                 # Profile configurations
β”‚   β”œβ”€β”€ shell/                # Core CLI tools (priority 100)
β”‚   β”œβ”€β”€ neovim/               # Editor configuration (priority 110)
β”‚   β”œβ”€β”€ development/          # Dev tools (priority 120)
β”‚   β”œβ”€β”€ macos-desktop/        # GUI apps, fonts (priority 130)
β”‚   β”œβ”€β”€ work/                 # Work-specific configuration
β”‚   β”œβ”€β”€ personal/             # Personal configuration
β”‚   └── private/              # Private profiles (git-ignored, nested repos)
β”œβ”€β”€ roles/                    # Ansible roles (18 modular roles)
β”œβ”€β”€ schemas/                  # JSON schemas for config validation
β”œβ”€β”€ molecule/                 # Integration tests
β”œβ”€β”€ docs/                     # Documentation
└── config.yml                # Local overrides (git-ignored)

Troubleshooting

First run is slow

The initial run bootstraps all dependencies (Homebrew, mise, uv, Ansible, Python). Subsequent runs are much faster.

Homebrew permission issues

Run sudo chown -R $(whoami) /usr/local/ (Intel) or ensure /opt/homebrew/ is owned by your user (Apple Silicon).

How do I remove a package?

Set state: absent in your profile's config.yml and run the relevant install tag. Ansible will uninstall it for you.

How do I skip certain tags?

Use Ansible's skip-tags directly: mise x -- ansible-playbook playbook.yml --skip-tags mas,docker

How do I use this with an existing Homebrew setup?

It works out of the box. The installer only adds packages listed in your profiles β€” it won't modify or remove existing packages.

Dotfile conflicts

If a target file already exists and isn't a symlink, the dotfiles role will skip it. Back up or remove the existing file to allow symlinking.

Contributing

Contributions are welcome. Please follow Conventional Commits for commit messages and run mise x -- uv run pre-commit run --all-files before submitting.

See CONTRIBUTING.md for the full guide.

Acknowledgments

License

MIT

About

🎯 From zero to hero: Deploy your entire dev paradise in minutes

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •