diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/PLAN-dotfiles-separation.org | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/docs/PLAN-dotfiles-separation.org b/docs/PLAN-dotfiles-separation.org new file mode 100644 index 0000000..d835acd --- /dev/null +++ b/docs/PLAN-dotfiles-separation.org @@ -0,0 +1,364 @@ +#+TITLE: Plan — Separate dotfiles from archsetup +#+AUTHOR: Craig Jennings & Claude +#+DATE: 2026-05-13 + +* Overview + +Extract =dotfiles/= from the =archsetup= repo into a standalone repository at +=cjennings.net/archsetup-dotfiles.git=. Archsetup keeps the install logic; +dotfiles live in their own repo and get cloned-then-stowed at install time. + +Add a new =minimal/= stow target (Tier B — TUI-tooled headless server) for +the =DESKTOP_ENV=none= path, alongside the existing =common/=, =dwm/=, and +=hyprland/= targets. + +This is non-disturbing to a running session: all script edits, repo creates, +and VM tests happen out-of-band. The only disruptive step is the final +migration of existing machines (Phase 3), which unstows and re-stows local +dotfile symlinks. + +* Decisions + +| # | Question | Answer | +|----+-------------------------------------------+------------------------------| +| Q1 | Extract dotfiles to a standalone repo? | Yes — now (not deferred) | +| Q2 | Clone target when DOTFILES_REPO is set? | =~/.dotfiles= (convention) | +| Q3 | What does DOTFILES_REPO unset mean? | No opt-out — always clones | +| Q4 | Include a minimal/ headless target? | Yes — Tier B (TUI server) | +| Q5 | Behavior when dotfiles dir isn't a git checkout? | Error out | + +** Rationale notes + +- *Q1 = now.* Folds the extraction into this work rather than deferring it. + Adds steps (create new repo, push content, update default URL) but + produces a clean end-state in one pass. +- *Q3 = no opt-out.* DOTFILES_REPO always has a default value (the new + cjennings.net URL). The =DESKTOP_ENV=none= path stows =minimal/= rather + than "no dotfiles at all". Simpler than supporting a SKIP_DOTFILES flag. +- *Q4 = Tier B.* The headless-tooled server: shells + git + tmux + ssh + + TUI apps (btop, htop, mc, ranger, tickrs, topgrade, wavemon) + the + =.local/bin/= utility scripts. Craig runs TUI apps on personal servers + regularly, so the "bare-bones" tier (Tier A) would feel sparse. +- *Q5 = error out.* Strict. If the dotfiles dir somehow isn't a git + checkout after archsetup's clone (manually modified, tarball extraction), + archsetup refuses to proceed rather than silently skipping the + =git restore= cleanup step. Prefers loud failure over surprising state. + +* Target tree — minimal/ + +The =minimal/= directory is a *standalone* stow target. It does NOT depend +on =common/=; everything universal that =common/= ships is duplicated into +=minimal/=. Drift between the two is a risk; a later refactor of =common/= +to drop GUI bits (so =common/= itself becomes headless-safe) would +eliminate the duplication, but that's out of scope here. + +#+begin_example +minimal/ +├── .bash_logout +├── .bash_profile +├── .bashrc +├── .bashrc.d/ # 6 files: aliases, emacs, fzf, git, media, utilities +├── .config/ +│ ├── btop/ # TUI system monitor +│ ├── htop/ # TUI system monitor +│ ├── mc/ # TUI file manager +│ ├── ranger/ # TUI file manager +│ ├── tickrs/ # TUI stock ticker +│ ├── topgrade.toml # CLI updater +│ ├── user-dirs.dirs +│ ├── user-dirs.locale +│ └── wavemon/ # TUI WiFi monitor +├── .gitconfig # real config (templating later, in open-source cleanup) +├── .gitignore # user's global gitignore +├── .hushlogin +├── .local/ +│ └── bin/ # full .local/bin/ from common/ — all CLI/TUI scripts +│ # (notify, ifinstalled, et/em/ec, ssh-createkeys, +│ # refresharchkeys, decryptfile/encryptfile, dab, +│ # timezone-set, etc.) +├── .profile +├── .profile.d/ # 4 files: auto-tmux-session, browser, display, framework +├── .ssh/ # 4 items: config, decrypt_ssh, set_perms, ssh.tar.gz.gpg +├── .stow-global-ignore +├── .stow-local-ignore +├── .tmux.conf +├── .zshrc +└── .zshrc.d/ # 7 files: aliases, arch-downgrade, emacs, fzf, git, media, utilities +#+end_example + +** Excluded from minimal/ + +| Category | Reason | +|-------------------------+-------------------------------------------------------| +| Mail configs | =.mbsyncrc=, =.msmtprc=, =.authcode=, =.authinfo.gpg= — credential-bearing, separate open-source-release cleanup | +| GTK / Qt theming | =.gtkrc-2.0=, =qt5ct/=, =qt6ct/=, =gtk-3.0/= — GUI-only | +| X11 only | =.Xmodmap=, =.xscreensaver=, =sxhkd/=, =rofi/=, =feh/= | +| GUI media tools | =audacious/=, =calibre/=, =mpv/=, =zathura/= — GUI-only; some credential-bearing | +| Daemons w/ creds | =mpd/= (personal =~cjennings/= paths), =transmission/= (bcrypt RPC pass), =yt-dlp/= (email in config) — separate cleanup | +| Notification / fonts | =dunst/=, =fontconfig/=, =mimeapps.list= — GUI-related | +| Personal dirs | =documents/=, =music/=, =pictures/= — content, not config | + +** SSH and GPG availability + +Confirmed both are installed regardless of =DESKTOP_ENV=: + +- =openssh= is installed in =essential_services()= at =archsetup:1094= — + always runs. +- =gnupg= is installed in =desktop_environment()= at =archsetup:1649= — + also always runs (function name is misleading; it doesn't skip when + =DESKTOP_ENV=none=). + +The =desktop_environment()= function is a misnamed grab-bag — it installs +fonts, qt themes, dunst, gnome-keyring (pure GUI) alongside universal +authentication tools (gnupg, polkit, pass, rbw). On a =DESKTOP_ENV=none= +install today, it ships 50+ pointless GUI packages just to get gnupg. A +later refactor should split universal auth tools out into +=essential_services= or its own step, but it's out of scope for this work. + +GPG key import remains a manual post-install step on every machine +(unchanged from current behavior). The =gnupg= binary is installed; key +import is the user's responsibility (=gpg --import keys.asc= from a +backup or scp from another machine). + +The =.ssh/= directory in dotfiles uses a clever pattern: SSH keys are +GPG-encrypted (=ssh.tar.gz.gpg=) and committed to the repo. After GPG +keys are imported on a new machine, running =decrypt_ssh= recovers the +real SSH keys. Shipping the encrypted archive in =minimal/= is safe. + +* Implementation plan + +Three phases. Each is a discrete unit with a stopping point. + +** Phase 1 — Set up the new repo (~30 min, no archsetup changes) + +*** Step 1.1 — Create the bare repo on cjennings.net + +Manual SSH step (Craig). Probably: + +#+begin_src bash +ssh git@cjennings.net "cd /srv/git && git init --bare archsetup-dotfiles.git" +#+end_src + +Adjust path as needed for Craig's server layout. Confirm the final URL +(default assumption: =https://git.cjennings.net/archsetup-dotfiles.git=). + +*** Step 1.2 — Extract dotfiles/ with filtered history + +In a temp working dir (does NOT touch the live archsetup repo): + +#+begin_src bash +git clone --no-local /home/cjennings/code/archsetup /tmp/extract-dotfiles +cd /tmp/extract-dotfiles +git filter-repo --subdirectory-filter dotfiles/ +#+end_src + +Result: a fresh repo where every commit is rewritten to be about files +inside =dotfiles/= only. Per-file history and attribution preserved across +the 275 commits. + +If =git-filter-repo= isn't installed, =pacman -S git-filter-repo= or +=pip install --user git-filter-repo=. + +*** Step 1.3 — Add minimal/ tree + +In =/tmp/extract-dotfiles/=: + +1. Create =minimal/= directory. +2. Populate per the "Target tree" section above. Most files are direct + copies from =common/= (=.bashrc=, =.profile=, etc.) plus the + curated TUI app configs from =common/.config/=. +3. The =.local/bin/= is the full =common/.local/bin/= directory copied + over verbatim — every CLI/TUI script. +4. Commit: =feat: add minimal/ stow target for headless installs=. + +*** Step 1.4 — Push to the new remote + +#+begin_src bash +git remote add origin https://git.cjennings.net/archsetup-dotfiles.git +git push -u origin main +#+end_src + +**Stops here.** =archsetup-dotfiles.git= exists at cjennings.net with +=common/=, =dwm/=, =hyprland/=, =minimal/=. Archsetup itself unchanged. No +risk to running session or test infra. + +** Phase 2 — Wire archsetup to the new repo (~1-2 hours + 40 min VM test) + +*** Step 2.1 — Add config keys to archsetup.conf.example + +Under the existing "Git Repositories" block (after the =dwm_repo= / +=hyprland_repo= entries): + +#+begin_example +# Dotfiles +DOTFILES_REPO=https://git.cjennings.net/archsetup-dotfiles.git +DOTFILES_BRANCH=main +DOTFILES_DIR=$HOME/.dotfiles +#+end_example + +Document that the user's repo must contain =common/= plus =dwm/=, +=hyprland/=, and/or =minimal/= subdirs that stow cleanly to =~=. + +*** Step 2.2 — Update archsetup script + +Edits to =/home/cjennings/code/archsetup/archsetup=: + +1. *Read config* (around line 114-122): map =DOTFILES_REPO= / =DOTFILES_BRANCH= + / =DOTFILES_DIR= env vars to lowercase script variables. +2. *Set defaults* (around line 136-146, alongside =dwm_repo= etc.): + - =dotfiles_repo="${dotfiles_repo:-https://git.cjennings.net/archsetup-dotfiles.git}"= + - =dotfiles_branch="${dotfiles_branch:-main}"= + - =dotfiles_dir="${dotfiles_dir:-/home/$username/.dotfiles}"= +3. *Validate* (in =validate_config()=, around line 155+): security-only check on + =DOTFILES_REPO= (no leading dash, no whitespace/control chars) — same + pattern as the existing =*_REPO= keys after =2c1377b=. +4. *Clone and stow* (in =user_customizations()=, currently around line 882-1010): + - Replace the existing =dotfiles_dir="$user_archsetup_dir/dotfiles"= setup with + =git clone --depth 1 --branch "$dotfiles_branch" "$dotfiles_repo" "$dotfiles_dir"= + (run as the target user with =sudo -u= or =chown= after). + - Per Q5: error out if =[ ! -d "$dotfiles_dir/.git" ]= after the clone. + - Stow target based on =$desktop_env=: + + =dwm= → stow =common/= + =dwm/= + + =hyprland= → stow =common/= + =hyprland/= + + =none= → stow =minimal/= only (NOT =common/=) + - Guard the waybar-battery sed block (currently around line 856-865) + and the =git restore= step (currently around line 896-902) so they + only run on the dwm/hyprland paths. The minimal/ path skips both. + +*** Step 2.3 — Update test infra + +1. =scripts/testing/archsetup-vm.conf=: add =DOTFILES_REPO=/tmp/archsetup-dotfiles-test= + (mirrors the =ARCHSETUP_REPO=/tmp/archsetup-test= pattern that's + already there). +2. =scripts/testing/run-test.sh=: before VM launch, clone (or =cp -r=) the + dotfiles repo to =/tmp/archsetup-dotfiles-test= so the in-VM + =git clone "$dotfiles_repo"= against the local path succeeds. + +*** Step 2.4 — VM test + +=make test= with default =DESKTOP_ENV=hyprland=. Verify: +- Dotfiles clone from the new local path succeeds. +- Stow pattern still works (=common/= + =hyprland/= go to =~/=). +- No regressions from the install pre/post 2026-05-11 cleanup. + +Optionally re-run with =DESKTOP_ENV=none= to validate the new =minimal/= +path. *Recommended* since it's the new code path. + +**Stops here.** =dotfiles/= is still in the archsetup repo. Local machines +(this one, ratio, velox) are still using =~/.config/foo → ~/code/archsetup/dotfiles/foo= +symlinks — *no migration done yet*. Running session safe. + +** Phase 3 — Cleanup and migration (~15 min per machine, blocks on migration) + +*** Step 3.1 — Migrate this machine + +#+begin_src bash +cd ~/code/archsetup +make unstow hyprland # remove symlinks pointing at archsetup/dotfiles/ +git clone https://git.cjennings.net/archsetup-dotfiles.git ~/.dotfiles +make stow hyprland DOTFILES=~/.dotfiles +#+end_src + +*Disturbs the running session briefly* — hyprland config, waybar, foot, +dunst, etc. all get unstowed and re-stowed mid-session. Reload after +(=hyprctl reload=, restart waybar/dunst). Verify nothing visibly broke. + +Repeat for ratio and velox at appropriate times. + +*** Step 3.2 — Remove dotfiles/ from archsetup repo + +Once all local machines are migrated: + +#+begin_src bash +git rm -r dotfiles/ +git commit -m "chore(archsetup): remove dotfiles/ — moved to archsetup-dotfiles repo" +#+end_src + +The directory is no longer needed since archsetup now clones from the +external repo. + +*** Step 3.3 — Update CLAUDE.md + +Update the "Project Structure" and "Makefile Targets" sections to reflect +the new layout. Document the =DOTFILES_REPO=, =DOTFILES_BRANCH=, and +=DOTFILES_DIR= config keys. Note the =DOTFILES== Makefile override. + +* Commit map + +Approximate commit boundaries for each phase. May split or combine as +implementation reveals natural seams. + +** Phase 1 commits (in the new archsetup-dotfiles.git repo) + +| C | Subject | +|-----+-------------------------------------------------------------| +| D1 | initial import — filter-extracted from archsetup | +| D2 | feat: add minimal/ stow target for headless installs | + +** Phase 2 commits (in archsetup repo) + +| C | Subject | +|-----+-------------------------------------------------------------| +| A1 | feat(archsetup): add DOTFILES_REPO config keys + validation | +| A2 | feat(archsetup): clone dotfiles repo, stow per DESKTOP_ENV | +| A3 | refactor(testing): clone dotfiles repo in VM test setup | + +** Phase 3 commits (in archsetup repo) + +| C | Subject | +|-----+-------------------------------------------------------------| +| A4 | chore(archsetup): remove dotfiles/ — moved to archsetup-dotfiles repo | +| A5 | docs: document external dotfiles layout in CLAUDE.md | + +* Open observations / future work + +These come up during the design but are out of scope for this work. + +- *=desktop_environment()= is a misnomer.* It installs both pure-GUI + packages (fonts, qt themes, dunst, gnome-keyring) AND universal + authentication tools (gnupg, polkit, pass, rbw). The latter belong in + =essential_services()= or their own step. On =DESKTOP_ENV=none= installs + today, you get 50+ pointless GUI packages just to get =gnupg=. Worth a + follow-up refactor. +- *Drift between common/ and minimal/.* Both ship =.bashrc=, =.profile=, + =.gitconfig=, etc. If Craig updates one, the other rots. A future + refactor could move all universal files out of =common/= and into a new + shared base, with =common/= becoming "common GUI bits only". Out of scope + here. Could be mitigated short-term by a CI check that errors if the + duplicated files diverge. +- *Personal-info cleanup in shipped configs.* =.gitconfig=, =.ssh/config=, + =.config/yt-dlp/config=, =hyprland.conf:3=, etc. are all flagged in the + open-source-release audit. This work ships =.gitconfig= and =.ssh/config= + as-is (real values, not templated) — the templating belongs in the + broader =[#A] Prepare for GitHub open-source release= cluster, where it + can be done as a single consistent pass. +- *Audit of dotfiles/common/ scripts.* The =[#B] Audit dotfiles/common + directory= task in todo.org has unfinished subtasks for reviewing + =.local/bin/= for unused scripts, orphaned configs, and unused stowed + files. =minimal/= currently includes the full =.local/bin/= directory + verbatim — a pruning pass on that audit would also improve =minimal/=. +- *=common/.config/git/= and global gitignore.* The proposed =minimal/= + ships =.gitconfig= and =.gitignore= at the top level. If Craig has + anything under =.config/git/= worth shipping, add it. + +* Stopping points + +Pick a stopping point for this session: + +1. *Phase 1 only* (~30 min). Get the new repo live, build =minimal/=, push. + Stop. Pick up Phase 2 fresh later. *Recommended* — discrete unit, low + risk, sets foundation. +2. *Phases 1 + 2* (~2-3 hours, includes a VM test). Repo live + archsetup + wired + VM-tested. Stop before migrating local machines. Phase 3 + happens whenever the migration is convenient. +3. *All three phases* (~3-5 hours). Full end-to-end. Long session. + +* Before starting + +Confirm: + +1. New repo URL (default assumption: =https://git.cjennings.net/archsetup-dotfiles.git=). +2. Path on cjennings.net for creating the bare repo (=/srv/git/=? other?). +3. Scope: 1, 2, or 3 above. +4. Anything in the =minimal/= tree that needs adjustment. +5. Anything in the implementation plan that needs adjustment. |
