# dotctl — Fresh Machine Bootstrap Roadmap ## Goal A single command on a fresh machine should clone a dotfiles repo and fully apply it: ```bash dotctl apply https://github.com/user/dotfiles.git ``` No manual git clone. No rebuilding from source. No multi-step dance. --- ## Current State Summary | Capability | Status | |---|---| | `init` / `add` / `link` core loop | ✅ Working | | Symlink creation | ✅ Working | | Config stored at `~/dotfiles/dotctl/config.yaml` | ✅ Working | | `sync` command (pull + push) | ⚠️ Unstable — README warns against it | | Bootstrap from existing repo | ❌ Missing | | Conflict detection on `link` | ❌ Missing | | Idempotent `link` (safe to re-run) | ❌ Missing | | One-liner install script | ❌ Missing | | Templating / per-machine config | ❌ Not in scope (yet) | | Secret management | ❌ Not in scope (yet) | --- ## Phase 1 — Fix the Foundation (prerequisite for everything) These are bugs/gaps that block safe automation. ### 1.1 Make `link` idempotent **Problem:** Running `link` a second time likely fails or clobbers without warning if a target path already exists. **Fix:** Before creating each symlink, check the target: - If missing → create symlink (happy path) - If already a symlink pointing to the correct source → skip, log "already linked" - If already a symlink pointing somewhere else → warn, skip unless `--overwrite` flag passed - If a real file/directory exists → back it up to `.dotctl.bak`, then link (or skip with `--no-backup`) **Files to touch:** `cmd/link.go` **Acceptance criteria:** Running `dotctl link` twice on a clean setup produces no errors and no duplicate symlinks. --- ### 1.2 Fix or remove `sync` **Problem:** The README actively warns against `sync`. It should either be fixed or removed so it doesn't cause data loss for new users. **Decision to make (pick one):** - **Option A — Fix it:** Proper git pull → detect conflicts → commit changed files → push. Use `go-git` for in-process git operations instead of shelling out. - **Option B — Remove it:** Drop `sync`, document that users should manage the `~/dotfiles` directory as a normal git repo. Add a note in README pointing to how to do `git pull` and then `dotctl link`. Recommendation: **Option B** now, **Option A** later in Phase 3. It's safer to remove a broken command than ship a half-working one. --- ### 1.3 Validate that config round-trips cleanly **Problem:** The tracked file list lives in `~/dotfiles/dotctl/config.yaml`. On a fresh machine, this file needs to already exist in the cloned repo for bootstrap to work. Need to confirm this file is actually committed and not gitignored. **Fix:** Add a note in `.gitignore` explicitly *not* ignoring `dotctl/config.yaml`. Add a check in `init` that warns if the config file would be gitignored. --- ## Phase 2 — The Bootstrap Command (core deliverable) ### 2.1 `dotctl apply ` This is the main feature. It should: 1. Check if `~/dotfiles` already exists - If yes and it's a git repo → `git pull` (or prompt user) - If yes but not a git repo → error with clear message - If no → `git clone ~/dotfiles` 2. Read `~/dotfiles/dotctl/config.yaml` (fail clearly if not found — tells user their repo isn't set up for dotctl) 3. Run the equivalent of `dotctl link` with idempotent behavior from Phase 1.1 4. Print a summary: N linked, M skipped, K backed up **Flags:** - `--overwrite` — overwrite existing files instead of backing up - `--dry-run` — print what would happen without touching the filesystem - `--no-backup` — skip backups, just skip conflicts **Files to touch:** New `cmd/apply.go` **Acceptance criteria:** On a machine with nothing but Go installed and git in PATH, running `dotctl apply https://github.com/user/dotfiles.git` produces a fully linked dotfiles setup. --- ### 2.2 One-liner install + apply script The bootstrap UX needs to work before `dotctl` itself is installed. Options: **Option A — `go install` (simplest):** ```bash go install github.com/Marcusk19/dotctl@latest && dotctl apply https://github.com/user/dotfiles.git ``` Requires Go on the machine. Works great for developer setups. **Option B — Shell script + prebuilt binary:** ```bash curl -fsSL https://raw.githubusercontent.com/Marcusk19/dotctl/main/install.sh | bash -s -- https://github.com/user/dotfiles.git ``` The script: detects OS/arch → downloads the correct binary from GitHub releases → runs `dotctl apply `. No Go required. Recommendation: **ship both**. `go install` for devs, the curl script for fresh machines where Go may not be present. goreleaser is already set up, so binary releases exist — just need the install script. **Files to add:** `install.sh` in repo root --- ## Phase 3 — Quality of Life (after core bootstrap works) ### 3.1 Fix `sync` properly With `go-git` as a dependency (or shelling to git with proper error handling): - `dotctl sync` → pull remote changes, re-run link, push any local changes - Detect and surface merge conflicts clearly instead of silently failing --- ### 3.2 `dotctl status` Shows current state of all tracked files: ``` nvim → ~/.config/nvim [linked ✓] zshrc → ~/.zshrc [linked ✓] kitty → ~/.config/kitty [CONFLICT — real file exists] tmux.conf → ~/.tmux.conf [broken symlink] ``` --- ### 3.3 `dotctl remove ` Removes a tracked file from the manifest and optionally replaces the symlink with the real file. --- ### 3.4 `dotctl list` Prints all tracked entries from config — useful for auditing before running `apply` on a new machine. --- ## Phase 4 — Advanced (optional, post-stabilization) | Feature | Notes | |---|---| | Per-machine profiles | Tag entries in config with `profiles: [work, home]`; pass `--profile` to `apply` | | Templating | Go `text/template` over files with a `.tmpl` extension — render before linking | | Secret redaction | Warn when a tracked file contains patterns that look like secrets (API keys, tokens) | | XDG-aware paths | Auto-resolve `$XDG_CONFIG_HOME` instead of hardcoding `~/.config` | --- ## Implementation Order ``` Phase 1.1 → idempotent link (1–2 days) Phase 1.2 → remove/fix sync (0.5 days) Phase 1.3 → config gitignore audit (0.5 days) Phase 2.1 → dotctl apply command (2–3 days) Phase 2.2 → install.sh script (1 day) Phase 3.x → status, list, remove (2–3 days) Phase 4.x → profiles, templating (future) ``` --- ## Definition of Done (for "point at a repo and go") - [ ] `install.sh` downloads the correct binary for current OS/arch - [ ] `dotctl apply ` clones repo if not present, reads config, links all tracked files - [ ] Running `apply` twice produces no errors - [ ] Existing files are backed up, not silently overwritten - [ ] `--dry-run` works and shows exactly what would happen - [ ] README updated with the new one-liner bootstrap flow - [ ] At least one end-to-end test that: clones a test dotfiles repo → runs apply → asserts symlinks exist