Skip to content

sanchpet/dotfiles

Repository files navigation

dotfiles

smoke

Personal macOS development environment, managed declaratively with chezmoi (dotfiles), mise (CLI tools), and Homebrew (GUI apps). One command on a bare machine → a fully configured setup. Secrets never touch the repo.

Quick start (bare machine)

git clone https://github.com/sanchpet/dotfiles ~/dotfiles && ~/dotfiles/bootstrap.sh

bootstrap.sh is idempotent and runs, in order (mise-first):

  1. mise — install the base tool manager (curl https://mise.run), then chezmoi via mise
  2. chezmoi init — clone this repo into chezmoi's source
  3. apply mise config — lay down ~/.config/mise/config.toml before installing tools (breaks the chicken-and-egg: the mise config is itself a managed dotfile)
  4. mise install — install CLI tools from the config (bitwarden-cli, uv, …)
  5. Bitwarden unlock — only if the source contains *.tmpl secrets (interactive)
  6. Oh My Zsh — install the zsh framework (without touching .zshrc or changing the shell)
  7. chezmoi apply — render and place all dotfiles
  8. brew bundle — GUI casks (Homebrew is installed lazily, only if the Brewfile needs it)

Tools

Foundation

Tool Purpose Link
mise Polyglot tool & runtime manager — single declarative source for CLI tooling https://mise.jdx.dev · github
chezmoi Dotfiles manager — templating, per-machine, secrets https://www.chezmoi.io · github
Oh My Zsh Zsh configuration framework https://ohmyz.sh · github
Homebrew macOS package manager — used only for GUI casks https://brew.sh

CLI tools (managed via mise)

Tool Purpose Link
Bitwarden CLI (bw) Secret retrieval at chezmoi apply https://bitwarden.com/help/cli/ · github
uv Fast Python package & project manager — also backs mise's pipx: tools (settings.pipx.uvx) docs · github
Yandex Cloud CLI (yc) Manage Yandex Cloud resources (IAM, compute, k8s, …) docs
Claude Code (claude) Anthropic agentic CLI — self-update off (DISABLE_AUTOUPDATER), update via mise up claude docs
GitHub CLI (gh) GitHub from the terminal docs
GitLab CLI (glab) GitLab from the terminal docs
kubectl Kubernetes cluster CLI docs
kubectx Switch kubectl context / namespace github
node Node.js runtime docs
Starship Cross-shell prompt (zsh prompt; starship init in .zshrc) docs
zoxide Frecency cd — replaces cd (--cmd cd); cdi = interactive github
fzf Fuzzy finder (fzf --zsh in .zshrc) github
ripgrep (rg) Fast recursive search github
bat cat with syntax highlighting & paging (aliased to cat) github
eza Modern ls — git-aware, colors (aliased to ls/ll/la/tree) github
delta Syntax-highlighting pager for git diffs (wired as git core.pager) github
dust Intuitive du — disk-usage tree (aliased to du) github
duf Better df — disk free, tabular (aliased to df) github
dua (dua i) Interactive disk-usage explorer — find & delete big dirs github
fd Fast, user-friendly find github
python Python runtime docs
helm Kubernetes package manager docs
terragrunt Terraform/OpenTofu wrapper docs
yq YAML/JSON processor github
awscli (aws) AWS CLI docs
go Go toolchain docs
terraform Infrastructure as code docs
vault Secrets management CLI docs
flux2 (flux) GitOps continuous delivery for Kubernetes docs
cfssl Cloudflare PKI/TLS toolkit github
typst Markup-based typesetting (LaTeX alternative) github
ansible (ansible-core) IT automation engine — installed via uv (pipx: backend) docs
ansible-lint Ansible playbook linter (via uv) github
yamllint YAML linter (via uv) github

Quality / dev workflow

Tool Purpose Link
pre-commit Git pre-commit hook framework https://pre-commit.com · github
shellcheck Static analysis for shell scripts (via shellcheck-py) shellcheck · hook
pre-commit-hooks Standard hygiene hooks (whitespace, EOF, YAML, …) github

GUI (Homebrew cask)

Tool Purpose Profile Link
Visual Studio Code Primary code editor (self-updating; adopted into brew) all docs
Freelens Kubernetes IDE (open-source Lens fork) all github
WakaTime Menu-bar time tracker — whole-system activity beyond editor plugins all docs
Pearcleaner App uninstaller + orphaned-file finder (open-source CleanMyMac alt) all github
.NET SDK .NET toolchain work only docs

Homebrew formulae (CLI mise can't provide)

Tool Purpose Profile Link
sshpass Non-interactive ssh password auth (used by ansible) — not in the mise registry all docs

Zsh shell (Oh My Zsh)

The prompt is Starship (dot_config/starship.toml — the kubernetes, aws and terraform modules are on, so the active cluster / profile / workspace is always visible). Oh My Zsh loads plugins only (theme off — Starship draws the prompt). Built-in plugins ship with Oh My Zsh; external ones are cloned into $ZSH_CUSTOM/plugins by bootstrap.sh.

Plugin Source Purpose
git built-in Git aliases (gst, gco, gp, …)
kubectl built-in k* aliases + completion (kgp, kgaa, kdp, …)
helm built-in Helm completion
terraform built-in tf* aliases + completion + workspace
aws built-in asp/acp profile switch + completion
ansible built-in Ansible aliases + completion
gh built-in GitHub CLI completion
colored-man-pages built-in Colored man pages
extract built-in x <archive> — extract any archive
sudo built-in Double-Esc prepends sudo
copypath / copybuffer built-in Copy $PWD / the current command line to the clipboard
dirhistory built-in Alt+←/→ directory history, Alt+ parent dir
forgit external fzf-powered git (ga, glo, gd)
zsh-completions external Extra completion definitions
zsh-autosuggestions external Fish-style suggestions from history
zsh-you-should-use external Reminds you when a typed command already has an alias
zsh-syntax-highlighting external Command-line syntax highlighting
zsh-autocomplete external Live menu completion (loaded last so its keybindings win)

Load order matters. zsh-autocomplete owns the completion/history UI, so it loads last, and plugins that fight over the same keys — fzf-tab, zsh-history-substring-search — are deliberately not used. Beyond the plugins, dot_zshrc.tmpl adds custom aliases (kg, kgy, kctx; modern-CLI swaps catbat, lseza, dudust, dfduf) and the miseg/miserm helpers (add / remove a global mise tool and re-import the config).

Repository layout

Path Role
dot_* Dotfiles rendered into $HOME by chezmoi (e.g. dot_gitconfig~/.gitconfig)
dot_config/mise/config.toml Global mise config → ~/.config/mise/config.toml (user CLI tools)
dot_config/starship.toml Starship prompt config → ~/.config/starship.toml (kubernetes/aws/terraform modules)
dot_zshrc.tmpl ~/.zshrc — Oh My Zsh (plugins only) + Starship prompt + zoxide + mise + aliases (kubectl, modern CLI); secrets pending
dot_local/bin/executable_cleanup ~/.local/bin/cleanup — disk-reclaim tool (reports by default; --apply deletes Tier 1 caches + orphan caches of removed tools, --deep adds Go modcache)
.chezmoi.toml.tmpl Generates per-machine chezmoi config at init (prompts profile); never deployed
bootstrap.sh Bare-machine bootstrap (operational, not deployed)
Brewfile.tmpl GUI casks for brew bundle, templated per profile (operational; rendered at bootstrap)
mise.toml Repo-local dev tooling (pre-commit)
.pre-commit-config.yaml Lint hooks (shellcheck + hygiene)
.chezmoiignore Keeps operational files in the repo but out of $HOME

Design decisions (Decision Record)

  • chezmoi over GNU Stow / bare-git. Needed templating (per-machine values), first-class secret handling, and a source tree where dotfiles stay visible (dot_ prefix) instead of hidden. Stow only symlinks; bare-git has no templating or secrets.
  • mise-first for CLI tools. All CLI tooling is declared in mise (config.toml), versioned and cross-machine. Homebrew is reserved for what mise can't provide — GUI casks, plus the rare CLI with heavy native deps or no upstream release (e.g. sshpass). This keeps the toolchain reproducible and the Brewfile minimal.
  • Bitwarden for secrets. Secrets are pulled from Bitwarden at chezmoi apply via {{ bitwarden ... }} templates — nothing secret (encrypted or otherwise) lives in this public repo. Trade-off: bootstrap needs an interactive bw unlock before applying secret-bearing files (vs. age/secrets.env, which keep apply offline but place material in/near the repo).
  • pre-commit + shellcheck. Every commit lints shell scripts and runs hygiene checks, so bootstrap.sh and friends stay correct. pre-commit itself is installed via mise (postinstall wires the git hooks automatically).
  • Bootstrap ordering. The mise config is itself a managed dotfile, so it is applied before mise install to break the chicken-and-egg; Homebrew is installed lazily, only when GUI casks are present.
  • Per-machine via profile, not per-machine directories. One source tree; machine-specific variation is driven by a single profile value (work/personal), prompted once at chezmoi init (override in CI/headless with DOTFILES_PROFILE) and stored in the machine-local chezmoi config (never in this repo). Templates branch on it — Brewfile.tmpl installs the .NET SDK only when profile == "work", and dot_gitconfig.tmpl selects the work vs personal git identity. This keeps a single declarative source of truth and avoids the duplication/drift of per-machine dirs.

Syncing changes (chezmoi)

chezmoi has two locations: the source (this repo, chezmoi source-path) and the live files in $HOME. Always edit the source, then push it to live — never edit the live file directly.

Scenario Command
Changed a dotfile (e.g. .zshrc) edit the source (dot_zshrc.tmpl), then chezmoi apply ~/.zshrc (alias cza)
Pull latest on another machine chezmoi update (= git pull + apply) (alias czu)
Check source ↔ live drift chezmoi diff (alias czd)
A tool wrote to a non-templated target (e.g. mise use -g~/.config/mise/config.toml) re-import: chezmoi add <target> (see the miseg helper)

Never run chezmoi add ~/.zshrc. It is a template (dot_zshrc.tmpl) — add would overwrite it with the rendered content and destroy the {{ ... }} directives (incl. future secrets). Templated files are source-edited only; chezmoi add is for non-templated targets.

Secrets

Secrets are never committed. They are resolved at apply time from Bitwarden via chezmoi templates. On a fresh machine, bootstrap.sh prompts for bw unlock only when the source actually contains secret templates.

The zsh config (dot_zshrc.tmpl) is kept as a .tmpl so a {{ bitwarden ... }} secret line can be added later without a rename — see Zsh shell for the plugin set and prompt.

Pending: the OBSIDIAN_API_KEY secret reference (via Bitwarden) is not wired yet — .zshrc is kept as a .tmpl so the {{ bitwarden ... }} line can be added without a rename.

About

My MacOS dotfiles managed with chezmoi - mise, omz, brew, etc.

Resources

Stars

Watchers

Forks

Contributors