You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dotctl/dotctl-roadmap.md

187 lines
7.0 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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 `<path>.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 <repo-url>`
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 <repo-url> ~/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 <repo>`. 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 <path>`
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 (12 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 (23 days)
Phase 2.2 → install.sh script (1 day)
Phase 3.x → status, list, remove (23 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 <url>` 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