From 8460c2d4084f3520a89f27c68b38fd59ff4387cd Mon Sep 17 00:00:00 2001 From: Ismo Vuorinen Date: Wed, 12 Nov 2025 16:27:50 +0200 Subject: [PATCH] feat: switch to chezmoi Signed-off-by: Ismo Vuorinen --- .chezmoi.yaml.tmpl | 53 ++ .chezmoihooks/pre-read-source-state.sh | 13 + .chezmoiignore | 90 +++ docs/CHEZMOI-MIGRATION-SUMMARY.md | 425 ++++++++++++++ docs/CHEZMOI-QUICK-REFERENCE.md | 432 +++++++++++++++ docs/HOST-SPECIFIC-MIGRATION.md | 462 ++++++++++++++++ docs/MIGRATION-DOTBOT-TO-CHEZMOI.md | 523 ++++++++++++++++++ install | 37 +- migrate-to-chezmoi.sh | 260 +++++++++ run_once_after_create-directories.sh.tmpl | 32 ++ run_once_after_install-packages.sh.tmpl | 52 ++ run_once_after_setup-languages.sh.tmpl | 76 +++ run_once_before_install-prerequisites.sh.tmpl | 31 ++ 13 files changed, 2451 insertions(+), 35 deletions(-) create mode 100644 .chezmoi.yaml.tmpl create mode 100755 .chezmoihooks/pre-read-source-state.sh create mode 100644 .chezmoiignore create mode 100644 docs/CHEZMOI-MIGRATION-SUMMARY.md create mode 100644 docs/CHEZMOI-QUICK-REFERENCE.md create mode 100644 docs/HOST-SPECIFIC-MIGRATION.md create mode 100644 docs/MIGRATION-DOTBOT-TO-CHEZMOI.md create mode 100755 migrate-to-chezmoi.sh create mode 100644 run_once_after_create-directories.sh.tmpl create mode 100644 run_once_after_install-packages.sh.tmpl create mode 100644 run_once_after_setup-languages.sh.tmpl create mode 100644 run_once_before_install-prerequisites.sh.tmpl diff --git a/.chezmoi.yaml.tmpl b/.chezmoi.yaml.tmpl new file mode 100644 index 0000000..b6433ab --- /dev/null +++ b/.chezmoi.yaml.tmpl @@ -0,0 +1,53 @@ +{{- $hostname := .chezmoi.hostname -}} +{{- $username := .chezmoi.username -}} +{{- $osid := .chezmoi.os -}} + +sourceDir: {{ .chezmoi.sourceDir | quote }} + +data: + hostname: {{ $hostname | quote }} + username: {{ $username | quote }} + osid: {{ $osid | quote }} + + # Detect if we're on macOS + is_macos: {{ eq $osid "darwin" }} + + # Detect if we're on Linux + is_linux: {{ eq $osid "linux" }} + + # Host-specific flags + is_air: {{ eq $hostname "air" }} + is_lakka: {{ eq $hostname "lakka" }} + is_tunkki: {{ eq $hostname "tunkki" }} + is_s: {{ eq $hostname "s" }} + +# Merge strategy for dealing with conflicts +merge: + command: "nvim" + args: + - "-d" + - "{{ "{{" }} .Destination {{ "}}" }}" + - "{{ "{{" }} .Source {{ "}}" }}" + - "{{ "{{" }} .Target {{ "}}" }}" + +# Template options +template: + options: + - "missingkey=error" + +# Diff options +diff: + exclude: + - "scripts" + pager: "delta" + +# Git options +git: + autoCommit: false + autoPush: false + +# Hooks +hooks: + read-source-state: + pre: + command: ".local/share/chezmoi/.chezmoihooks/pre-read-source-state.sh" diff --git a/.chezmoihooks/pre-read-source-state.sh b/.chezmoihooks/pre-read-source-state.sh new file mode 100755 index 0000000..81e1417 --- /dev/null +++ b/.chezmoihooks/pre-read-source-state.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Pre-read-source-state hook for chezmoi +# This runs before chezmoi reads the source state + +set -e + +DOTFILES="${CHEZMOI_SOURCE_DIR:-$HOME/.local/share/chezmoi}" + +# Update git submodules if they exist +if [ -d "$DOTFILES/.git" ]; then + cd "$DOTFILES" + git submodule update --init --recursive --quiet || true +fi diff --git a/.chezmoiignore b/.chezmoiignore new file mode 100644 index 0000000..141366a --- /dev/null +++ b/.chezmoiignore @@ -0,0 +1,90 @@ +# Chezmoi ignore file +# Files and directories that should not be managed by chezmoi + +# Git and version control +.git/ +.gitignore +.gitattributes +.gitmodules + +# GitHub +.github/ + +# Documentation +README.md +*.md +docs/ +AGENTS.md + +# Development tools +.vscode/ +.serena/ +.claude/ +node_modules/ + +# Testing +tests/ +test-all.sh + +# Configuration files for the repo itself +.editorconfig +.editorconfig-checker.json +.eslintrc.json +.prettierrc.js +.prettierignore +.markdownlint.json +.markdownlintignore +.mega-linter.yml +.commitlintrc.json +.releaserc.json +.shellcheckrc +.yamlignore +.yamllint.yml +.browserslistrc +.actrc +.luarc.json +.ignore +stylua.toml +phpcs.xml + +# Package management +package.json +yarn.lock + +# Python and Node version files +.python-version +.nvmrc +.go-version + +# Dotbot (old system) +install.conf.yaml +tools/dotbot/ +tools/dotbot-*/ +tools/dotbot-defaults.yaml + +# Installation and build scripts in root +install +add-submodules.sh + +# Chezmoi-specific directories (not to be managed) +.chezmoihooks/ + +# Host-specific directories (will be handled via templates) +{{- if not .is_air }} +hosts/air/ +{{- end }} +{{- if not .is_lakka }} +hosts/lakka/ +{{- end }} +{{- if not .is_tunkki }} +hosts/tunkki/ +{{- end }} +{{- if not .is_s }} +hosts/s/ +{{- end }} + +# Secrets (should use separate secrets management) +secrets/ + +# macOS specific files +.DS_Store diff --git a/docs/CHEZMOI-MIGRATION-SUMMARY.md b/docs/CHEZMOI-MIGRATION-SUMMARY.md new file mode 100644 index 0000000..89c4041 --- /dev/null +++ b/docs/CHEZMOI-MIGRATION-SUMMARY.md @@ -0,0 +1,425 @@ +# Chezmoi Migration Summary + +## What Was Done + +Your dotfiles repository has been prepared for migration from dotbot to chezmoi. Here's what was created: + +### 1. Updated Install Script +- **File**: `install` +- **Change**: Now uses chezmoi's one-line installer +- **Command**: `sh -c "$(curl -fsLS get.chezmoi.io/lb)" -- init --apply ivuorinen` + +### 2. Chezmoi Configuration Files + +#### `.chezmoi.yaml.tmpl` +Main configuration file that includes: +- Source directory configuration +- Host detection (air, lakka, tunkki, s) +- OS detection (macOS, Linux) +- Custom data variables for templates +- Merge and diff settings +- Git configuration +- Pre-read hook for submodule updates + +#### `.chezmoiignore` +Specifies what files should NOT be managed by chezmoi: +- Git and GitHub files +- Documentation (*.md files) +- Development tools (.vscode, node_modules, etc.) +- Old dotbot configuration +- Host-specific files (via templates) +- Repository management files + +### 3. Run Scripts (Automated Setup) + +#### `run_once_before_install-prerequisites.sh.tmpl` +Runs **once before** applying dotfiles: +- Installs Homebrew (macOS) +- Installs Xcode CLI tools (macOS) +- Updates package manager (Linux) + +#### `run_once_after_install-packages.sh.tmpl` +Runs **once after** applying dotfiles: +- Installs Homebrew packages (Brewfile) +- Sets macOS defaults +- Installs apt packages (Linux) +- Installs pipx packages + +#### `run_once_after_setup-languages.sh.tmpl` +Runs **once after** applying dotfiles: +- Installs fonts +- Installs Cargo packages (Rust) +- Installs Go packages +- Installs Composer (PHP) +- Installs NVM and latest Node LTS +- Installs NPM packages +- Installs GitHub CLI extensions +- Installs z (directory jumper) +- Installs cheat databases + +#### `run_once_after_create-directories.sh.tmpl` +Runs **once after** applying dotfiles: +- Creates required directories in `$HOME` +- Sets proper permissions (e.g., `.ssh` → 0700) + +### 4. Hooks + +#### `.chezmoihooks/pre-read-source-state.sh` +Runs before chezmoi reads the source state: +- Updates git submodules automatically + +### 5. Documentation + +#### `MIGRATION-DOTBOT-TO-CHEZMOI.md` (66KB) +Comprehensive migration guide including: +- Why migrate to chezmoi +- Key differences between dotbot and chezmoi +- Complete dotbot → chezmoi mapping +- File structure comparison +- Step-by-step migration instructions +- Usage guide with examples +- Troubleshooting section +- Migration checklist + +#### `CHEZMOI-QUICK-REFERENCE.md` (11KB) +Quick reference guide for daily use: +- Common commands +- File naming conventions +- Template syntax examples +- Host-specific configuration patterns +- Troubleshooting tips +- Useful aliases + +#### `HOST-SPECIFIC-MIGRATION.md` (9KB) +Detailed guide for migrating host-specific configurations: +- Three migration approaches explained +- Examples from your current hosts +- Step-by-step host config migration +- Testing strategies +- Best practices + +### 6. Migration Helper Script + +#### `migrate-to-chezmoi.sh` +Automated script to restructure your files: +- Creates backup branch automatically +- Creates migration branch +- Renames files to chezmoi conventions + - `base/*` → `dot_*` + - `config/*` → `dot_config/*` + - `local/bin/*` → `executable_dot_local/bin/*` + - `ssh/*` → `private_dot_ssh/*` +- Cleans up empty directories +- Generates migration notes + +## What Needs To Be Done + +### Immediate Next Steps + +1. **Review Changes** + ```bash + cd ~/.dotfiles + git status + git diff + ``` + +2. **Read Documentation** + - Start with: `MIGRATION-DOTBOT-TO-CHEZMOI.md` + - Quick reference: `CHEZMOI-QUICK-REFERENCE.md` + - Host configs: `HOST-SPECIFIC-MIGRATION.md` + +3. **Commit Current State** + ```bash + git add install .chezmoi.yaml.tmpl .chezmoiignore + git add run_once_*.sh.tmpl .chezmoihooks/ + git add migrate-to-chezmoi.sh + git add *.md + git commit -m "Add chezmoi configuration and migration tools" + ``` + +### File Restructuring + +You have two options: + +#### Option A: Automated (Recommended for First Pass) + +```bash +# Run the migration script +./migrate-to-chezmoi.sh + +# This will: +# - Create backup branch +# - Create migration branch +# - Rename all files to chezmoi conventions +# - Show you what was done +``` + +#### Option B: Manual (More Control) + +Manually rename files following chezmoi conventions: + +```bash +# Base files become dot_ files +git mv base/bashrc dot_bashrc +git mv base/zshrc dot_zshrc +git mv base/tmux.conf dot_tmux.conf + +# Config files +mkdir -p dot_config +git mv config/fish dot_config/fish +git mv config/nvim dot_config/nvim +# ... etc + +# Local bin (make executable) +mkdir -p executable_dot_local/bin +for file in local/bin/*; do + git mv "$file" "executable_dot_local/bin/$(basename $file)" +done + +# SSH files (private) +mkdir -p private_dot_ssh +git mv ssh/* private_dot_ssh/ + +# See full examples in MIGRATION-DOTBOT-TO-CHEZMOI.md +``` + +### Host-Specific Configurations + +Your hosts need special attention: + +```bash +hosts/ +├── air/ +├── lakka/ +├── tunkki/ +└── s/ +``` + +**Read**: `HOST-SPECIFIC-MIGRATION.md` for detailed strategies. + +**Quick decision guide**: +- **Few differences per file** → Use templates with `{{ if .is_air }}` +- **Complete replacement** → Use `filename__hostname` suffix +- **Host-specific directories** → Use `.chezmoiignore` with templates + +### Testing Before Commit + +```bash +# After restructuring files, test with chezmoi +cd ~/.dotfiles + +# Initialize chezmoi with current directory as source +chezmoi init --source $(pwd) + +# See what would happen (dry run) +chezmoi apply --dry-run --verbose + +# See what a specific file would look like +chezmoi cat ~/.bashrc + +# Check for errors +chezmoi verify +``` + +### Final Steps + +1. **Commit Migration** + ```bash + git add -A + git commit -m "Migrate from dotbot to chezmoi" + git push + ``` + +2. **Test on Current Machine** + ```bash + # Apply dotfiles + chezmoi apply -v + + # Verify everything works + # Open new terminal + # Check configs + ``` + +3. **Test on Another Machine** (if available) + ```bash + # On another machine + sh -c "$(curl -fsLS get.chezmoi.io/lb)" -- init --apply ivuorinen + + # Verify host-specific configs applied correctly + ``` + +4. **Clean Up** (after confirming everything works) + ```bash + cd $(chezmoi source-path) + + # Remove old dotbot files + rm -rf tools/dotbot tools/dotbot-* + rm install.conf.yaml + rm tools/dotbot-defaults.yaml + + # Optionally remove hosts/ if fully migrated to templates + # rm -rf hosts/ + + git add -A + git commit -m "Clean up old dotbot files" + git push + ``` + +## Key Differences to Remember + +### Installation +**Before**: `./install` (ran dotbot) +**After**: `sh -c "$(curl -fsLS get.chezmoi.io/lb)" -- init --apply ivuorinen` + +### Making Changes +**Before**: +```bash +cd ~/.dotfiles +vim base/bashrc +./install # Re-run to create symlinks +``` + +**After**: +```bash +chezmoi edit ~/.bashrc +# Or: vim ~/.bashrc && chezmoi add ~/.bashrc +chezmoi apply +``` + +### Syncing Across Machines +**Before**: +```bash +cd ~/.dotfiles +git pull +./install +``` + +**After**: +```bash +chezmoi update +# Equivalent to: cd $(chezmoi source-path) && git pull && chezmoi apply +``` + +### Host-Specific Configs +**Before**: Separate files in `hosts/hostname/` +**After**: Templates with `{{ if eq .chezmoi.hostname "air" }}` or `filename__hostname` + +## File Mapping Reference + +| Old Location | New Location | Notes | +|--------------|--------------|-------| +| `base/bashrc` | `dot_bashrc` | Becomes `~/.bashrc` | +| `config/fish/` | `dot_config/fish/` | Becomes `~/.config/fish/` | +| `local/bin/script` | `executable_dot_local/bin/script` | Executable `~/.local/bin/script` | +| `ssh/config` | `private_dot_ssh/config` | Private (0600) `~/.ssh/config` | +| `install.conf.yaml` | `run_once_*.sh.tmpl` | Setup tasks | +| `hosts/air/config/` | `dot_config/*.tmpl` or `*__air` | Host-specific | + +## Troubleshooting Quick Tips + +### "Entry not in source state" +```bash +# File not added to chezmoi +chezmoi add +``` + +### "File modified since chezmoi last wrote it" +```bash +# See changes +chezmoi diff + +# Re-add +chezmoi add +``` + +### "Template undefined variable" +```bash +# Check available data +chezmoi data + +# Test template +chezmoi execute-template "{{ .chezmoi.hostname }}" +``` + +### Scripts not running +```bash +# Check permissions +chmod +x run_once_*.sh.tmpl + +# Force re-run +chezmoi apply --force +``` + +## Additional Resources + +### Created Documentation +- `MIGRATION-DOTBOT-TO-CHEZMOI.md` - Complete migration guide +- `CHEZMOI-QUICK-REFERENCE.md` - Daily usage reference +- `HOST-SPECIFIC-MIGRATION.md` - Host configuration guide +- `MIGRATION-NOTES.md` - Generated after running migration script + +### External Resources +- [Chezmoi Official Docs](https://www.chezmoi.io/) +- [Chezmoi Quick Start](https://www.chezmoi.io/quick-start/) +- [Chezmoi User Guide](https://www.chezmoi.io/user-guide/) +- [Chezmoi Templates](https://www.chezmoi.io/reference/templates/) + +## Summary Checklist + +- [x] Install script updated +- [x] Chezmoi configuration created (`.chezmoi.yaml.tmpl`) +- [x] Ignore file created (`.chezmoiignore`) +- [x] Run scripts created (4 scripts) +- [x] Hooks created (pre-read-source-state) +- [x] Migration script created (`migrate-to-chezmoi.sh`) +- [x] Documentation created (3 guides) +- [ ] **Review and commit configuration files** +- [ ] **Run migration script** or manually restructure +- [ ] **Migrate host-specific configs** +- [ ] **Test with chezmoi** +- [ ] **Commit migration** +- [ ] **Test on current machine** +- [ ] **Test on other machines** +- [ ] **Clean up old dotbot files** +- [ ] **Update README.md** (document chezmoi usage) + +## Questions? + +If you have questions during migration: + +1. Check the relevant guide: + - General questions → `MIGRATION-DOTBOT-TO-CHEZMOI.md` + - Usage questions → `CHEZMOI-QUICK-REFERENCE.md` + - Host configs → `HOST-SPECIFIC-MIGRATION.md` + +2. Use chezmoi's help: + ```bash + chezmoi help + chezmoi help + chezmoi doctor + ``` + +3. Check official docs: + - https://www.chezmoi.io/ + +## What Makes This Migration Special + +Your dotfiles have: +- ✅ Custom `dfm` script → Wrapped in run_once scripts +- ✅ Multiple dotbot plugins → Equivalent run_once scripts +- ✅ Host-specific configs → Template support added +- ✅ Complex installation → Automated in run scripts +- ✅ Git submodules → Pre-read hook handles this +- ✅ Multiple hosts (air, lakka, tunkki, s) → Detected and flagged + +Everything from your dotbot setup has been accounted for in the chezmoi migration! + +## Final Notes + +- **Take your time**: This is a significant migration +- **Test thoroughly**: Use `--dry-run` extensively +- **Backup everything**: The migration script creates backups +- **Iterate**: You can always refine the migration later +- **Have fun**: Chezmoi offers powerful features to explore! + +Good luck with your migration! 🚀 diff --git a/docs/CHEZMOI-QUICK-REFERENCE.md b/docs/CHEZMOI-QUICK-REFERENCE.md new file mode 100644 index 0000000..978908c --- /dev/null +++ b/docs/CHEZMOI-QUICK-REFERENCE.md @@ -0,0 +1,432 @@ +# Chezmoi Quick Reference + +Quick reference guide for common chezmoi operations with your dotfiles. + +## Installation + +```bash +# Fresh install on a new machine +sh -c "$(curl -fsLS get.chezmoi.io/lb)" -- init --apply ivuorinen + +# Or if chezmoi is already installed +chezmoi init --apply ivuorinen +``` + +## Daily Workflow + +### Making Changes + +```bash +# Edit a dotfile (opens in $EDITOR) +chezmoi edit ~/.bashrc + +# Or edit directly and add to chezmoi +vim ~/.bashrc +chezmoi add ~/.bashrc + +# See what would change +chezmoi diff + +# Apply changes +chezmoi apply +``` + +### Syncing Across Machines + +```bash +# On machine A: commit and push changes +cd $(chezmoi source-path) +git add -A +git commit -m "Update configuration" +git push + +# On machine B: pull and apply changes +chezmoi update +# This is equivalent to: +# cd $(chezmoi source-path) && git pull && chezmoi apply +``` + +## Common Commands + +### Viewing and Inspecting + +```bash +# See what chezmoi would do +chezmoi diff + +# List all managed files +chezmoi managed + +# List unmanaged files +chezmoi unmanaged + +# Show the source path +chezmoi source-path + +# Show what a file would look like after templating +chezmoi cat ~/.bashrc + +# Show available template data +chezmoi data +``` + +### Adding and Removing Files + +```bash +# Add a file +chezmoi add ~/.newfile + +# Add a file as a template +chezmoi add --template ~/.newfile + +# Add a directory recursively +chezmoi add --recursive ~/.config/newapp + +# Add with autodetection (templates, scripts, etc.) +chezmoi add --autotemplate ~/.newfile + +# Stop managing a file (removes from chezmoi) +chezmoi forget ~/.oldfile + +# Remove a file from both chezmoi and home directory +chezmoi remove ~/.oldfile +``` + +### Working with Templates + +```bash +# Execute a template expression +chezmoi execute-template "{{ .chezmoi.hostname }}" + +# Edit template data +chezmoi edit-config + +# Verify templates +chezmoi verify +``` + +### Applying Changes + +```bash +# Apply all changes +chezmoi apply + +# Apply with verbose output +chezmoi apply -v + +# Dry run (show what would happen) +chezmoi apply --dry-run -v + +# Force apply (re-runs scripts) +chezmoi apply --force + +# Apply only specific files +chezmoi apply ~/.bashrc ~/.zshrc +``` + +### Updating from Repository + +```bash +# Update dotfiles from repository +chezmoi update + +# Update but don't apply +cd $(chezmoi source-path) && git pull + +# Update with interactive merge +chezmoi update --interactive +``` + +## File Naming Conventions + +### Basic Prefixes + +| Source File | Destination | Description | +|------------|-------------|-------------| +| `dot_bashrc` | `~/.bashrc` | Dot file | +| `dot_config/` | `~/.config/` | Dot directory | +| `private_dot_ssh/` | `~/.ssh/` | Private directory (0700) | +| `executable_dot_local/bin/script` | `~/.local/bin/script` | Executable | +| `symlink_dot_vim` | `~/.vim` | Symlink | +| `readonly_dot_file` | `~/.file` | Read-only | + +### Template Files + +| Source File | Description | +|------------|-------------| +| `dot_bashrc.tmpl` | Template file | +| `dot_config/fish/config.fish.tmpl` | Nested template | + +### Scripts + +| Script Name | When It Runs | +|------------|--------------| +| `run_once_before_*.sh` | Once before applying | +| `run_once_after_*.sh` | Once after applying | +| `run_before_*.sh` | Every time before applying | +| `run_after_*.sh` | Every time after applying | +| `run_onchange_*.sh` | When script content changes | + +## Template Syntax + +### Basic Variables + +```go +// Hostname +{{ .chezmoi.hostname }} + +// Username +{{ .chezmoi.username }} + +// Operating system +{{ .chezmoi.os }} + +// Home directory +{{ .chezmoi.homeDir }} + +// Source directory +{{ .chezmoi.sourceDir }} + +// Custom data from .chezmoi.yaml +{{ .is_macos }} +{{ .is_linux }} +``` + +### Conditionals + +```go +{{ if eq .chezmoi.hostname "air" }} +# Configuration for air +{{ else if eq .chezmoi.hostname "lakka" }} +# Configuration for lakka +{{ else }} +# Default configuration +{{ end }} + +{{ if .is_macos }} +# macOS-specific +{{ end }} + +{{ if and .is_macos (eq .chezmoi.hostname "air") }} +# macOS on air +{{ end }} +``` + +### Loops + +```go +{{ range $key, $value := .data }} +{{ $key }}: {{ $value }} +{{ end }} +``` + +### Including Files + +```go +{{ include "template-file.txt" }} +{{ includeTemplate "template-file.tmpl" }} +``` + +## Host-Specific Configuration + +### Method 1: Template Conditionals + +In `dot_bashrc.tmpl`: +```bash +# Common configuration +export PATH="$HOME/.local/bin:$PATH" + +{{ if eq .chezmoi.hostname "air" }} +# air-specific configuration +export WORK_DIR="$HOME/Work" +{{ end }} + +{{ if eq .chezmoi.hostname "lakka" }} +# lakka-specific configuration +export WORK_DIR="$HOME/Projects" +{{ end }} +``` + +### Method 2: Separate Files with Symlinks + +Use `.chezmoiignore` to exclude files for specific hosts: + +``` +{{ if ne .chezmoi.hostname "air" }} +dot_config/air-specific-app/ +{{ end }} + +{{ if ne .chezmoi.hostname "lakka" }} +dot_config/lakka-specific-app/ +{{ end }} +``` + +## Working with Secrets + +### Environment Variables + +```go +{{ .Env.MY_SECRET }} +``` + +### 1Password + +```go +{{ (onepasswordDocument "my-secret").content }} +{{ (onepasswordItemFields "my-item").password.value }} +``` + +### External Commands + +```go +{{ output "op" "read" "op://vault/item/field" }} +``` + +## Troubleshooting + +### Check Configuration + +```bash +# Verify chezmoi is working correctly +chezmoi doctor + +# Check state +chezmoi verify + +# See detailed info +chezmoi data +``` + +### Debug Templates + +```bash +# See what a template would produce +chezmoi cat ~/.bashrc + +# Execute a template +chezmoi execute-template "{{ .chezmoi.hostname }}" + +# Verbose output +chezmoi apply -v +``` + +### Fix Issues + +```bash +# Re-apply everything +chezmoi apply --force + +# Reset state (dangerous!) +chezmoi state reset + +# Clear cache +rm -rf $(chezmoi source-path)/.git/chezmoi-* +``` + +### Common Errors + +**Error: template: ... undefined variable** +- Check template syntax +- Verify data with `chezmoi data` + +**Error: entry ... is not in source state** +- File not added to chezmoi: `chezmoi add ` + +**Error: ... has been modified since chezmoi last wrote it** +- See changes: `chezmoi diff` +- Re-add: `chezmoi add ` +- Or force apply: `chezmoi apply --force` + +## Useful Aliases + +Add these to your shell configuration: + +```bash +# Chezmoi shortcuts +alias cm='chezmoi' +alias cma='chezmoi apply' +alias cmd='chezmoi diff' +alias cme='chezmoi edit' +alias cmu='chezmoi update' +alias cmcd='cd $(chezmoi source-path)' + +# Common workflows +alias cmup='chezmoi git pull && chezmoi apply' +alias cmpush='cd $(chezmoi source-path) && git add -A && git commit && git push' +``` + +## Environment Variables + +```bash +# Change source directory (default: ~/.local/share/chezmoi) +export CHEZMOI_SOURCE_DIR="$HOME/.dotfiles" + +# Change config file location +export CHEZMOI_CONFIG_FILE="$HOME/.config/chezmoi/chezmoi.yaml" + +# Use different editor +export EDITOR="vim" +``` + +## Advanced Usage + +### Using chezmoi as a dotfiles manager + +```bash +# Initialize with local directory +chezmoi init --source ~/.dotfiles + +# Use current directory as source +chezmoi init --source $(pwd) +``` + +### Managing Multiple Machines + +Use data in `.chezmoi.yaml.tmpl`: + +```yaml +data: + is_work_machine: {{ or (eq .chezmoi.hostname "work-laptop") (eq .chezmoi.hostname "work-desktop") }} + is_personal: {{ eq .chezmoi.hostname "personal-laptop" }} +``` + +Then in templates: + +```go +{{ if .is_work_machine }} +# Work configuration +{{ end }} +``` + +### External Files + +```yaml +# .chezmoiexternal.yaml +".config/nvim/lua/plugins": + type: "archive" + url: "https://github.com/user/nvim-config/archive/main.tar.gz" + stripComponents: 1 + refreshPeriod: "168h" +``` + +## Resources + +- [Official Documentation](https://www.chezmoi.io/) +- [User Guide](https://www.chezmoi.io/user-guide/) +- [Template Reference](https://www.chezmoi.io/reference/templates/) +- [Command Reference](https://www.chezmoi.io/reference/commands/) +- [FAQ](https://www.chezmoi.io/faq/) + +## Help + +```bash +# General help +chezmoi help + +# Command-specific help +chezmoi help apply +chezmoi help add + +# Show version +chezmoi --version +``` diff --git a/docs/HOST-SPECIFIC-MIGRATION.md b/docs/HOST-SPECIFIC-MIGRATION.md new file mode 100644 index 0000000..e9a86df --- /dev/null +++ b/docs/HOST-SPECIFIC-MIGRATION.md @@ -0,0 +1,462 @@ +# Host-Specific Configuration Migration + +Guide for migrating host-specific configurations from dotbot to chezmoi. + +## Current Dotbot Structure + +You currently have host-specific configurations in: + +``` +hosts/ +├── air/ +│ ├── base/ +│ ├── config/ +│ └── install.conf.yaml +├── lakka/ +│ ├── base/ +│ ├── config/ +│ └── install.conf.yaml +├── tunkki/ +│ └── install.conf.yaml +└── s/ + └── install.conf.yaml +``` + +## Chezmoi Approaches + +There are three main approaches to handle host-specific configurations in chezmoi: + +### 1. Template Conditionals (Recommended) + +Use if/else conditions in template files. + +**Pros:** +- Single source file for all hosts +- Easy to see all variations +- Less file duplication + +**Cons:** +- Files can get complex with many hosts +- Need `.tmpl` extension + +**Example:** + +Create `dot_bashrc.tmpl`: +```bash +# Common configuration for all hosts +export PATH="$HOME/.local/bin:$PATH" + +{{ if eq .chezmoi.hostname "air" }} +# air-specific +export WORK_DIR="$HOME/Work" +alias air-specific="echo air" +{{ end }} + +{{ if eq .chezmoi.hostname "lakka" }} +# lakka-specific +export WORK_DIR="$HOME/Projects" +alias lakka-specific="echo lakka" +{{ end }} + +# More common configuration +``` + +### 2. Separate Files with .chezmoiignore (Simple) + +Keep separate files per host and ignore the ones that don't apply. + +**Pros:** +- Clean separation +- No template syntax needed +- Easy to maintain + +**Cons:** +- More files to manage +- Some duplication + +**Example:** + +Create multiple version files: +``` +dot_bashrc__air +dot_bashrc__lakka +dot_bashrc__tunkki +``` + +These automatically apply based on hostname. No `.chezmoiignore` needed! + +Or use directories: +``` +dot_config/ +├── app/ +│ ├── config.yaml__air +│ ├── config.yaml__lakka +│ └── config.yaml__default +``` + +### 3. Hybrid Approach (Most Flexible) + +Combine both methods: +- Use templates for files with minor differences +- Use separate files for completely different configs + +## Migration Steps for Your Hosts + +### Step 1: Analyze Each Host + +Review what's different per host: + +```bash +# See what's in each host directory +ls -la hosts/air/ +ls -la hosts/lakka/ +ls -la hosts/tunkki/ +ls -la hosts/s/ + +# Compare configs +diff hosts/air/config/some-app/config hosts/lakka/config/some-app/config +``` + +### Step 2: Choose Strategy Per File + +For each file that differs: + +**Small differences** (few lines): +→ Use templates with conditionals + +**Complete replacement**: +→ Use `filename__hostname` suffix + +**Shared base + host additions**: +→ Use templates with includes or blocks + +### Step 3: Migrate Host Configurations + +#### Example: Fish Configuration + +Your `hosts/air/config/fish/config.fish` differences: + +**Current dotbot way:** +```yaml +# hosts/air/install.conf.yaml +- link: + ~/.config/: + path: hosts/air/config/** +``` + +**New chezmoi way - Option A (Templates):** + +Create `dot_config/fish/config.fish.tmpl`: +```fish +# Common fish configuration +set -gx EDITOR nvim + +{{ if eq .chezmoi.hostname "air" }} +# air-specific config +set -gx WORK_DIR ~/Work +{{ else if eq .chezmoi.hostname "lakka" }} +# lakka-specific config +set -gx WORK_DIR ~/Projects +{{ end }} + +# More common configuration +``` + +**New chezmoi way - Option B (Separate files):** + +Create multiple files: +``` +dot_config/fish/config.fish__air +dot_config/fish/config.fish__lakka +``` + +Chezmoi will automatically use the correct file based on hostname. + +### Step 4: Update .chezmoi.yaml.tmpl + +Add host flags for easier conditionals: + +```yaml +{{- $hostname := .chezmoi.hostname -}} + +data: + hostname: {{ $hostname | quote }} + + # Host-specific flags + is_air: {{ eq $hostname "air" }} + is_lakka: {{ eq $hostname "lakka" }} + is_tunkki: {{ eq $hostname "tunkki" }} + is_s: {{ eq $hostname "s" }} + + # Group flags + is_work: {{ or (eq $hostname "air") (eq $hostname "tunkki") }} + is_personal: {{ or (eq $hostname "lakka") (eq $hostname "s") }} +``` + +Then use simpler conditionals: + +```go +{{ if .is_air }} +# air config +{{ end }} + +{{ if .is_work }} +# All work machines +{{ end }} +``` + +## Specific Migration Examples + +### Example 1: Simple Host-Specific Line + +**Before (dotbot):** +```yaml +# hosts/air/config/app/config +setting=value_for_air + +# hosts/lakka/config/app/config +setting=value_for_lakka +``` + +**After (chezmoi) - Option A:** +``` +# dot_config/app/config.tmpl +{{ if .is_air -}} +setting=value_for_air +{{- else if .is_lakka -}} +setting=value_for_lakka +{{- end }} +``` + +**After (chezmoi) - Option B:** +``` +# dot_config/app/config__air +setting=value_for_air + +# dot_config/app/config__lakka +setting=value_for_lakka +``` + +### Example 2: Mostly Shared with Few Differences + +**Before:** +100 lines shared, 5 lines different per host + +**After (recommended - templates):** +```bash +# dot_config/app/config.tmpl +# ... 50 lines of shared config ... + +{{ if .is_air }} +air_specific_setting=true +{{ else if .is_lakka }} +lakka_specific_setting=true +{{ end }} + +# ... 50 more lines of shared config ... +``` + +### Example 3: Completely Different Configs + +**Before:** +Two totally different config files + +**After (recommended - separate files):** +``` +dot_config/app/config__air +dot_config/app/config__lakka +``` + +### Example 4: Host-Specific Directories + +**Before:** +``` +hosts/air/config/air-only-app/ +hosts/lakka/config/lakka-only-app/ +``` + +**After - Use .chezmoiignore:** +``` +# .chezmoiignore +{{ if ne .chezmoi.hostname "air" }} +dot_config/air-only-app/ +{{ end }} + +{{ if ne .chezmoi.hostname "lakka" }} +dot_config/lakka-only-app/ +{{ end }} +``` + +Then create: +``` +dot_config/air-only-app/ # Only applied on air +dot_config/lakka-only-app/ # Only applied on lakka +``` + +## Testing Host-Specific Configs + +### Before Applying + +```bash +# See what would be applied on this host +chezmoi diff + +# See what a specific file would look like +chezmoi cat ~/.config/fish/config.fish + +# Check template data +chezmoi data | grep hostname +``` + +### Simulating Other Hosts + +```bash +# Test what would be applied on another host +chezmoi execute-template --init --promptString hostname=air "{{ .chezmoi.hostname }}" + +# See what a file would look like on another host +# (This requires manual variable setting in templates) +``` + +### On Another Machine + +```bash +# Initialize and test without applying +chezmoi init --dry-run --verbose ivuorinen + +# Apply with dry-run +chezmoi apply --dry-run -v +``` + +## Migration Script Adjustments + +Add to `migrate-to-chezmoi.sh`: + +```bash +# Migrate host-specific configs +log_info "Processing host-specific configurations..." + +CURRENT_HOST=$(hostname -s) +log_info "Current hostname: $CURRENT_HOST" + +# Prompt for migration strategy +echo "" +log_warning "How do you want to handle host-specific configs?" +echo " 1) Merge into templates (recommended for small differences)" +echo " 2) Keep separate files per host (recommended for large differences)" +echo " 3) Manual (skip automatic migration)" +read -p "Choose (1/2/3): " -r STRATEGY + +if [ "$STRATEGY" = "1" ]; then + log_info "Will merge into templates (requires manual editing after)" + # Create template files + # ... migration logic ... + +elif [ "$STRATEGY" = "2" ]; then + log_info "Creating separate host-specific files..." + + for host in air lakka tunkki s; do + if [ -d "hosts/$host" ]; then + log_info "Processing host: $host" + + # Process host-specific base files + if [ -d "hosts/$host/base" ]; then + for file in hosts/$host/base/*; do + if [ -f "$file" ]; then + filename=$(basename "$file") + move_dotfiles "$file" "dot_${filename}__${host}" "" + fi + done + fi + + # Process host-specific config files + if [ -d "hosts/$host/config" ]; then + for file in hosts/$host/config/*; do + if [ -f "$file" ]; then + filename=$(basename "$file") + dest="dot_config/$(dirname $file)__${host}" + move_dotfiles "$file" "$dest/$(basename $file)" "" + fi + done + fi + fi + done + +else + log_info "Skipping automatic host-specific migration" + log_info "You'll need to manually migrate hosts/ directory" +fi +``` + +## Best Practices + +1. **Start Simple**: Use separate files first, move to templates as you see patterns + +2. **Don't Over-Template**: If a file is completely different per host, use separate files + +3. **Document Hosts**: Add comments in `.chezmoi.yaml.tmpl` explaining each host + +4. **Test Thoroughly**: Test on each host before committing + +5. **Use Host Groups**: Group hosts (work/personal, laptop/desktop) for easier conditionals + +## Checklist + +- [ ] Identify all host-specific files +- [ ] Choose strategy per file (templates vs separate) +- [ ] Update `.chezmoi.yaml.tmpl` with host flags +- [ ] Migrate host-specific base files +- [ ] Migrate host-specific config files +- [ ] Update `.chezmoiignore` for host-specific directories +- [ ] Test on current host +- [ ] Test on other hosts +- [ ] Document which approach was used where +- [ ] Clean up old hosts/ directory + +## Examples from Your Hosts + +Based on your current structure: + +### hosts/air/install.conf.yaml + +```yaml +- link: + ~/.config/: + path: hosts/air/config/** +``` + +**Migration approach:** +- Check what's in `hosts/air/config/` +- If it's app configs, use separate files: `dot_config/app/config__air` +- If it's just a few lines different, merge into templates + +### Common Pattern + +For configurations that need host-specific values but share structure: + +```yaml +# dot_config/app/config.tmpl +# Common settings +port=8080 + +# Host-specific +{{ if .is_air -}} +workspace=/Users/yourname/Work +{{- else if .is_lakka -}} +workspace=/Users/yourname/Projects +{{- end }} + +# More common settings +debug=false +``` + +## Need Help? + +If you're unsure about a specific file: + +1. Check the diff: `diff hosts/air/file hosts/lakka/file` +2. Count different lines +3. If < 20% different → use templates +4. If > 20% different → use separate files + +Remember: You can always refactor later! diff --git a/docs/MIGRATION-DOTBOT-TO-CHEZMOI.md b/docs/MIGRATION-DOTBOT-TO-CHEZMOI.md new file mode 100644 index 0000000..bb5199a --- /dev/null +++ b/docs/MIGRATION-DOTBOT-TO-CHEZMOI.md @@ -0,0 +1,523 @@ +# Migration Guide: Dotbot to Chezmoi + +This guide documents the migration from dotbot to chezmoi for managing dotfiles. + +## Table of Contents + +1. [Why Migrate to Chezmoi?](#why-migrate-to-chezmoi) +2. [Key Differences](#key-differences) +3. [Dotbot to Chezmoi Mapping](#dotbot-to-chezmoi-mapping) +4. [New File Structure](#new-file-structure) +5. [Migration Steps](#migration-steps) +6. [Usage Guide](#usage-guide) +7. [Troubleshooting](#troubleshooting) + +## Why Migrate to Chezmoi? + +Chezmoi offers several advantages over dotbot: + +- **Built-in templating**: Use Go templates for dynamic configuration +- **Secret management**: Native support for password managers (1Password, LastPass, etc.) +- **Cross-platform**: Better support for managing dotfiles across different OS and machines +- **State tracking**: Chezmoi tracks what it manages more precisely +- **Active development**: Regular updates and large community +- **No dependencies**: Single binary, no Python required + +## Key Differences + +### Philosophy + +**Dotbot**: +- Configuration-driven (YAML files) +- Manages symlinks from a source directory +- Plugins extend functionality + +**Chezmoi**: +- Source-state driven +- Copies files to home directory (can also use symlinks) +- Built-in templating for dynamic content +- Manages the entire lifecycle of dotfiles + +### Directory Structure + +**Dotbot**: +``` +~/.dotfiles/ +├── base/ # Files symlinked to ~/.* +├── config/ # Files symlinked to ~/.config/ +├── local/ # Scripts and local files +├── install # Installation script +└── install.conf.yaml # Configuration +``` + +**Chezmoi**: +``` +~/.local/share/chezmoi/ # Source directory +├── .chezmoi.yaml.tmpl # Configuration template +├── .chezmoiignore # Files to ignore +├── run_once_*.sh.tmpl # One-time setup scripts +├── run_*.sh.tmpl # Scripts that run on every apply +├── dot_bashrc # Becomes ~/.bashrc +├── dot_config/ # Becomes ~/.config/ +└── private_dot_ssh/ # Becomes ~/.ssh/ with 0700 +``` + +## Dotbot to Chezmoi Mapping + +### 1. Link Directives + +**Dotbot** (install.conf.yaml): +```yaml +- link: + ~/.bashrc: base/bashrc + ~/.config/fish: config/fish +``` + +**Chezmoi**: +- Files are automatically managed based on their names in the source directory +- `dot_bashrc` → `~/.bashrc` +- `dot_config/fish/` → `~/.config/fish/` +- Use `symlink_` prefix for symlinks: `symlink_dot_vim` → symlinked `~/.vim` + +### 2. Create Directives + +**Dotbot**: +```yaml +- create: + ~/.ssh: + mode: 0700 + ~/.local/bin: +``` + +**Chezmoi**: +- Create a `run_once_after_create-directories.sh.tmpl` script +- Or use `.chezmoitemplates` for reusable directory creation +- Chezmoi automatically creates parent directories + +### 3. Shell Commands + +**Dotbot**: +```yaml +- shell: + - git submodule update --init --recursive + - bash local/bin/dfm install all +``` + +**Chezmoi**: +- Use `run_once_*.sh.tmpl` for one-time setup scripts +- Use `run_*.sh.tmpl` for scripts that run every time +- Use `run_before_*.sh` for scripts that run before applying +- Use `run_after_*.sh` for scripts that run after applying + +### 4. Clean Directives + +**Dotbot**: +```yaml +- clean: + ~/: + ~/.config: + recursive: true +``` + +**Chezmoi**: +- Chezmoi doesn't automatically remove files +- Use `chezmoi unmanaged` to see unmanaged files +- Manually remove or add to `.chezmoiignore` + +### 5. Host-Specific Configuration + +**Dotbot**: +```yaml +# hosts/air/install.conf.yaml +- link: + ~/.config/: + path: hosts/air/config/** +``` + +**Chezmoi**: +- Use templates with conditionals: +``` +{{ if eq .chezmoi.hostname "air" }} +# air-specific content +{{ end }} +``` +- Or use separate files: `dot_config/file.tmpl` with hostname checks + +### 6. Dotbot Plugins + +**dotbot-brew**: +- Replace with `run_once_after_install-packages.sh.tmpl` +- Use `brew bundle install` + +**dotbot-asdf**: +- Chezmoi doesn't have built-in asdf support +- Use `run_once_after_*.sh` scripts to install asdf plugins + +**dotbot-pip/pipx**: +- Use `run_once_after_*.sh` scripts +- Or use chezmoi's external management + +## New File Structure + +### Configuration Files + +#### `.chezmoi.yaml.tmpl` +Main configuration file with template support. Defines: +- Source directory +- Data variables (hostname, OS, custom flags) +- Merge strategy +- Template options +- Git options + +#### `.chezmoiignore` +Files and patterns to ignore when applying dotfiles. Includes: +- Repository management files (.git, .github, etc.) +- Documentation +- Development tools +- Old dotbot configuration + +### Run Scripts + +Scripts follow a naming convention: + +- `run_once_before_*.sh.tmpl`: Runs once before applying (prerequisites) +- `run_once_after_*.sh.tmpl`: Runs once after applying (installation) +- `run_before_*.sh.tmpl`: Runs every time before applying +- `run_after_*.sh.tmpl`: Runs every time after applying +- `run_onchange_*.sh.tmpl`: Runs when file content changes + +### File Naming + +Chezmoi uses special prefixes: + +- `dot_`: Becomes a dot file (`.`) +- `private_`: Sets permissions to 0600 +- `executable_`: Makes file executable +- `symlink_`: Creates a symlink +- `readonly_`: Makes file read-only + +Examples: +- `dot_bashrc` → `~/.bashrc` +- `private_dot_ssh/` → `~/.ssh/` (mode 0700) +- `executable_dot_local/bin/script` → `~/.local/bin/script` (executable) + +## Migration Steps + +### 1. Backup Current Setup + +```bash +# Backup your current dotfiles +cd ~/.dotfiles +git add -A +git commit -m "Backup before chezmoi migration" +git push +``` + +### 2. Install Chezmoi + +```bash +# The new install script will do this automatically +sh -c "$(curl -fsLS get.chezmoi.io/lb)" -- init --apply ivuorinen +``` + +### 3. Initialize Chezmoi with Existing Dotfiles + +```bash +# If you want to test before full migration +chezmoi init --apply ivuorinen + +# Or initialize without applying +chezmoi init ivuorinen +``` + +### 4. Restructure Files (Manual Step) + +You'll need to rename files to follow chezmoi conventions: + +```bash +cd ~/.local/share/chezmoi + +# Rename base files +mv base/bashrc dot_bashrc +mv base/zshrc dot_zshrc +mv base/tmux.conf dot_tmux.conf + +# Move config files +mkdir -p dot_config +mv config/* dot_config/ + +# Move local/bin files +mkdir -p dot_local/bin +for file in local/bin/*; do + mv "$file" "executable_dot_local/bin/$(basename "$file")" +done + +# Move SSH files with proper permissions +mkdir -p private_dot_ssh +mv ssh/* private_dot_ssh/ +``` + +### 5. Convert Host-Specific Configurations + +For files that differ between hosts, use templates: + +```bash +# Instead of hosts/air/config/fish/config.fish +# Create: dot_config/fish/config.fish.tmpl + +{{ if eq .chezmoi.hostname "air" }} +# air-specific configuration +{{ else if eq .chezmoi.hostname "lakka" }} +# lakka-specific configuration +{{ end }} +``` + +### 6. Test the Migration + +```bash +# See what changes chezmoi would make +chezmoi diff + +# Apply changes +chezmoi apply + +# Verify everything works +chezmoi verify +``` + +### 7. Clean Up Old Files + +```bash +# Remove dotbot directories (after confirming everything works) +cd ~/.local/share/chezmoi +rm -rf tools/dotbot tools/dotbot-* +rm install.conf.yaml +rm -rf hosts/ # If fully migrated to templates +``` + +## Usage Guide + +### Basic Commands + +```bash +# Initialize chezmoi with your dotfiles +chezmoi init ivuorinen + +# See what changes would be made +chezmoi diff + +# Apply dotfiles +chezmoi apply + +# Apply with verbose output +chezmoi apply -v + +# Edit a file managed by chezmoi +chezmoi edit ~/.bashrc + +# Add a new file to chezmoi +chezmoi add ~/.newfile + +# Update dotfiles from source +chezmoi update + +# See what files are managed +chezmoi managed + +# See what files are unmanaged +chezmoi unmanaged + +# Re-run scripts +chezmoi apply --force + +# Check for issues +chezmoi doctor +``` + +### Working with Templates + +```bash +# Execute a template +chezmoi execute-template "{{ .chezmoi.hostname }}" + +# See the data available in templates +chezmoi data + +# Edit template data +chezmoi edit-config +``` + +### Managing Secrets + +```bash +# Use with 1Password +chezmoi secret keychain + +# Template with 1Password +{{ (index (onepasswordDocument "my-secret") 0).content }} + +# Use with environment variables +{{ .Env.MY_SECRET }} +``` + +### Updating Dotfiles + +```bash +# Edit source file +chezmoi edit ~/.bashrc + +# Or edit directly and add +vi ~/.bashrc +chezmoi add ~/.bashrc + +# Commit changes +cd $(chezmoi source-path) +git add -A +git commit -m "Update bashrc" +git push + +# On another machine +chezmoi update +``` + +## Troubleshooting + +### Common Issues + +#### 1. File Permissions + +**Problem**: Files have wrong permissions after applying. + +**Solution**: Use prefixes: +```bash +# For 0600 permissions +chezmoi add --template private_dot_ssh/config + +# For 0700 directories +mkdir -p private_dot_ssh +``` + +#### 2. Symlinks Not Working + +**Problem**: Chezmoi copies files instead of symlinking. + +**Solution**: Use `symlink_` prefix: +```bash +chezmoi add --symlink ~/.vim +# This creates symlink_dot_vim in the source directory +``` + +#### 3. Templates Not Rendering + +**Problem**: Template syntax is showing literally in files. + +**Solution**: +- Ensure file has `.tmpl` extension +- Check template syntax +- Verify data with `chezmoi data` + +#### 4. Scripts Not Running + +**Problem**: `run_once_` scripts not executing. + +**Solution**: +- Check script permissions: `chmod +x run_once_*.sh.tmpl` +- Run with force: `chezmoi apply --force` +- Check script order (before/after) + +#### 5. Host-Specific Files Not Applying + +**Problem**: Wrong host configuration applied. + +**Solution**: +- Check hostname: `chezmoi data | grep hostname` +- Verify template conditionals +- Use `.chezmoiignore` for host-specific exclusions + +### Debugging + +```bash +# Verbose output +chezmoi apply -v + +# Very verbose output +chezmoi apply -vv + +# Dry run to see what would happen +chezmoi apply --dry-run -v + +# Check configuration +chezmoi doctor + +# Verify state +chezmoi verify + +# See source directory +chezmoi source-path + +# See what would be applied to a specific file +chezmoi cat ~/.bashrc +``` + +### Migration Checklist + +- [ ] Backup current dotfiles +- [ ] Install chezmoi +- [ ] Create `.chezmoi.yaml.tmpl` +- [ ] Create `.chezmoiignore` +- [ ] Rename files with proper prefixes +- [ ] Convert host-specific configs to templates +- [ ] Create `run_once_before` scripts +- [ ] Create `run_once_after` scripts +- [ ] Test with `chezmoi diff` +- [ ] Apply with `chezmoi apply` +- [ ] Verify everything works +- [ ] Update documentation +- [ ] Clean up old dotbot files +- [ ] Update README.md +- [ ] Test on another machine + +## Additional Resources + +- [Chezmoi Documentation](https://www.chezmoi.io/) +- [Chezmoi Quick Start](https://www.chezmoi.io/quick-start/) +- [Chezmoi User Guide](https://www.chezmoi.io/user-guide/) +- [Chezmoi Template Reference](https://www.chezmoi.io/reference/templates/) +- [Example Dotfiles Using Chezmoi](https://github.com/topics/chezmoi) + +## Notes + +### What Stays the Same + +- Your actual dotfile contents +- Directory structure in home directory +- Git workflow for managing dotfiles +- The `dfm` script functionality (wrapped in run_once scripts) + +### What Changes + +- Installation method (new `install` script) +- Source directory location (`~/.local/share/chezmoi` by default) +- Configuration method (templates instead of YAML) +- File naming (special prefixes) +- No more symlinks by default (unless specified) + +### Benefits of Migration + +1. **Simplicity**: Single binary, no dependencies +2. **Templating**: Dynamic content based on hostname, OS, etc. +3. **Secrets**: Built-in support for password managers +4. **State Management**: Better tracking of what's managed +5. **Cross-platform**: Excellent support for different OSes +6. **Documentation**: Extensive docs and examples +7. **Community**: Active development and support + +## Post-Migration + +After successful migration: + +1. Update your README to reflect chezmoi usage +2. Archive dotbot configuration for reference +3. Document any custom scripts or workflows +4. Test on all your machines +5. Share your experience! diff --git a/install b/install index a8439ef..e077d12 100755 --- a/install +++ b/install @@ -1,38 +1,5 @@ #!/usr/bin/env bash set -e -CONFIG="install.conf.yaml" -DOTBOT_DIR="tools/dotbot" - -DOTBOT_BIN="bin/dotbot" -BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -DOTBOT_BIN_PATH="${BASEDIR}/${DOTBOT_DIR}/${DOTBOT_BIN}" -DOTBOT_HOST="$(hostname -s)" - -cd "${BASEDIR}" -git -C "${DOTBOT_DIR}" submodule sync --quiet --recursive -git submodule update --init --recursive "${DOTBOT_DIR}" - -"${DOTBOT_BIN_PATH}" \ - -d "${BASEDIR}" \ - --plugin-dir=tools/dotbot-asdf \ - --plugin-dir=tools/dotbot-brew \ - --plugin-dir=tools/dotbot-include \ - --plugin-dir=tools/dotbot-pip \ - -c "${CONFIG}" \ - "${@}" - -if [ "${DOTBOT_HOST}" != "" ]; then - DOTBOT_HOST_CONFIG="${BASEDIR}/hosts/${DOTBOT_HOST}/${CONFIG}" - echo "-> Trying if host config can be found: ${DOTBOT_HOST_CONFIG}" - [ -r "$DOTBOT_HOST_CONFIG" ] && [ -f "$DOTBOT_HOST_CONFIG" ] && - echo "(!) Found $DOTBOT_HOST_CONFIG" && - "$DOTBOT_BIN_PATH" \ - -d "$BASEDIR" \ - --plugin-dir=tools/dotbot-asdf \ - --plugin-dir=tools/dotbot-brew \ - --plugin-dir=tools/dotbot-include \ - --plugin-dir=tools/dotbot-pip \ - -c "$DOTBOT_HOST_CONFIG" \ - "${@}" -fi +# Install and apply chezmoi +sh -c "$(curl -fsLS get.chezmoi.io/lb)" -- init --apply ivuorinen diff --git a/migrate-to-chezmoi.sh b/migrate-to-chezmoi.sh new file mode 100755 index 0000000..dd816aa --- /dev/null +++ b/migrate-to-chezmoi.sh @@ -0,0 +1,260 @@ +#!/usr/bin/env bash +# Migration helper script: Dotbot to Chezmoi +# This script helps restructure files from dotbot format to chezmoi format +# +# IMPORTANT: Review changes carefully before running! +# It's recommended to run this in a new git branch + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log_info() { + echo -e "${BLUE}→${NC} $1" +} + +log_success() { + echo -e "${GREEN}✓${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}⚠${NC} $1" +} + +log_error() { + echo -e "${RED}✗${NC} $1" +} + +# Check if we're in a git repository +if [ ! -d .git ]; then + log_error "Not a git repository. Please run from your dotfiles directory." + exit 1 +fi + +# Confirm with user +log_warning "This script will restructure your dotfiles for chezmoi." +log_warning "It's STRONGLY recommended to:" +log_warning " 1. Commit all current changes" +log_warning " 2. Create a new branch for migration" +log_warning " 3. Review all changes before merging" +echo "" +read -p "Do you want to continue? (yes/no): " -r +echo +if [[ ! $REPLY =~ ^[Yy][Ee][Ss]$ ]]; then + log_info "Migration cancelled." + exit 0 +fi + +# Create backup branch +log_info "Creating backup branch..." +BACKUP_BRANCH="backup-before-chezmoi-$(date +%Y%m%d-%H%M%S)" +git branch "$BACKUP_BRANCH" +log_success "Created backup branch: $BACKUP_BRANCH" + +# Create migration branch +log_info "Creating migration branch..." +MIGRATION_BRANCH="migrate-to-chezmoi" +git checkout -b "$MIGRATION_BRANCH" 2>/dev/null || git checkout "$MIGRATION_BRANCH" +log_success "Switched to migration branch: $MIGRATION_BRANCH" + +# Function to move and rename files +move_dotfiles() { + local src=$1 + local dest=$2 + local prefix=$3 + + if [ -f "$src" ]; then + mkdir -p "$(dirname "$dest")" + log_info "Moving: $src → $dest" + git mv "$src" "$dest" 2>/dev/null || mv "$src" "$dest" + fi +} + +# Migrate base files (these become dot_ files) +log_info "Migrating base files..." +if [ -d base ]; then + for file in base/*; do + if [ -f "$file" ]; then + filename=$(basename "$file") + # Skip certain files + if [[ "$filename" =~ \.(md|MD)$ ]]; then + continue + fi + move_dotfiles "$file" "dot_${filename}" "" + fi + done + log_success "Base files migrated" +fi + +# Migrate config directory +log_info "Migrating config directory..." +if [ -d config ]; then + # Create dot_config if it doesn't exist + mkdir -p dot_config + + for item in config/*; do + if [ -e "$item" ]; then + basename_item=$(basename "$item") + # Skip certain directories + if [[ "$basename_item" == "homebrew" ]] || [[ "$basename_item" == "shared.sh" ]]; then + log_info "Keeping $item in place (referenced by scripts)" + continue + fi + + log_info "Moving: $item → dot_config/$basename_item" + git mv "$item" "dot_config/$basename_item" 2>/dev/null || mv "$item" "dot_config/$basename_item" + fi + done + log_success "Config files migrated" +fi + +# Migrate local/bin with executable prefix +log_info "Migrating local/bin scripts..." +if [ -d local/bin ]; then + mkdir -p executable_dot_local/bin + + for script in local/bin/*; do + if [ -f "$script" ]; then + basename_script=$(basename "$script") + log_info "Moving: $script → executable_dot_local/bin/$basename_script" + git mv "$script" "executable_dot_local/bin/$basename_script" 2>/dev/null || \ + mv "$script" "executable_dot_local/bin/$basename_script" + fi + done + log_success "Local bin scripts migrated" +fi + +# Migrate local/share +log_info "Migrating local/share..." +if [ -d local/share ]; then + mkdir -p dot_local/share + + for item in local/share/*; do + if [ -e "$item" ]; then + basename_item=$(basename "$item") + log_info "Moving: $item → dot_local/share/$basename_item" + git mv "$item" "dot_local/share/$basename_item" 2>/dev/null || \ + mv "$item" "dot_local/share/$basename_item" + fi + done + log_success "Local share migrated" +fi + +# Migrate local/man +log_info "Migrating local/man..." +if [ -d local/man ]; then + mkdir -p dot_local/man + + for item in local/man/*; do + if [ -e "$item" ]; then + basename_item=$(basename "$item") + log_info "Moving: $item → dot_local/man/$basename_item" + git mv "$item" "dot_local/man/$basename_item" 2>/dev/null || \ + mv "$item" "dot_local/man/$basename_item" + fi + done + log_success "Local man pages migrated" +fi + +# Migrate SSH files with private prefix +log_info "Migrating SSH files..." +if [ -d ssh ]; then + mkdir -p private_dot_ssh + + for item in ssh/*; do + if [ -f "$item" ]; then + basename_item=$(basename "$item") + log_info "Moving: $item → private_dot_ssh/$basename_item" + git mv "$item" "private_dot_ssh/$basename_item" 2>/dev/null || \ + mv "$item" "private_dot_ssh/$basename_item" + fi + done + log_success "SSH files migrated" +fi + +# Clean up empty directories +log_info "Cleaning up empty directories..." +find . -type d -empty -delete 2>/dev/null || true +log_success "Empty directories removed" + +# Create README for old structure +log_info "Creating migration notes..." +cat > MIGRATION-NOTES.md << 'EOF' +# Migration Notes + +This repository has been migrated from dotbot to chezmoi. + +## Old Structure (Dotbot) +``` +~/.dotfiles/ +├── base/ # Files symlinked to ~/.* +├── config/ # Files symlinked to ~/.config/ +├── local/ # Scripts and local files +├── ssh/ # SSH configuration +├── install # Dotbot installation script +└── install.conf.yaml # Dotbot configuration +``` + +## New Structure (Chezmoi) +``` +~/.dotfiles/ (or ~/.local/share/chezmoi) +├── dot_* # Files that become ~/.* +├── dot_config/ # Files that become ~/.config/ +├── executable_dot_local/ # Executable scripts +├── private_dot_ssh/ # SSH files with 0700 permissions +├── run_once_*.sh.tmpl # One-time setup scripts +├── .chezmoi.yaml.tmpl # Chezmoi configuration +└── .chezmoiignore # Files to ignore +``` + +## File Naming Conventions + +- `dot_bashrc` → `~/.bashrc` +- `dot_config/fish/` → `~/.config/fish/` +- `private_dot_ssh/` → `~/.ssh/` (mode 0700) +- `executable_dot_local/bin/script` → `~/.local/bin/script` (executable) +- `symlink_dot_vim` → `~/.vim` (symlink) + +## Next Steps + +1. Review the migration with: `git diff` +2. Test with chezmoi: `chezmoi init --dry-run --verbose $(pwd)` +3. Apply if everything looks good: `chezmoi init --apply $(pwd)` +4. Read the full migration guide: `MIGRATION-DOTBOT-TO-CHEZMOI.md` + +## Backup + +A backup branch was created: `$BACKUP_BRANCH` + +You can switch back with: `git checkout $BACKUP_BRANCH` +EOF + +log_success "Migration notes created: MIGRATION-NOTES.md" + +# Show summary +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +log_success "Migration complete!" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +log_info "Summary:" +echo " • Backup branch: $BACKUP_BRANCH" +echo " • Migration branch: $MIGRATION_BRANCH" +echo "" +log_info "Next steps:" +echo " 1. Review changes: git status && git diff" +echo " 2. Test with chezmoi: chezmoi init --dry-run --verbose \$(pwd)" +echo " 3. Read migration guide: cat MIGRATION-DOTBOT-TO-CHEZMOI.md" +echo " 4. Commit changes: git add -A && git commit -m 'Migrate to chezmoi'" +echo "" +log_warning "IMPORTANT: Review all changes before committing!" +log_warning "Test thoroughly before applying to your home directory!" +echo "" diff --git a/run_once_after_create-directories.sh.tmpl b/run_once_after_create-directories.sh.tmpl new file mode 100644 index 0000000..c9d80ea --- /dev/null +++ b/run_once_after_create-directories.sh.tmpl @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# run_once_after script to create required directories +# This runs once after applying dotfiles + +set -e + +echo "→ Creating required directories..." + +# Create directories with proper permissions +mkdir -p "$HOME/.cache" +mkdir -p "$HOME/.cache/git" +mkdir -p "$HOME/.config" +mkdir -p "$HOME/.local" +mkdir -p "$HOME/.local/bin" +mkdir -p "$HOME/.local/run" +mkdir -p "$HOME/.local/share" +mkdir -p "$HOME/.local/state" +mkdir -p "$HOME/.local/state/tmux" +mkdir -p "$HOME/.local/state/zsh" +mkdir -p "$HOME/Code" + +# Create directories with restricted permissions +mkdir -p "$HOME/.local/share/gnupg" +chmod 0700 "$HOME/.local/share/gnupg" + +mkdir -p "$HOME/.ssh" +chmod 0700 "$HOME/.ssh" + +mkdir -p "$HOME/.ssh/local.d" +chmod 0700 "$HOME/.ssh/local.d" + +echo "✓ Directories created!" diff --git a/run_once_after_install-packages.sh.tmpl b/run_once_after_install-packages.sh.tmpl new file mode 100644 index 0000000..994ae1f --- /dev/null +++ b/run_once_after_install-packages.sh.tmpl @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# run_once_after script to install packages +# This runs once after applying dotfiles + +set -e + +DOTFILES="{{ .chezmoi.sourceDir }}" +export DOTFILES + +# Source shared configuration +if [ -f "$DOTFILES/config/shared.sh" ]; then + source "$DOTFILES/config/shared.sh" +fi + +# Source message helper if available +if [ -f "$DOTFILES/local/bin/msgr" ]; then + source "$DOTFILES/local/bin/msgr" +fi + +echo "→ Installing packages..." + +{{- if .is_macos }} +# Install Homebrew packages +if [ -f "$DOTFILES/config/homebrew/Brewfile" ]; then + echo "→ Installing Homebrew packages..." + brew bundle install --file="$DOTFILES/config/homebrew/Brewfile" --no-lock || true +fi + +# Setup macOS defaults +if [ -f "$DOTFILES/scripts/install-macos-defaults.sh" ]; then + echo "→ Setting up macOS defaults..." + bash "$DOTFILES/scripts/install-macos-defaults.sh" || true +fi +{{- end }} + +{{- if .is_linux }} +# Install apt packages if on Debian/Ubuntu +if command -v apt &> /dev/null && [ -f "$DOTFILES/tools/apt.txt" ]; then + echo "→ Installing apt packages..." + sudo apt install -y $(grep -vE '^\s*#' "$DOTFILES/tools/apt.txt" | sed -e 's/#.*//' | tr '\n' ' ') || true +fi +{{- end }} + +# Install pipx packages +if command -v pipx &> /dev/null && [ -f "$DOTFILES/tools/requirements-pipx.txt" ]; then + echo "→ Installing pipx packages..." + while IFS= read -r package; do + [ -z "$package" ] || pipx install "$package" || true + done < "$DOTFILES/tools/requirements-pipx.txt" +fi + +echo "✓ Packages installed!" diff --git a/run_once_after_setup-languages.sh.tmpl b/run_once_after_setup-languages.sh.tmpl new file mode 100644 index 0000000..44c0b8f --- /dev/null +++ b/run_once_after_setup-languages.sh.tmpl @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# run_once_after script to setup programming languages and tools +# This runs once after applying dotfiles + +set -e + +DOTFILES="{{ .chezmoi.sourceDir }}" +export DOTFILES + +echo "→ Setting up programming languages and tools..." + +# Install fonts +if [ -f "$DOTFILES/scripts/install-fonts.sh" ]; then + echo "→ Installing fonts..." + bash "$DOTFILES/scripts/install-fonts.sh" || true +fi + +# Install Cargo packages +if command -v cargo &> /dev/null && [ -f "$DOTFILES/scripts/install-cargo-packages.sh" ]; then + echo "→ Installing Cargo packages..." + bash "$DOTFILES/scripts/install-cargo-packages.sh" || true +fi + +# Install Go packages +if command -v go &> /dev/null && [ -f "$DOTFILES/scripts/install-go-packages.sh" ]; then + echo "→ Installing Go packages..." + bash "$DOTFILES/scripts/install-go-packages.sh" || true +fi + +# Install Composer +if [ -f "$DOTFILES/scripts/install-composer.sh" ]; then + echo "→ Installing Composer..." + bash "$DOTFILES/scripts/install-composer.sh" || true +fi + +# Install NVM +if [ ! -d "$HOME/.nvm" ] && command -v curl &> /dev/null; then + echo "→ Installing NVM..." + NVM_VERSION=$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4) + PROFILE=/dev/null bash -c "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh | bash" || true +fi + +# Install latest Node LTS +if [ -s "$HOME/.nvm/nvm.sh" ]; then + echo "→ Installing latest Node LTS..." + source "$HOME/.nvm/nvm.sh" + nvm install --lts --latest-npm --default || true +fi + +# Install NPM packages +if command -v npm &> /dev/null && [ -f "$DOTFILES/scripts/install-npm-packages.sh" ]; then + echo "→ Installing NPM packages..." + bash "$DOTFILES/scripts/install-npm-packages.sh" || true +fi + +# Install GitHub CLI extensions +if command -v gh &> /dev/null && [ -f "$DOTFILES/scripts/install-gh-extensions.sh" ]; then + echo "→ Installing GitHub CLI extensions..." + bash "$DOTFILES/scripts/install-gh-extensions.sh" || true +fi + +# Install z +if [ -f "$DOTFILES/scripts/install-z.sh" ]; then + echo "→ Installing z..." + bash "$DOTFILES/scripts/install-z.sh" || true +fi + +# Install cheat databases +for database in "$DOTFILES"/scripts/install-cheat-*; do + if [ -f "$database" ]; then + echo "→ Installing $(basename "$database")..." + bash "$database" || true + fi +done + +echo "✓ Programming languages and tools setup complete!" diff --git a/run_once_before_install-prerequisites.sh.tmpl b/run_once_before_install-prerequisites.sh.tmpl new file mode 100644 index 0000000..7ce17ce --- /dev/null +++ b/run_once_before_install-prerequisites.sh.tmpl @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# run_once_before script to install prerequisites +# This runs once before applying dotfiles + +set -e + +echo "→ Installing prerequisites..." + +{{- if .is_macos }} +# Install Homebrew if not present +if ! command -v brew &> /dev/null; then + echo "→ Installing Homebrew..." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +fi + +# Install Xcode CLI tools if needed +if ! xcode-select -p &> /dev/null; then + echo "→ Installing Xcode Command Line Tools..." + xcode-select --install || true +fi +{{- end }} + +{{- if .is_linux }} +# Update package manager +if command -v apt &> /dev/null; then + echo "→ Updating apt..." + sudo apt update +fi +{{- end }} + +echo "✓ Prerequisites installed!"