Skip to main content

Dotfiles

Modern developer configuration with safe installation and Nix package management.

Installation

Clone the repository and run the supported bootstrap entry point:

git clone https://github.com/wcygan/dotfiles.git
cd dotfiles
./bootstrap.sh

The bootstrapper loads Nix, enters the default development shell, and invokes the locked Python CLI:

nix develop --no-write-lock-file .#default --command uv run --locked python -m dotfiles_setup install

By default, installation upgrades or adds the repository Nix profile, links managed configuration, prepares Rust tooling, and runs verification. Existing configuration files are backed up before they are replaced by managed links. Interactive Bash and zsh handoff to Fish is deliberately opt-in:

./bootstrap.sh install --shell-handoff

Installing Nix

On macOS, if Nix is unavailable, ./bootstrap.sh opens the Determinate macOS installer package. Complete the package installation, start a fresh shell, and rerun ./bootstrap.sh.

On Linux and WSL, installation is explicit:

./bootstrap.sh --install-nix --yes

That confirmation is required before the bootstrapper invokes the official Determinate installer.

What You Get

  • 🚀 Modern CLI Tools: ripgrep, fd, bat, eza, fzf, delta, and more
  • 🐟 Fish Shell: Full configuration with functions and abbreviations
  • ⭐ Starship Prompt: Beautiful, fast, and context-aware
  • 📦 Nix Packages: Reproducible installations across all platforms
  • 🔗 Safe Symlinks: Automatic backups before any changes

Supported Platforms

I am personally test the dotfiles on these Operating Systems:

PlatformVersionStatus
macOS12+ (Intel & Apple Silicon)✅ Fully supported
Ubuntu20.04+✅ Fully supported
Fedora38+✅ Fully supported

Note: Other Operating Systems may "just work".

After Installation

Depending on your shell:

exec fish -l

Bash/Zsh

Run the supported shell handoff:

./bootstrap.sh shell-handoff

Then start a new shell. The command adds the managed handoff block and preserves unrelated startup content.

Repository Structure

dotfiles/
├── config/ # Configuration files
│ ├── agents/ # Authoritative global agent instructions
│ ├── codex/ # Codex template and instruction compatibility link
│ ├── fish/ # Fish shell config
│ ├── starship.toml # Starship prompt
│ └── shell-nix.sh # Bash/zsh compatibility
├── src/dotfiles_setup/ # Python setup CLI modules
├── tests/ # Pytest and platform test suites
├── flake.nix # Nix package definitions
├── bootstrap.sh # Nix bootstrap and CLI bridge
├── pyproject.toml # Python project metadata
├── uv.lock # Locked Python dependencies
└── docs/ # This documentation

Core Principles

  • Idempotent: Every operation is safe to run multiple times
  • Cross-platform: Same experience on macOS, Ubuntu, and Fedora
  • Minimal: Clean configs under config/, packages in flake.nix
  • Safe: Pre-flight and destination checks stop unexpected replacements; backups enable rollback
  • Reproducible: Nix ensures identical tool versions everywhere

Common Tasks

Update Packages

make update

Add New Tools

Edit flake.nix, then:

make profile

Setup Commands

doctor is an advisory, read-only view of the current host and development environment. Missing optional or not-yet-installed state is shown as a warning and does not make the command fail. verify is strict post-installation acceptance: it checks that the user profile comes from this exact checkout, required commands and Python 3.13 come from that profile rather than only the development shell, managed links resolve to the repository sources for the current platform, and Codex configuration remains a regular machine-local file. The install workflow always runs strict verification last.

The repository pins Rust and rust-analyzer in rust-toolchain.toml. ./bootstrap.sh rustup installs that exact toolchain without changing the user's global rustup default; strict verification resolves rust-analyzer with the pinned toolchain explicitly.

Pull-request validation consumes pinned GitHub Action commits and Docker image manifest digests. Scheduled/manual freshness checks use current upstream tags only in temporary runner files and never update the repository automatically.

make install                 # Full setup; same as ./bootstrap.sh
make link # Link managed config only
make link-dry # Preview managed link changes
make git-user # Configure Git identity
make setup-rustup-components # Prepare rust-analyzer
make setup-shell-handoff # Opt in to Bash/zsh -> Fish handoff
make verify # Strict post-installation acceptance
make doctor # Advisory read-only diagnostics
make uninstall # Remove managed links after confirmation
make uninstall-dry # Preview managed-link removal

Each setup target has a direct ./bootstrap.sh <command> equivalent. The bridge consistently executes nix develop and uv run --locked; use it rather than calling project modules from an arbitrary host Python.

Run Tests

make test-pre    # Locked Ruff and pytest checks
make test-local # Ephemeral-HOME-focused pytest
make test-shell # Shell-handoff pytest
make test-docker # Python Ubuntu/Fedora driver
make test # All non-Docker tests

Roll Back

nix profile rollback restores a previous Nix profile generation. Managed links save pre-existing paths with timestamped backup names, and rerunning make link or ./bootstrap.sh link is safe. Tag a known-good repository revision before a broader migration to retain a straightforward source rollback. Link setup records each destination's prior identity and stops when a recheck detects a change before replacement.

Python Project Structure

The locked Python project is under src/dotfiles_setup/. cli.py dispatches commands to focused modules for Nix profile updates, configuration links and cleanup, advisory diagnostics, strict verification, Rust setup, shell handoff, and Git identity. Tests live under tests/; pyproject.toml and uv.lock define the reproducible Python environment.

Troubleshooting

Command Not Found

After installation, restart your shell or source the appropriate config file.

Nix Commands Not Working

# Multi-user installations
source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh

Permission Denied

The installer will request sudo when needed. Ensure you're in the sudoers group on Linux.