No description
  • Shell 41.8%
  • Go Template 32.5%
  • Lua 25.2%
  • Dockerfile 0.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-02 00:53:45 +01:00
.chezmoiscripts feat(zsh): generate rg completions at apply time, autoloaded from fpath 2026-07-14 15:26:32 +01:00
.claude Retire Fish shell; Zsh is now the only managed shell 2026-06-20 15:00:16 +01:00
.github/workflows chore(deps): update actions/checkout action to v7.0.0 (#60) 2026-07-12 15:14:50 +01:00
bench perf(zsh): defer plugin sourcing to first idle (-13% time-to-first-prompt) 2026-07-14 15:12:06 +01:00
dev/raycast-scripts Add Tuna + Raycast launcher scripts, gated by OS and app install 2026-06-15 11:18:20 +01:00
dot_claude fix(claude): mark settings.json private so it stops fighting Claude Code 2026-07-25 03:32:09 +01:00
private_dot_config chore(mise): bump node LTS pin to 24.18.1 (security release) 2026-08-02 00:53:45 +01:00
private_dot_gnupg Manage gpg-agent.conf so fresh machines wire up pinentry 2026-05-08 13:00:45 +01:00
private_Library/Scripts Add Tuna + Raycast launcher scripts, gated by OS and app install 2026-06-15 11:18:20 +01:00
scripts feat(bench): add tuning-routine prompt + behaviour-snapshot guard (#61) 2026-06-24 00:29:05 +01:00
.chezmoi.toml.tmpl feat(mise): make hunkdiff an opt-in per-machine tool 2026-07-26 00:35:46 +01:00
.chezmoiexternal.toml.tmpl feat(zsh): replace zsh-abbr with a built-in expansion widget; git shortcuts → abbreviations 2026-06-24 00:29:05 +01:00
.chezmoiignore feat(bench): add zsh startup benchmark + profiler harness (#61) 2026-06-24 00:29:05 +01:00
.chezmoiremove feat(zsh): replace zsh-abbr with a built-in expansion widget; git shortcuts → abbreviations 2026-06-24 00:29:05 +01:00
.dockerignore fix: convert remaining CRLF files to LF and enforce repo-wide 2026-02-12 13:32:57 +00:00
.editorconfig Retire Fish shell; Zsh is now the only managed shell 2026-06-20 15:00:16 +01:00
.gitattributes fix: convert remaining CRLF files to LF and enforce repo-wide 2026-02-12 13:32:57 +00:00
.gitignore Retire Fish shell; Zsh is now the only managed shell 2026-06-20 15:00:16 +01:00
CLAUDE.md feat(mise): make heavy language runtimes opt-in per machine 2026-06-23 02:00:11 +01:00
docker-test.sh Retire Fish shell; Zsh is now the only managed shell 2026-06-20 15:00:16 +01:00
Dockerfile Retire Fish shell; Zsh is now the only managed shell 2026-06-20 15:00:16 +01:00
dot_gitconfig.tmpl Enable SSH commit signing and prefer helix editor 2026-04-21 11:34:24 +01:00
dot_inputrc fix: convert dot_inputrc from CRLF to LF line endings 2026-02-12 13:29:07 +00:00
dot_lesskey fix: convert remaining CRLF files to LF and enforce repo-wide 2026-02-12 13:32:57 +00:00
dot_zshenv.tmpl Retire Fish shell; Zsh is now the only managed shell 2026-06-20 15:00:16 +01:00
dot_zshrc.tmpl fix(zsh): invalidate _evalcache when the resolved binary path changes 2026-07-14 20:31:00 +01:00
install.sh Retire Fish shell; Zsh is now the only managed shell 2026-06-20 15:00:16 +01:00
README.md feat(zsh): extend update to cover mise + bun and restart the shell 2026-07-14 20:31:10 +01:00
renovate.json feat(mise): make heavy language runtimes opt-in per machine 2026-06-23 02:00:11 +01:00
run_once_before_purge-orphaned-repo-meta.sh fix(chezmoi): purge orphaned repo-meta files via run_once script 2026-06-10 11:21:21 +01:00
SCRIPTS.md feat(bench): add zsh startup benchmark + profiler harness (#61) 2026-06-24 00:29:05 +01:00
setup-aliases.sh feat: initial commit 2025-08-08 02:43:50 +01:00
test-chezmoi-data.yaml feat: initial commit 2025-08-08 02:43:50 +01:00
test.sh fix(test): make hunk assertion respect the opt-in gate 2026-07-26 01:01:29 +01:00

Cross-Platform Dotfiles with Chezmoi

This repository contains my personal dotfiles managed by chezmoi, supporting both Linux and macOS. Zsh is the shell.

Features

  • 🦓 Zsh configuration with cross-platform support
  • 🍎 macOS-specific optimizations (Homebrew, Ghostty, etc.)
  • 🐧 Linux compatibility
  • 🛠️ Useful shell functions and utilities
  • 📦 Template-based configuration for different environments
  • 🔧 mise for language runtimes (Node.js always; Go, Java, Python opt-in per machine; bun self-managed)
  • 🌊 mise aqua backend for the modern CLI suite (bat, fd, eza, kubectl, gh, etc.)
  • 🔍 Modern CLI tools: Complete suite of replacements for traditional Unix tools
  • Starship cross-shell prompt with git integration
  • 📜 Atuin for enhanced shell history (better search, statistics, deduplication)
  • 🧹 Automatic cleanup of old tool installations

Prerequisites

  • Git
  • curl or wget
  • sudo access (for package installation)

Installation

# One-liner installation
curl -fsLS https://raw.githubusercontent.com/ryanlewis/dotfiles/main/install.sh | bash

Or if you prefer using wget:

wget -qO- https://raw.githubusercontent.com/ryanlewis/dotfiles/main/install.sh | bash

Alternative: Using Chezmoi Directly

If you already have chezmoi installed:

chezmoi init --apply ryanlewis/dotfiles

Environment Variables

Control installation behavior with these environment variables:

# Skip all mise-managed tools (runtimes + CLI suite) for a fast, minimal bootstrap
QUICK_INSTALL=true curl -fsLS https://raw.githubusercontent.com/ryanlewis/dotfiles/main/install.sh | bash

# Provide git config to avoid prompts
CHEZMOI_USER_NAME="Your Name" CHEZMOI_USER_EMAIL="you@example.com" \
  curl -fsLS https://raw.githubusercontent.com/ryanlewis/dotfiles/main/install.sh | bash

What Gets Installed

The installation process automatically sets up:

  1. Zsh - Interactive shell with plugins and abbreviations
  2. chezmoi - Dotfiles manager
  3. mise - Universal version manager
  4. Modern CLI tools:
    • eza - Better ls
    • bat - Better cat with syntax highlighting
    • ripgrep - Better grep
    • fd - Better find
    • fzf - Fuzzy finder
    • zoxide - Smarter cd
    • starship - Cross-shell prompt
    • btop - Better top
    • duf - Better df
    • dust - Better du
    • gum - Pretty shell scripts
    • kubectl - Kubernetes CLI
    • kubectx - K8s context switcher
    • kubens - K8s namespace switcher
    • And more...
  5. Language runtimes:
    • Node.js — always installed via mise (the dotfiles tooling depends on it)
    • Bun — installed by its official installer and self-updated via bun upgrade (deliberately not pinned in mise)
    • Go, Java (Temurin), Python/Miniconda — opt-in per machine, chosen at chezmoi init (default off, so VMs/CI stay lean). See Per-machine language runtimes.

Usage

Common Commands

  • chezmoi diff - See what changes would be made
  • chezmoi apply - Apply the configuration
  • chezmoi add ~/.config/zsh/functions/foo.zsh - Add a new file
  • chezmoi edit ~/.zshrc - Edit a managed file
  • chezmoi update - Pull latest changes and apply

Zsh Functions

This configuration includes several useful Zsh functions:

  • mkcd <dir> - Create a directory and cd into it
  • backup <file> - Create a timestamped backup of a file
  • extract <archive> - Extract various archive formats
  • update - Update mise tools, system packages (brew/apt/dnf/pacman) and bun, then restart the shell
  • ports - Show listening ports
  • myip - Display local and public IP addresses
  • yank - Copy text to clipboard via OSC 52 (works over SSH)
  • mise-setup - Show configured mise tools and install hints
  • mise-install-latest - Install latest stable versions of all tools
  • mise-update - Update mise and all plugins

Clipboard Function: yank

The yank function enables clipboard access from anywhere, even over SSH:

# Copy command output
git diff | yank
cat ~/.ssh/id_rsa.pub | yank
echo "some text" | yank

# Works over SSH - copies to your local clipboard!
ssh server "cat /var/log/nginx/error.log | yank"

Why use yank?

  • Works over SSH without X11 forwarding
  • Universal solution across different terminals (iTerm2, Terminal.app, Alacritty, Windows Terminal, etc.)
  • No need for platform-specific tools (pbcopy/xclip)

FZF-Powered Functions

  • fcd - Fuzzy change directory with preview
  • fopen - Fuzzy find and open file in editor
  • fkill - Fuzzy find and kill processes
  • fgrep <term> - Fuzzy grep with file preview
  • fgit <cmd> - Interactive git operations:
    • fgit add - Stage files interactively
    • fgit checkout - Checkout branches with preview
    • fgit log - Browse git log with commit preview
    • fgit diff - View file diffs interactively

macOS-Specific Features

When running on macOS, additional features are enabled:

  • Homebrew integration
  • GNU coreutils in PATH (if installed)
  • macOS-specific aliases:
    • flushdns - Flush DNS cache
    • ql <file> - Quick Look preview
    • showfiles/hidefiles - Toggle hidden files in Finder
    • showdesktop/hidedesktop - Toggle desktop icons
    • afk - Start the screensaver (lock away from keyboard)
    • cleanup - Remove .DS_Store files

Modern CLI Tools

This configuration includes a comprehensive suite of modern CLI tools:

File & Directory Tools

  • eza - Modern ls replacement
    • Icons, colors, git integration, tree view
    • Aliased to replace all ls variants
  • fd - Better find
    • Simple syntax, respects .gitignore
    • Default file finder for fzf
  • ripgrep - Better grep
    • Extremely fast, respects .gitignore
    • Powers the fgrep function
  • bat - Better cat
    • Syntax highlighting, line numbers
    • File previews in fzf
  • zoxide - Smarter cd
    • Learns your most used directories
    • Jump with partial names

System Monitoring

  • btop - Better top/htop
    • Beautiful terminal UI
    • Aliased to replace top and htop
  • duf - Better df
    • User-friendly disk usage display
  • dust - Better du
    • Intuitive disk usage analyzer

Development Tools

  • lazygit - Terminal UI for git
    • Interactive staging, branching, merging
    • Launch with lg
  • delta - Better git diffs
    • Syntax highlighting, side-by-side view
    • Auto-configured in gitconfig
  • hunk - Diff viewer/pager
    • npm package hunkdiff; run as hunk
  • gh - GitHub CLI
    • Manage PRs, issues from terminal
  • cf - Cloudflare CLI
    • Manage DNS, zones, Workers deploys from terminal
  • httpie - Better curl
    • Human-friendly HTTP client
    • https alias for HTTPS requests
  • jq - JSON processor
    • Query and manipulate JSON data
  • just - Modern make
    • Simpler command runner
  • worktrunk - Git worktree manager
    • Create/switch worktrees fast; launched via wt (and the wsc abbreviation)
  • biome - JS/TS formatter and linter
    • Single fast toolchain for formatting and linting
  • uv - Fast Python package/project manager
  • Neovim - Hyperextensible modal editor; resolved as $EDITOR
    • Launched via nvim (also aliased to vim); personal config lives in ~/.config/nvim
  • helix - Modal editor with LSP built in
    • Launched via hx; kept as a fallback editor
  • ktlint - Kotlin linter/formatter (macOS only)
  • md-tui - Markdown viewer (TUI)
    • Render and browse markdown in the terminal; launched via mdt

Shell Enhancements

  • fzf - Fuzzy finder
    • Ctrl+R (history), Ctrl+T (files), Alt+C (directories)
    • Powers many custom functions
  • starship - Cross-shell prompt
    • Fast, customizable, git-aware
    • Pre-configured with icons
  • atuin - Better shell history
    • Advanced fuzzy search, statistics, intelligent deduplication
  • direnv - Directory environments
    • Auto-load .envrc files
  • broot - Better tree
    • Navigate directories efficiently
  • tldr - Simplified man pages
    • Quick command examples

Container & Kubernetes Tools

  • kubectl - Kubernetes CLI
    • Manage Kubernetes clusters
  • kubectx - Context switcher
    • Quickly switch between Kubernetes contexts
  • kubens - Namespace switcher
    • Quickly switch between Kubernetes namespaces

Version Management with mise

This configuration includes mise for managing:

Language Runtimes:

  • Node.js (LTS) — always installed (via mise)
  • Go — opt-in per machine
  • Java (Eclipse Temurin LTS) — opt-in per machine
  • Python (via Miniconda) — opt-in per machine

(Bun is installed separately by its official installer and self-updates via bun upgrade — it is not managed by mise.)

Per-machine language runtimes

node is always installed via mise because the dotfiles tooling depends on it; bun is installed by its own official installer and self-updates via bun upgrade (not mise-managed). The heavier, project-specific runtimes — Go, Java, Python/Miniconda — are off by default and opted into per machine:

  • Choosing at setup: chezmoi init prompts "Install Go/Java/Python on this machine?" (default no). In CI or any non-interactive bootstrap the prompts are skipped and nothing extra is installed, so VMs stay lean.
  • Changing later: re-run chezmoi init --prompt (or edit ~/.config/chezmoi/chezmoi.toml), then chezmoi apply. mise picks up the new selection from the managed ~/.config/mise/conf.d/runtimes.toml.
  • Version pins for these runtimes live in private_dot_config/mise/conf.d/runtimes.toml.tmpl and are still bumped by Renovate.
  • Removing a runtime: turn it off and apply; mise leaves the old install in place. Every chezmoi apply prints the current selection and flags any runtime that's installed but no longer selected, with the mise uninstall / mise prune command to reclaim the disk. Nothing is deleted automatically.
  • Ad-hoc, machine-only tools that shouldn't be tracked in this repo go in the unmanaged ~/.config/mise/conf.d/local.toml.

CLI Tools via the mise aqua backend:

  • Modern CLI replacements: bat, fd, eza, ripgrep, zoxide, duf, dust
  • Development tools: fzf, starship, atuin, delta, lazygit, gh, jq, just, gum, direnv, uv
  • Editor: neovim (nvim), tree-sitter CLI (builds nvim-treesitter parsers)
  • Kubernetes tools: kubectl, kubectx, kubens
  • AWS tools: granted (assume)
  • Fuzzy finder TUI: television (tv, via a mise plugin)

Tools outside the aqua registry (btop, httpie, broot, tldr, pinentry, helix, md-tui, worktrunk, biome, and macOS-only eza/ktlint) are installed by .chezmoiscripts/run_onchange_after_05-install-tools.sh.tmpl.

The exact tool list and versions track private_dot_config/mise/config.toml.tmpl, which is the source of truth.

All tools are automatically installed via ~/.config/mise/config.toml when you run:

# Install all configured tools (languages + CLI tools)
mise install

# Or install latest stable versions
mise-install-latest

Note: Old installations (Homebrew, apt packages, binaries) are automatically cleaned up after mise aqua setup.

Development

Making Changes to Dotfiles

After installation, your dotfiles are managed by chezmoi. To make changes:

# Go to chezmoi's source directory
chezmoi cd

# Edit files directly
vim dot_zshrc.tmpl

# Preview changes
chezmoi diff

# Apply changes locally
chezmoi apply

# Commit and push
git add -A
git commit -m "Update zsh config"
git push

Or use the included dotfiles function:

dotfiles edit     # Go to source directory
dotfiles diff     # Preview changes
dotfiles apply    # Apply locally
dotfiles push     # Commit and push all changes
dotfiles pull     # Pull latest from GitHub

Updating from GitHub

On any machine with your dotfiles installed:

chezmoi update    # Pull latest changes and apply them

Customization

Local Configuration

Create ~/.config/zsh/config.local.zsh for machine-specific configuration that won't be managed by chezmoi.

Zsh

Zsh is the login shell (the provisioning script recommends it on fresh machines; chsh -s $(which zsh) to switch an existing one). It comprises:

  • ~/.zshenv — PATH/environment for all shells (the non-interactive half).
  • ~/.zshrc — interactive config: vi mode, completions, abbreviations, tool inits (mise, zoxide, fzf, atuin, starship, direnv, broot), aliases, and the MOTD/greeting.
  • ~/.config/zsh/functions/*.zsh — one function per file (mkcd, extract, fcd, fgit, crpr, tools, …).
  • ~/.config/zsh/conf.d/*.zsh — fzf options, macOS extras, greeting, and MOTD.
  • ~/.config/zsh/completions/ — completion functions generated at apply time (currently _rg for ripgrep; mise/aqua binaries don't ship completion files the way brew does). On fpath, autoloaded by compinit — zero startup cost.

The prompt (starship) and history (atuin) round out the setup.

Plugins (autosuggestions, syntax highlighting) are fetched by chezmoi into ~/.config/zsh/plugins (see .chezmoiexternal.toml) — there is no separate plugin manager. Both are deferred: sourced at the first idle moment after the prompt is drawn rather than at rc time, keeping ~13% off time-to-first-prompt while staying behaviourally identical within a frame of an interactive prompt. Abbreviations (git shortcuts, wsc, cca) are a small built-in ZLE widget (ZABBR in .zshrc), not a plugin — typing one at command position then space/enter expands it inline to the full command.

Try it without switching: just run zsh. To make it your login shell when ready: chsh -s "$(command -v zsh)".

Templates

This repository uses chezmoi templates to handle OS-specific differences. Key template variables:

  • {{ .chezmoi.os }} - "darwin" or "linux"
  • {{ .chezmoi.arch }} - System architecture
  • {{ .brewPrefix }} - Homebrew prefix path
  • {{ .packageManager }} - System package manager

Directory Structure

The chezmoi source directory lives at ~/.local/share/chezmoi (run chezmoi cd to jump there; ~/dev/dotfiles is only the mount path used by docker-test.sh).

chezmoi source/
├── .chezmoi.toml.tmpl              # Per-machine data (prompts, .isWork, etc.)
├── .chezmoiignore                  # Files present in the repo but not deployed
├── .chezmoiremove                  # Files chezmoi deletes from $HOME on apply
├── .chezmoiexternal.toml.tmpl      # Externally-fetched files (zsh plugins)
├── .chezmoiscripts/                # Ordered run_once / run_onchange install scripts
├── dot_zshenv.tmpl                 # Zsh env for all shells (PATH, etc.)
├── dot_zshrc.tmpl                  # Main Zsh interactive config
├── dot_gitconfig.tmpl              # Git config (delta, etc.)
├── dot_inputrc / dot_lesskey       # readline / less key bindings
├── dot_claude/                     # Claude Code config (agents, commands, hooks, statusline)
├── private_dot_config/
│   ├── zsh/                        # functions/, conf.d/
│   ├── mise/                       # mise config (tool + runtime source of truth)
│   ├── ghostty/ helix/ lazygit/    # per-tool configs
│   ├── starship.toml.tmpl          # shared prompt
│   └── herdr/ worktrunk/           # herdr (multiplexer) and worktrunk configs
├── private_dot_gnupg/              # gpg-agent config
├── install.sh test.sh docker-test.sh setup-aliases.sh
├── Dockerfile renovate.json
└── README.md CLAUDE.md SCRIPTS.md  # docs (not deployed)

Testing with Docker

You can test the dotfiles installation in a clean Docker environment:

Quick Test

# Run the interactive test script
./docker-test.sh

# Inside the container:
cd /home/testuser/dev/dotfiles
./install.sh
zsh
./test.sh  # Run the test suite

Manual Docker Commands

# Build the test image
docker build -f Dockerfile -t dotfiles-test .

# Run container with mounted dotfiles
docker run -it --rm -v "$(pwd):/home/testuser/dev/dotfiles:ro" dotfiles-test

./docker-test.sh (and ./docker-test.sh --ci) wrap these commands; there is no Dockerfile.test or docker-compose file.

Test Script

The test.sh script verifies all tools are installed correctly:

  • Checks all CLI tools are available
  • Verifies Zsh functions exist
  • Confirms configurations are in place
  • Tests mise tools

CI/CD

This repository includes automated testing via GitHub Actions:

Continuous Integration

  • Tests on Ubuntu 24.04 and macOS 26 (GitHub-hosted runners)
  • Full tool installation verification (install.sh + test.sh)
  • Docker-based testing is available locally via ./docker-test.sh

Running in CI Mode

install.sh takes no CLI flags; CI behaviour is driven by environment variables.

# Non-interactive install (skips prompts and the login-shell step)
CI=true ./install.sh

# Non-interactive and skip language runtimes
CI=true QUICK_INSTALL=true ./install.sh

Environment variables for CI:

  • CI=true - Automatically detected by GitHub Actions
  • CHEZMOI_USER_NAME - Name for git config
  • CHEZMOI_USER_EMAIL - Email for git config

Troubleshooting

Installation Issues

Python/Miniconda fails with "Terms of Service" error

Only relevant if you opted Python in for this machine (see Per-machine language runtimes). Miniconda requires ToS acceptance; this is a known issue and the install continues without Python. To fix:

# Accept Conda ToS manually
~/.local/share/mise/installs/python/miniconda3-latest/bin/conda init
~/.local/share/mise/installs/python/miniconda3-latest/bin/conda config --set auto_activate_base false

Script fails with "command not found"

Ensure ~/.local/bin is in your PATH:

export PATH="$HOME/.local/bin:$PATH"

Zsh doesn't start with correct configuration

# Re-apply dotfiles
chezmoi apply -v

There is no plugin manager to set up: fzf key bindings come from conf.d/fzf.zsh (and fzf --zsh in ~/.zshrc), and every CLI tool is provided by mise. If a tool is missing, ensure mise is active (eval "$(mise activate bash)") and re-run chezmoi apply -v.

Tools not found after installation

# Activate mise in current shell
eval "$(mise activate bash)"

# Or start a new Zsh shell
zsh

Automated Dependency Updates with Renovate Bot

This repository uses Renovate Bot to automatically keep dependencies up-to-date. Renovate creates pull requests when new versions are available.

What Renovate Updates

1. mise Tool Versions (private_dot_config/mise/config.toml.tmpl + conf.d/runtimes.toml.tmpl)

  • Node.js version (core, in config.toml.tmpl)
  • Go, Java, and Python/Miniconda versions (opt-in runtimes, in conf.d/runtimes.toml.tmpl)

(Bun is self-managed via bun upgrade, so Renovate does not track it.)

2. GitHub Actions (.github/workflows/*.yml)

  • Action versions (e.g., actions/checkout)
  • GitHub-hosted runner versions

3. Pinned binaries in the install-tools script (.chezmoiscripts/run_onchange_after_05-install-tools.sh.tmpl)

  • btop (system monitor)
  • tealdeer / tldr (man-page examples)
  • broot (tree navigation)
  • md-tui (markdown viewer)

These are the only tools with hardcoded version pins; everything else tracks latest (or a pinned version) via the mise config above. install.sh itself is a flagless bootstrap and contains no version pins.

How It Works

  1. Automated Runs: Renovate app runs automatically on its schedule
  2. Pull Requests: Creates PRs for each dependency update with conventional commits
  3. Auto-merge: Minor and patch updates are auto-merged if tests pass
  4. Major Updates: Require manual review and approval

Configuration

  • Main config: renovate.json
  • Managed by: GitHub Renovate App (no additional setup needed)

For Contributors

If you fork this repo, you'll need to:

  1. Install the Renovate GitHub App on your fork
  2. Renovate will automatically detect the configuration and start creating PRs

Contributing

Feel free to fork and customize for your own use!

Tool Versions

Last updated: June 2026

The authoritative version list is private_dot_config/mise/config.toml.tmpl; Renovate keeps it current. CLI tools track latest, so only the pinned language runtimes are listed here.

Core Tools

  • chezmoi: latest from official installer
  • mise: latest

Programming Languages (pinned via mise)

Node.js is always installed (pinned in private_dot_config/mise/config.toml.tmpl). Go, Java (Eclipse Temurin LTS), and Python (miniconda3) are opt-in per machine (pinned in private_dot_config/mise/conf.d/runtimes.toml.tmpl); see Per-machine language runtimes. Both files are the source of truth and are bumped automatically by Renovate. Bun is installed by its official installer and self-updates via bun upgrade (not mise-managed).

License

MIT